@task-handoff/control-plane 0.0.10 → 0.0.12

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 (74) hide show
  1. package/dist/cli.js +2 -2
  2. package/package.json +1 -1
  3. package/ui/assets/{CodeBlockNode-BtbYq2wp.js → CodeBlockNode-xSpWesFO.js} +1 -1
  4. package/ui/assets/{Tooltip-DaClvWTo.js → Tooltip-D4LSWu33.js} +1 -1
  5. package/ui/assets/{abnfDiagram-VRR7QNED-Bnx1sOU8.js → abnfDiagram-VRR7QNED-BhcSZPmk.js} +1 -1
  6. package/ui/assets/{arc-BETG8i7o.js → arc-BRh9s-qs.js} +1 -1
  7. package/ui/assets/{architectureDiagram-ZJ3FMSHR-DWBnI9pW.js → architectureDiagram-ZJ3FMSHR-DQAt7_hn.js} +1 -1
  8. package/ui/assets/{blockDiagram-677ZJIJ3-CtKmezxj.js → blockDiagram-677ZJIJ3-Ctv37W0M.js} +1 -1
  9. package/ui/assets/{c4Diagram-LMCZKHZV-BCgo3sMu.js → c4Diagram-LMCZKHZV-DJB8beKt.js} +1 -1
  10. package/ui/assets/channel-DQppH5aL.js +1 -0
  11. package/ui/assets/{chunk-2Q5K7J3B-8bQLBwio.js → chunk-2Q5K7J3B-BB-YQBxL.js} +1 -1
  12. package/ui/assets/{chunk-32BRIVSS-BBX04KKY.js → chunk-32BRIVSS-Cn2xqeqv.js} +1 -1
  13. package/ui/assets/{chunk-5VM5RSS4-CKzoR0fj.js → chunk-5VM5RSS4--7ZHKCdY.js} +1 -1
  14. package/ui/assets/{chunk-EX3LRPZG-C-bOPNX9.js → chunk-EX3LRPZG-B3BEX1FY.js} +1 -1
  15. package/ui/assets/{chunk-JWPE2WC7-DTjdDpO8.js → chunk-JWPE2WC7-Dtp_583_.js} +1 -1
  16. package/ui/assets/{chunk-MOJQB5TN-DGkPzQVe.js → chunk-MOJQB5TN-aQQZ-3Gt.js} +1 -1
  17. package/ui/assets/{chunk-RYQCIY6F-CP2_tuE_.js → chunk-RYQCIY6F-nzbO-mZH.js} +1 -1
  18. package/ui/assets/{chunk-V7JOEXUC-DE7voctE.js → chunk-V7JOEXUC-Dip91Oib.js} +1 -1
  19. package/ui/assets/{chunk-VR4S4FIN-Drq1aHiP.js → chunk-VR4S4FIN-EkbomQX_.js} +1 -1
  20. package/ui/assets/{chunk-XXDRQBXY-DV-TLV2r.js → chunk-XXDRQBXY-C4Khy9qm.js} +1 -1
  21. package/ui/assets/classDiagram-OUVF2IWQ-BSAxJ_Rj.js +1 -0
  22. package/ui/assets/classDiagram-v2-EOCWNBFH-BSAxJ_Rj.js +1 -0
  23. package/ui/assets/{cose-bilkent-JH36ORCC-U_ZZIr4Y.js → cose-bilkent-JH36ORCC-De_U5e_m.js} +1 -1
  24. package/ui/assets/{cynefin-VYW2F7L2-CrvSF7q0.js → cynefin-VYW2F7L2-CWE68x6G.js} +1 -1
  25. package/ui/assets/{cynefinDiagram-TSTJHNR4-C_5zQvfh.js → cynefinDiagram-TSTJHNR4-BaR8EhcK.js} +1 -1
  26. package/ui/assets/{dagre-VKFMJZFB-BhacgQWw.js → dagre-VKFMJZFB-CyBCMj5-.js} +1 -1
  27. package/ui/assets/{diagram-FQU43EPY-6RgxC-hG.js → diagram-FQU43EPY-f73YSBz2.js} +1 -1
  28. package/ui/assets/{diagram-G47NLZAW-0ocEt4Ru.js → diagram-G47NLZAW-BKsIOk_T.js} +1 -1
  29. package/ui/assets/{diagram-NH7WQ7WH-BfhonPGi.js → diagram-NH7WQ7WH-PISpxSfV.js} +1 -1
  30. package/ui/assets/{diagram-OA4YK3LP-DnlBKcCE.js → diagram-OA4YK3LP-CuFP1oad.js} +1 -1
  31. package/ui/assets/{diagram-WEI45ONY-DhL2ScOV.js → diagram-WEI45ONY-DYRp-9BM.js} +1 -1
  32. package/ui/assets/{ebnfDiagram-CCIWWBDH-CwoEsoFo.js → ebnfDiagram-CCIWWBDH-DFUngbI3.js} +1 -1
  33. package/ui/assets/{erDiagram-Q63AITRT-CZfuwWRj.js → erDiagram-Q63AITRT-CSucT73y.js} +1 -1
  34. package/ui/assets/{flowDiagram-23GEKE2U-BEa-RrCH.js → flowDiagram-23GEKE2U-Dt6ZmvQK.js} +1 -1
  35. package/ui/assets/{ganttDiagram-NO4QXBWP-CBwdmt9r.js → ganttDiagram-NO4QXBWP-CzftFpEh.js} +1 -1
  36. package/ui/assets/{gitGraphDiagram-IHSO6WYX-DkMwW949.js → gitGraphDiagram-IHSO6WYX-D3vC5xgi.js} +1 -1
  37. package/ui/assets/{index-f6RGMlh-.css → index-D6ZJbMJL.css} +1 -1
  38. package/ui/assets/{index-88I7hjag.js → index-gSVObeDz.js} +105 -105
  39. package/ui/assets/{index10-DtQMxj68.js → index10-CtMEVXU2.js} +1 -1
  40. package/ui/assets/{index11-0xoJVeZ_.js → index11-GxUlhpED.js} +1 -1
  41. package/ui/assets/{index5-BEML6mMT.js → index5-12CZvQ5H.js} +1 -1
  42. package/ui/assets/{index6-CYF4i1d0.js → index6--Pp0L0zd.js} +1 -1
  43. package/ui/assets/{index7-DR_NMpdQ.js → index7-QvuSxOKc.js} +1 -1
  44. package/ui/assets/{index8-BCpkngmC.js → index8-Wl2m0AMp.js} +1 -1
  45. package/ui/assets/{infoDiagram-FWYZ7A6U-CE2En-Tp.js → infoDiagram-FWYZ7A6U-D66DGmrK.js} +1 -1
  46. package/ui/assets/{ishikawaDiagram-FXEZZL3T-C1cHFAhT.js → ishikawaDiagram-FXEZZL3T-Ba_aeje2.js} +1 -1
  47. package/ui/assets/{journeyDiagram-5HDEW3XC-BjQO4HDR.js → journeyDiagram-5HDEW3XC-eC0kYTEG.js} +1 -1
  48. package/ui/assets/{kanban-definition-HUTT4EX6-BMOOLKMj.js → kanban-definition-HUTT4EX6-D869fMN9.js} +1 -1
  49. package/ui/assets/{linear-B-fYhqGU.js → linear-CPewzNzq.js} +1 -1
  50. package/ui/assets/{mermaid.core-CoQzdwl7.js → mermaid.core-MGwtTjmr.js} +4 -4
  51. package/ui/assets/{mhchem-DFxeNE7M.js → mhchem-Bn80l2bT.js} +1 -1
  52. package/ui/assets/{mindmap-definition-LN4V7U3C-Dv3a5yXr.js → mindmap-definition-LN4V7U3C-DAuFJLjv.js} +1 -1
  53. package/ui/assets/{pegDiagram-2B236MQR-Bzd8DPeB.js → pegDiagram-2B236MQR-TYQdWOfW.js} +1 -1
  54. package/ui/assets/{pieDiagram-ENE6RG2P-DDpJYdAy.js → pieDiagram-ENE6RG2P-CVj0ZoBO.js} +1 -1
  55. package/ui/assets/{quadrantDiagram-ABIIQ3AL-7_2ruXKw.js → quadrantDiagram-ABIIQ3AL-D9TUkawU.js} +1 -1
  56. package/ui/assets/{railroadDiagram-RFXS5EU6-BTRQoKZD.js → railroadDiagram-RFXS5EU6-D_qJS6gh.js} +1 -1
  57. package/ui/assets/{requirementDiagram-TGXJPOKE-JhBE4EBG.js → requirementDiagram-TGXJPOKE-BNSa-iqh.js} +1 -1
  58. package/ui/assets/{sankeyDiagram-HTMAVEWB-CgBeYTHT.js → sankeyDiagram-HTMAVEWB-B4zR6oJk.js} +1 -1
  59. package/ui/assets/{sequenceDiagram-DBY2YBRQ-D_JJkWcz.js → sequenceDiagram-DBY2YBRQ-BjqVkN5f.js} +1 -1
  60. package/ui/assets/{sizeCapture-X5ZJPWSS-c1rpy8mv.js → sizeCapture-X5ZJPWSS-BQfh69_C.js} +1 -1
  61. package/ui/assets/{stateDiagram-2N3HPSRC-Ls4ageXm.js → stateDiagram-2N3HPSRC-D5Ye_6a2.js} +1 -1
  62. package/ui/assets/stateDiagram-v2-6OUMAXLB-BhWoPy2J.js +1 -0
  63. package/ui/assets/{swimlanes-5IMT3BWC-DDVOPRNC.js → swimlanes-5IMT3BWC-EIDdXYZM.js} +2 -2
  64. package/ui/assets/swimlanesDiagram-G3AALYLV-C2HzBn5s.js +8 -0
  65. package/ui/assets/{timeline-definition-FHXFAJF6-gi-T8Dng.js → timeline-definition-FHXFAJF6-Bhc11V11.js} +1 -1
  66. package/ui/assets/{vennDiagram-L72KCM5P-BgsGxfR0.js → vennDiagram-L72KCM5P-wDM5lwZ-.js} +1 -1
  67. package/ui/assets/{wardleyDiagram-EHGQE667-hvmfJ787.js → wardleyDiagram-EHGQE667-C2tf6AoE.js} +1 -1
  68. package/ui/assets/{xychartDiagram-FW5EYKEG-tgj8AqRq.js → xychartDiagram-FW5EYKEG-ChSUv1_2.js} +1 -1
  69. package/ui/index.html +2 -2
  70. package/ui/assets/channel-BF6c4VyU.js +0 -1
  71. package/ui/assets/classDiagram-OUVF2IWQ-Cs7wUF4c.js +0 -1
  72. package/ui/assets/classDiagram-v2-EOCWNBFH-Cs7wUF4c.js +0 -1
  73. package/ui/assets/stateDiagram-v2-6OUMAXLB-CHUderdi.js +0 -1
  74. package/ui/assets/swimlanesDiagram-G3AALYLV-BW6CEcJr.js +0 -8
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var e=require("commander"),t=require("node:path"),s=require("node:fs"),n=require("fastify"),i=require("@fastify/cookie"),a=require("@fastify/websocket"),r=require("zod"),o=require("node:crypto"),d=require("node:util"),c=require("write-file-atomic"),l=require("ws"),p=require("node:http"),u=require("node:net"),m=require("node:stream"),g=require("telegramify-markdown"),h=require("node:fs/promises"),f=require("node:os"),I=require("dingtalk-stream"),y=require("node:child_process");function b(e,t){try{const n=JSON.parse(s.readFileSync(e,"utf8"));if(t&&n.name!==t)return;return"string"==typeof n.version&&n.version.trim()?n.version.trim():void 0}catch{return}}function v(e,s=process.env,n=e){const i=s.TASK_HANDOFF_VERSION?.trim();if(i)return i;const a=n.split("/").at(-1),r=[process.argv[1]?t.dirname(t.resolve(process.argv[1])):void 0,process.cwd()].filter(e=>Boolean(e));for(const s of new Set(r)){let n=s;for(;;){const s=b(t.join(n,"package.json"),e);if(s)return s;const i=t.dirname(n);if(i===n)break;n=i}}let o;for(const e of new Set(r)){let s=e;for(;;){if(a){const e=b(t.join(s,"packages",a,"package.json"),n);if(e)return e}o||=b(t.join(s,"package.json"));const e=t.dirname(s);if(e===s)break;s=e}}return o||"unknown"}const w=20971520,S=512e3,A=41943040,k="ai-session.snapshot",z="ai-session.patch",T="ai-session.removed",C="ai-session.unread.updated",E=r.z.string().trim().min(1).max(80),N=r.z.enum(["running","waiting","idle","failed"]),_=r.z.object({instanceId:r.z.string().trim().min(1).max(160),sessionId:r.z.string().trim().min(1).max(120),unread:r.z.boolean(),sessionUpdatedAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}).strict(),x=r.z.enum(["thinking","tool","editing","approval","responding","unknown"]),O=r.z.object({id:r.z.string().trim().min(1).max(240).optional(),kind:r.z.string().trim().min(1).max(80).optional(),name:r.z.string().trim().min(1).max(120),inputPreview:r.z.string().trim().max(500).optional(),startedAt:r.z.string().datetime().optional()}).strict(),R=r.z.enum(["pending-init","running","interrupted","completed","errored","shutdown","not-found"]),j=r.z.enum(["started","interacted","interrupted"]),D=r.z.object({threadId:r.z.string().trim().min(1).max(240),path:r.z.string().trim().max(4096).optional(),status:R,activity:j.optional(),message:r.z.string().trim().max(1e3).optional(),updatedAt:r.z.string().datetime()}).strict(),M=r.z.object({toolCalls:r.z.number().int().min(0).default(0),edits:r.z.number().int().min(0).default(0),approvals:r.z.number().int().min(0).default(0)}).strict().default({toolCalls:0,edits:0,approvals:0}),P=r.z.object({send:r.z.boolean().optional(),interrupt:r.z.boolean().optional(),approval:r.z.boolean().optional()}).strict(),$=r.z.enum(["image","file"]),q=r.z.object({id:r.z.string().trim().min(1).max(120),kind:$,name:r.z.string().trim().min(1).max(240),mime:r.z.string().trim().min(1).max(120),size:r.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER)}),L=r.z.union([q.extend({source:r.z.object({type:r.z.literal("inline"),encoding:r.z.literal("base64"),data:r.z.string().min(1).max(31457280)}).strict()}).strict().superRefine((e,t)=>{const s="image"===e.kind?w:S;(e.size<=0||("file"===e.kind?e.size>=s:e.size>s))&&t.addIssue({code:r.z.ZodIssueCode.custom,path:["size"],message:`Inline ${e.kind} must be between 1 byte and ${s} bytes.`})}),q.extend({source:r.z.object({type:r.z.literal("runtime-path"),path:r.z.string().trim().min(1).max(4096)}).strict()}).strict()]),U=q.extend({sourceType:r.z.enum(["inline","runtime-path"])}).strict(),F=r.z.union([r.z.object({id:r.z.string().trim().min(1).max(120),kind:$.optional(),source:r.z.object({type:r.z.literal("upload-ref")}).strict().default({type:"upload-ref"})}).strict(),r.z.object({id:r.z.string().trim().min(1).max(120),kind:$,name:r.z.string().trim().min(1).max(240),mime:r.z.string().trim().min(1).max(120),size:r.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),source:r.z.object({type:r.z.literal("runtime-path"),path:r.z.string().trim().min(1).max(4096)}).strict()}).strict()]),B=r.z.enum(["auto","queue","steer","immediate"]),H=r.z.enum(["ask","auto-review","full-access"]),G=r.z.enum(["review","rename","goal","compact"]),W=r.z.object({command:G,argument:r.z.string().trim().max(4e3).optional()}).strict().superRefine((e,t)=>{"rename"!==e.command||e.argument||t.addIssue({code:"custom",path:["argument"],message:"Rename requires a thread name."}),"review"!==e.command&&"compact"!==e.command||!e.argument||t.addIssue({code:"custom",path:["argument"],message:`${e.command} does not accept an argument.`})}),V=r.z.object({command:G,value:r.z.string().max(4e3).optional(),turnId:r.z.string().trim().min(1).max(240).optional()}).strict();r.z.enum(["skill","app","plugin"]);const K=r.z.object({name:r.z.string().trim().min(1).max(240),path:r.z.string().trim().min(1).max(4096)}),J=r.z.discriminatedUnion("kind",[K.extend({kind:r.z.literal("skill"),path:r.z.string().trim().min(1).max(4096).refine(e=>e.startsWith("/"),"Skill paths must be absolute.")}).strict(),K.extend({kind:r.z.literal("app"),path:r.z.string().trim().min(7).max(4096).regex(/^app:\/\/[^\s/]+$/,"App paths must use app://<id>.")}).strict(),K.extend({kind:r.z.literal("plugin"),path:r.z.string().trim().min(10).max(4096).regex(/^plugin:\/\/[^\s/]+$/,"Plugin paths must use plugin://<id>.")}).strict()]),Q=r.z.array(J).max(20).default([]),Y=r.z.enum(["plugin","skill","file","directory","app"]),X=r.z.object({kind:Y,name:r.z.string().trim().min(1).max(240),description:r.z.string().trim().max(1e3).optional(),path:r.z.string().trim().min(1).max(4096),icon:r.z.string().trim().max(4096).optional()}).strict().superRefine((e,t)=>{"skill"!==e.kind||e.path.startsWith("/")||t.addIssue({code:"custom",path:["path"],message:"Skill paths must be absolute."}),"app"!==e.kind||/^app:\/\/[^\s/]+$/.test(e.path)||t.addIssue({code:"custom",path:["path"],message:"App paths must use app://<id>."}),"plugin"!==e.kind||/^plugin:\/\/[^\s/]+$/.test(e.path)||t.addIssue({code:"custom",path:["path"],message:"Plugin paths must use plugin://<id>."}),"file"!==e.kind&&"directory"!==e.kind||!e.path.startsWith("/")&&!e.path.split(/[\\/]+/).includes("..")||t.addIssue({code:"custom",path:["path"],message:"File and directory paths must be relative to the session cwd."})}),Z=r.z.object({category:r.z.enum(["skills","plugins","apps","files"]),code:r.z.string().trim().min(1).max(160),message:r.z.string().trim().min(1).max(2e3)}).strict(),ee=r.z.object({sessionId:r.z.string().trim().min(1).max(120),providerSessionId:r.z.string().trim().min(1).max(240),cwd:r.z.string().trim().min(1).max(4096),candidates:r.z.array(X).max(1e3).default([]),diagnostics:r.z.array(Z).max(20).default([])}).strict(),te=r.z.object({query:r.z.string().trim().max(240).default("")}).strict(),se=r.z.object({sessionId:r.z.string().trim().min(1).max(120),cwd:r.z.string().trim().min(1).max(4096),query:r.z.string().trim().max(240),requestId:r.z.string().trim().min(1).max(160),candidates:r.z.array(X.refine(e=>"file"===e.kind||"directory"===e.kind,"File search only returns files and directories.")).max(200).default([]),complete:r.z.boolean().default(!1)}).strict(),ne=r.z.object({message:r.z.string().trim().min(1).max(2e4),mode:B.optional(),permissionMode:H.optional()});ne.extend({attachments:r.z.array(L).max(6).default([]),references:Q}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>A&&t.addIssue({code:r.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})});const ie=ne.extend({attachments:r.z.array(F).max(6).default([]),references:Q}).strict(),ae=r.z.object({decision:r.z.enum(["allow","deny","skip"])}).strict();r.z.object({queueIds:r.z.array(r.z.string().trim().min(1).max(120)).max(100)}).strict(),r.z.object({code:r.z.string().trim().min(1).max(160),message:r.z.string(),instanceId:r.z.string().trim().min(1).max(160).optional(),nodeId:r.z.string().trim().min(1).max(160).optional(),route:r.z.string().trim().min(1).max(4096).optional()}).strict();const re=r.z.object({id:r.z.string().trim().min(1).max(120),message:r.z.string().trim().min(1).max(2e4),attachments:r.z.array(U).max(6).default([]),references:Q,permissionMode:H.optional(),status:r.z.enum(["queued","sending","failed"]).default("queued"),createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime(),error:r.z.string().trim().max(4e3).optional()}).strict(),oe=r.z.object({pendingCount:r.z.number().int().min(0).default(0),items:r.z.array(re).max(100).default([])}).strict().default({pendingCount:0,items:[]}),de=r.z.enum(["control","realtime","adapter-snapshot","transcript-tail","transcript-scan","process-scan","app-session"]),ce=r.z.object({id:r.z.string().trim().min(1).max(240),status:r.z.enum(["running","completed"]),startedAt:r.z.string().datetime().optional(),completedAt:r.z.string().datetime().optional()}).strict(),le=r.z.object({id:r.z.string().trim().min(1).max(240),providerTurnId:r.z.string().trim().max(240).optional(),source:de.optional(),userPrompt:r.z.string().trim().optional(),status:r.z.enum(["queued","running","waiting","completed","failed"]).default("running"),phase:x.optional(),summary:r.z.string().trim().max(1e3).optional(),lastMessage:r.z.string().trim().optional(),lastMessageItemId:r.z.string().trim().max(240).optional(),contextCompactions:r.z.array(ce).max(20).optional(),revision:r.z.number().int().min(0).default(0),sourcePriority:r.z.number().int().min(0).max(100).optional(),snapshotVersion:r.z.number().int().min(0).optional(),observedAt:r.z.string().datetime().optional(),startedAt:r.z.string().datetime().optional(),updatedAt:r.z.string().datetime().optional(),completedAt:r.z.string().datetime().optional()}).strict(),pe=le.pick({id:!0,providerTurnId:!0,userPrompt:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,contextCompactions:!0,startedAt:!0,updatedAt:!0,completedAt:!0}).strict(),ue=r.z.object({id:r.z.string().trim().min(1).max(120),agent:E,appSessionId:r.z.string().trim().max(120).optional(),appId:r.z.string().trim().max(120).optional(),providerSessionId:r.z.string().trim().max(240).optional(),providerMeta:r.z.record(r.z.string(),r.z.unknown()).optional(),appBindingKeys:r.z.array(r.z.string().trim().min(1).max(240)).max(20).optional(),actions:P.optional(),activeTurnId:r.z.string().trim().max(240).optional(),title:r.z.string().trim().max(240).optional(),cwd:r.z.string().trim().max(4096).optional(),userPrompt:r.z.string().trim().optional(),turns:r.z.array(le).max(50).optional(),status:N.default("running"),phase:x.default("unknown"),summary:r.z.string().trim().max(1e3).optional(),lastMessage:r.z.string().trim().optional(),lastMessageItemId:r.z.string().trim().max(240).optional(),currentTool:O.optional(),toolCallsSinceLastMessage:r.z.number().int().min(0).default(0),subAgents:r.z.array(D).max(50).default([]),transcriptPath:r.z.string().trim().max(4096).optional(),transcriptSize:r.z.number().int().min(0).optional(),startedAt:r.z.string().datetime(),updatedAt:r.z.string().datetime(),completedAt:r.z.string().datetime().optional(),error:r.z.string().trim().max(4e3).optional(),counters:M,queue:oe}).strict(),me=r.z.object({session:ue,provider:r.z.string().trim().min(1).max(80),action:r.z.enum(["send","queue","steer","interrupt","approval"]),decision:r.z.enum(["allow","deny","skip"]).optional(),turnId:r.z.string().trim().min(1).max(240).optional(),providerTurnId:r.z.string().trim().min(1).max(240).optional(),queueId:r.z.string().trim().min(1).max(120).optional()}).strict(),ge=r.z.object({id:r.z.string().trim().min(1).max(120),agent:r.z.enum(["codex","claude"]),providerSessionId:r.z.string().trim().min(1).max(240),title:r.z.string().trim().max(240).optional(),userPrompt:r.z.string().trim().optional(),lastMessage:r.z.string().trim().optional(),cwd:r.z.string().trim().min(1).max(4096),lastActiveAt:r.z.string().datetime(),archivedAt:r.z.string().datetime()}).strict();r.z.object({schemaVersion:r.z.literal(1).default(1),items:r.z.array(ge).max(50).default([])}).strict();const he=r.z.object({items:r.z.array(ge).max(50).default([])}).strict(),fe=r.z.object({item:ge,turns:r.z.array(pe).max(50).default([])}).strict(),Ie=r.z.object({disposition:r.z.enum(["resumed","already-open"]),aiSessionId:r.z.string().trim().min(1).max(120),providerSessionId:r.z.string().trim().min(1).max(240),appSessionId:r.z.string().trim().min(1).max(120)}).strict(),ye=ue.pick({id:!0,agent:!0,appSessionId:!0,appId:!0,providerSessionId:!0,providerMeta:!0,appBindingKeys:!0,actions:!0,activeTurnId:!0,title:!0,cwd:!0,userPrompt:!0,turns:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,lastMessageItemId:!0,currentTool:!0,toolCallsSinceLastMessage:!0,subAgents:!0,queue:!0,startedAt:!0,updatedAt:!0,error:!0}),be=r.z.object({runningCount:r.z.number().int().min(0).default(0),waitingCount:r.z.number().int().min(0).default(0),staleCount:r.z.number().int().min(0).default(0),sessions:r.z.array(ye).default([]),updatedAt:r.z.string().datetime()}).strict(),ve=r.z.object({streamId:r.z.string().trim().min(1).max(240),revision:r.z.number().int().min(0),lastEventAt:r.z.string().datetime(),snapshot:be}).strict(),we=be.pick({runningCount:!0,waitingCount:!0,staleCount:!0,updatedAt:!0}).extend({idleCount:r.z.number().int().min(0).default(0),problemCount:r.z.number().int().min(0).default(0),revision:r.z.number().int().min(0).optional()});function Se(e,t={}){const s=e.sessions.filter(e=>"idle"===e.status).length,n=e.sessions.filter(e=>"failed"===e.status).length;return we.parse({runningCount:e.runningCount,waitingCount:e.waitingCount,staleCount:e.staleCount,idleCount:s,problemCount:n,updatedAt:e.updatedAt,revision:t.revision})}const Ae=r.z.enum(["provider-event","app-session-created","app-session-updated","app-session-deleted","discovery-scan","control-action","heartbeat-sync","startup"]),ke=r.z.object({streamId:r.z.string().trim().min(1).max(240),instanceId:r.z.string().trim().min(1).max(160),nodeId:r.z.string().trim().min(1).max(160).optional(),revision:r.z.number().int().min(0),previousRevision:r.z.number().int().min(0).optional(),traceId:r.z.string().trim().min(1).max(160),generatedAt:r.z.string().datetime(),reason:Ae}).strict(),ze=r.z.object({meta:ke,snapshot:be}).strict(),Te=r.z.object({meta:ke,upserted:r.z.array(ye).default([]),removed:r.z.array(r.z.string().trim().min(1).max(120)).default([])}).strict(),Ce=r.z.object({meta:ke,sessionIds:r.z.array(r.z.string().trim().min(1).max(120)).default([]),expiresAt:r.z.string().datetime()}).strict(),Ee=r.z.object({instanceId:r.z.string().trim().min(1).max(160),nodeId:r.z.string().trim().min(1).max(160).optional(),sessionId:r.z.string().trim().min(1).max(120),providerSessionId:r.z.string().trim().min(1).max(240),turnId:r.z.string().trim().min(1).max(240),itemId:r.z.string().trim().min(1).max(240),delta:r.z.string().min(1),generatedAt:r.z.string().datetime()}).strict(),Ne=r.z.object({streamId:r.z.string().trim().min(1).max(240),instanceId:r.z.string().trim().min(1).max(160),sinceRevision:r.z.number().int().min(0),latestRevision:r.z.number().int().min(0),earliestRetainedRevision:r.z.number().int().min(0),syncRequired:r.z.boolean().default(!1),events:r.z.array(r.z.discriminatedUnion("type",[r.z.object({type:r.z.literal(k),payload:ze}).strict(),r.z.object({type:r.z.literal(z),payload:Te}).strict(),r.z.object({type:r.z.literal(T),payload:Ce}).strict()])).default([])}).strict(),_e=r.z.object({source:de,sourcePriority:r.z.number().int().min(0).max(100).optional(),observedAt:r.z.string().datetime().optional(),snapshotVersion:r.z.number().int().min(0).optional()}).strict(),xe=_e.extend({type:r.z.literal("snapshot"),agent:r.z.string().trim().min(1).max(80),appSessionId:r.z.string().trim().max(120).optional(),appId:r.z.string().trim().max(120).optional(),providerSessionId:r.z.string().trim().max(240).optional(),providerMeta:r.z.record(r.z.string(),r.z.unknown()).optional(),appBindingKeys:r.z.array(r.z.string().trim().min(1).max(240)).max(20).optional(),actions:P.optional(),title:r.z.string().trim().max(240).optional(),cwd:r.z.string().trim().max(4096).optional(),activeTurnId:r.z.string().trim().max(240).optional(),userPrompt:r.z.string().trim().optional(),turns:r.z.array(le).max(50).optional(),status:N.optional(),phase:x.optional(),summary:r.z.string().trim().max(1e3).optional(),lastMessage:r.z.string().trim().optional(),lastMessageItemId:r.z.string().trim().max(240).optional(),currentTool:O.optional(),toolCallsSinceLastMessage:r.z.number().int().min(0).optional(),subAgents:r.z.array(D).max(50).optional(),transcriptPath:r.z.string().trim().max(4096).optional(),transcriptSize:r.z.number().int().min(0).optional(),replaceActivity:r.z.boolean().optional()}).strict(),Oe=_e.extend({type:r.z.literal("event"),sessionId:r.z.string().trim().min(1).max(120),kind:r.z.enum(["lifecycle","send-ack","turn-started","user-message","assistant-message","approval-requested","turn-completed","tool-activity","sub-agent-activity","context-compaction"]),activeTurnId:r.z.string().trim().max(240).optional(),providerTurnId:r.z.string().trim().max(240).optional(),userPrompt:r.z.string().trim().optional(),text:r.z.string().optional(),itemId:r.z.string().trim().max(240).optional(),status:N.optional(),phase:x.optional(),summary:r.z.string().trim().max(1e3).optional(),error:r.z.string().trim().max(4e3).optional(),currentTool:O.nullable().optional(),toolCallsSinceLastMessage:r.z.number().int().min(0).optional(),subAgents:r.z.array(D).max(50).optional(),contextCompaction:ce.optional(),counters:r.z.object({toolCalls:r.z.number().int().min(0).optional(),edits:r.z.number().int().min(0).optional(),approvals:r.z.number().int().min(0).optional()}).strict().optional()}).strict().superRefine((e,t)=>{if("context-compaction"===e.kind?e.contextCompaction||t.addIssue({code:"custom",path:["contextCompaction"],message:"context-compaction events require contextCompaction"}):void 0!==e.contextCompaction&&t.addIssue({code:"custom",path:["contextCompaction"],message:"contextCompaction is only valid for context-compaction events"}),"sub-agent-activity"===e.kind?e.subAgents||t.addIssue({code:"custom",path:["subAgents"],message:"sub-agent-activity events require subAgents"}):void 0!==e.subAgents&&t.addIssue({code:"custom",path:["subAgents"],message:"subAgents is only valid for sub-agent-activity events"}),"tool-activity"!==e.kind)return void 0!==e.currentTool&&t.addIssue({code:"custom",path:["currentTool"],message:"currentTool is only valid for tool-activity events"}),void(void 0!==e.toolCallsSinceLastMessage&&t.addIssue({code:"custom",path:["toolCallsSinceLastMessage"],message:"toolCallsSinceLastMessage is only valid for tool-activity events"}));"currentTool"in e||t.addIssue({code:"custom",path:["currentTool"],message:"tool-activity events require currentTool"}),void 0===e.toolCallsSinceLastMessage&&t.addIssue({code:"custom",path:["toolCallsSinceLastMessage"],message:"tool-activity events require toolCallsSinceLastMessage"})});r.z.discriminatedUnion("type",[xe,Oe]);const Re=r.z.union([r.z.object({type:r.z.literal("schedule"),scheduleKind:r.z.literal("interval").default("interval"),intervalMs:r.z.number().int().min(1e3)}).strict(),r.z.object({type:r.z.literal("schedule"),scheduleKind:r.z.literal("daily"),timeOfDay:r.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:r.z.string().trim().min(1).max(120)}).strict(),r.z.object({type:r.z.literal("schedule"),scheduleKind:r.z.literal("weekly"),weekdays:r.z.array(r.z.number().int().min(0).max(6)).min(1).max(7),timeOfDay:r.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:r.z.string().trim().min(1).max(120)}).strict()]),je=r.z.union([Re,r.z.object({type:r.z.literal("file-change"),roots:r.z.array(r.z.string().trim().min(1).max(4096)).min(1).max(50),globs:r.z.array(r.z.string().trim().min(1).max(500)).min(1).max(100),ignore:r.z.array(r.z.string().trim().min(1).max(500)).max(100).optional(),debounceMs:r.z.number().int().min(100).max(6e4).default(1500)}).strict(),r.z.object({type:r.z.literal("ai-session"),agent:r.z.string().trim().min(1).max(80).optional(),statuses:r.z.array(r.z.enum(["running","waiting","idle","failed"])).max(20).optional(),phases:r.z.array(r.z.enum(["thinking","tool","editing","approval","responding","unknown"])).max(20).optional()}).strict()]),De=r.z.object({promptTemplate:r.z.string().trim().min(1).max(2e4)}).strict(),Me=r.z.object({cooldownMs:r.z.number().int().min(0).max(864e5).optional(),maxConcurrentRuns:r.z.number().int().min(1).max(20).default(1),whenBusy:r.z.enum(["skip","queue"]).default("skip")}).strict(),Pe=r.z.object({configHash:r.z.string().trim().min(8).max(80),name:r.z.string().trim().min(1).max(160),description:r.z.string().trim().max(1e3).optional(),source:je,action:De,policy:Me.default({maxConcurrentRuns:1,whenBusy:"skip"}),createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}).strict(),$e=r.z.object({type:r.z.literal("ai-session"),aiSessionId:r.z.string().trim().min(1).max(160)}).strict(),qe=r.z.object({configHash:r.z.string().trim().min(8).max(80),deploymentId:r.z.string().trim().min(1).max(240).optional(),instanceId:r.z.string().trim().min(1).max(160),origin:r.z.enum(["control-plane","controlled-instance"]).default("controlled-instance"),enabled:r.z.boolean().default(!0),target:$e,localName:r.z.string().trim().min(1).max(160).optional(),createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}).strict(),Le=r.z.object({configHash:r.z.string().trim().min(8).max(80),deploymentId:r.z.string().trim().min(1).max(240).optional(),instanceId:r.z.string().trim().min(1).max(160),status:r.z.enum(["idle","running","disabled","error"]).default("idle"),lastTriggeredAt:r.z.string().datetime().optional(),lastCompletedAt:r.z.string().datetime().optional(),lastSkippedAt:r.z.string().datetime().optional(),lastError:r.z.string().trim().max(4e3).optional(),runCount:r.z.number().int().min(0).default(0),skippedCount:r.z.number().int().min(0).default(0)}).strict(),Ue=r.z.object({id:r.z.string().trim().min(1).max(160),configHash:r.z.string().trim().min(8).max(80),deploymentId:r.z.string().trim().min(1).max(240).optional(),instanceId:r.z.string().trim().min(1).max(160),eventType:r.z.enum(["manual","schedule","file-change","ai-session"]),status:r.z.enum(["started","completed","failed","skipped"]),target:$e,promptPreview:r.z.string().trim().max(500),eventSummary:r.z.string().trim().max(1e3).optional(),error:r.z.string().trim().max(4e3).optional(),startedAt:r.z.string().datetime(),completedAt:r.z.string().datetime().optional()}).strict(),Fe=r.z.object({schemaVersion:r.z.literal(1).default(1),configs:r.z.array(Pe).default([]),deployments:r.z.array(qe).default([]),runtime:r.z.array(Le).default([]),recentRuns:r.z.array(Ue).default([])}).strict();function Be(e){return Array.isArray(e)?e.map(Be):e&&"object"==typeof e?Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>[e,Be(t)])):e}const He="2026-07-29",Ge=r.z.string(),We=r.z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),Ve=r.z.string().datetime(),Ke=r.z.record(r.z.string(),r.z.string()).default({}),Je=r.z.record(r.z.string(),r.z.string()).default({}),Qe=r.z.string().regex(/^\d{4}-\d{2}-\d{2}$/,"Protocol version must use YYYY-MM-DD format."),Ye=/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/,Xe=/^sha256:([a-fA-F0-9]{64})$/,Ze=/^(?:localhost|[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?)(?::[1-9][0-9]{0,4})?$/,et=/^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*$/;function tt(e){const t=e.trim();if(!t||t.length>512||t.includes("://")||/\s/.test(t))throw new Error("Docker image reference must not contain a URL scheme or whitespace.");const s=t.indexOf("@");if(-1!==s&&s!==t.lastIndexOf("@"))throw new Error("Docker image reference contains more than one digest separator.");const n=-1===s?void 0:t.slice(s+1),i=-1===s?t:t.slice(0,s),a=n?Xe.exec(n):void 0;if(n&&!a)throw new Error("Docker image digest must use sha256 followed by 64 hexadecimal characters.");const r=i.lastIndexOf("/"),o=i.lastIndexOf(":"),d=o>r,c=d?i.slice(o+1):void 0,l=d?i.slice(0,o):i;if(c&&!Ye.test(c))throw new Error("Docker image tag is invalid.");if(!c&&!a)throw new Error("Docker image reference must include an explicit tag or sha256 digest.");const p=l.split("/");if(!p.length||p.some(e=>!e))throw new Error("Docker image repository path is invalid.");const u=p.length>1&&(p[0].includes(".")||p[0].includes(":")||"localhost"===p[0]),m=u?p.slice(1):p;if(u&&!Ze.test(p[0].toLowerCase()))throw new Error("Docker image registry host or port is invalid.");if(!m.length||m.some(e=>!et.test(e)))throw new Error("Docker image repository names must be lowercase and use valid separators.");const g=p.map((e,t)=>u&&0===t?e.toLowerCase():e).join("/"),h=a?`sha256:${a[1].toLowerCase()}`:void 0;return`${g}${c?`:${c}`:""}${h?`@${h}`:""}`}function st(e){const t=tt(e),s=t.indexOf("@"),n=-1===s?void 0:t.slice(s+1),i=-1===s?t:t.slice(0,s),a=i.lastIndexOf("/"),r=i.lastIndexOf(":"),o=r>a;return{reference:t,repository:o?i.slice(0,r):i,tag:o?i.slice(r+1):void 0,digest:n}}const nt=r.z.string().trim().min(1).max(512).transform((e,t)=>{try{return tt(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),r.z.NEVER}}),it=r.z.string().trim().transform((e,t)=>{const s=Xe.exec(e);return s?`sha256:${s[1].toLowerCase()}`:(t.addIssue({code:"custom",message:"Docker image digest must use sha256 followed by 64 hexadecimal characters."}),r.z.NEVER)}),at=r.z.object({codexModelHash:We.nullable().optional(),claudeModelHash:We.nullable().optional()}).strict().default({}),rt=r.z.object({imageId:We,tag:r.z.string().trim().min(1).max(128).regex(Ye).optional()}).strict(),ot={img_default:"market_taskhandoff_browser",img_codex:"market_taskhandoff_codex",img_ai:"market_taskhandoff_ai"};function dt(e,t){if(e&&"object"==typeof e&&!Array.isArray(e)){const t=e;return{imageId:"string"==typeof t.imageId&&ot[t.imageId]||t.imageId,..."string"==typeof t.tag?{tag:t.tag}:{}}}if("string"==typeof t)return{imageId:ot[t]||t}}function ct(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,{defaultImageId:s,...n}=t;return{...n,defaultImageSelection:dt(t.defaultImageSelection,s)}}const lt=r.z.object({id:We,name:r.z.string().trim().min(1).max(120),kind:r.z.enum(["tty","gui","web"]),source:r.z.enum(["builtin","custom"]),availability:r.z.enum(["available","missing-dependency"]),capabilities:r.z.object({automation:r.z.enum(["cdp"]).optional(),supportsCwdSelection:r.z.boolean().default(!1)}).strict().default({supportsCwdSelection:!1}),diagnosticCode:r.z.enum(["APP_EXECUTABLE_NOT_FOUND"]).optional()}).strict(),pt=r.z.object({code:r.z.enum(["APP_CATALOG_INVALID"]),message:r.z.string().trim().min(1).max(500)}).strict(),ut=r.z.object({items:r.z.array(lt).max(256),observedAt:Ve,issues:r.z.array(pt).max(32).default([])}).strict(),mt=r.z.enum(["linux","darwin","win32","freebsd","openbsd","aix","sunos","unknown"]),gt=r.z.enum(["x64","arm64","arm","ia32","ppc64","s390x","riscv64","unknown"]),ht=r.z.enum(["apt","dnf","brew","npm"]),ft=r.z.enum(["user","passwordless-sudo","root"]),It=r.z.object({platform:mt,arch:gt,installers:r.z.array(ht).max(8),privilege:ft,installerAccess:r.z.object({npmGlobalWritable:r.z.boolean().optional()}).strict().optional()}).strict(),yt=r.z.enum(["installed","not-installed","broken","unsupported"]),bt=r.z.enum(["recipe","bundled","external","none"]),vt=r.z.object({code:r.z.enum(["BUNDLED","EXTERNALLY_MANAGED","ALREADY_INSTALLED","NOT_INSTALLED","UNSUPPORTED_PLATFORM","INSTALLER_UNAVAILABLE","INSTALLER_NOT_WRITABLE","INSUFFICIENT_PRIVILEGE","OPERATION_IN_PROGRESS"]),message:r.z.string().trim().min(1).max(500)}).strict(),wt=r.z.object({id:We,name:r.z.string().trim().min(1).max(120),kind:r.z.enum(["tty","gui","web"]),description:r.z.string().trim().max(500).optional(),state:yt,managementSource:bt,version:r.z.string().trim().max(120).optional(),canInstall:r.z.boolean(),canUninstall:r.z.boolean(),installReason:vt.optional(),uninstallReason:vt.optional(),activeJobId:We.optional()}).strict(),St=r.z.enum(["install","uninstall"]),At=r.z.enum(["queued","running","succeeded","failed","cancelled","interrupted"]),kt=r.z.object({current:r.z.number().finite().nonnegative().optional(),total:r.z.number().finite().positive().optional(),unit:r.z.string().trim().min(1).max(40).optional()}).strict().refine(e=>void 0===e.total||void 0===e.current||e.current<=e.total,{message:"Progress current must not exceed total.",path:["current"]}),zt=r.z.object({executable:r.z.string().trim().min(1).max(4096),args:r.z.array(r.z.string().max(4096)).max(128)}).strict(),Tt=r.z.object({code:r.z.string().trim().min(1).max(120).regex(/^[a-z0-9][a-z0-9._-]*$/),message:r.z.string().trim().min(1).max(4096),retryable:r.z.boolean(),sessionIds:r.z.array(We).max(64).optional()}).strict(),Ct=r.z.object({id:We,requestId:We.optional(),appId:We,operation:St,state:At,phase:r.z.string().trim().min(1).max(120).optional(),progress:kt.optional(),command:zt.optional(),logTail:r.z.string().max(32768).optional(),logTruncated:r.z.boolean().optional(),error:Tt.optional(),requestedAt:Ve,startedAt:Ve.optional(),finishedAt:Ve.optional(),updatedAt:Ve}).strict(),Et=r.z.object({streamId:We,sequence:r.z.number().int().nonnegative(),capabilities:It,apps:r.z.array(wt).max(256),activeJobs:r.z.array(Ct).max(256),recentJobs:r.z.array(Ct).max(256),observedAt:Ve}).strict(),Nt=r.z.object({requestId:We.optional()}).strict(),_t=r.z.object({job:Ct}).strict();r.z.object({type:r.z.literal("app-management"),streamId:We,sequence:r.z.number().int().nonnegative(),observedAt:Ve,job:Ct.optional(),snapshot:Et.optional()}).strict().refine(e=>void 0!==e.job||void 0!==e.snapshot,{message:"An app management event requires a job or snapshot."});const xt=r.z.object({component:r.z.enum(["control-plane","node-agent","controlled-instance"]),packageName:r.z.string().trim().max(160).optional(),packageVersion:r.z.string().trim().max(80).optional(),protocolVersion:Ge.optional(),buildId:r.z.string().trim().max(160).optional(),builtAt:r.z.string().trim().max(120).optional(),gitCommit:r.z.string().trim().max(120).optional(),imageRef:r.z.string().trim().max(512).optional(),imageDigest:r.z.string().trim().max(240).optional()}).strict(),Ot=r.z.enum(["stable","beta","alpha"]),Rt=r.z.object({packageName:r.z.literal("@task-handoff/controlled-instance"),version:r.z.string().trim().min(1).max(80),platform:r.z.string().trim().min(1).max(40),arch:r.z.string().trim().min(1).max(40),formatVersion:r.z.number().int().positive(),launcherAbi:r.z.number().int().positive(),entrypoint:r.z.string().trim().min(1).max(512),sha256:r.z.string().regex(/^[a-f0-9]{64}$/)}).strict(),jt=r.z.object({code:r.z.enum(["INSTANCE_RUNTIME_VERSION_MISMATCH","INSTANCE_RUNTIME_ARTIFACT_UNAVAILABLE","INSTANCE_RUNTIME_ARTIFACT_INVALID","INSTANCE_RUNTIME_INSTALL_FAILED","INSTANCE_RUNTIME_RESTART_FAILED","INSTANCE_RUNTIME_VERIFICATION_FAILED","INSTANCE_BASE_RUNTIME_INCOMPATIBLE","NODE_UPDATE_PREFLIGHT_FAILED","NODE_UPDATE_FAILED","LEGACY_INSTANCE_UPDATE_RETIRED"]),message:r.z.string().trim().min(1).max(4096),expectedVersion:r.z.string().trim().min(1).max(80).optional(),actualVersion:r.z.string().trim().min(1).max(80).optional(),retryable:r.z.boolean().default(!1)}).strict(),Dt=r.z.object({desiredVersion:r.z.string().trim().min(1).max(80),actualVersion:r.z.string().trim().min(1).max(80).optional(),phase:r.z.enum(["pending","draining","installing","restarting","verifying","matched","failed"]),attempt:r.z.number().int().nonnegative().default(0),lastAttemptAt:Ve.optional(),matchedAt:Ve.optional(),error:jt.optional()}).strict(),Mt=r.z.object({runningInstanceCount:r.z.number().int().nonnegative(),stoppedInstanceCount:r.z.number().int().nonnegative(),activeInstanceCount:r.z.number().int().nonnegative(),restartInstanceCount:r.z.number().int().nonnegative(),runningInstanceIds:r.z.array(We).max(1024).default([]),stoppedInstanceIds:r.z.array(We).max(1024).default([]),activeInstanceIds:r.z.array(We).max(1024).default([])}).strict(),Pt=r.z.object({phase:r.z.enum(["queued","updating-node","restarting-node","converging-instances","succeeded","degraded","failed"]),desiredVersion:r.z.string().trim().min(1).max(80),nodeVersion:r.z.string().trim().min(1).max(80).optional(),expectedInstanceIds:r.z.array(We).max(1024).default([]),expectedInstanceCount:r.z.number().int().nonnegative(),matchedInstanceCount:r.z.number().int().nonnegative(),pendingInstanceCount:r.z.number().int().nonnegative(),failedInstanceCount:r.z.number().int().nonnegative(),deferredInstanceCount:r.z.number().int().nonnegative().default(0)}).strict(),$t=r.z.object({channel:Ot.default("stable")}).strict(),qt=r.z.object({source:r.z.literal("npm"),channel:Ot,currentVersion:r.z.string().trim().max(240).optional(),availableVersion:r.z.string().trim().min(1).max(240),artifactRef:r.z.string().trim().min(1).max(512).optional(),runtimeArtifacts:r.z.array(Rt).default([]),impact:Mt,updateAvailable:r.z.boolean(),supported:r.z.boolean().default(!0),reason:r.z.string().trim().max(2048).optional(),checkedAt:Ve,preflightToken:r.z.string().trim().min(16).max(240).optional()}).strict(),Lt=r.z.object({id:We,nodeId:We,source:r.z.literal("npm"),channel:Ot,fromVersion:r.z.string().trim().max(240).optional(),toVersion:r.z.string().trim().min(1).max(240),artifactRef:r.z.string().trim().min(1).max(512).optional(),runtimeArtifacts:r.z.array(Rt).default([]),impact:Mt,rollout:Pt,status:r.z.enum(["queued","updating-node","restarting-node","converging-instances","succeeded","degraded","failed"]),error:jt.optional(),startedAt:Ve.optional(),completedAt:Ve.optional(),createdAt:Ve,updatedAt:Ve}).strict(),Ut=r.z.object({channel:Ot,targetVersion:r.z.string().trim().min(1).max(240),preflightToken:r.z.string().trim().min(16).max(240)}).strict(),Ft=r.z.object({type:r.z.enum(["branch","tag","commit"]),name:r.z.string().trim().min(1).max(240).optional(),commit:r.z.string().trim().max(80).optional()}).strict(),Bt=r.z.object({type:r.z.enum(["none","ssh-key","https-token"]).default("none"),secretId:r.z.string().trim().min(1).max(120).optional()}).strict(),Ht=r.z.object({depth:r.z.number().int().positive().max(1e5).optional(),submodules:r.z.boolean().default(!1),lfs:r.z.boolean().default(!1),subdirectory:r.z.string().trim().max(240).default("")}).strict();r.z.object({id:We,name:r.z.string().trim().min(1).max(160),url:r.z.string().trim().min(1).max(2048),provider:r.z.string().trim().max(80).optional(),ref:Ft.default({type:"branch",name:"main"}),auth:Bt.default({type:"none"}),clone:Ht.default({submodules:!1,lfs:!1,subdirectory:""}),defaultImageSelection:rt.optional(),labels:Ke,createdAt:Ve,updatedAt:Ve}).strict();const Gt=r.z.object({id:We,nodeId:We,name:r.z.string().trim().min(1).max(160),path:r.z.string().trim().min(1).max(4096),defaultImageSelection:rt.optional(),labels:Ke,createdAt:Ve,updatedAt:Ve}).strict(),Wt=r.z.discriminatedUnion("type",[r.z.object({type:r.z.literal("git-repository"),repositoryId:We.optional(),url:r.z.string().trim().min(1).max(2048),provider:r.z.string().trim().max(80).optional(),ref:Ft.default({type:"branch",name:"main"}),auth:Bt.default({type:"none"}),clone:Ht.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),r.z.object({type:r.z.literal("git-template"),url:r.z.string().trim().min(1).max(2048),templateId:r.z.string().trim().min(1).max(120).optional(),ref:Ft.default({type:"branch",name:"main"}),auth:Bt.default({type:"none"}),clone:Ht.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),r.z.object({type:r.z.literal("local-folder"),localFolderId:We.optional(),path:r.z.string().trim().min(1).max(4096),ownerNodeId:We.optional()}).strict()]),Vt=r.z.object({mode:r.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]),path:r.z.string().trim().min(1).max(4096).default("/workspace"),volumeName:r.z.string().trim().max(240).optional(),readOnly:r.z.boolean().default(!1)}).strict(),Kt=r.z.enum(["codex","claude"]),Jt=r.z.object({id:We,name:r.z.string().trim().min(1).max(160),source:Wt,defaultImageSelection:rt.optional(),defaultNodeId:We.optional(),defaultRuntimeId:We.optional(),workspacePolicy:Vt,labels:Ke,createdAt:Ve,updatedAt:Ve}).strict(),Qt=r.z.object({id:We,name:r.z.string().trim().min(1).max(160),endpoint:r.z.string().trim().min(1).max(2048),key:r.z.string().trim().min(1).max(4096),model:r.z.string().trim().min(1).max(240),app:Kt,enabled:r.z.boolean().default(!0),order:r.z.number().int().min(0).max(1e6).default(0),labels:Ke,createdAt:Ve,updatedAt:Ve}).strict(),Yt=Qt.omit({key:!0}).extend({keyPreview:r.z.string().trim().min(1).max(160),keySet:r.z.boolean()}).strict(),Xt=Yt.extend({referenceCount:r.z.number().int().min(0)}).strict(),Zt=r.z.object({instanceId:We,codexModelHash:We.optional(),claudeModelHash:We.optional(),updatedAt:Ve}).strict(),es=r.z.discriminatedUnion("type",[r.z.object({type:r.z.literal("control-plane"),name:Qt.shape.name,enabled:Qt.shape.enabled,order:Qt.shape.order}).strict(),r.z.object({type:r.z.literal("node"),nodeId:We,name:Qt.shape.name,enabled:Qt.shape.enabled,order:Qt.shape.order,referenceCount:r.z.number().int().min(0)}).strict()]),ts=r.z.object({id:We,model:Yt,locations:r.z.array(es).min(1),referenceCount:r.z.number().int().min(0).default(0)}).strict(),ss=r.z.object({nodeId:We,code:r.z.string().trim().min(1).max(120),message:r.z.string().trim().min(1).max(2e3)}).strict(),ns=r.z.object({models:r.z.array(ts),nodeDiagnostics:r.z.array(ss).default([]),updatedAt:Ve}).strict();function is(e){const t={app:Kt.parse(e.app),endpoint:Qt.shape.endpoint.parse(e.endpoint),key:Qt.shape.key.parse(e.key),model:Qt.shape.model.parse(e.model)};return`mdl_${o.createHash("sha256").update(JSON.stringify(t)).digest("hex")}`}Qt.omit({id:!0,createdAt:!0,updatedAt:!0}).strict().partial().strict(),r.z.object({modelSelection:at,codexModelHash:We.optional(),claudeModelHash:We.optional()}).strict();const as=r.z.enum(["market","custom"]),rs=r.z.enum(["active","deprecated","yanked"]),os=r.z.discriminatedUnion("kind",[r.z.object({kind:r.z.literal("builtin"),key:r.z.string().trim().min(1).max(120)}).strict(),r.z.object({kind:r.z.literal("remote"),url:r.z.string().trim().url().max(2048),mediaType:r.z.enum(["image/png","image/jpeg","image/webp"]).optional(),digest:it.optional()}).strict()]),ds={kind:"builtin",key:"default-image-cover"},cs=r.z.array(r.z.string().trim().min(1).max(80)).default([]),ls=r.z.array(r.z.string().trim().min(1).max(120)).default([]),ps=r.z.record(r.z.string().trim().min(2).max(35),r.z.string().trim().min(1).max(4096)),us=r.z.object({os:r.z.string().trim().min(1).max(40),architecture:r.z.string().trim().min(1).max(40),digest:it.optional(),downloadSizeBytes:r.z.number().int().positive().optional(),unpackedSizeBytes:r.z.number().int().positive().optional()}).strict(),ms=r.z.object({name:r.z.string().trim().min(1).max(128).regex(Ye),version:r.z.string().trim().min(1).max(160).optional(),reference:nt,manifestDigest:it.optional(),publishedAt:Ve.optional(),platforms:r.z.array(us).default([]),status:rs.default("active")}).strict(),gs=r.z.object({id:We,publisher:r.z.string().trim().min(1).max(160),slug:r.z.string().trim().min(1).max(160),name:r.z.string().trim().min(1).max(160),description:r.z.string().trim().min(1).max(4096),localizedDescriptions:ps.optional(),cover:os.optional(),repository:r.z.string().trim().min(1).max(512),defaultTag:r.z.string().trim().min(1).max(128).regex(Ye),tags:r.z.array(ms).min(1),capabilities:cs,optionalApps:ls,defaultEnv:Je,labels:Ke,status:rs.default("active")}).strict().superRefine((e,t)=>{e.tags.some(t=>t.name===e.defaultTag)||t.addIssue({code:"custom",path:["defaultTag"],message:"Market defaultTag must identify one of the image tags."})}),hs=r.z.object({protocolVersion:Qe,catalogId:We,revision:r.z.string().trim().min(1).max(240),source:r.z.enum(["embedded","remote","cache"]),generatedAt:Ve,expiresAt:Ve.optional(),items:r.z.array(gs)}).strict(),fs=r.z.object({source:r.z.enum(["embedded","remote","cache"]),state:r.z.enum(["ready","refreshing","stale","failed"]),revision:r.z.string().trim().min(1).max(240),updatedAt:Ve,error:r.z.string().trim().max(4096).optional()}).strict(),Is=r.z.object({id:We,origin:r.z.literal("custom"),name:r.z.string().trim().min(1).max(160),description:r.z.string().trim().max(4096).optional(),localizedDescriptions:ps.optional(),cover:os.optional(),reference:nt,repository:r.z.string().trim().min(1).max(512),tag:r.z.string().trim().min(1).max(128).regex(Ye).optional(),pullPolicy:r.z.literal("if-not-present").default("if-not-present"),capabilities:cs,optionalApps:ls,defaultEnv:Je,labels:Ke,createdAt:Ve,updatedAt:Ve}).strict(),ys=r.z.object({name:r.z.string().trim().min(1).max(128).regex(Ye),version:r.z.string().trim().min(1).max(160).optional(),reference:nt,manifestDigest:it.optional(),status:rs}).strict(),bs=r.z.object({id:We,origin:as,name:r.z.string().trim().min(1).max(160),description:r.z.string().trim().max(4096).optional(),localizedDescriptions:ps.optional(),cover:os,repository:r.z.string().trim().min(1).max(512),tag:r.z.string().trim().min(1).max(128).regex(Ye).optional(),availableTags:r.z.array(ys),reference:nt,digest:it.optional(),downloadSizeBytes:r.z.number().int().positive().optional(),unpackedSizeBytes:r.z.number().int().positive().optional(),capabilities:cs,optionalApps:ls,defaultEnv:Je,labels:Ke,readOnly:r.z.boolean(),lifecycleStatus:rs.optional(),market:r.z.object({catalogId:We,catalogRevision:r.z.string().trim().min(1).max(240),publisher:r.z.string().trim().min(1).max(160),version:r.z.string().trim().min(1).max(160).optional()}).strict().optional()}).strict(),vs=r.z.object({id:We,origin:as,name:r.z.string().trim().min(1).max(160),description:r.z.string().trim().max(4096).optional(),localizedDescriptions:ps.optional(),cover:os.optional(),repository:r.z.string().trim().min(1).max(512),tag:r.z.string().trim().min(1).max(128).regex(Ye).optional(),requestedReference:nt,resolvedDigest:it.optional(),resolvedReference:nt.optional(),downloadSizeBytes:r.z.number().int().positive().optional(),pullPolicy:r.z.literal("if-not-present").default("if-not-present"),capabilities:cs,optionalApps:ls,defaultEnv:Je,labels:Ke,market:r.z.object({catalogId:We,catalogRevision:r.z.string().trim().min(1).max(240),publisher:r.z.string().trim().min(1).max(160),version:r.z.string().trim().min(1).max(160).optional()}).strict().optional(),createdAt:Ve,updatedAt:Ve}).strict(),ws=r.z.object({phase:r.z.enum(["checking-image","pulling-image","resolving-image","ready","failed"]),requestedReference:nt,generation:r.z.number().int().nonnegative().default(0),error:r.z.string().trim().max(4096).optional(),startedAt:Ve,updatedAt:Ve}).strict(),Ss="image.pull.terminal.output",As="image.pull.terminal.finished",ks=r.z.object({instanceId:We,generation:r.z.number().int().nonnegative(),requestedReference:nt,sequence:r.z.number().int().nonnegative(),observedAt:Ve}).strict(),zs=ks.extend({data:r.z.string().min(1).max(65536),replay:r.z.boolean().optional()}).strict(),Ts=ks.extend({outcome:r.z.enum(["succeeded","failed"])}).strict(),Cs=ks.extend({status:r.z.enum(["connecting","pulling","extracting","complete","failed"]),layers:r.z.object({total:r.z.number().int().nonnegative(),completed:r.z.number().int().nonnegative(),downloaded:r.z.number().int().nonnegative(),downloading:r.z.number().int().nonnegative(),extracting:r.z.number().int().nonnegative()}).strict(),bytes:r.z.object({current:r.z.number().finite().nonnegative(),total:r.z.number().finite().positive()}).strict().optional(),percent:r.z.number().finite().min(0).max(100).optional(),message:r.z.string().trim().min(1).max(500),terminalTail:r.z.string().max(262144).optional(),terminalTruncated:r.z.boolean().optional()}).strict();function Es(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const s=e,n=new Set(["id","origin","name","description","localizedDescriptions","cover","reference","repository","tag","image","registry","pullPolicy","capabilities","optionalApps","defaultEnv","labels","createdAt","updatedAt"]);for(const e of Object.keys(s))n.has(e)||t?.({imageId:"string"==typeof s.id?s.id:void 0,field:e});const i="string"==typeof s.id?s.id:void 0,a="string"==typeof s.image?s.image:void 0;void 0!==a&&t?.({imageId:i,field:"image"}),Object.prototype.hasOwnProperty.call(s,"registry")&&t?.({imageId:i,field:"registry"});let r,o="string"==typeof s.reference?s.reference:a;if(o)try{o=tt(o)}catch{if(!o.includes("@")&&o.lastIndexOf(":")<=o.lastIndexOf("/"))try{o=tt(`${o}:latest`),t?.({imageId:i,field:"reference:implicit-latest"})}catch{}}try{r=o?st(o):void 0}catch{}return{id:s.id,origin:"custom",name:s.name,description:s.description,localizedDescriptions:s.localizedDescriptions,cover:Ys(s.cover,["kind","key","url","mediaType","digest"]),reference:r?.reference??o,repository:r?.repository??s.repository,tag:r?.tag??s.tag,pullPolicy:s.pullPolicy??"if-not-present",capabilities:s.capabilities,optionalApps:s.optionalApps,defaultEnv:s.defaultEnv,labels:s.labels,createdAt:s.createdAt,updatedAt:s.updatedAt}}const Ns=r.z.object({id:We,name:r.z.string().trim().min(1).max(160),connectionMode:r.z.enum(["local-ipc","local-loopback","direct-http","reverse-wss"]).default("direct-http"),auth:r.z.object({mode:r.z.enum(["local-static-key","paired-hmac"]).default("local-static-key"),keyId:We.optional(),secret:r.z.string().trim().max(4096).optional(),pairedAt:Ve.optional(),pairing:r.z.object({status:r.z.enum(["pending","paired","expired"]).default("pending"),joinToken:r.z.string().trim().max(4096).optional(),expiresAt:Ve.optional()}).strict().optional()}).strict().default({mode:"local-static-key"}),endpoint:r.z.string().trim().max(2048).optional(),controlEndpoint:r.z.string().trim().max(2048).optional(),containerEndpoint:r.z.string().trim().max(2048).optional(),publicWebBase:r.z.string().trim().max(2048).optional(),status:r.z.enum(["unknown","online","offline","degraded"]).default("unknown"),health:r.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),capabilities:r.z.record(r.z.string(),r.z.unknown()).default({}),appInventory:ut.optional(),labels:Ke,lastSeenAt:Ve.optional(),createdAt:Ve,updatedAt:Ve}).strict(),_s=r.z.object({id:We,nodeId:We,name:r.z.string().trim().min(1).max(160),type:r.z.enum(["docker","kubernetes","local"]),status:r.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:r.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),capabilities:r.z.record(r.z.string(),r.z.unknown()).default({}),labels:Ke,createdAt:Ve,updatedAt:Ve}).strict(),xs=r.z.object({instanceId:We,runtimeKind:r.z.literal("docker"),state:r.z.enum(["pending","available","stopped","unavailable"]),sampledAt:Ve,cpu:r.z.object({usagePercent:r.z.number().nonnegative()}).strict().optional(),memory:r.z.object({usageBytes:r.z.number().nonnegative(),limitBytes:r.z.number().nonnegative().optional(),usagePercent:r.z.number().nonnegative().optional()}).strict().optional(),network:r.z.object({rxBytes:r.z.number().nonnegative(),txBytes:r.z.number().nonnegative()}).strict().optional(),blockIo:r.z.object({readBytes:r.z.number().nonnegative(),writeBytes:r.z.number().nonnegative()}).strict().optional(),pids:r.z.number().int().nonnegative().optional(),error:r.z.string().trim().max(2048).optional()}).strict(),Os="instance.metrics.snapshot",Rs=r.z.object({ok:r.z.boolean().optional(),role:r.z.string().optional(),nodeId:We.optional(),platform:mt.optional()}).passthrough(),js=r.z.object({bindScope:r.z.enum(["loopback","all-ipv4"]),port:r.z.number().int().min(1).max(65535)}).strict().extend({host:r.z.enum(["127.0.0.1","0.0.0.0"]),status:r.z.enum(["listening","error"]),source:r.z.enum(["bootstrap","persisted"]),error:r.z.string().trim().max(2048).optional()}).strict(),Ds=r.z.object({nodeId:We,joinToken:r.z.string().trim().min(1),expiresAt:Ve}).passthrough(),Ms=r.z.object({id:We,keyId:We,url:r.z.string().trim().min(1).max(2048).optional(),name:r.z.string().trim().min(1).max(160).optional(),pairedAt:Ve,updatedAt:Ve.optional(),active:r.z.boolean().optional(),current:r.z.boolean().optional()}).passthrough(),Ps=r.z.object({remote:r.z.object({id:We,url:r.z.string().trim().min(1).max(2048).optional(),keyId:We,pairedAt:Ve,active:r.z.boolean()}).passthrough(),tunnel:r.z.object({status:r.z.enum(["disabled","saved","connecting","connected","failed"]),error:r.z.string().trim().max(2048).optional()}).passthrough()}).passthrough(),$s=r.z.object({deleted:r.z.boolean().optional()}).passthrough(),qs=r.z.object({repository:r.z.string().trim().optional(),tag:r.z.string().trim().optional(),id:r.z.string().trim(),createdSince:r.z.string().trim().optional(),size:r.z.string().trim().optional(),sizeBytes:r.z.number().int().nonnegative().optional(),reference:r.z.string().trim().min(1),repoDigests:r.z.array(r.z.string().trim().min(1).max(512)).default([])}).passthrough(),Ls=r.z.object({image:bs,status:r.z.enum(["available","pull-required","unknown"]),localImage:qs.optional(),localSizeBytes:r.z.number().int().nonnegative().optional(),downloadSizeBytes:r.z.number().int().positive().optional(),unpackedSizeBytes:r.z.number().int().positive().optional(),error:r.z.string().trim().max(2048).optional()}).strict(),Us=r.z.lazy(()=>r.z.object({name:r.z.string().trim().min(1),path:r.z.string().trim().min(1).max(4096),children:r.z.array(Us)}).passthrough()),Fs=r.z.object({status:r.z.number().int().min(100).max(599).optional(),headers:r.z.record(r.z.string(),r.z.string()).optional(),bodyBase64:r.z.string().optional()}).passthrough(),Bs=r.z.object({strategy:r.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),web:r.z.string().trim().max(2048).optional(),api:r.z.string().trim().max(2048).optional(),vnc:r.z.string().trim().max(2048).optional(),tty:r.z.string().trim().max(2048).optional(),logs:r.z.string().trim().max(2048).optional(),status:r.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict(),Hs=r.z.object({strategy:r.z.enum(["control-plane-proxy","direct-port","node-proxy","kubernetes-ingress","kubernetes-port-forward"]).default("control-plane-proxy"),web:r.z.string().trim().max(2048).optional(),api:r.z.string().trim().max(2048).optional(),ws:r.z.string().trim().max(2048).optional(),status:r.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict().default({strategy:"control-plane-proxy",status:"unknown"}),Gs=r.z.object({mode:r.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]).optional(),status:r.z.enum(["unknown","pending","ready","failed"]).default("unknown"),path:r.z.string().trim().max(4096).optional(),resolvedCommit:r.z.string().trim().max(120).optional(),error:r.z.string().trim().max(2048).optional()}).passthrough(),Ws=r.z.object({id:We,name:r.z.string().trim().min(1).max(160),projectId:We.optional(),source:Wt,sourceSnapshot:r.z.record(r.z.string(),r.z.unknown()).default({}),modelSelection:at,nodeId:We,runtimeId:We,imageSelection:rt.optional(),imageSnapshot:vs.optional(),imageProvisioning:ws.optional(),stateRevision:r.z.number().int().min(0).default(0),status:r.z.enum(["created","provisioning","starting","registering","registered","running","stopping","stopped","failed","unhealthy"]).default("created"),health:r.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),connectionStatus:r.z.enum(["unknown","online","offline","endpoint-unreachable"]).default("unknown"),agentStatus:r.z.enum(["unknown","online","offline"]).default("unknown"),targetStatus:r.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),uiAccessStatus:r.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),controlMode:r.z.enum(["standalone","controlled"]).default("controlled"),protocolVersion:Ge.optional(),instanceVersion:r.z.string().trim().max(80).optional(),build:xt.optional(),runtimeVersion:Dt.optional(),ready:r.z.boolean().default(!1),capabilities:r.z.record(r.z.string(),r.z.unknown()).default({}),appInventory:ut.optional(),config:r.z.object({autoImportAgentConfigs:r.z.boolean().default(!0),defaultCodexPermissionMode:H.default("ask")}).strict().default({autoImportAgentConfigs:!0,defaultCodexPermissionMode:"ask"}),workspace:Gs.default({status:"unknown"}),target:Bs.default({strategy:"direct-port",status:"unknown"}),access:Hs,apps:r.z.object({runningCount:r.z.number().int().min(0).default(0),problemCount:r.z.number().int().min(0).default(0),updatedAt:Ve.optional(),revision:r.z.number().int().min(0).optional()}).strict().default({runningCount:0,problemCount:0}),aiSessions:be.default(()=>({runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:(new Date).toISOString()})),triggers:r.z.object({enabledCount:r.z.number().int().min(0).default(0),runningCount:r.z.number().int().min(0).default(0),errorCount:r.z.number().int().min(0).default(0),configs:r.z.array(r.z.object({configHash:r.z.string().trim().min(1),config:Pe,deployments:r.z.array(qe).default([]),runtime:r.z.array(Le).default([])}).strict()).default([]),recentRuns:r.z.array(Ue).default([])}).strict().optional(),runtime:r.z.object({kind:r.z.string().trim().max(80).optional(),containerName:r.z.string().trim().max(240).optional(),containerId:r.z.string().trim().max(240).optional(),workspacePath:r.z.string().trim().max(4096).optional(),pid:r.z.number().int().positive().optional(),port:r.z.number().int().positive().max(65535).optional(),labels:r.z.record(r.z.string(),r.z.string()).default({})}).strict().default({labels:{}}),registrationToken:r.z.string().trim().min(1).max(240).optional(),lastHeartbeatAt:Ve.optional(),createdAt:Ve,updatedAt:Ve}).strict(),Vs="instance.lifecycle.snapshot",Ks=r.z.object({instanceId:Ws.shape.id,revision:r.z.number().int().min(0),updatedAt:Ve,status:Ws.shape.status.unwrap(),health:Ws.shape.health.unwrap(),connectionStatus:Ws.shape.connectionStatus.unwrap(),accessStatus:r.z.enum(["reachable","endpoint-unreachable"]),imageProvisioning:ws.optional(),workspace:Gs,runtime:Ws.shape.runtime.unwrap(),runtimeVersion:Dt.optional(),ready:r.z.boolean(),lastHeartbeatAt:Ve.optional()}).strict();function Js(e,t,s,n,i){if(!t||"object"!=typeof t||Array.isArray(t))return;let a=structuredClone(t);for(let t=0;t<20;t+=1){const t=e.safeParse(a);if(t.success)return t.data;let s=!1;for(const e of t.error.issues){if("unrecognized_keys"!==e.code)continue;const t=Qs(a,e.path);if(t&&"object"==typeof t&&!Array.isArray(t))for(const n of e.keys)Object.prototype.hasOwnProperty.call(t,n)&&(delete t[n],s=!0)}if(!s){const s=structuredClone(a);let n=!1;for(const e of t.error.issues){if(!e.path.length||"string"!=typeof e.path.at(-1))continue;const t=Qs(s,e.path.slice(0,-1)),i=e.path.at(-1);t&&"object"==typeof t&&!Array.isArray(t)&&Object.prototype.hasOwnProperty.call(t,i)&&(delete t[i],n=!0)}if(n){const t=e.safeParse(s);if(t.success)return t.data}break}}}function Qs(e,t){let s=e;for(const e of t){if(!s||"object"!=typeof s)return;s=s[e]}return s}function Ys(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const s=e;return Object.fromEntries(t.filter(e=>Object.prototype.hasOwnProperty.call(s,e)).map(e=>[e,s[e]]))}r.z.object({instanceId:We.optional(),projectId:We.optional(),source:Wt.optional(),nodeId:We.optional(),runtimeId:We.optional(),imageSelection:rt.optional(),instanceVersion:r.z.string().trim().max(80).optional(),protocolVersion:Ge,build:xt.optional(),controlMode:r.z.enum(["standalone","controlled"]).default("controlled"),capabilities:r.z.record(r.z.string(),r.z.unknown()).default({}),appInventory:ut.optional(),target:Bs.default({strategy:"direct-port",status:"unknown"}),workspace:Gs.default({status:"unknown"}),registrationToken:r.z.string().trim().max(240).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==He||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol register payload requires appInventory."})}),r.z.object({status:Ws.shape.status.optional(),health:Ws.shape.health.optional(),protocolVersion:Ge,build:xt.optional(),capabilities:Ws.shape.capabilities.optional(),appInventory:ut.optional(),apps:Ws.shape.apps.optional(),aiSessions:Ws.shape.aiSessions.optional(),triggers:Ws.shape.triggers.optional(),workspace:Gs.optional(),target:Bs.partial().optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==He||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol heartbeat payload requires appInventory."})});const Xs=r.z.enum(["web","telegram","wechat","dingding"]),Zs=r.z.object({id:We,channel:Xs,name:r.z.string().trim().min(1).max(160),enabled:r.z.boolean().default(!1),token:r.z.string().trim().max(4096).optional(),defaultChatId:r.z.string().trim().max(240).optional(),allowedUserIds:r.z.array(r.z.string().trim().min(1).max(240)).default([]),pollIntervalMs:r.z.number().int().positive().max(6e4).default(3e3),settings:r.z.record(r.z.string(),r.z.unknown()).default({}),createdAt:Ve,updatedAt:Ve}).strict(),en=r.z.object({id:We,channel:Xs,bridgeId:We.optional(),chatSessionId:r.z.string().trim().min(1).max(240),userId:r.z.string().trim().max(240).optional(),activeProjectId:We.optional(),activeInstanceId:We.optional(),activeAiSessionId:r.z.string().trim().min(1).max(120).optional(),lastUsedAt:Ve,createdAt:Ve,updatedAt:Ve}).strict(),tn=r.z.object({source:r.z.object({type:r.z.literal("chat-gateway").default("chat-gateway"),channel:Xs,bridgeId:We.optional(),chatSessionId:r.z.string().trim().min(1).max(240),userId:r.z.string().trim().max(240).optional()}).strict(),message:r.z.object({text:r.z.string().trim().max(2e4).default(""),attachments:r.z.array(L).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>A&&t.addIssue({code:r.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})}),target:r.z.object({instanceId:We,aiSessionId:r.z.string().trim().min(1).max(120)}).strict().optional()}).strict(),sn=r.z.object({id:We,instanceId:We,projectId:We.optional(),aiSessionId:r.z.string().trim().min(1).max(120),providerSessionId:r.z.string().trim().max(240).optional(),result:r.z.string().default(""),timeoutMs:r.z.number().positive().optional(),source:r.z.string().trim().max(120).optional(),kind:r.z.enum(["task","approval"]).default("task"),status:r.z.enum(["pending","unreachable"]).default("pending"),error:r.z.string().trim().max(2048).optional(),lastSeenAt:Ve}).strict();function nn(e){return e.startsWith("ai-session.")||e.startsWith("ai.session.")?"ai.sessions":e.startsWith("app-session.")?"app.sessions":e.startsWith("app.management")?"apps":e.startsWith("trigger.")?"triggers":e.startsWith("instance.")?"instances":e.startsWith("node.")?"nodes":e.startsWith("project.")?"projects":e.startsWith("model.")?"models":e.startsWith("image.")?"images":e.startsWith("market.")?"market":"system"}const an=r.z.enum(["ai.sessions","app.sessions"]),rn=r.z.object({topic:an,instanceId:r.z.string().trim().min(1).max(160),streamId:r.z.string().trim().min(1).max(240),latestRevision:r.z.number().int().min(0),earliestRetainedRevision:r.z.number().int().min(0)}).strict(),on=r.z.object({protocolVersion:r.z.literal(1),streams:r.z.array(rn)}).strict();function dn(e){s.mkdirSync(e,{recursive:!0})}function cn(e,s){dn(t.dirname(e)),c.sync(e,`${JSON.stringify(s,null,2)}\n`,{encoding:"utf8"})}function ln(e,t){console.warn(JSON.stringify({message:e,...t}))}function pn(e){return e&&"object"==typeof e&&"issues"in e?{issues:e.issues}:{error:e instanceof Error?e.message:String(e)}}function un(e,t){const n=t.logger||ln;let i;try{i=JSON.parse(s.readFileSync(e,"utf8"))}catch(t){return void n("stored JSON could not be read",{filePath:e,...pn(t)})}if(!t.schema)return i;const a=t.sanitize?t.sanitize(i):i,r=t.schema.safeParse(a);if(r.success)return r.data;const o="strip"in t.schema&&"function"==typeof t.schema.strip?t.schema.strip().safeParse(a):void 0;if(o?.success)return n("unknown stored fields were ignored",{filePath:e,issues:r.error.issues}),o.data;n("stored record failed validation and was isolated",{filePath:e,issues:r.error.issues})}function mn(e){return`${e}_${o.randomUUID().replace(/-/g,"").slice(0,20)}`}function gn(){return o.randomBytes(24).toString("base64url")}class hn{directory;options;constructor(e,t={}){this.directory=e,this.options=t}init(){dn(this.directory)}filePath(e){return t.join(this.directory,`${e}.json`)}list(){return this.init(),s.readdirSync(this.directory).filter(e=>e.endsWith(".json")).sort().flatMap(e=>{const s=un(t.join(this.directory,e),this.options);return s?[s]:[]})}get(e){const t=this.filePath(e);return s.existsSync(t)?un(t,this.options):void 0}put(e){const t=this.options.sanitize?this.options.sanitize(e):e,s=this.options.schema?this.options.schema.parse(t):t;return cn(this.filePath(s.id),s),s}patch(e,t){const s=this.get(e);if(!s)return;const n={...s,...t,id:s.id,createdAt:s.createdAt,updatedAt:(new Date).toISOString()};return this.put(n)}delete(e){const t=this.filePath(e);return!!s.existsSync(t)&&(s.unlinkSync(t),!0)}}class fn{filePathValue;defaults;options;constructor(e,t,s={}){this.filePathValue=e,this.defaults=t,this.options=s}init(){dn(t.dirname(this.filePathValue)),s.existsSync(this.filePathValue)||this.put(this.defaults())}get(){return this.init(),s.existsSync(this.filePathValue)?un(this.filePathValue,this.options)??this.defaults():this.defaults()}put(e){const t=this.options.sanitize?this.options.sanitize(e):e,s=this.options.schema?this.options.schema.parse(t):t;return cn(this.filePathValue,s),s}}const In=d.promisify(o.scrypt),yn="task_handoff_cp_session",bn=r.z.enum(["disabled","password"]),vn=r.z.string().trim().min(1).max(80).regex(/^[a-zA-Z0-9_.@-]+$/),wn=r.z.string().min(8).max(4096),Sn=r.z.object({username:vn,password:wn}).strict(),An=r.z.object({username:r.z.string().trim().min(1).max(80),password:r.z.string().min(1).max(4096)}).strict(),kn=r.z.object({id:r.z.string().trim().min(1),createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}),zn=kn.extend({username:vn,passwordHash:r.z.string().trim().min(1),role:r.z.enum(["viewer","operator","admin"]),lastLoginAt:r.z.string().datetime().optional()}).strict(),Tn=kn.extend({userId:r.z.string().trim().min(1),tokenHash:r.z.string().trim().min(1),expiresAt:r.z.string().datetime(),lastSeenAt:r.z.string().datetime().optional()}).strict();function Cn(){return(new Date).toISOString()}function En(e){return o.createHash("sha256").update(e).digest("base64url")}async function Nn(e){const t=o.randomBytes(16).toString("base64url");return`scrypt$${t}$${(await In(e,t,64)).toString("base64url")}`}function _n(e){return{id:e.id,username:e.username,role:e.role,createdAt:e.createdAt,updatedAt:e.updatedAt,lastLoginAt:e.lastLoginAt}}class xn{mode;users;sessions;constructor(e,t={}){this.mode=function(e={}){return bn.parse(e.mode||process.env.TASK_HANDOFF_CONTROL_PLANE_AUTH_MODE||"disabled")}(t),this.users=new hn(e.authUsersDir,{schema:zn}),this.sessions=new hn(e.authSessionsDir,{schema:Tn})}init(){this.users.init(),this.sessions.init()}enabled(){return"password"===this.mode}state(){return{mode:this.mode,enabled:this.enabled(),requiresBootstrap:this.enabled()&&0===this.users.list().length}}async bootstrapAdmin(e){if(!this.enabled()){const e=new Error("Control Plane authentication is disabled.");throw Object.assign(e,{statusCode:400,code:"AUTH_DISABLED"}),e}if(this.users.list().length>0){const e=new Error("Control Plane admin user already exists.");throw Object.assign(e,{statusCode:409,code:"AUTH_BOOTSTRAP_ALREADY_DONE"}),e}const t=Sn.parse(e),s=Cn();return _n(this.users.put({id:mn("user"),username:t.username,passwordHash:await Nn(t.password),role:"admin",createdAt:s,updatedAt:s}))}async login(e){if(!this.enabled()){const e=new Error("Control Plane authentication is disabled.");throw Object.assign(e,{statusCode:400,code:"AUTH_DISABLED"}),e}const t=An.parse(e),s=this.users.list().find(e=>e.username===t.username);if(!s||!await async function(e,t){const[s,n,i]=t.split("$");if("scrypt"!==s||!n||!i)return!1;const a=await In(e,n,64),r=Buffer.from(i,"base64url");return r.length===a.length&&o.timingSafeEqual(r,a)}(t.password,s.passwordHash)){const e=new Error("Invalid username or password.");throw Object.assign(e,{statusCode:401,code:"AUTH_LOGIN_FAILED"}),e}const n=Cn(),i=gn(),a=new Date(Date.now()+12096e5).toISOString(),r=this.sessions.put({id:mn("sess"),userId:s.id,tokenHash:En(i),expiresAt:a,createdAt:n,updatedAt:n,lastSeenAt:n});return this.users.put({...s,lastLoginAt:n,updatedAt:n}),{user:_n({...s,lastLoginAt:n,updatedAt:n}),sessionToken:`${r.id}.${i}`,expiresAt:a}}async userForSessionToken(e){if(!this.enabled())return;if(!e)return;const[t,s]=e.split(".");if(!t||!s)return;const n=this.sessions.get(t);if(!n||n.expiresAt<=Cn()||n.tokenHash!==En(s))return;const i=this.users.get(n.userId);return i?(this.sessions.put({...n,lastSeenAt:Cn(),updatedAt:Cn()}),_n(i)):void 0}async currentSession(e){const t=this.state(),s=await this.userForSessionToken(e);return{...t,authenticated:!this.enabled()||Boolean(s),user:s}}logout(e){const t=e?.split(".")[0];return t&&this.sessions.delete(t),{ok:!0}}}const On=r.z.enum(["import","export"]),Rn=r.z.object({id:r.z.string().trim().min(1).max(120),label:r.z.string().trim().min(1).max(120),directoryName:r.z.string().trim().min(1).max(120)}).strict(),jn=r.z.object({direction:On,programIds:r.z.array(r.z.string().trim().min(1).max(120)).min(1).max(20).refine(e=>new Set(e).size===e.length,"Config sync program ids must be unique."),workspaceFolder:r.z.string().trim().min(1).max(1e3)}).strict(),Dn=r.z.object({import:r.z.string().trim().min(1).max(1e3).default("."),export:r.z.string().trim().min(1).max(1e3).default(".")}).strict(),Mn=r.z.object({programs:r.z.array(Rn),preferences:Dn}).strict(),Pn=r.z.object({id:r.z.string().trim().min(1).max(120),type:r.z.enum(["file","dir"]),projectPath:r.z.string().trim().min(1).max(1e3),containerPath:r.z.string().trim().min(1).max(1e3),source:r.z.string().trim().min(1).max(4096),target:r.z.string().trim().min(1).max(4096),status:r.z.enum(["copied","skipped_missing_source","failed"]),error:r.z.string().trim().max(4096).optional()}).strict(),$n=r.z.object({preset:r.z.object({id:r.z.string().trim().min(1).max(120),label:r.z.string().trim().min(1).max(120),projectRoot:r.z.string().trim().min(1).max(1e3)}).strict(),direction:On,items:r.z.array(Pn).max(100)}).strict(),qn=r.z.object({direction:On,workspaceFolder:r.z.string().trim().min(1).max(1e3),programs:r.z.array($n).min(1).max(20)}).strict(),Ln="app-session.snapshot",Un="app-session.patch",Fn="app-session.removed",Bn=r.z.enum(["created","starting","running","stopping","stopped","exited","failed","closed","terminated","unknown"]),Hn=r.z.object({type:r.z.enum(["app-session","provider-session","adapter-key"]),id:r.z.string().trim().min(1).max(240),agent:r.z.string().trim().min(1).max(80).optional(),adapter:r.z.string().trim().min(1).max(80).optional(),key:r.z.string().trim().min(1).max(240).optional()}).strict(),Gn=r.z.record(r.z.string(),r.z.unknown()).and(r.z.object({id:r.z.string().trim().min(1).max(160),appId:r.z.string().trim().min(1).max(160).optional(),title:r.z.string().trim().max(240).optional(),kind:r.z.string().trim().max(80).optional(),status:Bn.default("unknown"),bindings:r.z.array(Hn).max(40).default([]),workspace:r.z.object({cwd:r.z.string().trim().max(4096).optional()}).strict().optional(),createdAt:r.z.string().datetime().optional(),updatedAt:r.z.string().datetime().optional()})),Wn=r.z.object({runningCount:r.z.number().int().min(0).default(0),problemCount:r.z.number().int().min(0).default(0),sessions:r.z.array(Gn).default([]),updatedAt:r.z.string().datetime()}).strict(),Vn=r.z.object({streamId:r.z.string().trim().min(1).max(240),revision:r.z.number().int().min(0),lastEventAt:r.z.string().datetime(),snapshot:Wn}).strict();Wn.pick({runningCount:!0,problemCount:!0,updatedAt:!0}).extend({revision:r.z.number().int().min(0).optional()});const Kn=r.z.enum(["app-session-created","app-session-updated","app-session-deleted","app-session-recovered","control-action","heartbeat-sync","startup"]),Jn=r.z.object({streamId:r.z.string().trim().min(1).max(240),instanceId:r.z.string().trim().min(1).max(160),nodeId:r.z.string().trim().min(1).max(160).optional(),revision:r.z.number().int().min(0),previousRevision:r.z.number().int().min(0).optional(),traceId:r.z.string().trim().min(1).max(160),generatedAt:r.z.string().datetime(),reason:Kn}).strict(),Qn=r.z.object({meta:Jn,snapshot:Wn}).strict(),Yn=r.z.object({meta:Jn,session:Gn}).strict(),Xn=r.z.object({meta:Jn,sessionId:r.z.string().trim().min(1).max(160),tombstone:Gn.optional(),expiresAt:r.z.string().datetime().optional()}).strict(),Zn=r.z.object({streamId:r.z.string().trim().min(1).max(240),instanceId:r.z.string().trim().min(1).max(160),sinceRevision:r.z.number().int().min(0),latestRevision:r.z.number().int().min(0),earliestRetainedRevision:r.z.number().int().min(0),syncRequired:r.z.boolean().default(!1),events:r.z.array(r.z.discriminatedUnion("type",[r.z.object({type:r.z.literal(Ln),payload:Qn}).strict(),r.z.object({type:r.z.literal(Un),payload:Yn}).strict(),r.z.object({type:r.z.literal(Fn),payload:Xn}).strict()])).default([])}).strict();function ei(e,t=(new Date).toISOString()){const s=e.map(e=>ti(e));return Wn.parse({runningCount:s.filter(e=>"running"===e.status).length,problemCount:s.filter(e=>"failed"===e.status).length,sessions:s,updatedAt:t})}function ti(e){const t=si("string"==typeof e.status?e.status:void 0),s="string"==typeof e.id?e.id:"",n=function(e){const t=oi(e.workspace),s=oi(e.tty),n=oi(e.launch),i=oi(e.ai),a=oi(i.claude),r=di(t.cwd)||di(s.cwd)||di(a.cwd)||di(n.cwd);return r?{cwd:r}:void 0}(e),{workspace:i,...a}=e;return Gn.parse({...a,status:t,bindings:ri([...(r=e.bindings,Array.isArray(r)?r.flatMap(e=>{const t=Hn.safeParse(e);return t.success?[t.data]:[]}):[]),...s?[{type:"app-session",id:s}]:[],...ii(e)]),...n?{workspace:n}:{}});var r}function si(e){const t=(e||"unknown").trim().toLowerCase();return"ended"===t?"closed":"terminating"===t?"stopping":Bn.options.includes(t)?t:"unknown"}function ni(e){return"app-session"===e.type?`app:${e.id}`:"provider-session"===e.type?`provider:${e.agent||""}:${e.id}`:`adapter:${e.adapter||""}:${e.key||e.id}`}function ii(e){const t=oi(e.ai),s=oi(t.claude),n=oi(t.appServer),i=[],a=di(s.short);a&&i.push({type:"adapter-key",adapter:"claude",agent:"claude",id:`short:${a}`,key:`short:${a}`});const r=di(s.providerSessionId);r&&i.push({type:"provider-session",agent:"claude",id:r});const o=Array.isArray(t.threadIds)?t.threadIds.filter(e=>"string"==typeof e&&Boolean(e.trim())):[],d=di(t.activeThreadId);for(const e of[...new Set([d,...o].filter(Boolean))])i.push({type:"provider-session",agent:"codex",id:e});const c=di(n.socketPath);return c&&i.push({type:"adapter-key",adapter:"codex-app-server",agent:"codex",id:`socket:${c}`,key:`socket:${c}`}),i}function ai(e){const t="string"==typeof e.id?e.id:"",s=oi(e.ai),n=di(oi(s.claude).short);return[t?`app:${t}`:"",n?`claude-short:${n}`:""]}function ri(e){const t=new Map;for(const s of e)t.set(ni(s),s);return[...t.values()]}function oi(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function di(e){return"string"==typeof e&&e.trim()?e.trim():""}function ci(e=(new Date).toISOString()){return Wn.parse({runningCount:0,problemCount:0,sessions:[],updatedAt:e})}function li(e,t,s,n){const i=n&&"object"==typeof n?n:{};return{nodeId:e.id,route:"string"==typeof i.route?i.route:t,method:s,code:"string"==typeof i.code?i.code:"NODE_AGENT_REQUEST_FAILED",message:n instanceof Error?n.message:String(n),statusCode:"number"==typeof i.statusCode?i.statusCode:void 0,issues:Array.isArray(i.issues)?i.issues:void 0}}class pi{requestImpl;logger;constructor(e){this.requestImpl=e.request,this.logger=e.logger}async request(e,t,s={}){const n=s.method||"GET";let i,a;try{i=await this.requestImpl(e,t,s)}catch(s){throw this.logger?.warn?.({nodeId:e.id,route:t,method:n,error:s instanceof Error?s.message:String(s),errorCode:"NODE_AGENT_TRANSPORT_FAILED"},"node agent transport failed"),s}try{a=await i.json()}catch(s){this.logger?.warn?.({nodeId:e.id,route:t,method:n,statusCode:i.status,error:s instanceof Error?s.message:String(s),errorCode:"NODE_AGENT_RESPONSE_INVALID_JSON"},"node agent response was not valid json");const a=new Error(`Node agent ${e.id} returned invalid JSON for ${t}.`);throw Object.assign(a,{statusCode:502,code:"NODE_AGENT_RESPONSE_INVALID_JSON",nodeId:e.id,route:t}),a}if(!i.ok){const s=new Error(a.error?.message||`Node agent request failed with HTTP ${i.status}`);throw Object.assign(s,{statusCode:i.status,code:a.error?.code||"NODE_AGENT_REQUEST_FAILED",nodeId:e.id,route:t}),this.logger?.warn?.({nodeId:e.id,route:t,method:n,statusCode:i.status,errorCode:a.error?.code||"NODE_AGENT_REQUEST_FAILED",error:s.message},"node agent request failed"),s}return this.logger?.info?.({nodeId:e.id,route:t,method:n,statusCode:i.status},"node agent request ok"),a.data??a}async requestSchema(e,t,s,n={}){return this.parse(e,t,s,await this.request(e,t,n),n.method||"GET")}parse(e,t,s,n,i="GET"){const a=s.safeParse(n);if(a.success)return a.data;this.logger?.warn?.({nodeId:e.id,route:t,method:i,errorCode:"NODE_AGENT_PROTOCOL_INVALID",issues:a.error.issues},"node agent protocol response did not match schema");const r=new Error(`Node agent ${e.id} returned invalid protocol data for ${t}.`);throw Object.assign(r,{statusCode:502,code:"NODE_AGENT_PROTOCOL_INVALID",nodeId:e.id,route:t,issues:a.error.issues}),r}}class ui{client;constructor(e){this.client=e}request(e,t,s={}){return this.client.request(e,t,s)}health(e){return this.client.requestSchema(e,"/health",Rs)}checkUpdate(e,t){return this.client.requestSchema(e,"/updates/check",qt,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}applyUpdate(e,t){return this.client.requestSchema(e,"/updates/apply",Lt,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}listUpdateJobs(e){return this.client.requestSchema(e,"/updates/jobs",r.z.array(Lt))}getExternalListener(e){return this.client.requestSchema(e,"/settings/external-listener",js)}updateExternalListener(e,t){return this.client.requestSchema(e,"/settings/external-listener",js,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}createPairingInvite(e,t={}){return this.client.requestSchema(e,"/pairing/invites",Ds,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t&&"object"==typeof t?t:{})})}connectRemote(e,t){return this.client.requestSchema(e,"/remotes/connect",Ps,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}listRemotes(e){return this.client.requestSchema(e,"/remotes",r.z.array(Ms))}deleteRemote(e,t){return this.client.requestSchema(e,`/remotes/${encodeURIComponent(t)}`,$s,{method:"DELETE"})}listDockerImages(e){return this.client.requestSchema(e,"/docker/images",r.z.array(qs))}listFolderTree(e,t={}){const s=new URLSearchParams;t.path&&s.set("path",t.path),void 0!==t.depth&&s.set("depth",String(t.depth));const n=s.toString();return this.client.requestSchema(e,"/folders/tree"+(n?`?${n}`:""),r.z.array(Us))}listLocalFolders(e){return this.client.requestSchema(e,"/local-folders",r.z.array(Gt))}createLocalFolder(e,t){return this.client.requestSchema(e,"/local-folders",Gt,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}async requireLocalFolder(e,t){const s=(await this.listLocalFolders(e)).find(e=>e.id===t);if(!s){const s=new Error(`Local folder ${t} was not found on node ${e.id}.`);throw Object.assign(s,{statusCode:404,code:"NODE_LOCAL_FOLDER_NOT_FOUND",nodeId:e.id,route:"/local-folders"}),s}return s}listRuntimes(e){return this.client.requestSchema(e,"/runtimes",r.z.array(_s))}createRuntime(e,t){return this.client.requestSchema(e,"/runtimes",_s,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}updateRuntime(e,t,s){const n=`/runtimes/${encodeURIComponent(t)}`;return this.client.requestSchema(e,n,_s,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}checkRuntime(e,t){const s=`/runtimes/${encodeURIComponent(t)}/check`;return this.client.requestSchema(e,s,_s,{method:"POST"})}deleteRuntime(e,t){return this.client.requestSchema(e,`/runtimes/${encodeURIComponent(t)}`,$s,{method:"DELETE"})}deleteLocalFolder(e,t){return this.client.requestSchema(e,`/local-folders/${encodeURIComponent(t)}`,$s,{method:"DELETE"})}listModels(e){return this.client.requestSchema(e,"/models",r.z.array(Xt))}createModel(e,t){return this.client.requestSchema(e,"/models",Xt,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}deployModel(e,t,s){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}/deploy`,Xt,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}updateModel(e,t,s){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}`,Xt,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}deleteModel(e,t){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}`,$s,{method:"DELETE"})}assignInstanceModels(e,t,s){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/model-assignment`,r.z.object({assignment:Zt,instance:Ws}).strict(),{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}async listFleetModels(e){return(await Promise.allSettled(e.map(async e=>({node:e,models:await this.listModels(e)})))).reduce((t,s,n)=>{if("fulfilled"===s.status)t.items.push(...s.value.models.map(e=>({nodeId:s.value.node.id,model:e})));else{const i=e[n];i&&t.nodeErrors.push(li(i,"/models","GET",s.reason))}return t},{items:[],nodeErrors:[]})}async listFleetRuntimes(e){return(await Promise.allSettled(e.map(async e=>({node:e,runtimes:await this.listRuntimes(e)})))).reduce((t,s,n)=>{if("fulfilled"===s.status)t.items.push(...s.value.runtimes);else{const i=e[n];i&&t.nodeErrors.push(li(i,"/runtimes","GET",s.reason))}return t},{items:[],nodeErrors:[]})}listInstances(e){return this.listInstancesWithDiagnostics(e).then(e=>e.items)}async listInstancesWithDiagnostics(e){return this.client.requestSchema(e,"/instances",r.z.array(r.z.unknown())).then(t=>t.reduce((t,s)=>{const n=this.normalizeInstance(e,s);return n.instance&&t.items.push(n.instance),n.error&&t.nodeErrors.push(n.error),t},{items:[],nodeErrors:[]}))}createInstance(e,t){return this.client.requestSchema(e,"/instances",Ws,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}updateInstance(e,t,s){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}`,Ws,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}deleteInstance(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/delete`,$s,{method:"POST"})}startInstance(e,t,s={}){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/start`,Ws,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}retryInstanceImageProvisioning(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/image-provisioning/retry`,Ws,{method:"POST"})}stopInstance(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/stop`,Ws,{method:"POST"})}restartInstance(e,t,s={}){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/restart`,Ws,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}instanceResourceMetrics(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/metrics`,xs)}proxyRawInstance(e,t,s){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/proxy/raw`,Fs,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}async listFleetInstances(e){return(await Promise.allSettled(e.map(async e=>({node:e,result:await this.listInstancesWithDiagnostics(e)})))).reduce((t,s,n)=>("fulfilled"===s.status?(t.items.push(...s.value.result.items),t.nodeErrors.push(...s.value.result.nodeErrors)):t.nodeErrors.push(li(e[n],"/instances","GET",s.reason)),t),{items:[],nodeErrors:[]})}normalizeInstance(e,t){const s=(n=t,Ws.safeParse(function(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,s=new Set(Object.keys(Ws.shape)),n={};for(const[e,i]of Object.entries(t))s.has(e)&&(n[e]=i);if(t.capabilities&&"object"==typeof t.capabilities&&!Array.isArray(t.capabilities)){const e={...t.capabilities};Object.prototype.hasOwnProperty.call(e,"apps")&&delete e.apps,n.capabilities=e}else n.capabilities={};n.appInventory=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e;Object.keys(t).some(e=>!["observedAt","items","issues"].includes(e));const s={observedAt:t.observedAt,items:Array.isArray(t.items)?t.items.map(e=>{e&&"object"==typeof e&&!Array.isArray(e)&&Object.keys(e).some(e=>!["id","name","kind","source","availability","capabilities","diagnosticCode"].includes(e));const t=Ys(e,["id","name","kind","source","availability","diagnosticCode"]);return!t||"object"!=typeof t||Array.isArray(t)?t:{...t,capabilities:Ys(e.capabilities,["automation","supportsCwdSelection"])}}):t.items,issues:Array.isArray(t.issues)?t.issues.map(e=>Ys(e,["code","message"])):t.issues},n=ut.safeParse(s);return n.success?n.data:void 0}(t.appInventory,"string"==typeof t.id&&t.id),n.source=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,s=t.type,n=Wt.options.find(e=>e.shape.type.value===s);if(!n)return e;const i="local-folder"===s?["type","localFolderId","path","ownerNodeId"]:"git-template"===s?["type","url","templateId","ref","auth","clone"]:["type","repositoryId","url","provider","ref","auth","clone"],a=Ys(t,i);if("local-folder"!==s){const e=Js(Ft,Ys(t.ref,["type","name","commit"])),s=Js(Bt,Ys(t.auth,["type","secretId"])),n=Js(Ht,Ys(t.clone,["depth","submodules","lfs","subdirectory"]));e?a.ref=e:delete a.ref,s?a.auth=s:delete a.auth,n?a.clone=n:delete a.clone}const r=Js(n,a);return r?(Object.keys(t).some(e=>!i.includes(e)),r):a}(t.source,"string"==typeof t.id&&t.id),n.sourceSnapshot=t.sourceSnapshot&&"object"==typeof t.sourceSnapshot&&!Array.isArray(t.sourceSnapshot)?t.sourceSnapshot:{},n.build=Js(xt,t.build,0,0,"string"==typeof t.id&&t.id),n.aiSessions=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,s=Array.isArray(t.sessions)?t.sessions.flatMap((e,t)=>{const s=Js(ye,e);return s?[s]:[]}):[],n={runningCount:"number"==typeof t.runningCount&&Number.isInteger(t.runningCount)&&t.runningCount>=0?t.runningCount:0,waitingCount:"number"==typeof t.waitingCount&&Number.isInteger(t.waitingCount)&&t.waitingCount>=0?t.waitingCount:0,staleCount:"number"==typeof t.staleCount&&Number.isInteger(t.staleCount)&&t.staleCount>=0?t.staleCount:0,sessions:s,updatedAt:Ve.safeParse(t.updatedAt).success?t.updatedAt:(new Date).toISOString()};return Object.keys(t).some(e=>!["runningCount","waitingCount","staleCount","sessions","updatedAt"].includes(e)),be.parse(n)}(t.aiSessions,"string"==typeof t.id&&t.id),n.triggers=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e;Object.keys(t).some(e=>!["enabledCount","runningCount","errorCount","configs","recentRuns"].includes(e));const s=(e,t,s)=>Array.isArray(e)?e.flatMap((e,s)=>{const n=Js(t,e);return n?[n]:[]}):[],n=Array.isArray(t.configs)?t.configs.flatMap((e,t)=>{if(!e||"object"!=typeof e||Array.isArray(e))return[];const n=e;Object.keys(n).some(e=>!["configHash","config","deployments","runtime"].includes(e));const i=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,s=Ys(t,["configHash","name","description","createdAt","updatedAt"]);if(t.source&&"object"==typeof t.source&&!Array.isArray(t.source)){const e=t.source,n="schedule"===e.type?"interval"===e.scheduleKind?["type","scheduleKind","intervalMs"]:"daily"===e.scheduleKind?["type","scheduleKind","timeOfDay","timezone"]:["type","scheduleKind","weekdays","timeOfDay","timezone"]:"file-change"===e.type?["type","roots","globs","ignore","debounceMs"]:["type","agent","statuses","phases"];s.source=Ys(e,n)}return s.action=Ys(t.action,["promptTemplate"]),s.policy=Ys(t.policy,["cooldownMs","maxConcurrentRuns","whenBusy"]),Js(Pe,s)}(n.config);return"string"==typeof n.configHash&&i?[{configHash:n.configHash,config:i,deployments:s(n.deployments,qe),runtime:s(n.runtime,Le)}]:[]}):[];return{enabledCount:"number"==typeof t.enabledCount&&Number.isInteger(t.enabledCount)&&t.enabledCount>=0?t.enabledCount:0,runningCount:"number"==typeof t.runningCount&&Number.isInteger(t.runningCount)&&t.runningCount>=0?t.runningCount:0,errorCount:"number"==typeof t.errorCount&&Number.isInteger(t.errorCount)&&t.errorCount>=0?t.errorCount:0,configs:n,recentRuns:s(t.recentRuns,Ue)}}(t.triggers,"string"==typeof t.id&&t.id),n.apps=Js(Ws.shape.apps.unwrap(),Ys(t.apps,["runningCount","problemCount","updatedAt","revision"]),0,0,"string"==typeof t.id&&t.id)||{runningCount:0,problemCount:0},n.config=Js(Ws.shape.config.unwrap(),Ys(t.config,["autoImportAgentConfigs","defaultCodexPermissionMode"]),0,0,"string"==typeof t.id&&t.id)||{autoImportAgentConfigs:!0,defaultCodexPermissionMode:"ask"},n.modelSelection=Js(at.unwrap(),Ys(t.modelSelection,["codexModelHash","claudeModelHash"]),0,0,"string"==typeof t.id&&t.id)||{},n.imageSnapshot=function(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const s=e,n=Es(s),{reference:i,...a}=n,r=s.requestedReference??i;let o;try{o="string"==typeof r?st(r):void 0}catch{}const d={img_default:"market_taskhandoff_browser",img_codex:"market_taskhandoff_codex",img_ai:"market_taskhandoff_ai"},c="string"==typeof s.id?s.id:"string"==typeof t?t:void 0,l="market"===s.origin||c&&d[c]?"market":"custom";return void 0!==s.image||s.reference,{...a,id:c&&d[c]?d[c]:c,origin:l,repository:o?.repository??s.repository,tag:o?.tag??s.tag,requestedReference:o?.reference??r,resolvedDigest:s.resolvedDigest,resolvedReference:s.resolvedReference,downloadSizeBytes:s.downloadSizeBytes,market:"market"===l?Ys(s.market,["catalogId","catalogRevision","publisher","version"])??{catalogId:"task_handoff_embedded",catalogRevision:"legacy",publisher:"task-handoff"}:void 0}}(t.imageSnapshot,t.imageId,"string"==typeof t.id&&t.id);const i="string"==typeof t.imageId?{img_default:"market_taskhandoff_browser",img_codex:"market_taskhandoff_codex",img_ai:"market_taskhandoff_ai"}[t.imageId]||t.imageId:void 0,a=Ys(t.imageSelection,["imageId","tag"]);n.imageSelection=a||(i?{imageId:i,tag:n.imageSnapshot?.tag}:void 0),n.imageProvisioning=Js(ws,Ys(t.imageProvisioning,["phase","requestedReference","generation","error","startedAt","updatedAt"]),0,0,"string"==typeof t.id&&t.id),n.runtimeVersion=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,s=e=>{if("string"!=typeof e)return;const t=e.trim();return t&&t.length<=80?t:void 0},n=s(t.desiredVersion);if(!n)return;const i=new Set(Dt.shape.phase.options),a="string"==typeof t.phase?t.phase:"pending",r=i.has(a)?a:{idle:"pending",queued:"pending",checking:"pending",downloading:"installing",updating:"installing",completed:"matched",succeeded:"matched",ready:"matched",error:"failed"}[a]||"pending";let o;if(t.error&&"object"==typeof t.error&&!Array.isArray(t.error)){const e=t.error,n="string"==typeof e.message?e.message.trim().slice(0,4096):"";if(n){const t=new Set(jt.shape.code.options),i="string"==typeof e.code&&t.has(e.code)?e.code:"INSTANCE_RUNTIME_INSTALL_FAILED";e.code;const a=s(e.expectedVersion),r=s(e.actualVersion);o={code:i,message:n,...a?{expectedVersion:a}:{},...r?{actualVersion:r}:{},retryable:!0===e.retryable}}}"string"==typeof t.error&&t.error.trim()&&(o={code:"INSTANCE_RUNTIME_INSTALL_FAILED",message:t.error.trim().slice(0,4096),expectedVersion:n,...s(t.actualVersion)?{actualVersion:s(t.actualVersion)}:{},retryable:!1});const d=s(t.actualVersion),c=Ve.safeParse(t.lastAttemptAt),l=Ve.safeParse(t.matchedAt);return void 0!==t.lastAttemptAt&&c.success,void 0!==t.matchedAt&&l.success,{desiredVersion:n,...d?{actualVersion:d}:{},phase:r,attempt:"number"==typeof t.attempt&&Number.isInteger(t.attempt)&&t.attempt>=0?t.attempt:0,...c.success?{lastAttemptAt:c.data}:{},...l.success?{matchedAt:l.data}:{},...o?{error:o}:{}}}(t.runtimeVersion,"string"==typeof t.id&&t.id),n.workspace=Js(Gs,t.workspace,0,0,"string"==typeof t.id&&t.id)||{status:"unknown"},n.runtime=Js(Ws.shape.runtime.unwrap(),Ys(t.runtime,["kind","containerName","containerId","workspacePath","pid","port","labels"]),0,0,"string"==typeof t.id&&t.id)||{labels:{}},n.target=Js(Bs,Ys(t.target??t.endpoints,["strategy","web","api","vnc","tty","logs","status"]),0,0,"string"==typeof t.id&&t.id)||{strategy:"direct-port",status:"unknown"},n.access=Js(Hs,Ys(t.access,["strategy","web","api","ws","status"]),0,0,"string"==typeof t.id&&t.id)||{strategy:"control-plane-proxy",status:"unknown"};for(const[e,t]of Object.entries(Ws.shape))Object.prototype.hasOwnProperty.call(n,e)&&!t.safeParse(n[e]).success&&t.safeParse(void 0).success&&delete n[e];return n}(n)));var n;if(s.success)return s.data.protocolVersion&&s.data.protocolVersion!==He?(this.client.logger?.warn?.({nodeId:e.id,instanceId:s.data.id,expectedProtocolVersion:He,actualProtocolVersion:s.data.protocolVersion,errorCode:"PROTOCOL_VERSION_MISMATCH"},"node instance protocol version mismatch"),{instance:s.data}):{instance:s.data};const i=t&&"object"==typeof t?t:{},a="string"==typeof i.id?i.id:void 0;return this.client.logger?.warn?.({nodeId:e.id,instanceId:a,errorCode:"NODE_INSTANCE_PAYLOAD_INVALID",issues:s.error.issues.map(e=>({path:e.path.join("."),message:e.message}))},"node instance payload invalid"),{error:{nodeId:e.id,route:"/instances",method:"GET",code:"NODE_INSTANCE_PAYLOAD_INVALID",message:a?`Node instance ${a} payload is invalid.`:"Node instance payload is invalid.",statusCode:502,issues:s.error.issues}}}}function mi(){return(new Date).toISOString()}function gi(e){return e?e instanceof Headers?Object.fromEntries(e.entries()):Array.isArray(e)?Object.fromEntries(e.map(([e,t])=>[e,t])):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,String(t)])):{}}function hi(e){const t=String(e||"").split(":");if(3===t.length&&"ai"===t[1]&&t[0]&&t[2])return{instanceId:t[0],aiSessionId:t[2]}}function fi(e){return e instanceof Error?e.message:String(e)}function Ii(e,t){const s=new Error(t);throw Object.assign(s,{statusCode:404,code:e}),s}class yi{requireNode;nodeAgentRequest;nodeAgentStreamRequest;fetchImpl;constructor(e){this.requireNode=e.requireNode,this.nodeAgentRequest=e.nodeAgentRequest,this.nodeAgentStreamRequest=e.nodeAgentStreamRequest,this.fetchImpl=e.fetchImpl}async request(e,t,s={}){if(!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=new Error(`Instance ${e.name} is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_UNREACHABLE"}),t}const n=this.requireNode(e.nodeId),i=await this.nodeAgentRequest(n,`/instances/${encodeURIComponent(e.id)}/proxy`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({path:`/api/${t.replace(/^\/+/,"")}`,method:s.method||"GET",headers:gi(s.headers),body:(a=s.body,"string"==typeof a?a:null==a?void 0:String(a))})});var a;const r=await i.json().catch(()=>({}));if(!i.ok){const s=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,s="string"==typeof t.code&&t.code.trim()?t.code:void 0,n="string"==typeof t.message&&t.message?t.message:void 0,i=t.details&&"object"==typeof t.details&&!Array.isArray(t.details)?t.details:void 0;return s||n?{code:s,message:n,details:i}:void 0}(r.error),n=s?.message||function(e){if(!e||"object"!=typeof e||Array.isArray(e))return"";const t=e.message;return"string"==typeof t?t:""}(r.error)||`Instance request failed with HTTP ${i.status}`,a=new Error(n);throw Object.assign(a,{statusCode:i.status,code:s?.code||"INSTANCE_REQUEST_FAILED",...s?.details?{details:s.details}:{},instanceId:e.id,nodeId:e.nodeId,route:t}),a}return r.data??r}async proxyHttp(e,t,s={}){if("online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=new Error(`Instance ${e.name} is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_UNREACHABLE"}),t}if(!e.target.web){const t=new Error(`Instance ${e.name} web endpoint is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_WEB_UNREACHABLE"}),t}const n=this.requireNode(e.nodeId),i=await this.nodeAgentStreamRequest(n,`/instances/${encodeURIComponent(e.id)}/proxy/stream`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({path:t,method:s.method||"GET",headers:gi(s.headers),...(a=s.body,null==a?{}:"string"==typeof a?{body:a}:Buffer.isBuffer(a)?{bodyBase64:a.toString("base64")}:a instanceof Uint8Array?{bodyBase64:Buffer.from(a).toString("base64")}:{body:String(a)})})});var a;return{status:i.status||502,headers:Object.fromEntries(i.headers.entries()),body:i.body}}proxyWebSocket(e,t,s,n,i,a={}){if(!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=new Error(`Instance ${e.name} web endpoint is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_WEB_UNREACHABLE"}),t}const r=this.requireNode(e.nodeId),o=n.startsWith("/")?n.slice(1):n;t.proxyWebSocket(r,s,`/instances/${encodeURIComponent(e.id)}/proxy/ws/${o}`,i,a)}async reportHeartbeat(e,t,s){if(!e.registrationToken)return;const n=this.requireNode(e.nodeId),i=`/instances/${encodeURIComponent(e.id)}/heartbeat`,a={method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.registrationToken}`},body:JSON.stringify(t)},r="reverse-wss"===n.connectionMode?await(s?.request(n,i,a)):await this.fetchImpl(`${(n.controlEndpoint||n.endpoint||"").replace(/\/$/,"")}/api/node-agent${i}`,a);if(!r)return;const o=await r.json().catch(()=>({}));if(!r.ok){const e=new Error(o.error?.message||`Instance heartbeat sync failed with HTTP ${r.status}`);throw Object.assign(e,{statusCode:r.status,code:"INSTANCE_HEARTBEAT_SYNC_FAILED"}),e}}}function bi(e){if(!e||"object"!=typeof e)return;const t=e,s=xt.safeParse(t.build),n=mt.safeParse(t.platform),i=gt.safeParse(t.arch);return{..."boolean"==typeof t.ok?{ok:t.ok}:{},..."string"==typeof t.role?{role:t.role}:{},..."string"==typeof t.nodeId?{nodeId:t.nodeId}:{},..."string"==typeof t.protocolVersion?{protocolVersion:t.protocolVersion}:{},...n.success?{platform:n.data}:{},...i.success?{arch:i.data}:{},...s.success?{build:s.data}:{},..."string"==typeof t.serverTime?{serverTime:t.serverTime}:{}}}function vi(e){const{agent:t,...s}=e.capabilities,{secret:n,...i}=e.auth,a=i.pairing?{...i.pairing,joinToken:void 0}:void 0,r=bi(t);return{...e,auth:a?{...i,pairing:a}:i,capabilities:r?{...s,agent:r}:s}}function wi(e){return"local-folder"===e.type?{mode:"local-bind",path:"/workspace",readOnly:!1}:{mode:"git-clone",path:"/workspace",readOnly:!1}}function Si(e){return function(e){const{registrationToken:t,target:s,agentStatus:n,targetStatus:i,uiAccessStatus:a,...r}=e;return r}(function(e){const t=`/instances/${encodeURIComponent(e.id)}`,s={strategy:"control-plane-proxy",web:`${t}/`,api:`${t}/api`,ws:`${t}/api`,status:"online"===e.connectionStatus||"online"===e.agentStatus?"reachable":"endpoint-unreachable"};return{...e,access:s}}(e))}function Ai(e){const{key:t,...s}=e;return{...s,keyPreview:(n=e.key,n.length<=8?"set":`${n.slice(0,4)}...${n.slice(-4)}`),keySet:!0};var n}class ki{read(e){const t=new Map(e.projects.map(e=>[e.id,e])),s=new Map(e.images.map(e=>[e.id,e])),n=new Map(e.nodes.map(e=>[e.id,e])),i=new Map(e.runtimes.map(e=>[`${e.nodeId}:${e.id}`,e])),a=Date.now();return{items:e.instances.map(e=>function(e){const t=e.instance.lastHeartbeatAt?e.currentTime-Date.parse(e.instance.lastHeartbeatAt):void 0,s=void 0!==t&&t>3e4?"offline":e.instance.connectionStatus,n=Si(e.instance);return{...n,aiSessions:Se(n.aiSessions),connectionStatus:s,heartbeatAgeMs:t,project:e.project,image:e.image,node:e.node?vi(e.node):void 0,runtime:e.runtime,protocolCompatible:!e.instance.protocolVersion||e.instance.protocolVersion===He}}({instance:e,currentTime:a,project:e.projectId?t.get(e.projectId):void 0,image:e.imageSnapshot||(e.imageSelection?s.get(e.imageSelection.imageId):void 0),node:n.get(e.nodeId),runtime:i.get(`${e.nodeId}:${e.runtimeId}`)})),nodeErrors:e.nodeErrors||[]}}}const zi=Pe.extend({id:Pe.shape.configHash}),Ti=r.z.object({name:Pe.shape.name,description:Pe.shape.description,source:je,action:De,policy:Me.partial().optional()}).strict(),Ci=r.z.object({instanceIds:r.z.array(Ws.shape.id).min(1),target:$e,enabled:r.z.boolean().optional()}).strict(),Ei=r.z.object({configHash:Pe.shape.configHash,enabled:r.z.boolean().optional()}).strict();class Ni{triggers;listInstances;requireInstance;instanceRequest;constructor(e){this.triggers=e.triggers,this.listInstances=e.listInstances,this.requireInstance=e.requireInstance,this.instanceRequest=e.instanceRequest}async listTriggers(){const e=await this.listInstances(),t=new Map;for(const e of this.triggers.list())t.set(e.configHash,{configHash:e.configHash,config:e,deploymentCount:0,enabledCount:0,runningCount:0,errorCount:0,ownedByControlPlane:!0,controlPlaneDeploymentCount:0,deployments:[],recentRuns:[]});for(const s of e){const e=s.triggers;for(const n of e?.configs||[]){const i=t.get(n.configHash)||{configHash:n.configHash,config:n.config,deploymentCount:0,enabledCount:0,runningCount:0,errorCount:0,ownedByControlPlane:!1,controlPlaneDeploymentCount:0,deployments:[],recentRuns:[]},a=(n.deployments||[]).map(e=>({instanceId:s.id,instanceName:s.name,deployment:e,runtime:(n.runtime||[]).find(t=>t.deploymentId?t.deploymentId===e.deploymentId:t.configHash===e.configHash)}));i.deploymentCount+=a.length,i.enabledCount+=a.filter(e=>Boolean(e.deployment.enabled)).length,i.controlPlaneDeploymentCount+=a.filter(e=>"control-plane"===e.deployment.origin).length,i.runningCount+=(n.runtime||[]).filter(e=>"running"===e.status).length,i.errorCount+=(n.runtime||[]).filter(e=>"error"===e.status).length,i.deployments.push(...a),i.recentRuns.push(...(e.recentRuns||[]).filter(e=>e.configHash===n.configHash).map(e=>({...e,instanceId:s.id,instanceName:s.name}))),t.set(n.configHash,i)}}return{updatedAt:(new Date).toISOString(),triggers:[...t.values()].sort((e,t)=>String(e.config.name||e.configHash).localeCompare(String(t.config.name||t.configHash)))}}createTrigger(e){const t=Ti.parse(e||{}),s=mi(),n={maxConcurrentRuns:t.policy?.maxConcurrentRuns??1,whenBusy:t.policy?.whenBusy??"skip",cooldownMs:t.policy?.cooldownMs},i=function(e){const t=Be({source:e.source,action:e.action,policy:Me.parse(e.policy||{})});return`trg_${o.createHash("sha256").update(JSON.stringify(t)).digest("hex").slice(0,24)}`}({source:t.source,action:t.action,policy:n}),a=zi.parse({id:i,configHash:i,name:t.name,description:t.description,source:t.source,action:t.action,policy:n,createdAt:s,updatedAt:s});return this.triggers.put(a)}async deleteTrigger(e){const t=this.triggers.delete(e),s=await this.listInstances(),n=[];for(const t of s){const s=t.triggers?.configs?.find(t=>t.configHash===e);if(!s)continue;const i=(s.deployments||[]).filter(e=>"control-plane"===e.origin);if(!i.length)continue;const a=s.deployments.every(e=>"control-plane"===e.origin);for(const s of i)try{const i=s.deploymentId?`/triggers/${encodeURIComponent(e)}/deployments/${encodeURIComponent(s.deploymentId)}`:a?`/triggers/${encodeURIComponent(e)}`:void 0;if(!i){n.push({instanceId:t.id,configHash:e,deploymentId:s.deploymentId,deleted:!1,skipped:"missing-deployment-id"});continue}const r=await this.instanceRequest(t,i,{method:"DELETE"});if(n.push({instanceId:t.id,configHash:e,deploymentId:s.deploymentId,deleted:!0,data:r}),!s.deploymentId)break}catch(i){n.push({instanceId:t.id,configHash:e,deploymentId:s.deploymentId,deleted:!1,error:i instanceof Error?i.message:String(i)})}}return t||n.length||Ii("TRIGGER_NOT_FOUND",`Trigger ${e} was not found.`),{configHash:e,deletedTemplate:t,results:n}}async applyTrigger(e,t){const s=this.requireTrigger(e),n=Ci.parse(t||{}),i=[];for(const e of n.instanceIds){const t=await this.requireInstance(e);i.push({instanceId:e,data:await this.instanceRequest(t,"/triggers",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:s.name,description:s.description,source:s.source,action:s.action,policy:s.policy,deployment:{origin:"control-plane",enabled:n.enabled??!0,target:n.target}})})})}return{configHash:e,results:i}}async listInstanceTriggers(e){const t=await this.requireInstance(e);return this.instanceRequest(t,"/triggers")}async bindAiSessionTrigger(e,t,s){const n=Ei.parse(s||{}),i=this.requireTrigger(n.configHash),a=await this.requireInstance(e);return this.instanceRequest(a,"/triggers",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:i.name,description:i.description,source:i.source,action:i.action,policy:i.policy,deployment:{deploymentId:_i(t,n.configHash),origin:"control-plane",enabled:n.enabled??!0,target:{type:"ai-session",aiSessionId:t}}})})}async unbindAiSessionTrigger(e,t,s){const n=await this.requireInstance(e),i=_i(t,s);return this.instanceRequest(n,`/triggers/${encodeURIComponent(s)}/deployments/${encodeURIComponent(i)}`,{method:"DELETE"})}async runInstanceTrigger(e,t,s={}){const n=await this.requireInstance(e);return this.instanceRequest(n,`/triggers/${encodeURIComponent(t)}/run`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s||{})})}requireTrigger(e){const t=this.triggers.get(e);return t||Ii("TRIGGER_NOT_FOUND",`Trigger ${e} was not found.`),t}}function _i(e,t){return`ai_${Buffer.from(e).toString("base64url")}_${t}`}const xi=r.z.object({id:Jt.shape.id.optional(),name:Jt.shape.name,source:Wt,defaultImageSelection:Jt.shape.defaultImageSelection,defaultNodeId:Jt.shape.defaultNodeId,defaultRuntimeId:Jt.shape.defaultRuntimeId,workspacePolicy:Vt.optional(),labels:Jt.shape.labels.optional()}).strict(),Oi=xi.omit({id:!0}).partial().strict();function Ri(e){return 1===Array.from(e).length&&!/[\p{L}\p{N}\s]/u.test(e)&&"/"!==e&&"\\"!==e}const ji=r.z.string().refine(Ri,"Mention trigger must be one printable non-alphanumeric character other than / or \\.");function Di(e){return 1===Array.from(e).length&&!/[\p{L}\p{N}\s]/u.test(e)&&"\\"!==e}const Mi=r.z.string().refine(Di,"Command trigger must be one printable non-alphanumeric character other than \\."),Pi=r.z.object({publicBaseUrl:r.z.string().trim().max(2048).optional(),updateChannel:Ot.default("stable"),mentionTrigger:ji.default("@"),commandTrigger:Mi.default("/")}).strict().refine(e=>e.mentionTrigger!==e.commandTrigger,{path:["commandTrigger"],message:"Command and mention triggers must be different."}),$i=r.z.object({publicBaseUrl:r.z.string().trim().max(2048).optional(),updateChannel:Ot.optional(),mentionTrigger:ji.optional(),commandTrigger:Mi.optional()}).strict();function qi(e){if(!e||"object"!=typeof e||Array.isArray(e))return{};const t={...e};return"string"==typeof t.mentionTrigger&&Ri(t.mentionTrigger)||(t.mentionTrigger="@"),"string"==typeof t.commandTrigger&&Di(t.commandTrigger)&&t.commandTrigger!==t.mentionTrigger||(t.commandTrigger="/"),t}const Li=r.z.object({id:Is.shape.id.optional(),name:Is.shape.name,description:Is.shape.description,cover:Is.shape.cover,reference:Is.shape.reference,pullPolicy:Is.shape.pullPolicy.optional(),capabilities:r.z.array(r.z.string().trim().min(1).max(80)).optional(),optionalApps:r.z.array(r.z.string().trim().min(1).max(120)).optional(),defaultEnv:Is.shape.defaultEnv.optional(),labels:Is.shape.labels.optional()}).strict(),Ui=Li.omit({id:!0}).partial().strict();class Fi{options;constructor(e){this.options=e}getSettings(){return Pi.parse(this.options.settings.get())}updateSettings(e){const t=$i.parse(e),s=Pi.parse({...this.getSettings(),...t,..."publicBaseUrl"in t?{publicBaseUrl:t.publicBaseUrl?.trim()||void 0}:{}});return this.options.settings.put(s)}seedDefaults(){}listProjects(){return this.options.projects.list().map(e=>this.normalizeProjectRecord(e))}getProject(e){const t=this.options.projects.get(e);return t?this.normalizeProjectRecord(t):void 0}createProject(e){const t=xi.parse(e),s=mi(),n=Wt.parse(t.source);return this.options.projects.put(Jt.parse({...t,id:t.id||mn("proj"),source:n,defaultImageSelection:t.defaultImageSelection||{imageId:"market_taskhandoff_browser"},defaultNodeId:t.defaultNodeId||this.options.defaultNodeId(),defaultRuntimeId:t.defaultRuntimeId||"runtime_local_docker",workspacePolicy:Vt.parse(t.workspacePolicy||wi(n)),labels:t.labels||{},createdAt:s,updatedAt:s}))}updateProject(e,t){const s=Oi.parse(t),n=this.requireProject(e),i=s.source?Wt.parse(s.source):n.source;return this.options.projects.put(Jt.parse({...n,...s,id:e,source:i,workspacePolicy:s.workspacePolicy?Vt.parse(s.workspacePolicy):n.workspacePolicy,createdAt:n.createdAt,updatedAt:mi()}))}deleteProject(e){return this.options.projects.delete(e)}requireProject(e){const t=this.getProject(e);return t||Ii("PROJECT_NOT_FOUND",`Project ${e} was not found.`),t}listImages(){return this.options.images.list()}getMarketCatalog(){return{catalog:this.options.market.getCatalog(),status:this.options.market.getStatus()}}listImageOptions(e){return[...this.options.market.getCatalog().items.map(t=>this.marketImageOption(t,void 0,e,!0)),...this.listImages().map(e=>this.customImageOption(e))]}resolveImageSelection(e,t){const s=this.options.market.getCatalog().items.find(t=>t.id===e.imageId);if(s)return this.marketImageOption(s,e.tag,t);const n=this.options.images.get(e.imageId);if(n||Ii("IMAGE_NOT_FOUND",`Image ${e.imageId} was not found.`),e.tag&&e.tag!==n.tag)throw Object.assign(new Error(`Custom image ${n.id} cannot override its stored tag.`),{statusCode:400,code:"CUSTOM_IMAGE_TAG_OVERRIDE"});return this.customImageOption(n)}createImage(e){const t=Li.parse(e),s=mi(),n=st(t.reference);return this.options.images.put(Is.parse({...t,id:t.id||mn("img"),origin:"custom",reference:n.reference,repository:n.repository,tag:n.tag,pullPolicy:t.pullPolicy||"if-not-present",capabilities:t.capabilities||[],optionalApps:t.optionalApps||[],defaultEnv:t.defaultEnv||{},labels:t.labels||{},createdAt:s,updatedAt:s}))}updateImage(e,t){const s=Ui.parse(t),n=this.requireImage(e),i=st(s.reference||n.reference);return this.options.images.put(Is.parse({...n,...s,id:e,origin:"custom",reference:i.reference,repository:i.repository,tag:i.tag,createdAt:n.createdAt,updatedAt:mi()}))}deleteImage(e){return this.options.images.delete(e)}requireImage(e){if(this.options.market.getCatalog().items.some(t=>t.id===e))throw Object.assign(new Error(`Market image ${e} is read-only.`),{statusCode:403,code:"MARKET_IMAGE_READ_ONLY"});const t=this.options.images.get(e);return t||Ii("IMAGE_NOT_FOUND",`Image ${e} was not found.`),t}normalizeProjectRecord(e){const t=function(e){if(e&&"object"==typeof e&&!Array.isArray(e)){const t={...ct(e)};return"defaultRuntimeId"in t||(t.defaultRuntimeId="runtime_local_docker"),Jt.parse(t)}return Jt.parse(e)}(e);return t!==e&&this.options.projects.put(t),t}customImageOption(e){return bs.parse({id:e.id,origin:"custom",name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:e.cover||ds,repository:e.repository,tag:e.tag,availableTags:e.tag?[{name:e.tag,reference:e.reference,status:"active"}]:[],reference:e.reference,capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:e.defaultEnv,labels:e.labels,readOnly:!1})}marketImageOption(e,t,s,n=!1){if("yanked"===e.status&&!n)throw Object.assign(new Error(`Market image ${e.id} is no longer selectable.`),{statusCode:409,code:"MARKET_IMAGE_YANKED"});const i=t||e.defaultTag,a=e.tags.find(e=>e.name===i);if(!a)throw Object.assign(new Error(`Market image ${e.id} does not provide tag ${i}.`),{statusCode:400,code:"MARKET_IMAGE_TAG_NOT_FOUND"});if("yanked"===a.status&&!n)throw Object.assign(new Error(`Market image tag ${e.id}:${i} is no longer selectable.`),{statusCode:409,code:"MARKET_IMAGE_TAG_YANKED"});const r=a.platforms.find(e=>!(s?.os&&e.os!==s.os||s?.architecture&&e.architecture!==s.architecture)),o=this.options.market.getCatalog();return bs.parse({id:e.id,origin:"market",name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:e.cover||ds,repository:e.repository,tag:a.name,availableTags:e.tags.map(e=>({name:e.name,version:e.version,reference:e.reference,manifestDigest:e.manifestDigest,status:e.status})),reference:a.reference,digest:r?.digest||a.manifestDigest,downloadSizeBytes:r?.downloadSizeBytes,unpackedSizeBytes:r?.unpackedSizeBytes,capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:e.defaultEnv,labels:e.labels,readOnly:!0,lifecycleStatus:"yanked"===a.status||"yanked"===e.status?"yanked":"deprecated"===a.status||"deprecated"===e.status?"deprecated":"active",market:{catalogId:o.catalogId,catalogRevision:o.revision,publisher:e.publisher,version:a.version}})}}function Bi(e){const t=st(e.reference),s=t.tag||"latest";return{id:e.id,publisher:"task-handoff",slug:e.slug,name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:ds,repository:t.repository,defaultTag:s,tags:[{name:s,reference:t.reference,platforms:[],status:"active"}],capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:{},labels:{"task-handoff.image.kind":"controlled-instance","task-handoff.image.profile":e.slug},status:"active"}}function Hi(){const e=(t=process.env.TASK_HANDOFF_BUILT_AT)&&!Number.isNaN(Date.parse(t))?new Date(t).toISOString():(new Date).toISOString();var t;return hs.parse({protocolVersion:"2026-07-29",catalogId:"task_handoff_embedded",revision:process.env.TASK_HANDOFF_VERSION||"0.0.1",source:"embedded",generatedAt:e,items:[Bi({id:"market_taskhandoff_codex",slug:"codex",name:"TaskHandoff Codex",description:"Minimal Codex runtime with terminal and Codex.",localizedDescriptions:{"zh-CN":"最小 Codex 运行环境,包含终端和 Codex。"},reference:process.env.TASK_HANDOFF_CONTROLLED_CODEX_IMAGE||"huadream/task-handoff-controlled-codex:latest",capabilities:["terminal","codex"],optionalApps:["terminal-tty"]}),Bi({id:"market_taskhandoff_ai",slug:"ai",name:"TaskHandoff Codex + Claude",description:"AI development runtime with Codex, Claude, and terminal.",localizedDescriptions:{"zh-CN":"AI 开发运行环境,包含 Codex、Claude 和终端。"},reference:process.env.TASK_HANDOFF_CONTROLLED_AI_IMAGE||"huadream/task-handoff-controlled-ai:latest",capabilities:["terminal","codex","claude"],optionalApps:["terminal-tty"]}),Bi({id:"market_taskhandoff_browser",slug:"browser",name:"TaskHandoff Browser",description:"Full runtime with Codex, Claude, Chromium, VNC, and code-server.",localizedDescriptions:{"zh-CN":"完整运行环境,包含 Codex、Claude、Chromium、VNC 和 code-server。"},reference:process.env.TASK_HANDOFF_CONTROLLED_BROWSER_IMAGE||"huadream/task-handoff-controlled-browser:latest",capabilities:["browser","terminal","gui-terminal","vscode-web","codex","claude"],optionalApps:["chromium","terminal-tty","gui-terminal","vscode-web"]})]})}class Gi{async loadCatalog(){return Hi()}}class Wi{snapshot;status;constructor(e=Hi()){this.snapshot=hs.parse(e),this.status=fs.parse({source:this.snapshot.source,state:"ready",revision:this.snapshot.revision,updatedAt:this.snapshot.generatedAt})}getCatalog(){return this.snapshot}getStatus(){return this.status}acceptCandidate(e){const t=hs.safeParse(e);return t.success?(this.snapshot=t.data,this.status=fs.parse({source:t.data.source,state:"ready",revision:t.data.revision,updatedAt:(new Date).toISOString()}),{accepted:!0,snapshot:t.data}):(this.status=fs.parse({...this.status,state:"failed",updatedAt:(new Date).toISOString(),error:t.error.message}),{accepted:!1,error:t.error})}recordRefreshFailure(e){return this.status=fs.parse({...this.status,state:this.snapshot?"stale":"failed",updatedAt:(new Date).toISOString(),error:e instanceof Error?e.message:String(e)}),this.snapshot}async refresh(e){try{const t=await e.loadCatalog();return this.acceptCandidate(t)}catch(e){return this.recordRefreshFailure(e),{accepted:!1,error:e}}}}class Vi{records=new Map;options;constructor(e){this.options=e}create(e){const t=o.randomBytes(this.options.tokenBytes).toString("base64url"),s={...e,token:t,expiresAt:new Date(Date.now()+(e.ttlMs||this.options.ttlMs)).toISOString()};return this.records.set(t,s),s}resolve(e,t){const s=this.records.get(e);if(!s)throw this.invalidError();if(s.expiresAt<=(new Date).toISOString())throw this.records.delete(e),this.invalidError();if(t&&!t(s))throw this.invalidError();return s}invalidError(){const e=new Error(this.options.invalidMessage);return Object.assign(e,{statusCode:this.options.invalidStatusCode||400,code:this.options.invalidCode}),e}}class Ki{requireInstance;listAppSessions;tokens=new Vi({invalidMessage:"App access link is invalid or expired.",invalidCode:"APP_ACCESS_TOKEN_INVALID",invalidStatusCode:401,tokenBytes:24,ttlMs:9e5});constructor(e){this.requireInstance=e.requireInstance,this.listAppSessions=e.listAppSessions}createToken(e){return this.tokens.create({instanceId:e.instanceId,sessionId:e.sessionId,mode:e.mode,ttlMs:e.ttlMs})}resolveToken(e,t){return this.tokens.resolve(e,e=>!t||e.mode===t)}async proxyTarget(e,t,s=""){const n=this.resolveToken(e,t),i=await this.requireInstance(n.instanceId),a=await this.listAppSessions(),r=a.instances.find(e=>e.instanceId===n.instanceId)?.appSessions.sessions.find(e=>Yi(e.id)===n.sessionId);if(!r){const e=new Error("App session was not found.");throw Object.assign(e,{statusCode:404,code:"APP_SESSION_NOT_FOUND"}),e}return{access:n,instance:i,session:r,path:Ji(t,n.sessionId,r,s)}}}function Ji(e,t,s,n=""){return"tty"===e?`/api/apps/sessions/${encodeURIComponent(t)}/tty`:"vnc"===e?n?function(e,t){const s=`/${t.replace(/^\/+/,"")}`,n=s.split("?",1)[0]||"/",i=encodeURIComponent(e);if(!["/api/novnc/",`/api/apps/sessions/${i}/web/`,`/api/apps/sessions/${i}/vnc`,`/api/apps/sessions/${i}/novnc/`].some(e=>n===e.replace(/\/$/,"")||n.startsWith(e))){const e=new Error("App access token cannot be used for this path.");throw Object.assign(e,{statusCode:403,code:"APP_ACCESS_PATH_FORBIDDEN"}),e}return s}(t,n):Yi(Qi(s.vnc).noVncPath)||`/api/apps/sessions/${encodeURIComponent(t)}/web/`:Yi(Qi(s.web).webPath)||`/api/apps/sessions/${encodeURIComponent(t)}/web/`}function Qi(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function Yi(e){return"string"==typeof e&&e.trim()?e.trim():void 0}const Xi={allow:"a",deny:"d",skip:"s"};function Zi(e){return o.createHash("sha256").update(e).digest("base64url").slice(0,Math.ceil(16))}class ea{tokens=new Vi({invalidMessage:"Chat action expired.",invalidCode:"CHAT_ACTION_TOKEN_INVALID",tokenBytes:9,ttlMs:6e5});create(e){return this.tokens.create({type:e.type,instanceId:"instanceId"in e?e.instanceId:void 0,appId:"appId"in e?e.appId:void 0,routeId:"routeId"in e?e.routeId:void 0,decision:"decision"in e?e.decision:void 0,ttlMs:e.ttlMs})}resolve(e,t){return this.tokens.resolve(e,e=>!t||e.type===t)}pendingDecisionCallbackData(e,t){return`task_handoff:cp_p:v1:${Xi[t]}:${Zi(e)}`}}function ta(e){const{token:t,...s}=e;return{...s,settings:na(e.settings),tokenSet:Boolean(e.token)}}function sa(e,t){if(!t)return e;const s={...e};for(const[e,n]of Object.entries(t))void 0!==n&&""!==n&&(s[e]=n);return s}function na(e){const t={...e};return"string"==typeof t.clientSecret&&t.clientSecret&&(delete t.clientSecret,t.clientSecretSet=!0),t}const ia=r.z.object({name:Zs.shape.name.optional(),channel:Zs.shape.channel.optional(),enabled:r.z.boolean().optional(),token:Zs.shape.token,defaultChatId:Zs.shape.defaultChatId,allowedUserIds:r.z.array(r.z.string().trim().min(1).max(240)).optional(),pollIntervalMs:Zs.shape.pollIntervalMs.optional(),settings:Zs.shape.settings.optional()}).strict(),aa=ia.extend({channel:Zs.shape.channel,name:Zs.shape.name.optional()});class ra{chatBridges;chatSessions;constructor(e){this.chatBridges=e.chatBridges,this.chatSessions=e.chatSessions}list(){return this.chatBridges.list().map(ta)}require(e){const t=this.chatBridges.get(e);return t||Ii("CHAT_BRIDGE_NOT_FOUND",`Chat bridge ${e} was not found.`),t}create(e){const t=aa.parse(e),s=mi(),n=Zs.parse({...t,id:mn(`chat_${t.channel}`),name:t.name||(i=t.channel,a=this.chatBridges.list().filter(e=>e.channel===t.channel).length+1,`${"telegram"===i?"Telegram":"wechat"===i?"WeChat":"dingding"===i?"DingDing":"Web"} ${a}`),enabled:t.enabled??!1,allowedUserIds:t.allowedUserIds||[],pollIntervalMs:t.pollIntervalMs||3e3,settings:sa({},t.settings),createdAt:s,updatedAt:s});var i,a;return this.chatBridges.put(n),ta(n)}update(e,t){const s=ia.parse(t),n=this.require(e),i=sa(n.settings,s.settings),a=Zs.parse({...n,...s,id:e,channel:n.channel,name:s.name||n.name,settings:i,updatedAt:mi()});return this.chatBridges.put(a),ta(a)}delete(e){const t=this.chatBridges.delete(e);if(t)for(const t of this.chatSessions.list())t.bridgeId===e&&this.chatSessions.delete(t.id);return t}}class oa{chatSessions;constructor(e){this.chatSessions=e.chatSessions}list(){return this.chatSessions.list()}require(e){const t=this.chatSessions.get(e);return t||Ii("CHAT_SESSION_NOT_FOUND",`Chat session ${e} was not found.`),t}upsert(e){const t=mi(),s=(n=e.channel,i=e.chatSessionId,`${e.bridgeId||n}:${i.replace(/[^a-zA-Z0-9_.:-]/g,"_")}`);var n,i;const a=this.chatSessions.get(s),r=en.parse({...a||{},...e,id:s,channel:e.channel,bridgeId:e.bridgeId,chatSessionId:e.chatSessionId,lastUsedAt:t,createdAt:a?.createdAt||t,updatedAt:t});return this.chatSessions.put(r),r}}function da(e){return{inline_keyboard:e.map(e=>e.map(e=>({text:e.text,...e.url?{url:e.url}:{callback_data:e.callbackData||""}})))}}function ca(e,t=40){return da(e.slice(0,t).map(e=>[e]))}function la(e){return Buffer.from(e,"utf8").toString("base64url")}function pa(e){const t=ma(e).inline_keyboard,s=Array.isArray(t)?t:[],n=[];for(const e of s)if(Array.isArray(e))for(const t of e){const e=ma(t),s=String(e.callback_data||""),i=String(e.text||"").trim();i&&s&&n.push({text:i,id:`th_cb_${la(s)}`})}return n.slice(0,40)}function ua(e,t){const s=String(e||"").replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,Math.max(0,t-3))}...`}function ma(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}const ga=new Set(["stopped","failed","exited","closed","terminated"]);function ha(e){return!function(e){return ga.has(si(e))}(e)}function fa(e){return(e.aiSessions?.sessions||[]).filter(t=>function(e,t){const s=(e.apps?.sessions||[]).filter(Sa);if(t.appSessionId&&s.some(e=>e.id===t.appSessionId))return!0;const n=new Set(t.appBindingKeys||[]);return Boolean(n.size&&s.some(e=>function(e){return[...ti(e).bindings.map(ni),...ai(e)].filter(Boolean)}(e).some(e=>n.has(e))))}(e,t))}function Ia(e){return"waiting"===e.status?4:"failed"===e.status?3:"running"===e.status?2:"idle"===e.status?1:0}function ya(e){return[e.cwd||"",e.agent||"",e.providerSessionId||e.id].join("\0")}function ba(e){const t=e.session.title||e.session.userPrompt||e.session.summary||e.session.lastMessage||"",s=t?` - ${ua(t,18)}`:"";return`${e.instance.name} - ${e.session.agent} ${e.session.status}${s}`}function va(e,t=(e,t)=>`task_handoff:approval:${e.id}:${t}`){return ca(e.flatMap(e=>"approval"!==e.kind?[]:[{text:ua(`Allow ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"allow")},{text:ua(`Skip ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"skip")},{text:ua(`Deny ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"deny")}]))}function wa(e){const t=`${e.project?.name||e.projectId} / ${e.instance?.name||e.instanceId}`,s="approval"===e.kind?`/approve ${e.id} | /deny ${e.id} | /skip ${e.id}`:`/reply ${e.id} <message>`;return`[${t}] ${e.kind} ${e.id}: ${e.result}\n${s}`}function Sa(e){const t=e&&"object"==typeof e&&!Array.isArray(e)?e:{};return ha("string"==typeof t.status?t.status:void 0)}class Aa{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async handleAiSessionsCommand(e){const t=await this.targets.aiSessionChoices(e),s=t.find(e=>e.active),n=t.map(e=>function(e){const t=e.session.userPrompt||e.session.summary||e.session.lastMessage||"",s=e.active?"* ":"",n=e.session.phase&&"unknown"!==e.session.phase?`/${e.session.phase}`:"";return`${s}${e.index+1}. ${e.projectName} / ${e.instance.name} - ${e.session.agent} - ${e.session.status}${n} - ${e.session.id}${t?` - ${t}`:""}`}(e));return{accepted:!0,routed:!1,binding:e,instances:t.map(e=>e.instance),reply:t.length?["Select AI session",`Current ${s?`${s.projectName} / ${s.instance.name} / ${s.session.id}`:await this.targets.renderChatTarget(e)}, total ${t.length}`,"",...n].join("\n"):"No AI sessions found.",replyMarkup:t.length?ca(t.map(e=>({text:`${e.active?"✓ ":""}${ba(e)}`,callbackData:`task_handoff:cp_session:${e.index}`}))):void 0}}async handleAiSessionCommand(e,t){const s=String(t[0]||"").trim();if(!s)return this.handleAiSessionsCommand(e);const n=await this.targets.findAiSession(s);if(!n)return{accepted:!1,routed:!1,binding:e,reply:`AI session ${s} was not found. Try /sessions.`};const i=n.instance.projectId?this.deps.getProject(n.instance.projectId):void 0;return{accepted:!0,routed:!1,binding:this.deps.upsertChatSession({...e,activeProjectId:i?.id||e.activeProjectId,activeInstanceId:n.instance.id,activeAiSessionId:n.session.id}),instance:n.instance,aiSession:n.session,reply:`Active AI session: ${n.instance.name} / ${n.session.agent} ${n.session.id}`}}async handleAiSessionIndexAction(e,t){const s=(await this.targets.aiSessionChoices(e)).find(e=>e.index===t);if(!s)return{accepted:!1,routed:!1,binding:e,reply:"AI session was not found. Use /session to refresh the list.",replyMarkup:void 0};const n=this.deps.upsertChatSession({...e,activeProjectId:s.projectId||e.activeProjectId,activeInstanceId:s.instance.id,activeAiSessionId:s.session.id}),i=await this.handleAiSessionsCommand(n);return{...i,accepted:!0,binding:n,instance:s.instance,aiSession:s.session,message:`selected ${s.session.id}`,reply:[`Current chat is bound to AI session ${s.session.id}`,"",i.reply].join("\n")}}async handleAiSessionInterruptCommand(e){if(!e.activeInstanceId||!e.activeAiSessionId)return{accepted:!1,routed:!1,binding:e,reply:"No active AI session is selected. Use /sessions and /session <id> first."};const t=await this.deps.requireControlledInstance(e.activeInstanceId,!0),s=await this.deps.interruptAiSession(t.id,e.activeAiSessionId);return{accepted:!0,routed:!0,binding:e,instance:Si(t),aiSession:s,reply:`Interrupt sent to ${t.name} / ${e.activeAiSessionId}.`}}}function ka(e){return e.providerTurnId||e.turnId||""}function za(e){return ha(_a(e.status))}function Ta(e,t){const s=_a(t.appId)||"App",n=_a(t.title).trim();if(n&&n!==s)return n;const i=function(e,t){for(const s of e.appInventory?.items||[])if(s.id===t)return s.name;return""}(e,s);return i||s}function Ca(e){const t=function(e){return"online"!==e.connectionStatus?[]:(e.appInventory?.items||[]).filter(e=>"available"===e.availability)}(e),s=new Map;for(const e of t){const t=xa(e);t&&!s.has(t.id)&&s.set(t.id,t)}return[...s.values()]}function Ea(e,t,s,n){const i=_a(t.id)?function(e,t){return`/apps/access/${Na(e)}?token=${encodeURIComponent(t())}`}(t,n):`/instances/${encodeURIComponent(e.id)}/`,a=function(e,t){const s=_a(t);if(s&&new RegExp("^https?://","i").test(s))return new URL(e,s.endsWith("/")?s:s+"/").toString()}(i,s);return{text:a||i,url:a}}function Na(e){const t=_a(e.kind);return"gui"===t?"vnc":"web"===t?"web":"tty"}function _a(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function xa(e){if("string"==typeof e&&e.trim())return{id:e,label:Oa(e)};const t=function(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}(e),s=_a(t.id).trim();return s?{id:s,label:_a(t.name).trim()||Oa(s)}:void 0}function Oa(e){return{"terminal-tty":"Terminal","gui-terminal":"GUI Terminal",chromium:"Chromium",browser:"Browser","vscode-web":"VS Code"}[e]||e.replace(/[-_]+/g," ").replace(/\b\w/g,e=>e.toUpperCase())}class Ra{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async handleInstancesCommand(e){const t=await this.targets.instancesForBinding(e);return{accepted:!0,routed:!1,binding:e,instances:t,reply:(s=t.length,0===s?"No controlled instances are registered yet.":`Instances: ${s}. Tap an instance to create an app.`),replyMarkup:t.length?ca(t.map(e=>{return{text:ua(e.name||e.id,56),callbackData:(t=this.deps.createChatActionToken({type:"instance-app-menu",instanceId:e.id}).token,`task_handoff:cp_i:${t}`)};var t})):void 0};var s}async handleInstanceAppMenuAction(e,t){const s=(await this.targets.instancesForBinding(e)).find(e=>e.id===t);if(!s)return{accepted:!1,routed:!1,binding:e,reply:"Instance not found."};const n=Ca(s);return{accepted:!0,routed:!1,binding:e,instance:s,message:`Select an app for ${s.name}.`,reply:n.length?`New app for ${s.name}`:`No launchable apps for ${s.name}.`,replyMarkup:n.length?ca(n.map(e=>{return{text:ua(e.label,56),callbackData:(t=this.deps.createChatActionToken({type:"launch-app",instanceId:s.id,appId:e.id}).token,`task_handoff:cp_a:${t}`)};var t})):void 0}}async handleLaunchAppAction(e,t,s){const n=(await this.targets.instancesForBinding(e)).find(e=>e.id===t);if(!n)return{accepted:!1,routed:!1,binding:e,reply:"Instance not found."};const i=Ca(n).find(e=>e.id===s);return i?{accepted:!0,routed:!1,binding:e,instance:n,session:await this.deps.launchAppSession(n.id,i.id),message:`${i.label} launched`,reply:`Launched ${i.label} on ${n.name}.`}:{accepted:!1,routed:!1,binding:e,reply:`App ${s} is not launchable for ${n.name}.`}}async handleAppsCommand(e){const t=await this.deps.boardAsync(),s=new Map(t.map(e=>[e.id,e])),n=(await this.deps.listAppSessions()).instances.flatMap(e=>{const t=s.get(e.instanceId);return t?e.appSessions.sessions.filter(za).filter(e=>Boolean(_a(e.id))).map(e=>({instance:t,session:e,link:Ea(t,e,this.deps.controlPlanePublicBaseUrl(),()=>this.deps.createAppAccessToken({instanceId:t.id,sessionId:_a(e.id)||"",mode:Na(e)}).token)})):[]}),i=n.filter(e=>e.link.url).map(e=>({text:ua(`${e.instance.name} · ${Ta(e.instance,e.session)}`,56),url:e.link.url}));return{accepted:!0,routed:!1,binding:e,instances:t,reply:(a=n.length,r=i.length,0===a?"No app sessions found.":0===r?`App sessions: ${a}. Set the control plane public URL in Settings > Appearance to open them from chat.`:`App sessions: ${a}. Tap a button to open.`),replyMarkup:i.length?ca(i):void 0};var a,r}}class ja{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async forwardChatMessage(e,t){const s=t.target?.instanceId||e.activeInstanceId||"",n=t.target?.aiSessionId||e.activeAiSessionId,i=await this.deps.requireControlledInstance(s,!0),a=i.projectId?this.deps.requireProject(i.projectId):void 0;if("online"!==i.connectionStatus){const e=new Error(`Instance ${i.name} is not reachable.`);throw Object.assign(e,{statusCode:409,code:"CHAT_TARGET_UNREACHABLE"}),e}if(n){const s=await this.trySendAiSessionMessage(e,i.id,n,t.message.text,t.message.attachments,!t.target);return s.ok?this.sentResult(e,a,i,n,s.aiSession):this.failedSendResult(s.binding,a,i,s.reply)}const r=await this.targets.defaultAiSessionIdForInstance(i.id);if(r){const s=this.deps.upsertChatSession({...e,activeAiSessionId:r}),n=await this.trySendAiSessionMessage(s,i.id,r,t.message.text,t.message.attachments);return n.ok?this.sentResult(s,a,i,r,n.aiSession):this.failedSendResult(n.binding,a,i,n.reply)}return{accepted:!1,routed:!1,binding:e,project:a||void 0,instance:Si(i),reply:`No active AI session is selected for ${i.name}. Use /sessions and /session <id> first.`}}async trySendAiSessionMessage(e,t,s,n,i=[],a=!0){try{return{ok:!0,binding:e,aiSession:await this.deps.sendAiSessionMessage(t,s,n||"请查看附件。",void 0,i)}}catch(t){return function(e){return"AI_SESSION_NOT_FOUND"===(e&&"object"==typeof e?e:{}).code}(t)?{ok:!1,binding:a?this.deps.upsertChatSession({...e,activeAiSessionId:void 0}):e,reply:`The selected AI session ${s} no longer exists. Use /session to choose an active AI session.`}:{ok:!1,binding:e,reply:`Failed to send to AI session ${s}: ${fi(t)}`}}}failedSendResult(e,t,s,n){return{accepted:!1,routed:!1,binding:e,project:t||void 0,instance:Si(s),reply:n}}sentResult(e,t,s,n,i){return{accepted:!0,routed:!0,binding:e,project:t||void 0,instance:Si(s),aiSession:i,instanceId:s.id,aiSessionId:n,turnId:(a=i,a.turnId||a.providerTurnId||a.session.activeTurnId||""),providerTurnId:ka(i),reply:`Sent to ${t?.name||s.sourceSnapshot.name||"Source"} / ${s.name} / ${n}.`};var a}}class Da{deps;constructor(e){this.deps=e}async handlePendingCommand(e){const t=await this.deps.listPendingRoutes(),s=e.activeInstanceId?t.filter(t=>t.instanceId===e.activeInstanceId):t;return{accepted:!0,routed:!1,binding:e,pending:s,reply:s.length?s.map(wa).join("\n"):"No pending routes.",replyMarkup:s.length?va(s,(e,t)=>this.deps.pendingDecisionCallbackData(e.id,t)):void 0}}async handlePendingDecisionCommand(e,t,s){const n=hi(t[0]);return n?n.aiSessionId?{accepted:!0,routed:!0,binding:e,pending:n,aiSession:await this.deps.resolveAiSessionApproval(n.instanceId,n.aiSessionId,s),reply:`${s} sent for ${n.instanceId}:${n.aiSessionId}.`}:{accepted:!1,routed:!1,binding:e,pending:n,reply:"Only AI session pending routes are supported here."}:{accepted:!1,routed:!1,binding:e,reply:`Usage: /${"allow"===s?"approve":s} <instance-id:pending-id>`}}async handlePendingReplyCommand(e,t){const s=hi(t[0]),n=t.slice(1).join(" ").trim();return s&&n?s.aiSessionId?{accepted:!0,routed:!0,binding:e,pending:s,aiSession:await this.deps.sendAiSessionMessage(s.instanceId,s.aiSessionId,n),reply:`Reply sent for ${s.instanceId}:${s.aiSessionId}.`}:{accepted:!1,routed:!1,binding:e,pending:s,reply:"Only AI session pending routes are supported here."}:{accepted:!1,routed:!1,binding:e,reply:"Usage: /reply <instance-id:pending-id> <message>"}}}class Ma{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async handleUseCommand(e,t){if(0===t.length)return{accepted:!0,routed:!1,binding:e,instances:await this.targets.instancesForBinding(e),reply:await this.targets.renderUseHelp(e)};if(t.length>=2){const s=this.targets.resolveProject(t[0]),n=await this.targets.resolveInstance(t[1],s.id),i=this.deps.upsertChatSession({...e,activeProjectId:s.id,activeInstanceId:n.id,activeAiSessionId:await this.targets.defaultAiSessionIdForInstance(n.id)});return{accepted:!0,routed:!1,binding:i,reply:await this.targets.renderTargetSelected(i)}}const s=t[0],n=await this.targets.findInstance(s);if(n){const t=n.projectId?this.deps.requireProject(n.projectId):void 0,s=this.deps.upsertChatSession({...e,activeProjectId:t?.id,activeInstanceId:n.id,activeAiSessionId:await this.targets.defaultAiSessionIdForInstance(n.id)});return{accepted:!0,routed:!1,binding:s,reply:await this.targets.renderTargetSelected(s)}}const i=this.targets.resolveProject(s),a=(await this.deps.boardAsync()).filter(e=>e.projectId===i.id),r=a.filter(e=>"running"===e.status&&"online"===e.connectionStatus);if(1===r.length){const t=this.deps.upsertChatSession({...e,activeProjectId:i.id,activeInstanceId:r[0].id,activeAiSessionId:await this.targets.defaultAiSessionIdForInstance(r[0].id)});return{accepted:!0,routed:!1,binding:t,reply:await this.targets.renderTargetSelected(t)}}return{accepted:!0,routed:!1,binding:this.deps.upsertChatSession({...e,activeProjectId:i.id,activeInstanceId:void 0,activeAiSessionId:void 0}),instances:a,reply:`${i.name} selected. Choose an instance with /use ${i.id} <instance-id-or-name>.\n${this.targets.renderInstanceLines(a)}`}}async handleProjectCommand(e,t){if(0===t.length)return{accepted:!1,routed:!1,binding:e,reply:"Usage: /project <project-id-or-name>"};const s=this.targets.resolveProject(t[0]),n=e.activeInstanceId?await this.deps.requireControlledInstance(e.activeInstanceId).catch(()=>{}):void 0,i=this.deps.upsertChatSession({...e,activeProjectId:s.id,activeInstanceId:n?.projectId===s.id?n.id:void 0,activeAiSessionId:n?.projectId===s.id?e.activeAiSessionId:void 0});return{accepted:!0,routed:!1,binding:i,reply:`Active project: ${s.name}${i.activeInstanceId?"":". Choose an instance with /use."}`}}}class Pa{deps;constructor(e){this.deps=e}async instancesForBinding(e){const t=await this.deps.boardAsync();return e.activeProjectId?t.filter(t=>t.projectId===e.activeProjectId):t}async fullInstancesForBinding(e){const t=await this.deps.listNodeInstances();return e.activeProjectId?t.filter(t=>t.projectId===e.activeProjectId):t}async renderUseHelp(e){return`Active target: ${await this.renderChatTarget(e)}\nUse /instances to list targets, then /use <instance-id-or-name>. Use /sessions to choose an AI session when there is more than one.`}async renderChatStatus(e){const t=e.activeInstanceId?(await this.deps.boardAsync()).find(t=>t.id===e.activeInstanceId):void 0;return t?`Active target: ${await this.renderChatTarget(e)}\nStatus: ${t.status}, connection: ${t.connectionStatus}`:`Active target: ${await this.renderChatTarget(e)}`}async renderChatHelp(e){return["TaskHandoff chat commands:","/help - show this help","/status or /target - show the active project, instance, and AI session","/instances - list controlled instances","/apps - list app sessions","/use <instance-id-or-name> - choose an active instance","/use <project-id-or-name> <instance-id-or-name> - choose a project and instance","/project <project-id-or-name> - narrow instance selection to a project","/sessions - list all AI sessions","/session <ai-session-id> - choose an active AI session","/pending - list waiting approvals","/approve <instance-id:pending-id>, /deny <...>, /skip <...> - answer approvals","/reply <instance-id:pending-id> <message> - reply to a waiting session","/cancel - interrupt the active AI session","","Send normal text after selecting a target; if there is only one AI session, it is selected automatically.",`Current target: ${await this.renderChatTarget(e)}`].join("\n")}renderInstanceLines(e){return e.map(e=>`${e.name} (${e.id}) - ${e.status}/${e.connectionStatus}`).join("\n")}async renderChatTarget(e){const t=e.activeProjectId?this.deps.getProject(e.activeProjectId):void 0,s=e.activeInstanceId?await this.deps.requireControlledInstance(e.activeInstanceId).catch(()=>{}):void 0,n=e.activeAiSessionId||"none";return`${t?.name||e.activeProjectId||"none"} / ${s?.name||e.activeInstanceId||"none"} / ${n}`}async renderTargetSelected(e){const t=await this.renderChatTarget(e);return e.activeAiSessionId?`Active target: ${t}`:`Active target: ${t}\nNo AI session selected yet. Use /sessions and /session <id>, or send a message once exactly one session is available.`}async defaultAiSessionIdForInstance(e){const t=await this.aiSessionSnapshotForInstance(e),s=(await this.deps.boardAsync()).find(t=>t.id===e),n=(await this.deps.listAppSessions()).instances.find(t=>t.instanceId===e),i=s?{...s,apps:{...s.apps,sessions:n?.appSessions.sessions||[]}}:void 0,a=t&&s?fa({...i,aiSessions:t}):t?.sessions||[];return 1===a.length?a[0].id:void 0}async aiSessionChoices(e){return(await this.boardInstancesForBinding({},{refresh:!0})).flatMap(t=>fa(t).map(s=>({index:0,projectId:t.projectId,projectName:t.project?.name||("string"==typeof t.sourceSnapshot.name?t.sourceSnapshot.name:"Source"),instance:t,session:s,active:e.activeInstanceId===t.id&&e.activeAiSessionId===s.id}))).sort((e,t)=>{const s=Ia(t.session)-Ia(e.session);return s||(String(e.instance.name||"").localeCompare(String(t.instance.name||""))||e.instance.id.localeCompare(t.instance.id)||ya(e.session).localeCompare(ya(t.session)))}).map((e,t)=>({...e,index:t}))}async boardInstancesForBinding(e,t={}){const s=await this.deps.boardAsync(),n=await this.deps.listAiSessions(t),i=await this.deps.listAppSessions(t),a=new Map(n.instances.map(e=>[e.instanceId,e.aiSessions])),r=new Map(i.instances.map(e=>[e.instanceId,e.appSessions])),o=s.map(e=>{return{...e,apps:{...e.apps,sessions:r.get(e.id)?.sessions||[]},aiSessions:a.get(e.id)||(t=e.aiSessions.updatedAt,{runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:t})};var t});return e.activeProjectId?o.filter(t=>t.projectId===e.activeProjectId):o}async findAiSession(e){const t=await this.boardInstancesForBinding({},{refresh:!0});for(const s of t){const t=(s.aiSessions?.sessions||[]).find(t=>t.id===e||t.providerSessionId===e);if(t)return{instance:s,session:t}}}async aiSessionSnapshotForInstance(e){const t=await this.deps.listAiSessions();return t.instances.find(t=>t.instanceId===e)?.aiSessions}resolveProject(e){const t=this.deps.getProject(e)||this.deps.listProjects().find(t=>t.name===e);return t||Ii("PROJECT_NOT_FOUND",`Project ${e} was not found.`),t}async resolveInstance(e,t){const s=await this.findInstance(e,t);return s||Ii("CONTROLLED_INSTANCE_NOT_FOUND",`Controlled instance ${e} was not found.`),s}async findInstance(e,t){return(await this.deps.listNodeInstances()).find(s=>!(s.id!==e&&s.name!==e||t&&s.projectId!==t))}}class $a{deps;targets;sender;appCommands;pendingCommands;aiSessionCommands;targetCommands;constructor(e){this.deps=e,this.targets=new Pa(e),this.sender=new ja(e,this.targets),this.appCommands=new Ra(e,this.targets),this.pendingCommands=new Da(e),this.aiSessionCommands=new Aa(e,this.targets),this.targetCommands=new Ma(e,this.targets)}async handleChatGatewayMessage(e){const t=tn.parse(e),s=this.deps.upsertChatSession({channel:t.source.channel,bridgeId:t.source.bridgeId,chatSessionId:t.source.chatSessionId,userId:t.source.userId}),n=t.message.text.trim();return n.startsWith("/")?this.handleChatCommand(s,n):n||t.message.attachments.length?s.activeInstanceId?this.sender.forwardChatMessage(s,t):{accepted:!1,routed:!1,binding:s,reply:await this.targets.renderUseHelp(s)}:{accepted:!1,routed:!1,binding:s,reply:""}}async handleChatGatewayAction(e){const t=this.deps.upsertChatSession({channel:e.source.channel,bridgeId:e.source.bridgeId,chatSessionId:e.source.chatSessionId,userId:e.source.userId});return"ai-session"===e.action.type?this.aiSessionCommands.handleAiSessionIndexAction(t,e.action.index):"instance-app-menu"===e.action.type?this.appCommands.handleInstanceAppMenuAction(t,e.action.instanceId):"launch-app"===e.action.type?this.appCommands.handleLaunchAppAction(t,e.action.instanceId,e.action.appId):"pending-decision"===e.action.type?this.pendingCommands.handlePendingDecisionCommand(t,[e.action.routeId],e.action.decision):{accepted:!1,routed:!1,binding:t,reply:"Unknown action."}}async handleChatCommand(e,t){const[s="",...n]=t.split(/\s+/),i=s.replace(/@[\w_]+$/,"");switch(i.toLowerCase()){case"/help":case"/start":return{accepted:!0,routed:!1,binding:e,reply:await this.targets.renderChatHelp(e)};case"/sessions":return this.aiSessionCommands.handleAiSessionsCommand(e);case"/session":return this.aiSessionCommands.handleAiSessionCommand(e,n);case"/cancel":case"/interrupt":return this.aiSessionCommands.handleAiSessionInterruptCommand(e);case"/pending":return this.pendingCommands.handlePendingCommand(e);case"/approve":return this.pendingCommands.handlePendingDecisionCommand(e,n,"allow");case"/deny":return this.pendingCommands.handlePendingDecisionCommand(e,n,"deny");case"/skip":return this.pendingCommands.handlePendingDecisionCommand(e,n,"skip");case"/reply":return this.pendingCommands.handlePendingReplyCommand(e,n);case"/instances":return this.appCommands.handleInstancesCommand(e);case"/apps":return this.appCommands.handleAppsCommand(e);case"/use":return this.targetCommands.handleUseCommand(e,n);case"/project":return this.targetCommands.handleProjectCommand(e,n);case"/status":case"/target":return{accepted:!0,routed:!1,binding:e,instance:e.activeInstanceId?(await this.deps.boardAsync()).find(t=>t.id===e.activeInstanceId):void 0,reply:await this.targets.renderChatStatus(e)};default:return{accepted:!1,routed:!1,binding:e,reply:`Unknown command: ${i||s}. Try /help.`}}}}const qa=r.z.object({id:r.z.string().trim().min(1),preferences:Dn,createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}).strict();function La(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,s=t.preferences&&"object"==typeof t.preferences&&!Array.isArray(t.preferences)?t.preferences:{};return{id:t.id,preferences:{import:"string"==typeof s.import&&s.import.trim()?s.import:".",export:"string"==typeof s.export&&s.export.trim()?s.export:"."},createdAt:t.createdAt,updatedAt:t.updatedAt}}function Ua(){return Dn.parse({})}function Fa(e){return/^[a-z]:[\\/]/i.test(e)||/^[\\/]{2}[^\\/]+[\\/]+[^\\/]+/.test(e)}function Ba(e){return Fa(e)?t.win32:t.posix}const Ha=r.z.object({mode:r.z.enum(["local-static-key","paired-hmac"]).default("local-static-key"),keyId:Ns.shape.id.optional(),secret:r.z.string().trim().max(4096).optional(),pairedAt:Ns.shape.createdAt.optional(),pairing:r.z.object({status:r.z.enum(["pending","paired","expired"]).default("pending"),joinToken:r.z.string().trim().max(4096).optional(),expiresAt:Ns.shape.createdAt.optional()}).strict().optional()}).strict(),Ga=r.z.object({autoImportAgentConfigs:r.z.boolean().optional(),defaultCodexPermissionMode:H.optional()}).strict(),Wa=r.z.object({id:Ws.shape.id.optional(),name:Ws.shape.name.optional(),projectId:Ws.shape.projectId,source:Wt.optional(),sourceSnapshot:r.z.record(r.z.string(),r.z.unknown()).optional(),nodeId:Ws.shape.nodeId.optional(),runtimeId:Ws.shape.runtimeId.optional(),imageSelection:Ws.shape.imageSelection,config:Ga.optional(),modelSelection:Ws.shape.modelSelection.optional(),start:r.z.boolean().default(!1)}).strict(),Va=r.z.object({name:Ws.shape.name.optional(),config:Ga.optional(),modelSelection:Ws.shape.modelSelection.optional()}).strict(),Ka=r.z.object({name:Qt.shape.name,endpoint:Qt.shape.endpoint,key:Qt.shape.key,model:Qt.shape.model,app:Qt.shape.app,enabled:Qt.shape.enabled.optional(),order:Qt.shape.order.optional(),labels:Qt.shape.labels.optional()}).strict(),Ja=Ka.partial().strict(),Qa=r.z.object({id:Ns.shape.id.optional(),name:Ns.shape.name,connectionMode:Ns.shape.connectionMode.optional(),auth:Ha.optional(),joinToken:r.z.string().trim().min(1).max(4096).optional(),endpoint:Ns.shape.endpoint,controlEndpoint:Ns.shape.controlEndpoint,containerEndpoint:Ns.shape.containerEndpoint,publicWebBase:Ns.shape.publicWebBase,status:r.z.enum(["unknown","online","offline","degraded"]).optional(),health:r.z.enum(["unknown","ok","degraded","failed"]).optional(),capabilities:Ns.shape.capabilities.optional(),labels:Ns.shape.labels.optional(),lastSeenAt:Ns.shape.lastSeenAt}).strict(),Ya=r.z.object({name:Ns.shape.name.optional(),connectionMode:Ns.shape.connectionMode.unwrap().optional(),auth:Ha.optional(),endpoint:Ns.shape.endpoint,controlEndpoint:Ns.shape.controlEndpoint,containerEndpoint:Ns.shape.containerEndpoint,publicWebBase:Ns.shape.publicWebBase,status:Ns.shape.status.unwrap().optional(),health:Ns.shape.health.unwrap().optional(),capabilities:Ns.shape.capabilities.unwrap().optional(),labels:Ns.shape.labels.unwrap().optional(),lastSeenAt:Ns.shape.lastSeenAt}).strict(),Xa=r.z.object({controlPlaneUrl:r.z.string().trim().url().max(2048),joinToken:r.z.string().trim().min(1).max(4096),controlPlaneName:r.z.string().trim().min(1).max(160).optional(),activate:r.z.boolean().optional()}).strict(),Za=r.z.object({nodeName:r.z.string().trim().min(1).max(160).optional(),expiresInMs:r.z.number().int().positive().max(36e5).optional()}).strict();function er(e){return{enabledCount:e.deployments.filter(e=>e.enabled).length,runningCount:e.runtime.filter(e=>"running"===e.status).length,errorCount:e.runtime.filter(e=>"error"===e.status).length,configs:e.configs.map(t=>({configHash:t.configHash,config:t,deployments:e.deployments.filter(e=>e.configHash===t.configHash),runtime:e.runtime.filter(e=>e.configHash===t.configHash)})),recentRuns:e.recentRuns.slice(0,20)}}const tr="TASK_HANDOFF_NODE_AGENT_V1",sr=6e4;function nr(e=""){return o.createHash("sha256").update(e).digest("hex")}function ir(e,t){return o.createHmac("sha256",e).update(function(e){return[tr,e.keyId,e.method.toUpperCase(),e.pathWithQuery,e.timestamp,e.nonce,e.bodySha256].join("\n")}(t)).digest("hex")}function ar(e){const t=e.timestamp||(new Date).toISOString(),s=e.nonce||o.randomBytes(18).toString("base64url"),n=nr(e.body||""),i=ir(e.secret,{keyId:e.keyId,method:e.method,pathWithQuery:e.pathWithQuery,timestamp:t,nonce:s,bodySha256:n});return{"x-taskhandoff-node-id":e.nodeId,"x-taskhandoff-key-id":e.keyId,"x-taskhandoff-timestamp":t,"x-taskhandoff-nonce":s,"x-taskhandoff-body-sha256":n,"x-taskhandoff-signature":i}}function rr(e){if(e.startsWith("ipc://"))return decodeURIComponent(e.slice(6))}function or(e,t=process.platform){if(!function(e,t=process.platform){if("win32"===t)return e.startsWith("\\\\.\\pipe\\");const n=s.statSync(e),i=process.getuid?.();return void 0===i||n.uid===i}(e,t)){const e=new Error("Local node agent IPC socket is not owned by the current user.");throw Object.assign(e,{statusCode:403,code:"NODE_AGENT_IPC_OWNER_INVALID"}),e}}function dr(e,t,s,n={}){const i=`/api/node-agent${t}`,a={createConnection:()=>u.connect(e),headers:{host:"task-handoff-node-agent.local",...n}};return s&&(Array.isArray(s)?s.length>0:s.trim().length>0)?new l.WebSocket(`ws://task-handoff-node-agent.local${i}`,s,a):new l.WebSocket(`ws://task-handoff-node-agent.local${i}`,a)}const cr=r.z.object({id:r.z.string().trim().min(1),tokenHash:r.z.string().trim().min(1),expiresAt:r.z.string().datetime(),nodeName:r.z.string().trim().min(1).max(160).optional(),createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}).strict(),lr="task-handoff.control-plane.local",pr="task-handoff.control-plane.builtin";function ur(e){return"true"===e.labels[lr]}function mr(e){return"true"===e.labels[pr]}function gr(e){const t=function(e){return"paired-hmac"===e.auth.mode?e.auth.keyId:void 0}(e);if(!t){const t=new Error(`Node ${e.id} paired-HMAC auth is missing keyId.`);throw Object.assign(t,{statusCode:500,code:"NODE_AGENT_REMOTE_KEY_ID_MISSING"}),t}return t}function hr(e,t){const s=function(e){return"paired-hmac"===e.auth.mode?e.auth.secret:void 0}(e);if(s)return ar({nodeId:e.id,keyId:gr(e),secret:s,method:t.method,pathWithQuery:t.pathWithQuery,body:t.body});const n=function(e){if("local-ipc"===e.connectionMode||"local-loopback"===e.connectionMode)return"local-static-key"===e.auth.mode?e.auth.secret:void 0}(e);return n?{authorization:`Bearer ${n}`}:{}}class fr{projects;models;images;nodes;chatSessions;chatBridges;triggers;nodeJoinInvites;configSyncPreferences;settings;paths;fetchImpl;dockerCommandRunner;logger;appAccessService;chatActionTokenService=new ea;nodeAgentTransport;nodeAgentGateway;controlledInstanceGateway;instanceBoardReader=new ki;controlPlaneTriggerService;catalogService;marketCatalogService;chatBridgeService;chatSessionService;chatSessionRuntime;aiSessionSnapshotProvider;appSessionSnapshotProvider;constructor(e,s={}){var n;this.paths=e,this.fetchImpl=s.fetchImpl||fetch,this.dockerCommandRunner=s.dockerCommandRunner,this.nodeAgentTransport=s.nodeAgentTransport,this.logger=(n=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(n||"").trim().toLowerCase())?s.logger:void 0);const i=new pi({request:(e,t,s)=>this.nodeAgentFetch(e,t,s),logger:this.logger});this.nodeAgentGateway=new ui(i),this.controlledInstanceGateway=new yi({requireNode:e=>this.requireNode(e),nodeAgentRequest:(e,t,s)=>this.nodeAgentFetch(e,t,s),nodeAgentStreamRequest:(e,t,s)=>this.nodeAgentTransportFor(e).requestStream(e,t,s),fetchImpl:this.fetchImpl});const a=e=>({schema:e,logger:(e,t)=>this.logWarn(t,e)});this.projects=new hn(e.projectsDir,{...a(Jt),sanitize:ct}),this.models=new hn(e.modelsDir,a(Qt)),this.images=new hn(e.imagesDir,{...a(Is),sanitize:e=>Es(e,e=>this.logWarn(e,"legacy image profile field was migrated"))}),this.nodes=new hn(e.nodesDir,a(Ns)),this.chatSessions=new hn(e.chatSessionsDir,a(en)),this.chatBridges=new hn(e.chatBridgesDir,a(Zs)),this.triggers=new hn(e.triggersDir,a(zi)),this.nodeJoinInvites=new hn(e.nodeJoinInvitesDir,a(cr)),this.configSyncPreferences=new hn(t.join(e.dataDir,"config-sync-preferences"),{...a(qa),sanitize:La}),this.settings=new fn(e.settingsPath,()=>Pi.parse({}),{...a(Pi),sanitize:qi}),this.marketCatalogService=new Wi,this.catalogService=new Fi({projects:this.projects,images:this.images,market:this.marketCatalogService,settings:this.settings,defaultNodeId:()=>this.defaultNodeId()}),this.controlPlaneTriggerService=new Ni({triggers:this.triggers,listInstances:()=>this.listNodeInstances(),requireInstance:async e=>this.requireControlledInstance(e,!0),instanceRequest:(e,t,s)=>this.instanceRequest(e,t,s)}),this.appAccessService=new Ki({requireInstance:async e=>this.requireControlledInstance(e,!0),listAppSessions:()=>this.listAppSessions()}),this.chatBridgeService=new ra({chatBridges:this.chatBridges,chatSessions:this.chatSessions}),this.chatSessionService=new oa({chatSessions:this.chatSessions}),this.chatSessionRuntime=new $a({upsertChatSession:e=>this.upsertChatSession(e),boardAsync:()=>this.boardAsync(),listAiSessions:e=>this.listAiSessions(e),listAppSessions:e=>this.listAppSessions(e),listNodeInstances:()=>this.listNodeInstances(),listPendingRoutes:()=>this.listPendingRoutes(),pendingDecisionCallbackData:(e,t)=>this.pendingDecisionCallbackData(e,t),createChatActionToken:e=>this.createChatActionToken(e),createAppAccessToken:e=>this.createAppAccessToken(e),controlPlanePublicBaseUrl:()=>this.getSettings().publicBaseUrl||process.env.TASK_HANDOFF_CONTROL_PLANE_URL||process.env.TASK_HANDOFF_PUBLIC_BASE||process.env.TASK_HANDOFF_CONTROL_PLANE_PUBLIC_URL,requireControlledInstance:(e,t)=>this.requireControlledInstance(e,t),requireProject:e=>this.requireProject(e),getProject:e=>this.catalogService.getProject(e),listProjects:()=>this.catalogService.listProjects(),resolveAiSessionApproval:(e,t,s)=>this.resolveAiSessionApproval(e,t,s),sendAiSessionMessage:(e,t,s,n,i)=>this.sendAiSessionMessage(e,t,s,n,i),interruptAiSession:(e,t)=>this.interruptAiSession(e,t),launchAppSession:(e,t,s)=>this.launchAppSession(e,t,s)})}logInfo(e,t){this.logger?.info?.(e,t)}logWarn(e,t){this.logger?.warn?.(e,t)}logError(e,t){this.logger?.error?.(e,t)}setNodeAgentTransport(e){this.nodeAgentTransport=e}setAiSessionSnapshotProvider(e){this.aiSessionSnapshotProvider=e}async listAiSessions(e={}){return this.aiSessionSnapshotProvider?this.aiSessionSnapshotProvider(e):this.bootstrapAiSessionsFromInstances()}setAppSessionSnapshotProvider(e){this.appSessionSnapshotProvider=e}async listAppSessions(e={}){return this.appSessionSnapshotProvider?this.appSessionSnapshotProvider(e):this.bootstrapAppSessionsFromInstances()}init(){this.projects.init(),this.models.init(),this.images.init(),this.nodes.init(),this.chatSessions.init(),this.chatBridges.init(),this.triggers.init(),this.nodeJoinInvites.init(),this.settings.init(),this.migrateLegacyImageCatalog(),this.seedDefaults()}migrateLegacyImageCatalog(){const e=Object.keys(ot),n=[];for(const t of e){const e=this.images.filePath(t);if(s.existsSync(e))try{n.push({id:t,filePath:e,record:JSON.parse(s.readFileSync(e,"utf8"))})}catch(e){return this.writeMarketMigrationState("failed",{code:"LEGACY_IMAGE_BACKUP_READ_FAILED",legacyId:t,error:fi(e)}),void this.logWarn({legacyId:t,error:fi(e)},"legacy embedded image migration was not applied")}}if(!n.length)return;const i=s.readdirSync(this.paths.projectsDir).filter(e=>e.endsWith(".json")).sort().map(e=>t.join(this.paths.projectsDir,e)),a=[];for(const e of i)try{const t=JSON.parse(s.readFileSync(e,"utf8")),n=Jt.parse(ct(t));a.push({filePath:e,raw:t,migrated:n})}catch(t){return this.writeMarketMigrationState("failed",{code:"PROJECT_REFERENCE_VALIDATION_FAILED",filePath:e,error:fi(t)}),void this.logWarn({filePath:e,error:fi(t)},"legacy embedded image migration was not applied")}const r=a.find(({migrated:t})=>{const s=t.defaultImageSelection?.imageId;return!!s&&e.includes(s)});if(r)this.writeMarketMigrationState("failed",{code:"PROJECT_REFERENCE_MIGRATION_INCOMPLETE",filePath:r.filePath});else{s.mkdirSync(this.paths.marketDir,{recursive:!0}),c.sync(this.paths.marketMigrationBackupPath,`${JSON.stringify({version:He,createdAt:mi(),images:n.map(({id:e,record:t})=>({id:e,record:t})),projects:a.map(({filePath:e,raw:s})=>({file:t.basename(e),record:s}))},null,2)}\n`,{encoding:"utf8"});for(const{migrated:e}of a)this.projects.put(e);for(const{id:e}of n)this.images.delete(e),this.logWarn({legacyId:e,marketId:ot[e]},"embedded image profile migrated to Market");this.writeMarketMigrationState("complete",{code:"LEGACY_IMAGE_MIGRATION_COMPLETE",migratedImageIds:n.map(({id:e})=>e),backupPath:this.paths.marketMigrationBackupPath})}}writeMarketMigrationState(e,t){s.mkdirSync(this.paths.marketDir,{recursive:!0}),c.sync(this.paths.marketStatePath,`${JSON.stringify({status:e,updatedAt:mi(),...t},null,2)}\n`,{encoding:"utf8"})}getSettings(){return this.catalogService.getSettings()}updateSettings(e){return this.catalogService.updateSettings(e)}seedDefaults(){return this.catalogService.seedDefaults()}listProjects(){return this.catalogService.listProjects()}defaultNodeId(){return this.nodes.list().find(ur)?.id||this.nodes.list()[0]?.id}async inspectNodeAgent(e,t){const s="/health",n=t?hr(t,{method:"GET",pathWithQuery:`/api/node-agent${s}`}):{},i=await this.fetchNodeAgentEndpoint(e,s,{headers:n}),a=await i.json().catch(()=>({}));if(!i.ok){const t=new Error(a.error?.message||`Node agent health check failed with HTTP ${i.status}`);throw Object.assign(t,{statusCode:i.status,code:"NODE_AGENT_HEALTH_FAILED"}),this.logWarn({nodeEndpoint:e,statusCode:i.status,errorCode:"NODE_AGENT_HEALTH_FAILED"},"node agent health check failed"),t}const r="string"==typeof a.data?.nodeId&&a.data.nodeId.trim()?a.data.nodeId.trim():void 0;if(!r){const t=new Error("Node agent health response did not include nodeId.");throw Object.assign(t,{statusCode:502,code:"NODE_AGENT_ID_MISSING"}),this.logWarn({nodeEndpoint:e,payload:a.data,errorCode:"NODE_AGENT_ID_MISSING"},"node agent health response missing node id"),t}a.data?.protocolVersion!==He&&this.logWarn({nodeId:r,nodeEndpoint:e,expectedProtocolVersion:He,actualProtocolVersion:a.data?.protocolVersion,build:a.data?.build,errorCode:"PROTOCOL_VERSION_MISMATCH"},"node agent protocol version mismatch");const o=xt.safeParse(a.data?.build);return this.logInfo({nodeId:r,nodeEndpoint:e,protocolVersion:a.data.protocolVersion,build:o.success?o.data:a.data?.build},"node agent health check ok"),{nodeId:r,data:a.data||{}}}async fetchNodeAgentEndpoint(e,t,s={}){const n=rr(e);return n?(or(n),async function(e,t,s={}){const n=s.method||"GET",i="string"==typeof s.body||s.body instanceof Buffer?s.body:void 0===s.body||null===s.body?void 0:String(s.body),a=new Headers(s.headers||{});return void 0===i||a.has("content-length")||a.set("content-length",String(Buffer.byteLength(i))),new Promise((r,o)=>{const d=p.request({socketPath:e,method:n,path:`/api/node-agent${t}`,headers:Object.fromEntries(a.entries())},e=>{const t=e.statusCode||500;r(new Response("HEAD"!==n&&204!==t&&205!==t&&304!==t?m.Readable.toWeb(e):null,{status:t,headers:e.headers}))});d.on("error",o);const c=()=>d.destroy(new DOMException("The operation was aborted.","AbortError"));s.signal?.aborted?c():s.signal?.addEventListener("abort",c,{once:!0}),d.once("close",()=>s.signal?.removeEventListener("abort",c)),void 0!==i&&d.write(i),d.end()})}(n,t,s)):this.fetchImpl(`${e.replace(/\/$/,"")}/api/node-agent${t}`,s)}async completeNodeAgentPairing(e,t){const s=await this.fetchImpl(`${e.replace(/\/$/,"")}/api/node-agent/pairing/complete`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({joinToken:t.joinToken,controlPlaneName:t.controlPlaneName||"Control Plane",controlPlaneUrl:t.controlPlaneUrl})}),n=await s.json().catch(()=>({}));if(!s.ok){const e=new Error(n.error?.message||`Node agent pairing failed with status ${s.status}.`);throw Object.assign(e,{statusCode:s.status,code:"NODE_AGENT_PAIRING_FAILED"}),e}if("string"!=typeof n.data?.nodeId||!n.data.nodeId.trim()||"string"!=typeof n.data?.keyId||!n.data.keyId.trim()||"string"!=typeof n.data?.secret||!n.data.secret.trim()){const e=new Error("Node agent pairing response did not include nodeId, keyId, and secret.");throw Object.assign(e,{statusCode:502,code:"NODE_AGENT_PAIRING_RESPONSE_INVALID"}),e}return{nodeId:n.data.nodeId.trim(),keyId:n.data.keyId.trim(),secret:n.data.secret.trim(),pairedAt:"string"==typeof n.data.pairedAt?n.data.pairedAt:mi()}}async syncLocalNodeConnection(){const e={[lr]:"true",[pr]:"true"},t=process.env.TASK_HANDOFF_NODE_AGENT_TOKEN?.trim()||void 0,s=process.env.TASK_HANDOFF_NODE_AGENT_CONTROL_ENDPOINT||process.env.TASK_HANDOFF_NODE_AGENT_ENDPOINT||"http://127.0.0.1:8091",n=function(e){return rr(e)?"local-ipc":"local-loopback"}(s),i=Ns.parse({id:"node_local_probe",name:"Local Node Probe",connectionMode:n,auth:{mode:"local-static-key",secret:t},labels:e,createdAt:mi(),updatedAt:mi()}),a=await this.inspectNodeAgent(s,i),r=mi(),o=this.nodes.get(a.nodeId),d=this.nodes.list().filter(e=>ur(e)&&e.id!==a.nodeId);for(const e of d)this.nodes.delete(e.id);const c=Ns.parse({...o||{},id:a.nodeId,name:o?.name||"Local Node",connectionMode:n,auth:{mode:"local-static-key",secret:t},endpoint:s,controlEndpoint:s,containerEndpoint:process.env.TASK_HANDOFF_NODE_AGENT_CONTAINER_URL,publicWebBase:process.env.TASK_HANDOFF_INSTANCE_PUBLIC_WEB_BASE,status:"online",health:"ok",capabilities:{...o?.capabilities||{},agent:bi(a.data)},labels:{...o?.labels||{},...e},createdAt:o?.createdAt||r,updatedAt:r});return this.nodes.put(c)}listModels(){return this.listAllModels().map(Ai)}async listFederatedModels(){const e=this.listNodes(),t=await this.nodeAgentGateway.listFleetModels(e),s=new Map;for(const e of this.listAllModels())s.set(e.id,{id:e.id,model:Ai(e),locations:[{type:"control-plane",name:e.name,enabled:e.enabled,order:e.order}],referenceCount:0});for(const{nodeId:e,model:n}of t.items){const{referenceCount:t,...i}=n,a=s.get(n.id);a?(a.locations.push({type:"node",nodeId:e,name:n.name,enabled:n.enabled,order:n.order,referenceCount:n.referenceCount}),a.referenceCount+=n.referenceCount):s.set(n.id,{id:n.id,model:i,locations:[{type:"node",nodeId:e,name:n.name,enabled:n.enabled,order:n.order,referenceCount:n.referenceCount}],referenceCount:n.referenceCount})}return ns.parse({models:[...s.values()].sort((e,t)=>e.model.order-t.model.order||e.model.name.localeCompare(t.model.name)),nodeDiagnostics:t.nodeErrors.map(e=>({nodeId:e.nodeId,code:e.code,message:e.message})),updatedAt:mi()})}listAllModels(){return this.models.list().map(e=>this.normalizeModelRecord(e)).sort((e,t)=>e.order-t.order||e.createdAt.localeCompare(t.createdAt)||e.id.localeCompare(t.id))}async createModel(e){const t=Ka.parse(e),s=mi(),n=this.nextModelOrder(),i=is(t),a=this.models.get(i),r=Qt.parse({...t,id:i,enabled:t.enabled??!0,order:t.order??n,labels:t.labels||{},createdAt:a?.createdAt||s,updatedAt:s});return Ai(this.models.put(r))}async updateModel(e,t){const s=Ja.parse(t),n=this.requireModelSecret(e),i=Qt.parse({...n,...s,key:s.key?.trim()?s.key:n.key,createdAt:n.createdAt,updatedAt:mi()}),a=is(i);return Ai(this.models.put(Qt.parse({...i,id:a})))}async deleteModel(e){return this.requireModelSecret(e),this.models.delete(e)}async reorderModels(e){const t=[...new Set(e)],s=new Map(this.models.list().map(e=>[e.id,e]));for(const e of t)s.has(e)||Ii("MODEL_NOT_FOUND",`Model ${e} was not found.`);return t.forEach((e,t)=>{const n=s.get(e);this.models.put(Qt.parse({...n,order:100*(t+1),updatedAt:mi()}))}),this.listModels()}createNodeModel(e,t){return this.nodeAgentGateway.createModel(this.requireNode(e),t)}updateNodeModel(e,t,s){return this.nodeAgentGateway.updateModel(this.requireNode(e),t,s)}deleteNodeModel(e,t){return this.nodeAgentGateway.deleteModel(this.requireNode(e),t)}createProject(e){return this.catalogService.createProject(e)}updateProject(e,t){return this.catalogService.updateProject(e,t)}deleteProject(e){return this.catalogService.deleteProject(e)}listImages(){return this.catalogService.listImages()}getMarketCatalog(){return this.catalogService.getMarketCatalog()}async refreshMarketCatalog(){return await this.marketCatalogService.refresh(new Gi),this.getMarketCatalog()}listImageOptions(){return this.catalogService.listImageOptions()}createImage(e){return this.catalogService.createImage(e)}async listNodeDockerImages(e){const t=this.requireNode(e);return this.nodeAgentGateway.listDockerImages(t)}async listNodeImageAvailability(e){const t=this.requireNode(e),s=t.capabilities.agent&&"object"==typeof t.capabilities.agent?t.capabilities.agent:{},n={os:"string"==typeof s.platform?s.platform:void 0,architecture:"string"==typeof s.arch?"x64"===s.arch?"amd64":s.arch:void 0},i=this.catalogService.listImageOptions(n);try{const e=await this.nodeAgentGateway.listDockerImages(t);return i.map(t=>{const s=e.find(e=>e.reference===t.reference||!!t.digest&&e.repoDigests.some(e=>e.endsWith(`@${t.digest}`))||e.repoDigests.includes(t.reference));return Ls.parse({image:t,status:s?"available":"pull-required",localImage:s,localSizeBytes:s?.sizeBytes,downloadSizeBytes:s?void 0:t.downloadSizeBytes,unpackedSizeBytes:s?void 0:t.unpackedSizeBytes})})}catch(e){const t=e instanceof Error?e.message:String(e);return i.map(e=>Ls.parse({image:e,status:"unknown",error:t}))}}async listNodeLocalFolders(e){const t=this.requireNode(e);return this.nodeAgentGateway.listLocalFolders(t)}async listNodeFolderTree(e,t={}){const s=this.requireNode(e);return this.nodeAgentGateway.listFolderTree(s,t)}async createNodeLocalFolder(e,t){const s=this.requireNode(e);return this.nodeAgentGateway.createLocalFolder(s,t)}async deleteNodeLocalFolder(e,t){const s=this.requireNode(e);return this.nodeAgentGateway.deleteLocalFolder(s,t)}async checkNode(e){const t=this.requireNode(e);try{const e=await this.nodeAgentGateway.health(t),s=e&&"object"==typeof e?e:{},n="string"==typeof s.nodeId&&s.nodeId.trim()?s.nodeId.trim():t.id,i=Ns.parse({...t,id:n,status:"online",health:"ok",capabilities:"object"==typeof e&&e?{...t.capabilities,agent:bi(e)}:t.capabilities,lastSeenAt:mi(),updatedAt:mi()});return n!==t.id&&this.nodes.delete(t.id),this.nodes.put(i),{id:i.id,status:"online",checkedAt:mi(),agent:e}}catch(e){const s=Ns.parse({...t,status:"offline",health:"failed",updatedAt:mi()});return this.nodes.put(s),{id:t.id,status:"offline",checkedAt:mi(),error:e instanceof Error?e.message:String(e)}}}async checkNodeUpdate(e,t){return this.nodeAgentGateway.checkUpdate(this.requireNode(e),t)}async applyNodeUpdate(e,t){return this.nodeAgentGateway.applyUpdate(this.requireNode(e),t)}async listNodeUpdateJobs(e){return this.nodeAgentGateway.listUpdateJobs(this.requireNode(e))}async getLocalNodeExternalListener(e){return this.nodeAgentGateway.getExternalListener(this.requireLocalListenerNode(e))}async updateLocalNodeExternalListener(e,t){return this.nodeAgentGateway.updateExternalListener(this.requireLocalListenerNode(e),t)}async createNodePairingInvite(e,t={}){const s=this.requireNode(e);return this.nodeAgentGateway.createPairingInvite(s,t)}requireLocalListenerNode(e){const t=this.requireNode(e);if(!mr(t)||!ur(t)){const e=new Error("Node agent TCP listener settings are available only for the built-in local node.");throw Object.assign(e,{statusCode:403,code:"LOCAL_NODE_LISTENER_ONLY"}),e}return t}pruneNodeJoinInvites(){const e=Date.now();for(const t of this.nodeJoinInvites.list())Date.parse(t.expiresAt)<=e&&this.nodeJoinInvites.delete(t.id)}createNodeJoinInvite(e={}){this.pruneNodeJoinInvites();const t=Za.parse(e&&"object"==typeof e?e:{}),s=mi(),n=gn(),i={id:mn("node_join"),tokenHash:o.createHash("sha256").update(n).digest("hex"),expiresAt:new Date(Date.now()+(t.expiresInMs||6e5)).toISOString(),...t.nodeName?{nodeName:t.nodeName}:{},createdAt:s,updatedAt:s};return this.nodeJoinInvites.put(i),{id:i.id,joinToken:n,expiresAt:i.expiresAt}}completeNodeJoin(e){this.pruneNodeJoinInvites();const t=r.z.object({joinToken:r.z.string().trim().min(1).max(4096),nodeId:Ns.shape.id,nodeName:Ns.shape.name.optional(),keyId:r.z.string().trim().min(1).max(160),secret:r.z.string().trim().min(1).max(4096),pairedAt:r.z.string().datetime().optional()}).parse(e),s=o.createHash("sha256").update(t.joinToken).digest("hex"),n=this.nodeJoinInvites.list().find(e=>e.tokenHash===s&&Date.parse(e.expiresAt)>Date.now());if(!n){const e=new Error("Node join token is invalid or expired.");throw Object.assign(e,{statusCode:401,code:"NODE_JOIN_TOKEN_INVALID"}),e}this.nodeJoinInvites.delete(n.id);const i=mi();if(this.nodes.get(t.nodeId)){const e=new Error(`Node ${t.nodeId} already exists in this control-plane.`);throw Object.assign(e,{statusCode:409,code:"NODE_JOIN_NODE_ALREADY_EXISTS"}),e}const a=Ns.parse({id:t.nodeId,name:t.nodeName||n.nodeName||t.nodeId,connectionMode:"reverse-wss",auth:{mode:"paired-hmac",keyId:t.keyId,secret:t.secret,pairedAt:t.pairedAt||i,pairing:{status:"paired"}},status:"unknown",health:"unknown",capabilities:{},labels:{},createdAt:i,updatedAt:i});return this.nodes.put(a)}async connectNodeToControlPlane(e,t){const s=this.requireNode(e),n=Xa.parse(t);return this.nodeAgentGateway.connectRemote(s,n)}async listNodeRemoteControlPlanes(e){const t=this.requireNode(e);return this.nodeAgentGateway.listRemotes(t)}async deleteNodeRemoteControlPlane(e,t){const s=this.requireNode(e);return this.nodeAgentGateway.deleteRemote(s,t)}updateImage(e,t){return this.catalogService.updateImage(e,t)}async deleteImage(e){this.catalogService.requireImage(e);const t=this.listProjects().find(t=>t.defaultImageSelection?.imageId===e);if(t)throw Object.assign(new Error(`Image ${e} is the default for project ${t.name}.`),{statusCode:409,code:"IMAGE_IN_USE"});const s=(await Promise.all(this.listNodes().map(e=>this.nodeAgentGateway.listLocalFolders(e)))).flat().find(t=>t.defaultImageSelection?.imageId===e);if(s)throw Object.assign(new Error(`Image ${e} is the default for local folder ${s.name}.`),{statusCode:409,code:"IMAGE_IN_USE"});const n=(await this.listNodeInstances()).find(t=>t.imageSelection?.imageId===e);if(n)throw Object.assign(new Error(`Image ${e} is used by instance ${n.name}.`),{statusCode:409,code:"IMAGE_IN_USE"});return this.catalogService.deleteImage(e)}listNodes(){return this.nodes.list()}listPublicNodes(){return this.listNodes().map(vi)}async createNode(e){const t=Qa.parse(e),s=mi(),n=t.connectionMode||"direct-http";let i="auth"in(e&&"object"==typeof e&&!Array.isArray(e)?e:{})?t.auth:{mode:"local-ipc"===n||"local-loopback"===n?"local-static-key":"paired-hmac"};if(("direct-http"===n||"reverse-wss"===n)&&"paired-hmac"!==i.mode){const e=new Error("Remote node connections require paired-HMAC authentication.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_REMOTE_REQUIRES_PAIRED_HMAC"}),e}const a=t.controlEndpoint||t.endpoint;if(!a&&"reverse-wss"!==n){const e=new Error("Node agent direct HTTP mode requires an endpoint.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_ENDPOINT_REQUIRED"}),e}const r=t.joinToken&&a?await this.completeNodeAgentPairing(a,{joinToken:t.joinToken}):void 0;if(r&&(i={mode:"paired-hmac",keyId:r.keyId,secret:r.secret,pairedAt:r.pairedAt,pairing:{status:"paired"}}),("direct-http"===n||"reverse-wss"===n)&&!i.secret){const e=new Error("Remote node connections require a paired node secret or join token.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_REMOTE_SECRET_REQUIRED"}),e}if(("direct-http"===n||"reverse-wss"===n)&&!i.keyId){const e=new Error("Remote node connections require a paired key id.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_REMOTE_KEY_ID_REQUIRED"}),e}const o=t.id||r?.nodeId||mn("node"),d=Ns.parse({id:o,name:t.name,connectionMode:n,auth:i,labels:t.labels||{},createdAt:s,updatedAt:s}),c="reverse-wss"===n&&(t.id||r?.nodeId)?void 0:await this.inspectNodeAgent(a||"",d),l=t.id||r?.nodeId||c?.nodeId||o;if(!l){const e=new Error("Node id is required.");throw Object.assign(e,{statusCode:400,code:"NODE_ID_REQUIRED"}),e}if(t.id&&c?.nodeId&&c.nodeId!==t.id){const e=new Error(`Node agent id ${c.nodeId} does not match requested node id ${t.id}.`);throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_ID_MISMATCH"}),e}const{joinToken:p,...u}=t,m=Ns.parse({...u,id:l,connectionMode:n,auth:i,endpoint:a,controlEndpoint:a,status:t.status||(c?"online":"unknown"),health:t.health||(c?"ok":"unknown"),capabilities:{...t.capabilities||{},...c?{agent:bi(c.data)}:{}},labels:t.labels||{},createdAt:s,updatedAt:s});return this.nodes.put(m)}updateNode(e,t){const s=Ya.parse(t),n=this.requireNode(e),i=Ns.parse({...n,...s,id:e,createdAt:n.createdAt,updatedAt:mi()});return this.nodes.put(i)}deleteNode(e){if(mr(this.requireNode(e))){const e=new Error("The built-in local node connection cannot be deleted.");throw Object.assign(e,{statusCode:400,code:"LOCAL_NODE_CANNOT_BE_DELETED"}),e}return this.nodes.delete(e)}async listNodeRuntimes(e){const t=e?[this.requireNode(e)]:this.listNodes();return e?this.nodeAgentGateway.listRuntimes(t[0]):(await this.nodeAgentGateway.listFleetRuntimes(t)).items}async listNodeRuntimesWithDiagnostics(){return this.nodeAgentGateway.listFleetRuntimes(this.listNodes())}async createNodeRuntime(e,t){const s=this.requireNode(e);return this.nodeAgentGateway.createRuntime(s,t)}async updateNodeRuntime(e,t,s){const n=this.requireNode(e);return this.nodeAgentGateway.updateRuntime(n,t,s)}async deleteNodeRuntime(e,t){const s=this.requireNode(e);return this.nodeAgentGateway.deleteRuntime(s,t)}async checkNodeRuntime(e,t){const s=this.requireNode(e);return this.nodeAgentGateway.checkRuntime(s,t)}async listControlledInstances(){return(await this.listNodeInstances()).map(Si)}async listControlledInstancesWithDiagnostics(){const e=await this.listNodeInstancesWithDiagnostics();return{items:e.items.map(Si),nodeErrors:e.nodeErrors}}async instanceResourceMetrics(e){const t=await this.requireNodeInstance(e);return this.nodeAgentGateway.instanceResourceMetrics(this.requireNode(t.nodeId),t.id)}async nodeOwnsInstance(e,t){const s=this.requireNode(e);return(await this.nodeAgentGateway.listInstances(s)).some(s=>s.id===t&&s.nodeId===e)}async createControlledInstance(e){const t=Wa.parse(e),s=t.projectId?this.requireProject(t.projectId):void 0,n=t.runtimeId||s?.defaultRuntimeId||"runtime_local_docker",i=t.nodeId||s?.defaultNodeId||this.defaultNodeId();if(!i){const e=new Error("At least one node connection is required before creating an instance.");throw Object.assign(e,{statusCode:400,code:"NODE_REQUIRED"}),e}const a=this.requireNode(i),r=await this.requireNodeRuntimeOnNode(a.id,n),o="boolean"==typeof r.capabilities.requiresImage?r.capabilities.requiresImage:"local"!==r.type,d=t.imageSelection||(o?s?.defaultImageSelection:void 0),c=d?this.catalogService.resolveImageSelection(d):void 0;if(o&&!c){const e=new Error(`Runtime ${r.name} requires an image.`);throw Object.assign(e,{statusCode:400,code:"RUNTIME_IMAGE_REQUIRED"}),e}let l=t.source||s?.source,p=t.sourceSnapshot||s||{};if(!l){const e=new Error("Instance source is required.");throw Object.assign(e,{statusCode:400,code:"INSTANCE_SOURCE_REQUIRED"}),e}if("local"===r.type&&"local-folder"!==l.type){const e=new Error("Localhost runtime currently supports local folder sources only.");throw Object.assign(e,{statusCode:400,code:"LOCAL_RUNTIME_REQUIRES_LOCAL_FOLDER"}),e}if("local-folder"===l.type){if(l.ownerNodeId&&l.ownerNodeId!==a.id){const e=new Error(`Local folder ${l.path} belongs to node ${l.ownerNodeId}, not ${a.id}.`);throw Object.assign(e,{statusCode:400,code:"LOCAL_FOLDER_REQUIRES_OWNER_NODE"}),e}if(l.localFolderId){const e=await this.requireNodeLocalFolder(a,l.localFolderId);l={...l,localFolderId:e.id,ownerNodeId:e.nodeId,path:e.path},p=e}else{const e=await this.nodeAgentGateway.createLocalFolder(a,{name:"string"==typeof p.name?p.name:"Local folder",path:l.path,defaultImageSelection:c?{imageId:c.id,tag:c.tag}:void 0});l={...l,localFolderId:e.id,ownerNodeId:e.nodeId,path:e.path},p=e}}const u=await this.prepareInstanceModels(a,t.modelSelection||{}),m=mi(),g=c?{id:c.id,origin:c.origin,name:c.name,description:c.description,localizedDescriptions:c.localizedDescriptions,cover:c.cover,repository:c.repository,tag:c.tag,requestedReference:c.reference,resolvedDigest:c.digest,resolvedReference:c.digest?`${c.repository}@${c.digest}`:void 0,downloadSizeBytes:c.downloadSizeBytes,pullPolicy:"if-not-present",capabilities:c.capabilities,optionalApps:c.optionalApps,defaultEnv:c.defaultEnv,labels:c.labels,market:c.market,createdAt:m,updatedAt:m}:void 0,h=await this.nodeAgentGateway.createInstance(a,{id:t.id,name:t.name,runtimeId:n,...c&&g?{imageSelection:{imageId:c.id,tag:c.tag},image:g}:{},projectId:s?.id,source:l,sourceSnapshot:p,config:t.config,modelSelection:{}});let f=h;try{f=(await this.nodeAgentGateway.assignInstanceModels(a,h.id,u)).instance}catch(e){throw await this.nodeAgentGateway.deleteInstance(a,h.id).catch(()=>{}),e}let I={status:"not-requested"};if(t.start)try{f=await this.nodeAgentGateway.startInstance(a,f.id),I={status:"started"}}catch(e){I={status:"failed",error:Ir(e,"INSTANCE_START_FAILED")},f=await this.nodeAgentGateway.listInstances(a).then(e=>e.find(e=>e.id===f.id)||f).catch(()=>f)}return{...Si(f),registrationToken:f.registrationToken,startOutcome:I}}async updateControlledInstance(e,t){const s=Va.parse(t),n=await this.requireNodeInstance(e),i=this.requireNode(n.nodeId),{modelSelection:a,...r}=s;let o=Object.keys(r).length?await this.nodeAgentGateway.updateInstance(i,e,r):n;if(a){const t=await this.prepareInstanceModels(i,a);o=(await this.nodeAgentGateway.assignInstanceModels(i,e,t)).instance}return Si(o)}async deleteControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId),n=await this.nodeAgentGateway.deleteInstance(s,e);return this.configSyncPreferences.delete(e),Boolean(n.deleted)}async startControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId);return await this.ensureInstanceModelAssignment(t),Si(await this.nodeAgentGateway.startInstance(s,e))}async stopControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId);return Si(await this.nodeAgentGateway.stopInstance(s,e))}async restartControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId);return await this.ensureInstanceModelAssignment(t),Si(await this.nodeAgentGateway.restartInstance(s,e))}async retryControlledInstanceImageProvisioning(e){const t=await this.requireNodeInstance(e);return Si(await this.nodeAgentGateway.retryInstanceImageProvisioning(this.requireNode(t.nodeId),e))}async boardAsync(){return(await this.boardWithDiagnostics()).items}async boardWithDiagnostics(){const e=await this.listNodeRuntimesWithDiagnostics(),t=await this.listNodeInstancesWithDiagnostics();return this.instanceBoardReader.read({projects:this.listProjects(),images:this.listImageOptions(),nodes:this.nodes.list(),runtimes:e.items,instances:t.items,nodeErrors:[...e.nodeErrors,...t.nodeErrors]})}async bootstrapAiSessionsFromInstances(){const e=await this.listNodeInstances(),t=await Promise.all(e.map(async e=>{if(!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus)return{streamId:`bootstrap:${e.id}:ai.sessions`,revision:0,lastEventAt:e.aiSessions.updatedAt,snapshot:e.aiSessions};try{return ve.parse(await this.instanceRequest(e,"/ai-sessions/state"))}catch(t){return this.logWarn({instanceId:e.id,error:fi(t),errorCode:"AI_SESSION_LIVE_REFRESH_FAILED"},"control plane failed to refresh live AI sessions"),{streamId:`bootstrap:${e.id}:ai.sessions`,revision:0,lastEventAt:e.aiSessions.updatedAt,snapshot:e.aiSessions}}}));return{updatedAt:(new Date).toISOString(),instances:e.map((e,s)=>({instanceId:e.id,streamId:t[s].streamId,aiSessions:t[s].snapshot,revision:t[s].revision,lastEventAt:t[s].lastEventAt}))}}async bootstrapAppSessionsFromInstances(){const e=await this.listNodeInstances(),t=await Promise.all(e.map(async e=>{if(!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=ci();return{streamId:`bootstrap:${e.id}:app.sessions`,revision:0,lastEventAt:t.updatedAt,snapshot:t}}try{return Vn.parse(await this.instanceRequest(e,"/apps/sessions/state"))}catch(t){this.logWarn({instanceId:e.id,error:fi(t),errorCode:"APP_SESSION_LIVE_REFRESH_FAILED"},"control plane failed to refresh live app sessions");const s=ci();return{streamId:`bootstrap:${e.id}:app.sessions`,revision:0,lastEventAt:s.updatedAt,snapshot:s}}}));return{updatedAt:(new Date).toISOString(),instances:e.map((e,s)=>({instanceId:e.id,streamId:t[s].streamId,appSessions:t[s].snapshot,revision:t[s].revision,lastEventAt:t[s].lastEventAt}))}}async recoverAiSessionDelta(e,t,s){const n=await this.requireControlledInstance(e,!0),i=new URLSearchParams({streamId:t,sinceRevision:String(s)});return Ne.parse(await this.instanceRequest(n,`/ai-sessions?${i}`))}async recoverAiSessionSnapshot(e){const t=await this.requireControlledInstance(e,!0);return ve.parse(await this.instanceRequest(t,"/ai-sessions/state"))}async recoverAppSessionDelta(e,t,s){const n=await this.requireControlledInstance(e,!0),i=new URLSearchParams({streamId:t,sinceRevision:String(s)});return Zn.parse(await this.instanceRequest(n,`/apps/sessions?${i}`))}async recoverAppSessionSnapshot(e){const t=await this.requireControlledInstance(e,!0);return Vn.parse(await this.instanceRequest(t,"/apps/sessions/state"))}async listTriggers(){return this.controlPlaneTriggerService.listTriggers()}createTrigger(e){return this.controlPlaneTriggerService.createTrigger(e)}async deleteTrigger(e){return this.controlPlaneTriggerService.deleteTrigger(e)}async applyTrigger(e,t){return this.controlPlaneTriggerService.applyTrigger(e,t)}async listInstanceTriggers(e){return this.controlPlaneTriggerService.listInstanceTriggers(e)}async bindAiSessionTrigger(e,t,s){return this.controlPlaneTriggerService.bindAiSessionTrigger(e,t,s)}async unbindAiSessionTrigger(e,t,s){return this.controlPlaneTriggerService.unbindAiSessionTrigger(e,t,s)}async runInstanceTrigger(e,t,s={}){return this.controlPlaneTriggerService.runInstanceTrigger(e,t,s)}listChatSessions(){return this.chatSessionService.list()}createAppAccessToken(e){return this.appAccessService.createToken(e)}resolveAppAccessToken(e,t){return this.appAccessService.resolveToken(e,t)}createChatActionToken(e){return this.chatActionTokenService.create(e)}resolveChatActionToken(e,t){return this.chatActionTokenService.resolve(e,t)}pendingDecisionCallbackData(e,t){return this.chatActionTokenService.pendingDecisionCallbackData(e,t)}async appAccessProxyTarget(e,t,s=""){return this.appAccessService.proxyTarget(e,t,s)}listChatBridges(){return this.chatBridgeService.list()}requireChatBridge(e){return this.chatBridgeService.require(e)}createChatBridge(e){return this.chatBridgeService.create(e)}updateChatBridge(e,t){return this.chatBridgeService.update(e,t)}deleteChatBridge(e){return this.chatBridgeService.delete(e)}requireChatSession(e){return this.chatSessionService.require(e)}upsertChatSession(e){return this.chatSessionService.upsert(e)}async handleChatGatewayMessage(e){return this.chatSessionRuntime.handleChatGatewayMessage(e)}async handleChatGatewayAction(e){return this.chatSessionRuntime.handleChatGatewayAction(e)}async handleChatCommand(e,t){return this.chatSessionRuntime.handleChatCommand(e,t)}async listPendingRoutes(){const e=mi(),t=[],s=await this.listAiSessions(),n=new Map(s.instances.map(e=>[e.instanceId,e.aiSessions])),i=await this.nodeAgentGateway.listFleetInstances(this.listNodes());for(const s of i.items){if("online"!==s.connectionStatus&&"online"!==s.agentStatus||!s.target.web)continue;const i=s.projectId?this.catalogService.getProject(s.projectId):void 0;for(const a of n.get(s.id)?.sessions||[]){if("waiting"!==a.status)continue;const n=sn.parse({id:`${s.id}:ai:${a.id}`,instanceId:s.id,projectId:s.projectId,aiSessionId:a.id,providerSessionId:a.providerSessionId,result:a.summary||a.currentTool?.inputPreview||a.lastMessage||`${a.agent} is waiting for approval`,source:a.agent,kind:"approval",status:"pending",lastSeenAt:e});t.push({...n,project:i,instance:Si(s)})}}return t}async listAiSessionInstanceNames(){return(await this.nodeAgentGateway.listFleetInstances(this.listNodes())).items.map(e=>({id:e.id,name:e.name}))}async resolveAiSessionApproval(e,t,s){const n=await this.requireControlledInstance(e,!0);return me.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/approval`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({decision:s})}))}async listAiSessionHistory(e){const t=await this.requireControlledInstance(e,!0);return he.parse(await this.instanceRequest(t,"/ai-sessions/history"))}async getAiSessionHistoryDetail(e,t){const s=await this.requireControlledInstance(e,!0);return fe.parse(await this.instanceRequest(s,`/ai-sessions/history/${encodeURIComponent(t)}`))}async resumeAiSession(e,t){const s=await this.requireControlledInstance(e,!0),n=Ie.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/resume`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})}));return await Promise.all([this.listAppSessions({refresh:!0}),this.listAiSessions({refresh:!0})]),n}async launchAppSession(e,t="terminal-tty",s={}){const n=await this.requireControlledInstance(e,!0);if("online"!==n.connectionStatus&&"online"!==n.agentStatus){const e=new Error(`Instance ${n.name} is still starting. Wait for it to connect before launching apps.`);throw Object.assign(e,{statusCode:409,code:"INSTANCE_NOT_CONNECTED"}),e}const i=await this.resolveAppLaunchOptions(n,s),a=await this.instanceRequest(n,"/apps/sessions",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({appId:t,...i})});return await this.listAppSessions({refresh:!0}),a}async stopAppSession(e,t){const s=await this.requireControlledInstance(e,!0),n=await this.instanceRequest(s,`/apps/sessions/${encodeURIComponent(t)}/stop`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});return await this.listAppSessions({refresh:!0}),n}async instanceAppManagement(e){const t=await this.requireControlledInstance(e,!0);try{return function(e){try{return Et.parse(e)}catch(e){if(e instanceof r.z.ZodError){const e=new Error("This controlled instance does not support managed app operations.");throw Object.assign(e,{statusCode:409,code:"INSTANCE_APP_MANAGEMENT_UNSUPPORTED"}),e}throw e}}(await this.instanceRequest(t,"/apps/management"))}catch(e){const t=e&&"object"==typeof e?e:{};if(e instanceof r.z.ZodError||404===t.statusCode){const e=new Error("This controlled instance does not support managed app operations.");throw Object.assign(e,{statusCode:409,code:"INSTANCE_APP_MANAGEMENT_UNSUPPORTED"}),e}throw e}}async requestInstanceAppOperation(e,t,s,n={}){const i=await this.requireControlledInstance(e,!0);return _t.parse(await this.instanceRequest(i,`/apps/${encodeURIComponent(t)}/${s}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)}))}async instanceAppManagementJob(e,t){const s=await this.requireControlledInstance(e,!0);return _t.parse(await this.instanceRequest(s,`/apps/jobs/${encodeURIComponent(t)}`))}async renameAppSession(e,t,s){const n=await this.requireControlledInstance(e,!0),i=await this.instanceRequest(n,`/apps/sessions/${encodeURIComponent(t)}`,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({title:s})});return await this.listAppSessions({refresh:!0}),i}async sendAiSessionMessage(e,t,s,n,i=[],a=[],r){!function(e){if(e.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)<=A)return;const t=new Error("Inline attachments must be 41943040 bytes or less in total.");throw Object.assign(t,{statusCode:400,code:"AI_SESSION_ATTACHMENTS_TOO_LARGE"}),t}(i);const o=await this.requireControlledInstance(e,!0);i.some(e=>"runtime-path"===e.source.type)&&function(e,t){if("local"===t||!e.some(e=>"runtime-path"===e.source.type))return;const s=new Error("Runtime path attachments are currently available only for Local Runtime instances.");throw Object.assign(s,{statusCode:400,code:"AI_SESSION_RUNTIME_PATH_UNSUPPORTED"}),s}(i,(await this.requireNodeRuntimeOnNode(o.nodeId,o.runtimeId)).type);const d=o.aiSessions.sessions.find(e=>e.id===t),c=r||("codex"===d?.agent?o.config.defaultCodexPermissionMode:void 0),l={message:s,...n?{mode:n}:{},...i.length?{attachments:i}:{},...a.length?{references:a}:{},...c?{permissionMode:c}:{}};return me.parse(await this.instanceRequest(o,`/ai-sessions/${encodeURIComponent(t)}/messages`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(l)}))}async aiSessionMentionCatalog(e,t){const s=await this.requireControlledInstance(e,!0);return ee.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/mentions`))}async searchAiSessionMentionFiles(e,t,s){const n=await this.requireControlledInstance(e,!0);return se.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/mentions/files`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:s})}))}async executeAiSessionCommand(e,t,s){const n=await this.requireControlledInstance(e,!0);return V.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/commands`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)}))}async aiSessionQueue(e,t){const s=await this.requireControlledInstance(e,!0);return oe.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/queue`))}async steerAiSessionQueuedMessage(e,t,s){const n=await this.requireControlledInstance(e,!0);return me.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/queue/${encodeURIComponent(s)}/steer`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})}))}async retryAiSessionQueuedMessage(e,t,s){const n=await this.requireControlledInstance(e,!0);return ue.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/queue/${encodeURIComponent(s)}/retry`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})}))}async removeAiSessionQueuedMessage(e,t,s){const n=await this.requireControlledInstance(e,!0);return ue.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/queue/${encodeURIComponent(s)}`,{method:"DELETE"}))}async interruptAiSession(e,t){const s=await this.requireControlledInstance(e,!0);return me.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/interrupt`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})}))}async proxyInstanceHttp(e,t,s={}){const n=await this.requireControlledInstance(e,!0);return this.controlledInstanceGateway.proxyHttp(n,t,s)}async proxyInstanceWebSocket(e,t,s,n,i={}){const a=await this.requireControlledInstance(e,!0);this.controlledInstanceGateway.proxyWebSocket(a,this.nodeAgentTransportFor(this.requireNode(a.nodeId)),t,s,n,i)}async instanceConfigSyncState(e){const t=await this.requireControlledInstance(e,!0),s=r.z.array(Rn).parse(await this.instanceRequest(t,"/config-sync/programs"));return Mn.parse({programs:s,preferences:this.configSyncPreferences.get(e)?.preferences||Ua()})}async listInstanceConfigSyncFolders(e,t={}){const s=await this.requireControlledInstance(e,!0),n=new URLSearchParams;return t.path&&n.set("path",t.path),void 0!==t.depth&&n.set("depth",String(t.depth)),r.z.array(Us).parse(await this.instanceRequest(s,"/config-sync/folders"+(n.size?`?${n}`:"")))}async syncInstanceConfigs(e,t){const s=jn.parse(t),n=await this.requireControlledInstance(e,!0);if("export"===s.direction&&"local-folder"!==n.source.type){const e=new Error("Exporting instance config is only available for local folder projects.");throw Object.assign(e,{statusCode:400,code:"CONFIG_SYNC_EXPORT_REQUIRES_LOCAL_PROJECT"}),e}const i=function(e){const t=function(e){const t=[];for(const s of e.split("/"))if(s&&"."!==s)if(".."===s){if(!t.length)return"..";t.pop()}else t.push(s);return t.join("/")||"."}(e.trim().replace(/\\/g,"/")||".");if(t.startsWith("/")||".."===t||t.startsWith("../")){const e=new Error("Config sync folder must stay inside the instance workspace.");throw Object.assign(e,{statusCode:400,code:"CONFIG_SYNC_FOLDER_INVALID"}),e}return t||"."}(s.workspaceFolder),a={...s,workspaceFolder:i},r=qn.parse(await this.instanceRequest(n,"/config-sync",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a)})),o=mi(),d=this.configSyncPreferences.get(e);return this.configSyncPreferences.put({id:e,preferences:{...d?.preferences||Ua(),[s.direction]:i},createdAt:d?.createdAt||o,updatedAt:o}),r}async instanceRequest(e,t,s={}){return this.controlledInstanceGateway.request(e,t,s)}async reportInstanceHeartbeat(e,t){await this.controlledInstanceGateway.reportHeartbeat(e,t,this.nodeAgentTransport)}async nodeAgentFetch(e,t,s={}){return this.nodeAgentTransportFor(e).request(e,t,s)}nodeAgentTransportFor(e){if("reverse-wss"===e.connectionMode){if(!this.nodeAgentTransport){const e=new Error("Reverse node agent transport is not available.");throw Object.assign(e,{statusCode:503,code:"NODE_AGENT_REVERSE_TRANSPORT_UNAVAILABLE"}),e}return this.nodeAgentTransport}return this.directNodeAgentTransport()}directNodeAgentTransport(){const e=async(e,t,s={})=>{const n=e.controlEndpoint||e.endpoint;if(!n){const e=new Error("Node agent direct HTTP mode requires an endpoint.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_ENDPOINT_REQUIRED"}),e}const i=s.method||"GET",a="string"==typeof s.body||s.body instanceof Buffer?s.body:void 0===s.body||null===s.body?void 0:String(s.body),r=hr(e,{method:i,pathWithQuery:`/api/node-agent${t}`,body:a||""});return this.fetchNodeAgentEndpoint(n,t,{...s,body:a,headers:{...s.headers||{},...r}})};return{request:e,requestStream:e,proxyWebSocket:(e,t,s,n,i={})=>{const a=e.controlEndpoint||e.endpoint;if(!a)throw Object.assign(new Error("Node agent direct HTTP mode requires an endpoint."),{statusCode:400,code:"NODE_AGENT_ENDPOINT_REQUIRED"});const r=rr(a),o=`/api/node-agent${s}`,d=hr(e,{method:"GET",pathWithQuery:o});if(r){or(r);const e=dr(r,s,n,{...i,...d});return e.on("open",()=>{t.on("message",(t,s)=>e.readyState===l.WebSocket.OPEN&&e.send(s?t:String(t))),t.on("close",()=>e.close()),t.on("error",()=>e.close())}),e.on("message",(e,s)=>{t.readyState===l.WebSocket.OPEN&&t.send(s?e:e.toString())}),e.on("close",()=>t.close()),void e.on("error",()=>t.close(1011,"Instance websocket proxy failed."))}const c=new URL(o,a.replace(/\/$/,""));c.protocol="https:"===c.protocol?"wss:":"ws:";const p=n?new l.WebSocket(c.toString(),n,{headers:{...i,...d}}):new l.WebSocket(c.toString(),{headers:{...i,...d}});p.on("open",()=>{t.on("message",(e,t)=>p.readyState===l.WebSocket.OPEN&&p.send(t?e:String(e))),t.on("close",()=>p.close()),t.on("error",()=>p.close())}),p.on("message",(e,s)=>{t.readyState===l.WebSocket.OPEN&&t.send(s?e:e.toString())}),p.on("close",()=>t.close()),p.on("error",()=>t.close(1011,"Instance websocket proxy failed."))}}}async listNodeInstances(){return(await this.listNodeInstancesWithDiagnostics()).items}async listNodeInstancesWithDiagnostics(){const e=await this.nodeAgentGateway.listFleetInstances(this.listNodes());return{items:await Promise.all(e.items.map(e=>this.withFreshTriggerSnapshot(e))),nodeErrors:e.nodeErrors}}async withFreshTriggerSnapshot(e){if("online"!==e.connectionStatus&&"online"!==e.agentStatus)return e;try{const t=Fe.parse(await this.instanceRequest(e,"/triggers"));return Ws.parse({...e,triggers:er(t)})}catch{return e}}async requireNodeInstance(e){for(const t of this.listNodes())try{const s=(await this.nodeAgentGateway.listInstances(t)).find(t=>t.id===e);if(s)return s}catch{}Ii("CONTROLLED_INSTANCE_NOT_FOUND",`Controlled instance ${e} was not found.`)}async requireNodeRuntimeOnNode(e,t){const s=(await this.listNodeRuntimes(e)).find(e=>e.id===t);return s||Ii("NODE_RUNTIME_NOT_FOUND",`Runtime ${t} was not found on node ${e}.`),s}async requireNodeLocalFolder(e,t){return this.nodeAgentGateway.requireLocalFolder(e,t)}async resolveAppLaunchOptions(e,t){const s="string"==typeof t.cwdFolderId?t.cwdFolderId.trim():"",n={...t};if(delete n.cwdFolderId,!s)return n;const i=this.requireNode(e.nodeId),a=await this.requireNodeLocalFolder(i,s);if(a.nodeId!==e.nodeId){const t=new Error(`Local folder ${a.id} belongs to node ${a.nodeId}, not ${e.nodeId}.`);throw Object.assign(t,{statusCode:400,code:"LOCAL_FOLDER_REQUIRES_INSTANCE_NODE"}),t}const r=await this.requireNodeRuntimeOnNode(e.nodeId,e.runtimeId);return{...n,cwd:this.appLaunchCwdForFolder(e,a,r)}}appLaunchCwdForFolder(e,s,n){if("local"===n.type)return Ba(i=s.path).resolve(i);var i;if("local-folder"!==e.source.type){const e=new Error("App cwd folder selection is only supported for local-folder instances.");throw Object.assign(e,{statusCode:400,code:"APP_CWD_REQUIRES_LOCAL_FOLDER_SOURCE"}),e}const a=function(e,t){if(Fa(e)!==Fa(t))return;const s=Ba(e),n=s.resolve(e),i=s.resolve(t),a=s.relative(n,i);return a?".."===a||a.startsWith(`..${s.sep}`)||s.isAbsolute(a)?void 0:a.split(s.sep).filter(Boolean):[]}(e.source.path,s.path);if(!a){const t=new Error(`Local folder ${s.path} is outside the instance workspace ${e.source.path}.`);throw Object.assign(t,{statusCode:400,code:"APP_CWD_OUTSIDE_WORKSPACE"}),t}const r=e.runtime.workspacePath||e.workspace.path||wi(e.source).path||"/workspace";return a.length?t.posix.join(r,...a):r}async prepareInstanceModels(e,t){const s=await this.nodeAgentGateway.listModels(e),n={...null===t.codexModelHash?{codexModelHash:null}:t.codexModelHash?.trim()?{codexModelHash:t.codexModelHash.trim()}:{},...null===t.claudeModelHash?{claudeModelHash:null}:t.claudeModelHash?.trim()?{claudeModelHash:t.claudeModelHash.trim()}:{}},i=async(t,n)=>{if(null===n)return;const i=n?this.listAllModels().find(e=>e.id===n):this.listAllModels().find(e=>e.enabled&&e.app===t);if(i){if(i.app!==t)throw Object.assign(new Error(`Model ${i.id} is not a ${t} model.`),{statusCode:400,code:"MODEL_APP_MISMATCH"});if(!i.enabled)throw Object.assign(new Error(`Model ${i.id} is disabled.`),{statusCode:400,code:"MODEL_DISABLED"});return await this.nodeAgentGateway.deployModel(e,i.id,i),i.id}if(!n)return;const a=s.find(e=>e.id===n);if(a||Ii("MODEL_NOT_FOUND",`Model ${n} was not found on control-plane or node ${e.id}.`),a.app!==t)throw Object.assign(new Error(`Model ${n} is not a ${t} model.`),{statusCode:400,code:"MODEL_APP_MISMATCH"});if(!a.enabled)throw Object.assign(new Error(`Model ${n} is disabled.`),{statusCode:400,code:"MODEL_DISABLED"});return a.id};return{modelSelection:n,codexModelHash:await i("codex",n.codexModelHash),claudeModelHash:await i("claude",n.claudeModelHash)}}async ensureInstanceModelAssignment(e){const t=this.requireNode(e.nodeId),s=await this.prepareInstanceModels(t,e.modelSelection);return this.nodeAgentGateway.assignInstanceModels(t,e.id,s)}nextModelOrder(){return this.models.list().reduce((e,t)=>Math.max(e,t.order),0)+100}requireProject(e){return this.catalogService.requireProject(e)}requireModel(e,t=!1){const s=this.models.get(e);s||Ii("MODEL_NOT_FOUND",`Model ${e} was not found.`);const n=this.normalizeModelRecord(s);return t?n:Ai(n)}requireModelSecret(e){const t=this.models.get(e);return t||Ii("MODEL_NOT_FOUND",`Model ${e} was not found.`),this.normalizeModelRecord(t)}normalizeModelRecord(e){const t=function(e){if(e&&"object"==typeof e&&!Array.isArray(e)){const t={...e};if(!("app"in t)){const e=Array.isArray(t.apps)?t.apps:[];t.app=e.includes("claude")&&!e.includes("codex")?"claude":"codex"}return delete t.apps,Qt.parse(t)}return Qt.parse(e)}(e);return t!==e&&this.models.put(t),t}requireImage(e){return this.catalogService.requireImage(e)}requireNode(e){const t=this.nodes.get(e);return t||Ii("NODE_NOT_FOUND",`Node ${e} was not found.`),t}requirePublicNode(e){return vi(this.requireNode(e))}async requireControlledInstance(e,t=!1){const s=await this.requireNodeInstance(e);return t?s:Si(s)}}function Ir(e,t){const s=(n=e)&&"object"==typeof n&&!Array.isArray(n)?n:{};var n;return{code:"string"==typeof s.code&&s.code.trim()?s.code.trim():t,message:e instanceof Error&&e.message.trim()?e.message:"The operation failed."}}function yr(e){return g(String(e),"escape").replace(/\n+$/,"")}function br(e){const[t,...s]=String(e).split("\n");return[`*${yr(t)}*`,...s.map(yr)].join("\n")}function vr(e,t=4e3){const s=String(e),n=br(s);if(n.length<=t)return n;const[i,...a]=s.split("\n"),r=a.join("\n"),o="...\n";let d=i;for(;br(`${d}\n${o}`).length>t&&d.length>0;)d=d.slice(0,Math.floor(.8*d.length));let c=0,l=r.length,p=0,u=br(`${d}\n${o}`);for(;c<=l;){const e=Math.floor((c+l)/2),s=br(`${d}\n${o}${r.slice(-e)}`);s.length<=t?(u=s,p=e,c=e+1):l=e-1}const m=r.slice(-p),g=m.indexOf("\n");return g>=0&&g<200&&(u=br(`${d}\n${o}${m.slice(g+1)}`)),u}function wr(e){return String(e).replace(/\\([_*\[\]()~`>#+\-=|{}.!\\])/g,"$1")}async function Sr(e,t,s,n,i={}){if(!t.token)return;const a=s=>e(`https://api.telegram.org/bot${t.token}/sendMessage`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)}),r=function(e,t={}){const s=String(e),n=t.rawMarkdownV2?e=>e:yr;if(n(s).length<=4e3)return[s];const i=[];let a=s;for(;a;){let e=1,t=a.length,s=1;for(;e<=t;){const i=Math.floor((e+t)/2);n(a.slice(0,i)).length<=4e3?(s=i,e=i+1):t=i-1}if(s<a.length){const e=a.slice(0,s),t=Math.max(e.lastIndexOf("\n"),e.lastIndexOf(" "));t>=Math.floor(.6*s)&&(s=t+1)}i.push(a.slice(0,s).trimEnd()),a=a.slice(s).trimStart()}return i.filter(Boolean)}(n,i);let o;for(const[e,t]of r.entries())o=await kr(a,"sendMessage",t,{chat_id:s,...0===e&&i.replyToMessageId?{reply_to_message_id:i.replyToMessageId,allow_sending_without_reply:!0}:{},...e===r.length-1&&i.replyMarkup?{reply_markup:i.replyMarkup}:{}},i);return o}async function Ar(e,t,s){if(!t.token||!s)throw new Error("Telegram file id is required.");const n=await e(`https://api.telegram.org/bot${t.token}/getFile`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({file_id:s})}),i=await n.json().catch(()=>({}));if(!n.ok||!1===i.ok||!i.result?.file_path)throw new Error(i.description||`Telegram getFile failed with HTTP ${n.status}`);return`https://api.telegram.org/file/bot${t.token}/${i.result.file_path}`}async function kr(e,t,s,n,i){const a=i.parseMode||"MarkdownV2",r=i.rawMarkdownV2?String(s):yr(s),o=await e({...n,text:r,disable_web_page_preview:!0,parse_mode:a}),d=await o.json().catch(()=>({}));if(o.ok&&!1!==d.ok)return d.result;if("editMessageText"===t&&zr(d.description))return;if(i.rawMarkdownV2&&"MarkdownV2"===a){const i=await e({...n,text:wr(s),disable_web_page_preview:!0,parse_mode:"Markdown"}),a=await i.json().catch(()=>({}));if(i.ok&&!1!==a.ok)return a.result;if("editMessageText"===t&&zr(a.description))return}const c=await e({...n,text:i.rawMarkdownV2?wr(s):String(s),disable_web_page_preview:!0}),l=await c.json().catch(()=>({}));if(c.ok&&!1!==l.ok)return l.result;if("editMessageText"!==t||!zr(l.description))throw new Error(l.description||d.description||`Telegram ${t} failed with HTTP ${c.status}`)}function zr(e){return"string"==typeof e&&e.toLowerCase().includes("message is not modified")}function Tr(e,t){if("string"==typeof e&&e.trim())return e.trim();Er(`Chat action token is missing ${t}.`)}function Cr(e){if("allow"===e||"deny"===e||"skip"===e)return e;Er("Chat action token has an invalid decision.")}function Er(e){const t=new Error(e);throw Object.assign(t,{statusCode:400,code:"CHAT_ACTION_TOKEN_INVALID"}),t}function Nr(e){return Buffer.from(e,"base64url").toString("utf8")}function _r(e){return String(e||"").split(/\r?\n/,1)[0].replace(/^#+\s*/,"").trim().slice(0,20)||"消息通知"}function xr(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,"string"==typeof t?t:JSON.stringify(t)]))}function Or(e){return`dtv1.card//IM_GROUP.${e}`}function Rr(e){return`dtv1.card//IM_ROBOT.${e}`}function jr(e,t){return Jr(t.biz_conversation_id).trim()||Jr(e.spaceId).replace(/^dtv1\.card\/\/(?:IM_GROUP|IM_SINGLE|IM_ROBOT)\./,"")}function Dr(e,t={}){const s=Jr(e).trim().toUpperCase();return"2"===s||"IM_GROUP"===s?"IM_GROUP":"1"===s||"IM_SINGLE"===s?t.robotMessage?"IM_ROBOT":"IM_SINGLE":"IM_ROBOT"===s?"IM_ROBOT":void 0}function Mr(e){if(e.startsWith("task_handoff:"))return e;if(!e.startsWith("th_cb_"))return"";try{return Buffer.from(e.slice(6),"base64url").toString("utf8")}catch{return""}}function Pr(e,t,s,n,i){const a=jr(n,i);return{cardUpdateOptions:{updateCardDataByKey:!0,updatePrivateDataByKey:!1},cardData:{cardParamMap:xr({type:"actions",title:"TaskHandoff",description:e,list:pa(t),biz_out_track_id:n.outTrackId||i.biz_out_track_id||"",biz_conversation_id:a,biz_sender_id:i.biz_sender_id||n.userId||"",biz_session_webhook:i.biz_session_webhook||"",biz_conversation_type:i.biz_conversation_type||"",biz_step:s,error_msg:""})}}}function $r(e,t,s){const n=[];return qr(n,s),qr(n,t),qr(n,e),n.find(e=>Mr(e))||""}function qr(e,t){for(const s of["actionIdList","actionIds"]){const n=t[s];Array.isArray(n)&&e.push(...n.map(e=>Jr(e)).filter(Boolean))}for(const s of["actionId","action_id","id","actionValue"]){const n=Jr(t[s]);n&&e.push(n)}}async function Lr(e,t,s,n){n?.("info",{target:"sessionWebhook",hasSessionWebhook:Boolean(t),textPreview:Wr(s)},"dingding webhook send requested");const i=await e(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({msgtype:"markdown",markdown:{title:_r(s),text:s}})}),a=await i.json().catch(()=>{});if(n?.(i.ok?"info":"warn",{target:"sessionWebhook",status:i.status,response:Gr(a)},"dingding webhook send completed"),!i.ok)throw new Error(`DingDing webhook failed with HTTP ${i.status}`)}async function Ur(e){const t=pa(e.replyMarkup);if(!t.length&&!e.forceCard)return e.sessionWebhook&&await Lr(e.fetchImpl,e.sessionWebhook,e.text,e.runtime.onLog),e.sessionWebhook?{delivered:!0}:void 0;const s=e.bridge.settings||{},n=Jr(s.robotCode),i=Jr(s.cardTemplateId)||"13fc6717-12e4-43ed-8533-111a310d4995.schema",a=Jr(s.cardCallbackRouteKey)||"bi_workflow_ticket",r=e.senderId||e.runtime.senderIds.get(e.chatId)||Jr(s.senderId),o=(d=e.runtime,c=e.chatId,d.conversationTypes?.get(c)||"IM_GROUP");var d,c;if(!n||!i||!r){if(e.sessionWebhook&&!e.forceCard)return await Lr(e.fetchImpl,e.sessionWebhook,e.text,e.runtime.onLog),{delivered:!0};throw new Error("DingDing card target is incomplete.")}const l=`task_handoff_cp_${Date.now()}_${Math.random().toString(16).slice(2)}`,p=xr({type:"actions",title:e.title||_r(e.text),description:e.text,list:t,biz_out_track_id:l,biz_step:e.step||"task_handoff_actions",biz_conversation_id:e.chatId,biz_sender_id:r,biz_session_webhook:e.sessionWebhook||e.runtime.chatWebhooks.get(e.chatId)||"",biz_conversation_type:o,error_msg:""}),u=function(e,t,s,n){return"IM_SINGLE"===e?{openSpaceId:(i=t,`dtv1.card//IM_SINGLE.${i}`),imSingleOpenSpaceModel:{},imSingleOpenDeliverModel:{extension:{}}}:"IM_ROBOT"===e?{openSpaceId:Rr(s),imRobotOpenSpaceModel:{supportForward:!1},imRobotOpenDeliverModel:{extension:{},robotCode:n,spaceType:"IM_ROBOT"}}:{openSpaceId:Or(t),imGroupOpenSpaceModel:{supportForward:!1},imGroupOpenDeliverModel:{extension:{},robotCode:n}};var i}(o,e.chatId,r,n);return e.runtime.onLog?.("info",{target:"card",chatId:e.chatId,senderId:r,conversationType:o,openSpaceId:u.openSpaceId,hasSessionWebhook:Boolean(e.sessionWebhook||e.runtime.chatWebhooks.get(e.chatId)),hasReplyMarkup:Boolean(e.replyMarkup),actionCount:t.length,textPreview:Wr(e.text)},"dingding card send requested"),await Fr(e.fetchImpl,e.bridge,e.runtime,"POST","/v1.0/card/instances/createAndDeliver",{userId:r,cardTemplateId:i,outTrackId:l,callbackType:"STREAM",callbackRouteKey:a,cardData:{cardParamMap:p},userIdType:Number(s.cardUserIdType||1),...u}),{delivered:!0,outTrackId:l}}async function Fr(e,t,s,n,i,a){const r=await async function(e,t,s){if(s.accessToken&&s.accessToken.expiresAt>Date.now()+6e4)return s.accessToken.value;const n=Jr(t.settings.clientSecret);if(!t.token||!n)throw new Error("DingDing client id/secret is not configured.");const i=await e("https://api.dingtalk.com/v1.0/oauth2/accessToken",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({appKey:t.token,appSecret:n})}),a=await i.json().catch(()=>({}));if(!i.ok||!a.accessToken)throw new Error(`DingDing access token failed with HTTP ${i.status}`);const r=Number(a.expireIn||7200);return s.accessToken={value:a.accessToken,expiresAt:Date.now()+1e3*Math.max(60,r-120)},s.accessToken.value}(e,t,s);s.onLog?.("info",{method:n,path:i,body:Br(a)},"dingding api request");const o=await e(`https://api.dingtalk.com${i}`,{method:n,headers:{"content-type":"application/json","x-acs-dingtalk-access-token":r},body:JSON.stringify(a)}),d=await o.json().catch(()=>{});if(s.onLog?.(o.ok?"info":"warn",{method:n,path:i,status:o.status,response:Gr(d)},"dingding api response"),!o.ok)throw new Error(`DingDing API failed with HTTP ${o.status}`);const c=Vr(d),l=c.code??c.errcode,p=c.success;if(void 0!==l&&"0"!==String(l)||!1===p)throw new Error(Jr(c.message||c.errmsg)||`DingDing API returned ${String(l||"failure")}`);const u=function(e){const t=Vr(Vr(e).result).deliverResults;if(!Array.isArray(t))return"";const s=t.find(e=>!1===Vr(e).success);if(!s)return"";const n=Vr(s),i=Jr(n.spaceId),a=Jr(n.errorMsg||n.message||n.errmsg)||"delivery failed";return i?`DingDing card delivery failed for ${i}: ${a}`:`DingDing card delivery failed: ${a}`}(d);if(u)throw new Error(u);return o}function Br(e){const t=Vr(e),s=Vr(t.cardData),n=Vr(s.cardParamMap);return{openSpaceId:Jr(t.openSpaceId),outTrackId:Jr(t.outTrackId),userId:Jr(t.userId),userIdType:t.userIdType,callbackType:Jr(t.callbackType),callbackRouteKey:Jr(t.callbackRouteKey),hasImGroupTarget:Boolean(t.imGroupOpenSpaceModel||t.imGroupOpenDeliverModel),hasImSingleTarget:Boolean(t.imSingleOpenSpaceModel||t.imSingleOpenDeliverModel),hasImRobotTarget:Boolean(t.imRobotOpenSpaceModel||t.imRobotOpenDeliverModel),imGroupOpenDeliverModel:Hr(t.imGroupOpenDeliverModel),imSingleOpenDeliverModel:Hr(t.imSingleOpenDeliverModel),imRobotOpenDeliverModel:Hr(t.imRobotOpenDeliverModel),bizConversationId:Jr(n.biz_conversation_id),bizConversationType:Jr(n.biz_conversation_type),bizSenderId:Jr(n.biz_sender_id),hasBizSessionWebhook:Boolean(Jr(n.biz_session_webhook))}}function Hr(e){const t=Vr(e);if(Object.keys(t).length)return{hasExtension:Boolean(t.extension),hasRobotCode:Boolean(Jr(t.robotCode)),spaceType:Jr(t.spaceType),recipients:Array.isArray(t.recipients)?t.recipients.length:void 0,atUserIds:t.atUserIds&&"object"==typeof t.atUserIds?Object.keys(t.atUserIds).length:void 0}}function Gr(e){const t=Vr(e),s=Vr(t.result);return{code:t.code??t.errcode,message:Wr(t.message||t.errmsg,240),success:t.success,requestId:t.requestId,result:Object.keys(s).length?s:void 0}}function Wr(e,t=160){const s=String(e||"").replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,Math.max(0,t-3))}...`}function Vr(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function Kr(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:"string"==typeof e&&e.trim()?Qr(e):{}}function Jr(e){return"string"==typeof e?e:""}function Qr(e){try{return Vr(JSON.parse(String(e||"{}")))}catch{return{}}}function Yr(e){return JSON.stringify(pa(e))}function Xr(e){return e instanceof Error?e.message:String(e)}function Zr(e){const t=Number(function(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}(e).message_id);return Number.isInteger(t)&&t>0?t:void 0}function eo(e){return Array.isArray(e?.actionRows)?e.actionRows:(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e])}function to(e,t){const s=eo(e),n=eo(t);return s.length===n.length&&s.every((e,t)=>{const s=n[t];return e.length===s.length&&e.every((e,t)=>{const n=s[t];return e.text===n?.text&&e.callbackData===n.callbackData})})}class so{entries;updateIntervalMs;send;edit;deleteMessage;messageIdFromResult;retryAfterMs;isMessageTooLong;onLog;constructor(e){this.entries=e.entries||new Map,this.updateIntervalMs=e.updateIntervalMs||1e3,this.send=e.send,this.edit=e.edit,this.deleteMessage=e.delete,this.messageIdFromResult=e.messageIdFromResult||Zr,this.retryAfterMs=e.retryAfterMs,this.isMessageTooLong=e.isMessageTooLong,this.onLog=e.onLog}remember(e,t,s,n,i,a){const r=this.entries.get(e);return r&&(clearTimeout(r.timer),r.timer=void 0,r.pendingText=void 0),this.entries.set(e,{lastText:s,lastUpdateAt:Date.now(),messageId:t,route:n,options:i,lastOptions:i,renderOptions:a}),r}rekey(e,t){if(e===t)return this.entries.has(e);const s=this.entries.get(e);if(!s||this.entries.has(t))return!1;if(clearTimeout(s.timer),s.timer=void 0,this.entries.delete(e),this.entries.set(t,s),s.pendingText){const e=s.rateLimitedUntil?Math.max(0,s.rateLimitedUntil-Date.now()):Math.max(0,this.updateIntervalMs-(Date.now()-s.lastUpdateAt));this.scheduleFlush(t,e)}return!0}scheduleFlush(e,t){const s=this.entries.get(e);s&&!s.timer&&(s.timer=setTimeout(()=>{s.timer=void 0;const t=s.pendingText;s.pendingText=void 0,t&&this.applyUpdate(e,t,s.route,s.options)},t))}markRateLimited(e,t,s,n){const i=this.entries.get(e);i&&(i.rateLimitedUntil=Date.now()+s,i.pendingText=t,i.route=n||i.route,this.scheduleFlush(e,s))}async applyUpdate(e,t,s,n){let i=this.entries.get(e);const a=i,r=s||i?.route,o=n||i?.options;try{if(i&&(i.route=r,i.options=o),i?.pending&&await i.pending,i=this.entries.get(e),a&&i!==a)return!1;if(i?.messageId){if(i.lastText===t&&to(i.lastOptions,o))return!0;const s=Date.now()-(i.lastUpdateAt||0);return s<this.updateIntervalMs?(i.pendingText=t,i.route=r||i.route,i.options=o||i.options,this.scheduleFlush(e,this.updateIntervalMs-s),!0):(await this.edit(i.messageId,t,r||i.route,o),i.lastText=t,i.lastOptions=o,i.lastUpdateAt=Date.now(),i.rateLimitedUntil=void 0,!0)}const s={lastText:t,lastUpdateAt:Date.now(),route:r,options:o,lastOptions:o};return s.pending=this.send(t,r,o).then(e=>{const t=this.messageIdFromResult(e);t&&(s.messageId=t)}).finally(()=>{s.pending=void 0}),this.entries.set(e,s),await s.pending,this.entries.get(e)===s&&(s.rateLimitedUntil=void 0,Boolean(s.messageId))}catch(s){const n=this.retryAfterMs?.(s);return n&&this.markRateLimited(e,t,n,r),this.onLog?.(`Telegram progress update skipped: ${Xr(s)}`),!1}}update(e,t,s,n){const i=String(t||"").trim();if(!i)return;const a=this.entries.get(e);if(a&&(a.route=s||a.route,a.options=n||a.options),a?.lastText===i&&to(a.lastOptions,n||a.options)&&!a.pending&&!a.pendingText)return;const r=Date.now();if(a?.rateLimitedUntil){if(a.rateLimitedUntil>r)return a.pendingText=i,void this.scheduleFlush(e,a.rateLimitedUntil-r);a.rateLimitedUntil=void 0}if(a?.pending&&!a.messageId)return a.pendingText=i,void this.scheduleFlush(e,this.updateIntervalMs);if(!a||!a.messageId)return void this.applyUpdate(e,i,s,n);const o=Date.now()-(a.lastUpdateAt||0);if(o>=this.updateIntervalMs)return clearTimeout(a.timer),a.timer=void 0,a.pendingText=void 0,void this.applyUpdate(e,i,s||a.route,n||a.options);a.pendingText=i,this.scheduleFlush(e,this.updateIntervalMs-o)}async finish(e,t,s,n,i){const a=this.entries.get(e);if(!a)return!1;try{if(clearTimeout(a.timer),a.timer=void 0,a.pendingText=void 0,a.pending&&await a.pending,this.entries.delete(e),!a.messageId)return!1;const r=n||a.options;return a.lastText===t&&to(a.lastOptions,r)||await this.edit(a.messageId,t,s||a.route,r,i||a.renderOptions),!0}catch(t){return this.entries.delete(e),this.isMessageTooLong?.(t)?this.onLog?.("Telegram progress finish too long; sending result as multiple messages"):this.onLog?.(`Telegram progress finish skipped: ${Xr(t)}`),!1}}async delete(e,t){const s=this.entries.get(e);if(!s)return!1;try{return clearTimeout(s.timer),s.timer=void 0,s.pendingText=void 0,s.pending&&await s.pending,this.entries.delete(e),!(!this.deleteMessage||!s.messageId)&&(await this.deleteMessage(s.messageId,t||s.route),!0)}catch(t){return this.entries.delete(e),this.onLog?.(`Telegram progress delete skipped: ${Xr(t)}`),!1}}async flush(e){const t=this.entries.get(e);if(t?.pending&&await t.pending,t?.timer){clearTimeout(t.timer),t.timer=void 0;const s=t.pendingText;t.pendingText=void 0,s&&await this.applyUpdate(e,s,t.route,t.options)}}async wait(e){await function(e){return new Promise(t=>setTimeout(t,e))}(e)}}const no=20971520;function io(e){const t=[],s=[...e.photo||[]].filter(e=>e.file_id).sort((e,t)=>Number(t.file_size||t.width||0)-Number(e.file_size||e.width||0))[0];return s?.file_id&&t.push({fileId:s.file_id,fileName:"telegram-photo.jpg",fileSize:s.file_size}),e.document?.file_id&&String(e.document.mime_type||"").startsWith("image/")&&t.push({fileId:e.document.file_id,fileName:e.document.file_name,fileSize:e.document.file_size}),t}async function ao(e,s,n="telegram-image",i){if(i&&i>no)throw new Error(`image is too large: ${i} bytes`);const a=await e.getFileLink(s),r=String(a),o=await(e.fetchFile||fetch)(r);if(!o.ok)throw new Error(`download failed with HTTP ${o.status}`);const d=Number(o.headers.get("content-length")||0);if(d>no)throw new Error(`image is too large: ${d} bytes`);const c=t.extname(n)||".jpg";let l=c;try{l=t.extname(new URL(r).pathname)||c}catch{l=c}const p=t.join(f.tmpdir(),"task-handoff-images");await h.mkdir(p,{recursive:!0});const u=l.replace(/[^.\w-]/g,"")||".jpg",m=t.join(p,`telegram-${Date.now()}-${Math.random().toString(16).slice(2)}${u}`),g=Buffer.from(await o.arrayBuffer());if(g.length>no)throw new Error(`image is too large: ${g.length} bytes`);return await h.writeFile(m,g),m}class ro{store;constructor(e){this.store=new so({updateIntervalMs:e.updateIntervalMs,send:(t,s,n)=>e.send(e.requireBridge(s?.bridgeId||""),s?.chatId||"",vr(t),{rawMarkdownV2:!0,replyMarkup:mo(n)}),edit:(t,s,n,i)=>e.edit(e.requireBridge(n?.bridgeId||""),n?.chatId||"",t,vr(s),{rawMarkdownV2:!0,replyMarkup:mo(i)}),delete:(t,s)=>e.deleteMessage(e.requireBridge(s?.bridgeId||""),s?.chatId||"",t),onLog:e.onLog})}get entries(){return this.store.entries}remember(e,t,s,n){return this.store.remember(e,t,s,n)}rekey(e,t){return this.store.rekey(e,t)}delete(e){return this.store.delete(e)}async applyUpdate(e){return this.store.applyUpdate(e.key,e.text,{bridgeId:e.bridge.id,chatId:e.chatId},{actionRows:e.actionRows})}}async function oo(e){return(await async function(e,t,s,n){const i=[];for(const a of io(s))try{const s=await ao({getFileLink:s=>Ar(e,t,s),fetchFile:e},a.fileId,a.fileName,a.fileSize);i.push(s)}catch(e){n?.({fileId:a.fileId,fileName:a.fileName,error:e})}return i}(e.fetchImpl,e.bridge,e.message,e.onImageDownloadError)).map((e,n)=>{const i=s.readFileSync(e);return{id:`tg_${o.randomUUID().replace(/-/g,"").slice(0,24)}`,kind:"image",name:t.basename(e)||`telegram-image-${n+1}`,mime:co(e),size:i.length,source:{type:"inline",encoding:"base64",data:i.toString("base64")}}})}function co(e){const s=t.extname(e).toLowerCase();return".png"===s?"image/png":".webp"===s?"image/webp":".gif"===s?"image/gif":".bmp"===s?"image/bmp":"image/jpeg"}function lo(e){const t=Number(e?.message_id);return Number.isInteger(t)?t:void 0}function po(e){return e&&"object"==typeof e?e:{}}function uo(e){return"string"==typeof e?e:""}function mo(e){const t=Array.isArray(e?.actionRows)?e.actionRows:void 0;return da(t||(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e]))}function go(e){const t=Array.isArray(e.item_list)?e.item_list:[];for(const e of t){const t=ho(e);if(1!==t.type)continue;const s=fo(ho(t.text_item).text).trim();if(s)return s}return""}function ho(e){return e&&"object"==typeof e?e:{}}function fo(e){return"string"==typeof e?e:""}function Io(e){const{fetchImpl:t,bridge:s,dingdingRuntime:n}=e;return"telegram"===s.channel?function(e,t){return{bridge:t,send:async(s,n,i={})=>{const a=await Sr(e,t,s,n,{replyMarkup:i.replyMarkup,replyToMessageId:i.replyToMessageId,rawMarkdownV2:i.rawMarkdownV2,parseMode:i.parseMode});return{provider:"telegram",raw:a,messageId:Number(po(a).message_id)||void 0}}}}(t,s):"dingding"===s.channel?function(e,t,s){return{bridge:t,send:async(n,i,a={})=>{const r=a.sessionWebhook||s?.chatWebhooks.get(n)||Jr(t.settings.sessionWebhook);if(a.replyMarkup&&s){const o=await Ur({fetchImpl:e,bridge:t,runtime:s,chatId:n,text:i,replyMarkup:a.replyMarkup,sessionWebhook:r,senderId:a.senderId||s.senderIds.get(n)||Jr(t.settings.senderId)});if(o?.delivered)return{provider:"dingding",interactionId:o.outTrackId}}if(r)return await Lr(e,r,i,s?.onLog),{provider:"dingding"}}}}(t,s,n):"wechat"===s.channel?function(e,t){return{bridge:t,send:async(s,n,i={})=>{const a=i.contextToken||fo(t.settings.contextToken);if(a)return await async function(e,t,s,n,i){const a=fo(t.settings.baseUrl)||"https://ilinkai.weixin.qq.com",r=await e(`${a}/ilink/bot/sendmessage`,{method:"POST",headers:{"content-type":"application/json",AuthorizationType:"ilink_bot_token",Authorization:`Bearer ${t.token}`,"X-WECHAT-UIN":String(Math.floor(Math.random()*2**31))},body:JSON.stringify({msg:{from_user_id:"",to_user_id:s,client_id:o.randomUUID(),message_type:2,message_state:2,context_token:n,item_list:[{type:1,text_item:{text:i}}]},base_info:{channel_version:"1.0.2"}})}),d=await r.json().catch(()=>({}));if(!r.ok||d.errcode)throw new Error(fo(d.errmsg)||`WeChat send failed with HTTP ${r.status}`)}(e,t,s,a,n),{provider:"wechat"}}}}(t,s):{bridge:s,send:async()=>{}}}class yo{entries=new Map;fetchImpl;generations=new Map;constructor(e){this.fetchImpl=e}clear(){for(const e of new Set([...this.generations.keys(),...[...this.entries.values()].map(e=>e.bridgeId)]))this.clearBridge(e)}clearBridge(e){this.generations.set(e,this.generation(e)+1),this.clearEntries(t=>t.bridgeId===e)}generation(e){return this.generations.get(e)||0}isCurrent(e,t){return this.generation(e)===t}clearEntries(e){for(const[t,s]of this.entries)e(s)&&(s.timer&&clearTimeout(s.timer),s.cancelPending?.(new Error("DingDing progress update was cancelled.")),this.entries.delete(t))}async applyUpdate(e,t){const s=this.generation(e.bridge.id),n=Yr(e.replyMarkup);let i=this.entries.get(e.key);if(i?.pending&&await i.pending,!this.isCurrent(e.bridge.id,s))return!1;if(i=this.entries.get(e.key),i?.outTrackId){if(i.lastText===e.text&&i.lastActionsFingerprint===n)return!0;const a=Date.now()-(i.lastUpdateAt||0);return a<1e3?(i.pendingText=e.text,i.pendingReplyMarkup=e.replyMarkup,i.timer||(i.pending=new Promise((n,r)=>{i.cancelPending=r,i.timer=setTimeout(()=>{i.timer=void 0,i.cancelPending=void 0;const a=i.pendingText,o=i.pendingReplyMarkup;if(i.pendingText=void 0,i.pendingReplyMarkup=void 0,!a)return n(void 0);this.updateExistingEntry(e,t,i,a,o,Yr(o),s).then(n,r)},1e3-a)}).finally(()=>{i.pending=void 0,i.timer=void 0,i.cancelPending=void 0})),await i.pending,this.isCurrent(e.bridge.id,s)&&this.entries.has(e.key)):(await this.updateExistingEntry(e,t,i,e.text,e.replyMarkup,n,s),this.isCurrent(e.bridge.id,s))}const a={bridgeId:e.bridge.id,lastText:e.text,lastActionsFingerprint:n,lastUpdateAt:Date.now()};return a.pending=Ur({fetchImpl:this.fetchImpl,bridge:e.bridge,runtime:t,chatId:e.chatId,text:e.text,replyMarkup:e.replyMarkup,sessionWebhook:t.chatWebhooks.get(e.chatId)||bo(e.bridge.settings.sessionWebhook),senderId:t.senderIds.get(e.chatId)||bo(e.bridge.settings.senderId),title:"TaskHandoff 执行中",step:"progress",forceCard:!0}).then(t=>{this.isCurrent(e.bridge.id,s)&&(t?.outTrackId&&(a.outTrackId=t.outTrackId),t?.delivered&&(a.delivered=!0,a.lastUpdateAt=Date.now()))}).catch(t=>{throw this.isCurrent(e.bridge.id,s)&&this.entries.delete(e.key),t}).finally(()=>{a.pending=void 0}),this.entries.set(e.key,a),await a.pending,this.isCurrent(e.bridge.id,s)&&Boolean(a.delivered)}async updateExistingEntry(e,t,s,n,i,a,r){s.outTrackId&&(await async function(e){await Fr(e.fetchImpl,e.bridge,e.runtime,"PUT","/v1.0/card/instances",{outTrackId:e.outTrackId,cardData:{cardParamMap:xr({type:"actions",title:e.title,description:e.text,list:pa(e.replyMarkup),biz_step:e.step,error_msg:""})},cardUpdateOptions:{updateCardDataByKey:!0,updatePrivateDataByKey:!1},userIdType:Number(e.bridge.settings.cardUserIdType||1)})}({fetchImpl:this.fetchImpl,bridge:e.bridge,runtime:t,outTrackId:s.outTrackId,text:n,replyMarkup:i,title:"TaskHandoff 执行中",step:"progress"}),this.isCurrent(e.bridge.id,r)&&(s.lastText=n,s.lastActionsFingerprint=a,s.lastUpdateAt=Date.now()))}}function bo(e){return"string"==typeof e?e:""}function vo(e){const t=new I.DWClient({clientId:e.clientId,clientSecret:e.clientSecret,ua:"task-handoff-control-plane",keepAlive:!1,autoReconnect:!1});let s,n=!1;return{async connect(){n=!1,await t.getEndpoint(),await t._connect();const e=t.socket;e?.once("close",()=>{n||s?.(new Error("DingDing stream connection closed."))})},disconnect(){n=!0,t.disconnect()},onDisconnect(e){s=e},getConfig:()=>t.getConfig(),registerCallbackListener(e,s){t.registerCallbackListener(e,s)},socketCallBackResponse(e,s){t.socketCallBackResponse(e,s)}}}class wo{runtimes=new Map;connected=new Set;reconnectTimers=new Map;progress;options;createClient;constructor(e){this.options=e,this.createClient=e.createClient||vo,this.progress=new yo(e.fetchImpl)}has(e){return this.runtimes.has(e)}isRunning(e){return this.connected.has(e)}get(e){return this.runtimes.get(e)}ids(){return this.runtimes.keys()}start(e){const t=(s=e.settings?.clientSecret,"string"==typeof s?s:"");var s;if(!e.token||!t)return this.options.onError(e.id,"DingDing client id/secret is not configured."),!1;const n={client:this.createClient({clientId:e.token,clientSecret:t}),chatWebhooks:new Map,senderIds:new Map,conversationTypes:new Map,onLog:(t,s,n)=>{this.options.logger["warn"===t?"warn":"info"]({bridgeId:e.id,...s},n)}};return n.client.onDisconnect?.(t=>{this.runtimes.get(e.id)===n&&this.handleConnectionFailure(e,n,t)}),n.client.registerCallbackListener(I.TOPIC_ROBOT,t=>{this.options.onRobotMessage(e,n,t).catch(t=>{this.options.onError(e.id,t)})}),n.client.registerCallbackListener(I.TOPIC_CARD,t=>{const s=t;this.options.onCardCallback(e,n,s).then(e=>n.client.socketCallBackResponse(s.headers.messageId,e||{})).catch(t=>{this.options.onError(e.id,t),n.client.socketCallBackResponse(s.headers.messageId,{})})}),this.options.clearError(e.id),this.runtimes.set(e.id,n),this.connect(e,n),!0}connect(e,t){this.reconnectTimers.delete(e.id),this.connected.add(e.id),t.client.connect().then(()=>{this.runtimes.get(e.id)===t&&(this.connected.add(e.id),this.options.clearError(e.id))}).catch(s=>{this.runtimes.get(e.id)===t&&this.handleConnectionFailure(e,t,s)})}handleConnectionFailure(e,t,s){if(this.connected.delete(e.id),this.progress.clearBridge(e.id),this.options.onError(e.id,s||"DingDing stream connection failed."),this.reconnectTimers.has(e.id))return;const n=setTimeout(()=>{this.runtimes.get(e.id)===t&&this.connect(e,t)},this.options.reconnectDelayMs??1e3);n.unref?.(),this.reconnectTimers.set(e.id,n)}stop(e){const t=this.reconnectTimers.get(e);t&&clearTimeout(t),this.reconnectTimers.delete(e),this.connected.delete(e);const s=this.runtimes.get(e);if(s){try{s.client.disconnect()}catch{}this.runtimes.delete(e)}this.progress.clearBridge(e)}stopAll(){for(const e of[...this.runtimes.keys()])this.stop(e);this.progress.clear()}applyUpdate(e){const t=this.runtimes.get(e.bridge.id);return t?this.progress.applyUpdate(e,t):Promise.resolve(!1)}applyProgressUpdate(e){return this.applyUpdate(e)}}class So{service;fetchImpl;logger;bridgeTimers=new Map;dingdingBridges;bridgeErrors=new Map;telegramOffsets=new Map;seenTelegramUpdates=new Set;wechatCursors=new Map;announcedPending=new Set;deliveredAiSessionFingerprints=new Map;aiSessionCancelTokens=new Map;aiSessionQueueActionTokens=new Map;telegramMessageAggregates=new Map;telegramMessageAggregateEndTokens=new Map;telegramProgressMessageTargets=new Map;aiSessionInstanceNamesCache;aiSessionInstanceNamesPending;aiSessionPendingRoutesCache;aiSessionPendingRoutesPending;telegramProgress;stopAiSessionListener;constructor(e,t=fetch,s={}){var n;this.service=e,this.fetchImpl=t,this.logger=(n=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(n||"").trim().toLowerCase())?s.logger:void 0),this.telegramProgress=new ro({updateIntervalMs:s.telegramProgressUpdateIntervalMs,requireBridge:e=>this.service.requireChatBridge(e),send:(e,t,s,n={})=>this.sendTelegramMessage(e,t,s,n),edit:(e,t,s,n,i={})=>this.editTelegramMessage(e,t,s,n,i),deleteMessage:(e,t,s)=>this.deleteTelegramMessage(e,t,s),onLog:e=>this.logWarn({message:e},"telegram progress store warning")}),this.dingdingBridges=new wo({fetchImpl:this.fetchImpl,createClient:s.createDingdingClient,logger:{info:(e,t)=>this.logInfo(e,t),warn:(e,t)=>this.logWarn(e,t)},onRobotMessage:(e,t,s)=>this.handleDingdingRobotMessage(e,t,s),onCardCallback:(e,t,s)=>this.handleDingdingCardCallback(e,t,s),onError:(e,t)=>this.bridgeErrors.set(e,Oo(t)),clearError:e=>this.bridgeErrors.delete(e)}),this.stopAiSessionListener=s.aiSessions?.onSnapshot(e=>{this.deliverAiSessionSnapshot(e.instanceId,e.aiSessions).catch(t=>{this.logWarn({instanceId:e.instanceId,snapshotRevision:e.revision,error:Oo(t)},"chat ai session snapshot delivery failed")})})}logInfo(e,t){this.logger?.info?.({component:"control-plane-chat-gateway",...e},t)}logWarn(e,t){this.logger?.warn?.({component:"control-plane-chat-gateway",...e},t)}updateChatBridge(e,t){this.service.updateChatBridge?.(e,t)}startEnabled(){this.logAiSessionDelivery({stage:"runtime-start-enabled",bridgeCount:this.service.listChatBridges().length,enabledBridgeCount:this.service.listChatBridges().filter(e=>e.enabled).length});for(const e of this.service.listChatBridges())e.enabled&&this.startBridge(e.id)}startBridge(e){if(this.bridgeTimers.has(e)||this.dingdingBridges.has(e))return this.logAiSessionDelivery({stage:"bridge-start-skipped-running",bridgeId:e}),this.status();const t=this.service.requireChatBridge(e);return this.logAiSessionDelivery({stage:"bridge-start",bridgeId:t.id,channel:t.channel,enabled:t.enabled,hasToken:Boolean(t.token)}),"wechat"===t.channel?this.startPollingBridge(t,e=>this.pollWechat(e)):"dingding"===t.channel?this.startDingdingBridge(t):"telegram"!==t.channel?(this.bridgeErrors.set(e,`${t.channel} bridge is not supported in the control plane.`),this.status()):t.token?this.startPollingBridge(t,e=>this.pollTelegram(e)):(this.bridgeErrors.set(e,"Telegram token is not configured."),this.status())}stopBridge(e){const t=this.bridgeTimers.get(e);return t&&(clearInterval(t),this.bridgeTimers.delete(e)),this.dingdingBridges.stop(e),this.status()}stopAll(){for(const e of new Set([...this.bridgeTimers.keys(),...this.dingdingBridges.ids()]))this.stopBridge(e);this.dingdingBridges.stopAll();for(const e of this.telegramMessageAggregates.values())e.timer&&clearTimeout(e.timer);this.telegramMessageAggregates.clear(),this.telegramMessageAggregateEndTokens.clear(),this.stopAiSessionListener?.(),this.stopAiSessionListener=void 0}async pollBridgeNow(e){const t=this.service.requireChatBridge(e);return"telegram"===t.channel?(await this.pollTelegram(t),this.status()):"wechat"===t.channel?(await this.pollWechat(t),this.status()):("dingding"===t.channel||this.bridgeErrors.set(e,`${t.channel} bridge is not supported in the control plane.`),this.status())}async pollAiSessionsNow(){return await this.pollAiSessionUpdates(),this.status()}status(){const e=this.service.listChatBridges().map(e=>({id:e.id,channel:e.channel,name:e.name,running:this.bridgeTimers.has(e.id)||this.dingdingBridges.isRunning(e.id),tokenSet:Boolean(e.tokenSet),defaultChatId:e.defaultChatId,lastUpdateId:this.telegramOffsets.get(e.id),error:this.bridgeErrors.get(e.id)}));return{running:e.some(e=>e.running),bridges:e}}startPollingBridge(e,t){if(!e.token)return this.bridgeErrors.set(e.id,`${e.channel} token is not configured.`),this.status();this.bridgeErrors.delete(e.id);const s=setInterval(()=>{const s=this.service.requireChatBridge(e.id);s.enabled?t(s).catch(t=>this.bridgeErrors.set(e.id,t instanceof Error?t.message:String(t))):this.stopBridge(e.id)},e.pollIntervalMs);if(this.bridgeTimers.set(e.id,s),this.logAiSessionDelivery({stage:"bridge-poll-started",bridgeId:e.id,channel:e.channel,intervalMs:e.pollIntervalMs}),e.enabled){const s=this.service.requireChatBridge(e.id);t(s).catch(t=>this.bridgeErrors.set(e.id,t instanceof Error?t.message:String(t)))}return this.status()}startDingdingBridge(e){return this.dingdingBridges.start(e),this.status()}async handleDingdingRobotMessage(e,t,s){const n=function(e){const t=Qr(e),s=Jr(t.conversationId),n=Jr(Vr(t.text).content).trim();if(!s||!n)return;const i=Jr(t.senderStaffId||t.senderId);return{chatId:s,userId:i||void 0,senderId:i||void 0,sessionWebhook:Jr(t.sessionWebhook)||void 0,conversationType:Dr(t.conversationType,{robotMessage:!0}),text:n,raw:t}}(s.data);if(t.client.socketCallBackResponse(s.headers.messageId,{}),!n||!this.dingdingAllowed(e,n.senderId))return void this.logWarn({bridgeId:e.id,hasEvent:Boolean(n),senderId:n?.senderId,allowed:n?this.dingdingAllowed(e,n.senderId):void 0},"dingding robot message ignored");const{chatId:i,text:a,senderId:r,sessionWebhook:o}=n;o&&t.chatWebhooks.set(i,o),n.conversationType&&t.conversationTypes.set(i,n.conversationType);const d=o||t.chatWebhooks.get(i)||xo(e.settings.sessionWebhook);let c;r&&t.senderIds.set(i,r),e.defaultChatId||this.updateChatBridge(e.id,{defaultChatId:i}),this.logInfo({bridgeId:e.id,chatId:i,senderId:r,conversationType:n.conversationType,hasSessionWebhook:Boolean(o),hasCachedSessionWebhook:Boolean(t.chatWebhooks.get(i)),hasResolvedSessionWebhook:Boolean(d),textPreview:jo(a)},"dingding chat gateway message received");try{c=await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"dingding",bridgeId:e.id,chatSessionId:i,userId:r||void 0},message:{text:a,attachments:[]}})}catch(e){if(d)return void await Lr(this.fetchImpl,d,`Failed to handle message: ${Oo(e)}`,t.onLog);throw e}const l=Ao(c);if(l){const s=zo(c);if(await this.sendDingdingRoutedAiSessionProgress(c,e,t,i,l,{replyMarkup:s,sessionWebhook:d,senderId:r,sourcePrompt:a}))return;const n=Io({fetchImpl:this.fetchImpl,bridge:e,dingdingRuntime:t}),o=await n.send(i,l,{replyMarkup:s,sessionWebhook:d,senderId:r});if(o)return void this.logInfo({bridgeId:e.id,chatId:i,senderId:r,provider:o.provider,interactionId:o.interactionId,routed:!0===Po(c).routed,hasReplyMarkup:Boolean(s),replyPreview:jo(l)},"dingding chat gateway reply sent");this.logWarn({bridgeId:e.id,chatId:i,senderId:r,hasReplyMarkup:Boolean(s),hasSessionWebhook:Boolean(d),hasRobotCode:Boolean(xo(e.settings.robotCode)),replyPreview:jo(l)},"dingding chat gateway reply skipped")}}async sendDingdingRoutedAiSessionProgress(e,t,s,n,i,a={}){if(!0!==e.routed)return!1;const r=Lo(e),{instanceId:o,sessionId:d,turnId:c}=r;if(!o||!d||!c)return this.logWarn({bridgeId:t.id,chatId:n,instanceId:o,sessionId:d,turnId:c,turnIdSources:r.sources,routed:e.routed},"dingding ai session progress card not started"),!1;const l=Vo(o,d,c,t.id,n);return!!await this.dingdingBridges.applyProgressUpdate({bridge:t,key:l,chatId:n,text:i,replyMarkup:a.replyMarkup})&&(this.deliveredAiSessionFingerprints.delete(l),this.logInfo({bridgeId:t.id,chatId:n,instanceId:o,sessionId:d,turnId:c,key:l,hasSessionWebhook:Boolean(a.sessionWebhook),hasSenderId:Boolean(a.senderId),sourcePrompt:jo(a.sourcePrompt,80),textPreview:jo(i)},"dingding ai session progress card started"),!0)}async handleDingdingCardCallback(e,t,s){const n=function(e){const t=Qr(e),s=Kr(t.cardActionData||t.content),n=Kr(s.cardPrivateData||t.cardPrivateData||t.content),i=Kr(n.params||s.params||t.params),a=Jr(t.userId),r=Jr(i.biz_sender_id);return{userId:a||void 0,chatId:jr(t,i),senderId:a||void 0,deliverySenderId:r||void 0,sessionWebhook:Jr(i.biz_session_webhook)||void 0,conversationType:Dr(i.biz_conversation_type||t.conversationType||t.conversation_type),callbackData:Mr($r(t,s,n)),body:t,params:i}}(s.data);if(!this.dingdingAllowed(e,n.userId))return Pr("not authorized",void 0,"unauthorized",n.body,n.params);const i=n.chatId||xo(e.defaultChatId),a=n.deliverySenderId||n.senderId,r=n.sessionWebhook;let o;i&&r&&t.chatWebhooks.set(i,r),i&&n.conversationType&&t.conversationTypes.set(i,n.conversationType),i&&a&&t.senderIds.set(i,a);try{o=await this.parseChatGatewayCallbackAction(n.callbackData)}catch(e){if(Ro(e,"CHAT_PENDING_ACTION_STALE"))return Pr(Oo(e),void 0,"approval_stale",n.body,n.params);throw e}if(!o)return this.logWarn({bridgeId:e.id,chatId:i,userId:n.userId,senderId:a,...Do(n)},"dingding card callback action unsupported"),Pr("Unsupported action",void 0,"unsupported",n.body,n.params);this.logInfo({bridgeId:e.id,chatId:i,userId:n.userId,senderId:a,actionType:o.type,...Do(n)},"dingding card callback action parsed");const d=await this.service.handleChatGatewayAction({source:{channel:"dingding",bridgeId:e.id,chatSessionId:i,userId:n.userId||void 0},action:o});return"pending-decision"===o.type&&ko(d)?Pr(`${o.decision} sent`,void 0,"approval_done",n.body,n.params):Pr(Ao(d)||xo(d.message)||"updated",zo(d),"updated",n.body,n.params)}async pollWechat(e){if(!e.token)return;const t=e.settings||{},s=this.wechatCursors.get(e.id)||xo(t.updatesBuf),n=await async function(e){const t=fo((e.bridge.settings||{}).baseUrl)||"https://ilinkai.weixin.qq.com",s=await e.fetchImpl(`${t}/ilink/bot/getupdates`,{method:"POST",headers:{"content-type":"application/json",AuthorizationType:"ilink_bot_token",Authorization:`Bearer ${e.bridge.token}`,"X-WECHAT-UIN":String(Math.floor(Math.random()*2**31))},body:JSON.stringify({get_updates_buf:e.cursor,base_info:{channel_version:"1.0.2"}})}),n=await s.json().catch(()=>({}));if(!s.ok)throw new Error(`HTTP ${s.status}`);const i=Number(n.errcode??n.ret??0);if(0!==i)throw new Error(fo(n.errmsg||n.msg)||`chat bridge API returned ${i}`);const a=[];for(const e of Array.isArray(n.msgs)?n.msgs:[]){const t=ho(e);if(1!==t.message_type)continue;const s=fo(t.from_user_id),n=go(t);s&&n&&a.push({chatId:s,text:n,contextToken:fo(t.context_token)})}return{cursor:fo(n.get_updates_buf),messages:a}}({fetchImpl:this.fetchImpl,bridge:e,cursor:s});n.cursor&&(this.wechatCursors.set(e.id,n.cursor),this.updateChatBridge(e.id,{settings:{updatesBuf:n.cursor}}));for(const s of n.messages){const{chatId:n,contextToken:i,text:a}=s;if(e.defaultChatId||this.updateChatBridge(e.id,{defaultChatId:n,settings:{contextToken:i}}),e.defaultChatId&&e.defaultChatId!==n)continue;const r=Ao(await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"wechat",bridgeId:e.id,chatSessionId:n},message:{text:a,attachments:[]}}));if(r){const s=Io({fetchImpl:this.fetchImpl,bridge:e});await s.send(n,r,{contextToken:i||xo(t.contextToken)})}}}async pollPendingRoutes(){const e=await this.service.listPendingRoutes().catch(()=>[]);for(const t of e){if(this.announcedPending.has(t.id))continue;const e=Uo(t,(e,t)=>this.service.pendingDecisionCallbackData(e,t)),s=t.aiSessionId?this.service.listChatSessions().filter(e=>e.activeInstanceId===t.instanceId&&e.activeAiSessionId===t.aiSessionId):[],n=new Set;for(const t of s)t.bridgeId&&await this.sendToChatBinding(t,e).then(e=>{e&&n.add(t.id)});(0!==n.size||await this.sendToDefaultBridgeChats(e))&&this.announcedPending.add(t.id)}}async sendToDefaultBridgeChats(e){let t=!1;for(const s of this.service.listChatBridges()){if(!s.enabled||!s.defaultChatId)continue;let n=!1;await this.sendViaBridge(s,s.defaultChatId,e).catch(e=>{this.bridgeErrors.set(s.id,e instanceof Error?e.message:String(e)),n=!0})?t=!0:n||this.bridgeErrors.set(s.id,"Chat bridge message was not delivered.")}return t}async sendToChatBinding(e,t){if(!e.bridgeId)return!1;const s=this.service.requireChatBridge(e.bridgeId);if(!s.enabled)return!1;const n=await this.sendViaBridge(s,e.chatSessionId,t).catch(e=>{this.bridgeErrors.set(s.id,e instanceof Error?e.message:String(e))});return Boolean(n)}async sendViaBridge(e,t,s){const n="string"==typeof s?s:s.text,i="string"==typeof s?void 0:s.replyMarkup;return Io({fetchImpl:this.fetchImpl,bridge:e,dingdingRuntime:this.dingdingBridges.get(e.id)}).send(t,n,{replyMarkup:i})}async pollTelegram(e){if(!e.token)return;const t=Number(e.settings?.telegramLastUpdateId),s=this.telegramOffsets.get(e.id)||(Number.isInteger(t)?t:void 0),n=await async function(e){const t=new URLSearchParams({timeout:"0",...e.offset?{offset:String(e.offset+1)}:{}}),s=await e.fetchImpl(`https://api.telegram.org/bot${e.bridge.token}/getUpdates?${t.toString()}`),n=await s.json().catch(()=>({}));if(!s.ok||!1===n.ok){if(409===s.status||/terminated by other getUpdates request/i.test(String(n.description||"")))return{updates:[],conflict:n.description||"Telegram getUpdates conflict: another bot instance is polling this token."};throw new Error(n.description||`Telegram getUpdates failed with HTTP ${s.status}`)}const i=[];for(const t of n.result||[]){const s=Number(t.update_id),n=Number.isInteger(s)?s:void 0,a=po(t.callback_query);if(Object.keys(a).length>0){i.push({updateId:n,callbackQuery:a});continue}const r=po(t.message||t.edited_message);if(0===Object.keys(r).length)continue;const o=po(r.chat),d=po(r.from),c=void 0!==o.id?String(o.id):e.bridge.defaultChatId,l=void 0!==d.id?String(d.id):void 0;if(!c)continue;const p=uo(r.text??r.caption),u=io(r);(p||0!==u.length)&&i.push({updateId:n,chatId:c,userId:l,text:p,rawText:p,message:r,messageId:lo(r),replyToMessageId:lo(po(r.reply_to_message)),quoteText:uo(po(r.quote).text),autoBegin:1===u.length&&!p.trim(),hasSingleImageWithoutText:1===u.length&&!p.trim()})}return{updates:i}}({fetchImpl:this.fetchImpl,bridge:e,offset:s});if(n.conflict)this.bridgeErrors.set(e.id,n.conflict);else for(const t of n.updates){if(void 0!==t.updateId){const s=`${e.id}:${t.updateId}`;if(this.seenTelegramUpdates.has(s)||t.updateId<=(this.telegramOffsets.get(e.id)||0))continue;this.seenTelegramUpdates.add(s),this.telegramOffsets.set(e.id,t.updateId),this.updateChatBridge(e.id,{settings:{...e.settings,telegramLastUpdateId:t.updateId}})}if("callbackQuery"in t){const s=t.callbackQuery;await this.handleTelegramCallback(e,s).catch(async t=>{const n=Po(s.message),i=Po(n.chat),a=void 0!==i.id?String(i.id):e.defaultChatId;this.bridgeErrors.set(e.id,Oo(t)),a&&await this.sendTelegramMessage(e,a,`Action failed: ${Oo(t)}`).catch(t=>{this.bridgeErrors.set(e.id,Oo(t))})});continue}if(!this.telegramAllowed(e,t.userId))continue;const s=await oo({fetchImpl:this.fetchImpl,bridge:e,message:t.message,onImageDownloadError:({fileId:t,fileName:s,error:n})=>{this.logWarn({bridgeId:e.id,fileId:t,fileName:s,error:Oo(n)},"telegram image download skipped")}}),n=t.rawText.trim();(n||s.length)&&(this.logInfo({bridgeId:e.id,chatId:t.chatId,messageId:t.messageId,replyToMessageId:t.replyToMessageId,hasQuote:Boolean(t.quoteText),textPreview:jo(n)},"telegram chat gateway message received"),await this.handleTelegramIncomingText(e,t.chatId,t.userId,n,s,{sourceMessageId:t.messageId,replyToMessageId:t.replyToMessageId,quoteText:t.quoteText},{autoBegin:t.autoBegin}))}}async handleTelegramIncomingText(e,t,s,n,i=[],a={},r={}){const o=n.trim(),d=o.split(/\s+/,1)[0]?.toLowerCase()||"",c=Eo(e.id,t,s),l=this.telegramMessageAggregates.get(c);return r.autoBegin&&!l?(this.startTelegramAggregate(c,e,t,s,a),this.enqueueTelegramAggregate(c,e,t,s,n,i,a,!0),void await this.sendTelegramMessage(e,t,"Image received. Continue sending messages, then send /end when done.",{replyToMessageId:a.sourceMessageId,replyMarkup:da([[{text:"/end",callbackData:this.telegramAggregateEndCallbackData(c)}]])})):"/begin"===d?(this.startTelegramAggregate(c,e,t,s,a),void await this.sendTelegramMessage(e,t,"Started collecting messages. Send /end when done.",{replyToMessageId:a.sourceMessageId,replyMarkup:da([[{text:"/end",callbackData:this.telegramAggregateEndCallbackData(c)}]])})):void("/end"!==d?l?.explicit?this.enqueueTelegramAggregate(c,e,t,s,n,i,a,!0):this.isImmediateTelegramCommand(o)?await this.dispatchTelegramGatewayMessage(e,t,s,n,i,a):this.enqueueTelegramAggregate(c,e,t,s,n,i,a,!1):await this.flushTelegramAggregate(c,"manual"))}isImmediateTelegramCommand(e){return e.trim().startsWith("/")}startTelegramAggregate(e,t,s,n,i={}){const a=this.telegramMessageAggregates.get(e);a?.timer&&clearTimeout(a.timer),this.telegramMessageAggregates.set(e,{bridgeId:t.id,chatId:s,userId:n,texts:[],attachments:[],context:i,explicit:!0})}enqueueTelegramAggregate(e,t,s,n,i,a,r,o){const d=this.telegramMessageAggregates.get(e);d?.timer&&clearTimeout(d.timer);const c=d||{bridgeId:t.id,chatId:s,userId:n,texts:[],attachments:[],context:r,explicit:o};var l,p;c.bridgeId=t.id,c.chatId=s,c.userId=n,c.context=(l=c.context,p=r,{sourceMessageId:l?.sourceMessageId??p.sourceMessageId,replyToMessageId:l?.replyToMessageId??p.replyToMessageId,quoteText:l?.quoteText||p.quoteText}),c.explicit=c.explicit||o,i.trim()&&c.texts.push(i),c.attachments.push(...a),c.explicit?c.timer=void 0:c.timer=setTimeout(()=>{this.flushTelegramAggregate(e,"timer")},1e3),this.telegramMessageAggregates.set(e,c)}async flushTelegramAggregate(e,t){const s=this.telegramMessageAggregates.get(e);if(!s)return!1;s.timer&&clearTimeout(s.timer),this.telegramMessageAggregates.delete(e);const n=this.service.requireChatBridge(s.bridgeId),i=s.texts.join("\n\n").trim();return i||s.attachments.length?(await this.dispatchTelegramGatewayMessage(n,s.chatId,s.userId,i,s.attachments,s.context),!0):("timer"!==t&&await this.sendTelegramMessage(n,s.chatId,"No messages collected.",{replyToMessageId:s.context?.sourceMessageId}),!1)}async handleTelegramAggregateEndCallback(e,t,s,n,i){const a=this.telegramMessageAggregateEndTokens.get(n)||"";if(a!==Eo(e.id,t,i))return void await this.answerTelegramCallback(e,s,"This collection is not active here");const r=await this.flushTelegramAggregate(a,"callback");await this.answerTelegramCallback(e,s,r?"Collected message sent":"No messages collected")}telegramAggregateEndCallbackData(e){const t=o.createHash("sha256").update(e).digest("base64url").slice(0,16);return this.telegramMessageAggregateEndTokens.set(t,e),`task_handoff:cp_msg_end:${t}`}async dispatchTelegramGatewayMessage(e,t,s,n,i=[],a={}){try{const r=void 0!==a.replyToMessageId?this.telegramReplyAiSessionTarget(e.id,t,a.replyToMessageId):void 0,o=function(e,t){const s=String(t||"").trim(),n=e.trim();return s?[`引用:${s}`,n].filter(Boolean).join("\n"):n}(n,a.quoteText),d=await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"telegram",bridgeId:e.id,chatSessionId:t,userId:s},message:{text:o,attachments:i},...r?{target:{instanceId:r.instanceId,aiSessionId:r.sessionId}}:{}}),c=Ao(d),l=zo(d);if(c){const s=await this.sendTelegramMessage(e,t,c,{replyMarkup:l,replyToMessageId:a.sourceMessageId});this.logInfo({bridgeId:e.id,chatId:t,sourceMessageId:a.sourceMessageId,replyToMessageId:a.replyToMessageId,replyTargetInstanceId:r?.instanceId,replyTargetSessionId:r?.sessionId,sentMessageId:Number(Po(s).message_id)||void 0,routed:!0===Po(d).routed,replyPreview:jo(c)},"telegram chat gateway reply sent"),await this.rememberTelegramProgressMessage(d,e,t,s,c)}}catch(s){this.bridgeErrors.set(e.id,Oo(s)),await this.sendTelegramMessage(e,t,`Failed to handle message: ${Oo(s)}`,{replyToMessageId:a.sourceMessageId}).catch(t=>{this.bridgeErrors.set(e.id,Oo(t))})}}async handleTelegramCallback(e,t){const s=xo(t.data),n=Po(t.from),i=Po(t.message),a=Po(i.chat),r=void 0!==a.id?String(a.id):e.defaultChatId,o=void 0!==n.id?String(n.id):void 0;if(!r||!this.telegramAllowed(e,o))return;const d=s.match(/^task_handoff:cp_ai_cancel:([^:]+)$/);if(d){const s=this.aiSessionCancelTokens.get(d[1]);return s?void await this.handleTelegramAiSessionCancelCallback(e,r,xo(t.id),s.instanceId,s.sessionId,o,Co(i)):void await this.answerTelegramCallback(e,xo(t.id),"This AI session action expired")}const c=s.match(/^task_handoff:cp_ai_steer:([^:]+)$/);if(c){const s=this.aiSessionQueueActionTokens.get(c[1]);return s&&"steer"===s.type?void await this.handleTelegramAiSessionQueueSteerCallback(e,r,xo(t.id),s.instanceId,s.sessionId,s.queueId,o,Co(i)):void await this.answerTelegramCallback(e,xo(t.id),"This AI session action expired")}const l=s.match(/^task_handoff:cp_ai_qdel_menu:([^:]+)$/);if(l){const s=this.aiSessionQueueActionTokens.get(l[1]);return s&&"delete-menu"===s.type?void await this.handleTelegramAiSessionQueueDeleteMenuCallback(e,r,xo(t.id),s.instanceId,s.sessionId,o,Co(i)):void await this.answerTelegramCallback(e,xo(t.id),"This AI session action expired")}const p=s.match(/^task_handoff:cp_ai_qdel:([^:]+)$/);if(p){const s=this.aiSessionQueueActionTokens.get(p[1]);return s&&"delete-item"===s.type?void await this.handleTelegramAiSessionQueueDeleteItemCallback(e,r,xo(t.id),Co(i),s.instanceId,s.sessionId,s.queueId,o):void await this.answerTelegramCallback(e,xo(t.id),"This AI session action expired")}const u=s.match(/^task_handoff:cp_msg_end:([^:]+)$/);if(u)return void await this.handleTelegramAggregateEndCallback(e,r,xo(t.id),u[1],o);let m;try{m=await this.parseChatGatewayCallbackAction(s)}catch(s){if(Ro(s,"CHAT_PENDING_ACTION_STALE")){await this.answerTelegramCallback(e,xo(t.id),Oo(s));const n=Co(i);return void(void 0!==n&&await this.deleteTelegramStandaloneApprovalMessage(e,r,n))}throw s}if(!m)return void await this.answerTelegramCallback(e,xo(t.id),"Unsupported action");const g=await this.service.handleChatGatewayAction({source:{channel:"telegram",bridgeId:e.id,chatSessionId:r,userId:o},action:m});if("pending-decision"===m.type&&ko(g)){await this.answerTelegramCallback(e,xo(t.id),"");const s=Co(i);return void(void 0!==s&&await this.deleteTelegramStandaloneApprovalMessage(e,r,s))}await this.answerTelegramCallback(e,xo(t.id),xo(g.message)||"updated");const h=Ao(g),f=zo(g),I=i.message_id;h&&"number"==typeof I?await this.editTelegramMessage(e,r,I,h,{replyMarkup:f}):h&&await this.sendTelegramMessage(e,r,h,{replyMarkup:f})}async deleteTelegramStandaloneApprovalMessage(e,t,s){[...this.telegramProgress.entries.values()].find(n=>n.messageId===s&&n.route?.bridgeId===e.id&&n.route?.chatId===t)||await this.deleteTelegramMessage(e,t,s).catch(n=>{this.logWarn({bridgeId:e.id,chatId:t,messageId:s,error:Oo(n)},"telegram callback message delete failed")})}async handleTelegramAiSessionCancelCallback(e,t,s,n,i,a,r){if(this.telegramAiSessionActionAllowed(e,t,n,i,r))try{await this.service.interruptAiSession(n,i),await this.answerTelegramCallback(e,s,"Interrupt sent"),this.logInfo({bridgeId:e.id,chatId:t,instanceId:n,sessionId:i,userId:a},"telegram ai session cancel sent")}catch(t){this.bridgeErrors.set(e.id,Oo(t)),await this.answerTelegramCallback(e,s,`Interrupt failed: ${jo(Oo(t),120)}`)}else await this.answerTelegramCallback(e,s,"This chat is not bound to that AI session")}async handleTelegramAiSessionQueueSteerCallback(e,t,s,n,i,a,r,o){if(this.telegramAiSessionActionAllowed(e,t,n,i,o))try{await this.service.steerAiSessionQueuedMessage(n,i,a),await this.answerTelegramCallback(e,s,"Steered queued message"),this.logInfo({bridgeId:e.id,chatId:t,instanceId:n,sessionId:i,queueId:a,userId:r},"telegram ai session queued message steered")}catch(t){this.bridgeErrors.set(e.id,Oo(t)),await this.answerTelegramCallback(e,s,`Steer failed: ${jo(Oo(t),120)}`)}else await this.answerTelegramCallback(e,s,"This chat is not bound to that AI session")}async handleTelegramAiSessionQueueDeleteMenuCallback(e,t,s,n,i,a,r){if(this.telegramAiSessionActionAllowed(e,t,n,i,r))try{const r=Po(await this.service.aiSessionQueue(n,i)),o=(Array.isArray(r.items)?r.items:[]).map(e=>Po(e)).filter(e=>"queued"===xo(e.status)&&xo(e.id));if(!o.length)return void await this.answerTelegramCallback(e,s,"Queue is empty");const d=o.slice(0,Fo).map((e,t)=>[{text:Wo(xo(e.message),t),callbackData:this.aiSessionQueueDeleteItemCallbackData(n,i,xo(e.id))}]);await this.sendTelegramMessage(e,t,"Delete queued message",{replyMarkup:da(d)}),await this.answerTelegramCallback(e,s,"Select a queued message"),this.logInfo({bridgeId:e.id,chatId:t,instanceId:n,sessionId:i,itemCount:o.length,userId:a},"telegram ai session queue delete menu sent")}catch(t){this.bridgeErrors.set(e.id,Oo(t)),await this.answerTelegramCallback(e,s,`Queue menu failed: ${jo(Oo(t),120)}`)}else await this.answerTelegramCallback(e,s,"This chat is not bound to that AI session")}async handleTelegramAiSessionQueueDeleteItemCallback(e,t,s,n,i,a,r,o){if(this.telegramAiSessionActionAllowed(e,t,i,a,n))try{await this.service.removeAiSessionQueuedMessage(i,a,r),await this.answerTelegramCallback(e,s,"Queued message deleted"),Number.isInteger(n)&&await this.deleteTelegramMessage(e,t,n).catch(s=>{this.logWarn({bridgeId:e.id,chatId:t,instanceId:i,sessionId:a,queueId:r,messageId:n,error:Oo(s)},"telegram ai session queue delete menu message delete failed")}),this.logInfo({bridgeId:e.id,chatId:t,instanceId:i,sessionId:a,queueId:r,userId:o},"telegram ai session queued message deleted")}catch(t){this.bridgeErrors.set(e.id,Oo(t)),await this.answerTelegramCallback(e,s,`Delete failed: ${jo(Oo(t),120)}`)}else await this.answerTelegramCallback(e,s,"This chat is not bound to that AI session")}telegramAiSessionBindingActive(e,t,s,n){return this.service.listChatSessions().some(i=>i.bridgeId===e.id&&i.chatSessionId===t&&i.activeInstanceId===s&&i.activeAiSessionId===n)}telegramAiSessionActionAllowed(e,t,s,n,i){if(this.telegramAiSessionBindingActive(e,t,s,n))return!0;if(!Number.isInteger(i))return!1;const a=this.telegramReplyAiSessionTarget(e.id,t,i);return Boolean(a&&a.instanceId===s&&a.sessionId===n)}async parseChatGatewayCallbackAction(e){const t=function(e){const t=e.match(/^task_handoff:cp_p:v1:([ads]):([A-Za-z0-9_-]{16})$/);if(!t)return;const s=Object.entries(Xi).find(([,e])=>e===t[1])?.[0];return"allow"===s||"deny"===s||"skip"===s?{routeFingerprint:t[2],decision:s}:void 0}(e);if(t){const e=(await this.service.listPendingRoutes()).filter(e=>"approval"===e.kind&&"pending"===e.status&&Zi(e.id)===t.routeFingerprint);if(1!==e.length){const e=new Error("This approval is no longer pending.");throw Object.assign(e,{statusCode:409,code:"CHAT_PENDING_ACTION_STALE"}),e}return{type:"pending-decision",routeId:e[0].id,decision:t.decision}}return function(e,t){const s=e.match(/^task_handoff:cp_session:(\d+)$/);if(s)return{type:"ai-session",index:Number(s[1])};const n=e.match(/^task_handoff:cp_i:([^:]+)$/)||e.match(/^task_handoff:cp_instance_apps:([^:]+)$/);if(n)return n[0].startsWith("task_handoff:cp_i:")?{type:"instance-app-menu",instanceId:Tr(t(n[1],"instance-app-menu").instanceId,"instanceId")}:{type:"instance-app-menu",instanceId:Nr(n[1])};const i=e.match(/^task_handoff:cp_a:([^:]+)$/)||e.match(/^task_handoff:cp_launch_app:([^:]+):([^:]+)$/);if(i){if(i[0].startsWith("task_handoff:cp_a:")){const e=t(i[1],"launch-app");return{type:"launch-app",instanceId:Tr(e.instanceId,"instanceId"),appId:Tr(e.appId,"appId")}}return{type:"launch-app",instanceId:Nr(i[1]),appId:Nr(i[2])}}const a=e.match(/^task_handoff:cp_p:([^:]+)$/);if(a){const e=t(a[1],"pending-decision");return{type:"pending-decision",routeId:Tr(e.routeId,"routeId"),decision:Cr(e.decision)}}const r=e.match(/^task_handoff:approval:([^:]+:ai:[^:]+):(allow|deny|skip)$/);return r?{type:"pending-decision",routeId:r[1],decision:r[2]}:void 0}(e,(e,t)=>this.service.resolveChatActionToken(e,t))}dingdingAllowed(e,t){const s=String(t||"").trim();return!!s&&(0===e.allowedUserIds.length?(e.allowedUserIds=[s],this.updateChatBridge(e.id,{allowedUserIds:e.allowedUserIds}),this.logInfo({bridgeId:e.id,userId:s},"dingding user bound"),!0):e.allowedUserIds.includes(s))}telegramAllowed(e,t){const s=String(t||"").trim();return!!s&&(0===e.allowedUserIds.length?(e.allowedUserIds=[s],this.updateChatBridge(e.id,{allowedUserIds:e.allowedUserIds}),this.logInfo({bridgeId:e.id,userId:s},"telegram user bound"),!0):e.allowedUserIds.includes(s))}async answerTelegramCallback(e,t,s){return async function(e,t,s,n){t.token&&s&&await e(`https://api.telegram.org/bot${t.token}/answerCallbackQuery`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({callback_query_id:s,text:n.slice(0,200)})})}(this.fetchImpl,e,t,s)}async editTelegramMessage(e,t,s,n,i={}){return async function(e,t,s,n,i,a={}){if(t.token)return kr(s=>e(`https://api.telegram.org/bot${t.token}/editMessageText`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)}),"editMessageText",i,{chat_id:s,message_id:n,...a.replyMarkup?{reply_markup:a.replyMarkup}:{}},a)}(this.fetchImpl,e,t,s,n,i)}async sendTelegramMessage(e,t,s,n={}){return Sr(this.fetchImpl,e,t,s,n)}async deleteTelegramMessage(e,t,s){return async function(e,t,s,n){if(!t.token||!Number.isInteger(n))return;const i=await e(`https://api.telegram.org/bot${t.token}/deleteMessage`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({chat_id:s,message_id:n})}),a=await i.json().catch(()=>({}));if(!i.ok||!1===a.ok)throw new Error(a.description||`Telegram deleteMessage failed with HTTP ${i.status}`)}(this.fetchImpl,e,t,s)}async rememberTelegramProgressMessage(e,t,s,n,i){if(!0!==e.routed)return;const a=Lo(e),{instanceId:r,sessionId:o,turnId:d}=a,c=Number(Po(n).message_id);if(!(r&&o&&d&&Number.isInteger(c)))return void this.logWarn({bridgeId:t.id,chatId:s,instanceId:r,sessionId:o,turnId:d,turnIdSources:a.sources,messageId:Number.isInteger(c)?c:void 0,routed:e.routed},"telegram ai session progress message not remembered");const l=Vo(r,o,d,t.id,s),p=this.telegramProgress.remember(l,c,i,{bridgeId:t.id,chatId:s});this.telegramProgressMessageTargets.set(No(t.id,s,c),{instanceId:r,sessionId:o,turnId:d,messageId:c}),this.deliveredAiSessionFingerprints.delete(l),this.logInfo({bridgeId:t.id,chatId:s,instanceId:r,sessionId:o,turnId:d,turnIdSources:a.sources,key:l,messageId:c,progressKeys:this.telegramProgressKeysFor(r,o,t.id,s),textPreview:jo(i)},"telegram ai session progress message remembered"),"queue"===function(e){return e.aiSession&&"action"in e.aiSession?e.aiSession.action:void 0}(e)&&p?.messageId&&p.messageId!==c&&(this.telegramProgressMessageTargets.delete(No(t.id,s,p.messageId)),await this.deleteTelegramMessage(t,s,p.messageId).catch(e=>{this.logWarn({bridgeId:t.id,chatId:s,instanceId:r,sessionId:o,turnId:d,replacedMessageId:p.messageId,replacementMessageId:c,error:Oo(e)},"telegram queued progress message replacement cleanup failed")}))}telegramReplyAiSessionTarget(e,t,s){const n=this.telegramProgressMessageTargets.get(No(e,t,s));if(n)return n;for(const[n,i]of this.telegramProgress.entries)if(i.messageId===s&&i.route?.bridgeId===e&&i.route?.chatId===t){const i=_o(n,e,t);if(i){const n={...i,messageId:s};return this.telegramProgressMessageTargets.set(No(e,t,s),n),n}}}async pollAiSessionUpdates(){this.logAiSessionDelivery({stage:"poll-tick"});const e=await this.service.listAiSessions({refresh:!0}).catch(()=>{});if(!e)return void this.logAiSessionDelivery({stage:"poll-no-snapshot"});const t=new Map(e.instances.map(e=>[e.instanceId,e.aiSessions]));this.logAiSessionDelivery({stage:"poll-snapshot",instanceCount:t.size,instances:[...t.entries()].map(([e,t])=>({instanceId:e,sessionCount:t.sessions?.length||0,runningCount:t.runningCount,waitingCount:t.waitingCount,updatedAt:t.updatedAt}))});const s=await this.aiSessionInstanceNames();for(const[e,n]of t)await this.deliverAiSessionSnapshot(e,n,s)}async deliverAiSessionSnapshot(e,t,s){const n=Array.isArray(t.sessions)?t.sessions:[];if(this.pruneAiSessionDeliveryState(e,new Set(n.map(e=>e.id))),0===n.length)return;const i=s||await this.aiSessionInstanceNames(),a=await this.aiSessionPendingApprovalRoutes(e);await this.deliverOwnedTelegramProgress(e,n,i,a),await this.deliverActiveBindingTelegramProgress(e,n,i,a)}pruneAiSessionDeliveryState(e,t){for(const[s,n]of this.telegramProgress.entries){const i=n.route?.bridgeId&&n.route.chatId?this.telegramProgressMessageTargets.get(No(n.route.bridgeId,n.route.chatId,n.messageId||0))||_o(s,n.route.bridgeId,n.route.chatId):void 0;i&&i.instanceId===e&&!t.has(i.sessionId)&&(n.timer&&clearTimeout(n.timer),this.telegramProgress.entries.delete(s),this.deliveredAiSessionFingerprints.delete(s),n.route?.bridgeId&&n.route.chatId&&n.messageId&&this.telegramProgressMessageTargets.delete(No(n.route.bridgeId,n.route.chatId,n.messageId)))}}async deliverActiveBindingTelegramProgress(e,t,s,n){for(const i of this.service.listChatSessions()){if(!i.bridgeId||i.activeInstanceId!==e||!i.activeAiSessionId)continue;const a=t.find(e=>e.id===i.activeAiSessionId);if(!a)continue;const r=Xo(s.get(e)||e,a),o=Yo(a,r);if(!o){this.logAiSessionDelivery({stage:"skip-empty-text",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,status:a.status,phase:a.phase,activeTurnId:a.activeTurnId,latestTurn:Mo(ed(a))});continue}const d=Ko(a);if(!d){this.logAiSessionDelivery({stage:"skip-empty-turn-id",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,status:a.status,phase:a.phase,activeTurnId:a.activeTurnId,latestTurn:Mo(ed(a))});continue}if(this.listTelegramOwnedProgressEntries(e).some(e=>e.route.bridgeId===i.bridgeId&&e.route.chatId===i.chatSessionId&&e.owner.sessionId===a.id&&e.owner.turnId===d))continue;const c=Vo(e,a.id,d,i.bridgeId||"",i.chatSessionId),l=Jo(a,r);if(!l||this.deliveredAiSessionFingerprints.get(c)===l){this.logAiSessionDelivery({stage:"skip-duplicate-fingerprint",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,hasFingerprint:Boolean(l),fingerprintMatched:this.deliveredAiSessionFingerprints.get(c)===l,latestTurn:Mo(ed(a)),textPreview:jo(o)});continue}const p=Ho({cancelCallbackData:this.aiSessionCancelCallbackData(e,a.id),queueSteerCallbackData:t=>this.aiSessionQueueSteerCallbackData(e,a.id,t),queueDeleteMenuCallbackData:()=>this.aiSessionQueueDeleteMenuCallbackData(e,a.id),permissionCallbackData:(e,t)=>this.service.pendingDecisionCallbackData(e,t),pendingApproval:n.get(a.id)||Go(e,a),session:a});if(this.logAiSessionDelivery({stage:"attempt",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,relatedProgressKeys:this.telegramProgressKeysFor(e,a.id,i.bridgeId||"",i.chatSessionId),hasProgressEntry:this.telegramProgress.entries.has(c),status:a.status,phase:a.phase,activeTurnId:a.activeTurnId,latestTurn:Mo(ed(a)),textPreview:jo(o)}),await this.updateChatAiSessionProgress(i,o,c,p).catch(t=>(this.bridgeErrors.set(i.bridgeId||"",t instanceof Error?t.message:String(t)),this.logWarn({bridgeId:i.bridgeId,chatId:i.chatSessionId,instanceId:e,sessionId:a.id,turnId:d,key:c,error:Oo(t)},"chat ai session progress update failed"),!1)))this.logAiSessionDelivery({stage:"delivered",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,textPreview:jo(o)}),this.deliveredAiSessionFingerprints.set(c,l);else{if("telegram"===this.service.requireChatBridge(i.bridgeId).channel){const t=this.telegramProgress.entries.get(c),s=this.service.requireChatBridge(i.bridgeId);this.logAiSessionDelivery({stage:t?"telegram-update-not-delivered":"telegram-no-progress-entry",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,relatedProgressKeys:this.telegramProgressKeysFor(e,a.id,i.bridgeId||"",i.chatSessionId),bridgeEnabled:s.enabled,bridgeChannel:s.channel,progressMessageId:t?.messageId,progressLastText:jo(t?.lastText),progressHasPending:Boolean(t?.pending),latestTurn:Mo(ed(a)),textPreview:jo(o)});continue}await this.sendToChatBinding(i,{text:o}).catch(e=>(this.bridgeErrors.set(i.bridgeId||"",e instanceof Error?e.message:String(e)),!1))&&(this.logAiSessionDelivery({stage:"fallback-sent",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,textPreview:jo(o)}),this.deliveredAiSessionFingerprints.set(c,l))}}}async deliverOwnedTelegramProgress(e,t,s,n){for(const i of this.listTelegramOwnedProgressEntries(e)){const a=this.service.requireChatBridge(i.route.bridgeId);if("telegram"!==a.channel||!a.enabled)continue;const r=this.matchTelegramOwnedProgressSession(i,t);if(!r)continue;const o={bridgeId:i.route.bridgeId,chatSessionId:i.route.chatId},d=Xo(s.get(e)||e,r),c=Yo(r,d),l=Ko(r);if(!c||!l){this.logAiSessionDelivery({stage:"remembered-skip-turn-or-text",key:i.key,parsedTurnId:i.owner.turnId,currentTurnId:l,hasText:Boolean(c),instanceId:e,sessionId:r.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId});continue}if(i.owner.turnId&&l!==i.owner.turnId){this.logAiSessionDelivery({stage:"remembered-skip-different-active-turn",key:i.key,ownerTurnId:i.owner.turnId,currentTurnId:l,instanceId:e,sessionId:r.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId});continue}const p=l===i.owner.turnId?i.key:Vo(e,r.id,l,i.route.bridgeId,i.route.chatId);if(p!==i.key&&this.telegramProgress.entries.has(p))continue;if(p!==i.key&&!this.rekeyTelegramOwnedProgress(i,p,{instanceId:e,sessionId:r.id,turnId:l}))continue;const u=Jo(r,d);if(!u||this.deliveredAiSessionFingerprints.get(p)===u){this.logAiSessionDelivery({stage:"remembered-skip-fingerprint",key:i.key,hasFingerprint:Boolean(u),fingerprintMatched:this.deliveredAiSessionFingerprints.get(p)===u,instanceId:e,sessionId:r.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId});continue}const m=Ho({cancelCallbackData:this.aiSessionCancelCallbackData(e,r.id),queueSteerCallbackData:t=>this.aiSessionQueueSteerCallbackData(e,r.id,t),queueDeleteMenuCallbackData:()=>this.aiSessionQueueDeleteMenuCallbackData(e,r.id),permissionCallbackData:(e,t)=>this.service.pendingDecisionCallbackData(e,t),pendingApproval:n.get(r.id)||Go(e,r),session:r}),g=await this.updateChatAiSessionProgress(o,c,p,m).catch(e=>(this.bridgeErrors.set(o.bridgeId||"",e instanceof Error?e.message:String(e)),!1));this.logAiSessionDelivery({stage:g?"remembered-delivered":"remembered-not-delivered",key:p,instanceId:e,sessionId:r.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId,textPreview:jo(c)}),g&&(this.telegramProgressMessageTargets.set(No(i.route.bridgeId,i.route.chatId,i.messageId),{instanceId:e,sessionId:r.id,turnId:l,messageId:i.messageId}),this.deliveredAiSessionFingerprints.set(p,u))}}rekeyTelegramOwnedProgress(e,t,s){return!!this.telegramProgress.rekey(e.key,t)&&(this.deliveredAiSessionFingerprints.delete(e.key),this.telegramProgressMessageTargets.set(No(e.route.bridgeId,e.route.chatId,e.messageId),{...s,messageId:e.messageId}),!0)}logAiSessionDelivery(e){this.logInfo(e,"telegram ai session delivery flow")}telegramProgressKeysFor(e,t,s,n){const i=`${e}:${t}:`,a=`:${s}:${n}`;return[...this.telegramProgress.entries.keys()].filter(e=>e.startsWith(i)&&e.endsWith(a))}listTelegramOwnedProgressEntries(e){const t=[];for(const[s,n]of this.telegramProgress.entries){if(!n.route?.bridgeId||!n.route.chatId||!Number.isInteger(n.messageId))continue;const i=this.telegramProgressMessageTargets.get(No(n.route.bridgeId,n.route.chatId,n.messageId))||_o(s,n.route.bridgeId,n.route.chatId);i&&i.instanceId===e&&t.push({key:s,route:{bridgeId:n.route.bridgeId,chatId:n.route.chatId},messageId:n.messageId,owner:{...i,messageId:n.messageId}})}return t}matchTelegramOwnedProgressSession(e,t){const s=t.find(t=>t.id===e.owner.sessionId);if(s)return s;if(e.owner.turnId){const s=t.find(t=>t.activeTurnId===e.owner.turnId||ed(t)?.id===e.owner.turnId||ed(t)?.providerTurnId===e.owner.turnId);if(s)return s}}async updateChatAiSessionProgress(e,t,s,n){if(!e.bridgeId)return this.logAiSessionDelivery({stage:"chat-update-skip-missing-bridge",chatId:e.chatSessionId,key:s}),!1;const i=this.service.requireChatBridge(e.bridgeId);if(!i.enabled)return this.logAiSessionDelivery({stage:"chat-update-skip-bridge-state",bridgeId:e.bridgeId,chatId:e.chatSessionId,key:s,bridgeEnabled:i.enabled,bridgeChannel:i.channel}),!1;if("dingding"===i.channel){if(!this.dingdingBridges.get(i.id))return!1;const a=await this.dingdingBridges.applyProgressUpdate({bridge:i,key:s,chatId:e.chatSessionId,text:t,replyMarkup:To({actionRows:n})});return a&&this.logInfo({bridgeId:e.bridgeId,chatId:e.chatSessionId,key:s,textPreview:jo(t)},"dingding ai session progress updated"),a}if("telegram"!==i.channel)return!1;const a=this.telegramProgress.entries.get(s);if(!await this.telegramProgress.applyUpdate({bridge:i,key:s,chatId:e.chatSessionId,text:t,actionRows:n})){const t=this.telegramProgress.entries.get(s);return this.logAiSessionDelivery({stage:"telegram-update-returned-false",bridgeId:e.bridgeId,chatId:e.chatSessionId,key:s,progressMessageId:a?.messageId||t?.messageId,progressLastText:jo(a?.lastText||t?.lastText),progressHasPending:Boolean(a?.pending||t?.pending)}),!1}return this.logInfo({bridgeId:e.bridgeId,chatId:e.chatSessionId,key:s,messageId:a?.messageId||this.telegramProgress.entries.get(s)?.messageId,textPreview:jo(t)},a?"telegram ai session progress edited":"telegram ai session progress sent"),!0}async aiSessionInstanceNames(){return this.aiSessionInstanceNamesCache&&this.aiSessionInstanceNamesCache.expiresAt>Date.now()?this.aiSessionInstanceNamesCache.value:(this.aiSessionInstanceNamesPending||(this.aiSessionInstanceNamesPending=(this.service.listAiSessionInstanceNames?.()||this.service.boardAsync()).catch(()=>[]).then(e=>{const t=new Map(e.map(e=>[e.id,e.name||e.id]));return this.aiSessionInstanceNamesCache={expiresAt:Date.now()+3e4,value:t},t}).finally(()=>{this.aiSessionInstanceNamesPending=void 0})),this.aiSessionInstanceNamesPending)}async aiSessionPendingApprovalRoutes(e){let t=this.aiSessionPendingRoutesCache?.expiresAt&&this.aiSessionPendingRoutesCache.expiresAt>Date.now()?this.aiSessionPendingRoutesCache.value:void 0;return t||(this.aiSessionPendingRoutesPending||=this.service.listPendingRoutes().catch(()=>[]).then(e=>(this.aiSessionPendingRoutesCache={expiresAt:Date.now()+1e3,value:e},e)).finally(()=>{this.aiSessionPendingRoutesPending=void 0}),t=await this.aiSessionPendingRoutesPending),new Map(t.filter(t=>t.instanceId===e&&t.aiSessionId&&"approval"===t.kind).map(e=>[e.aiSessionId||"",e]))}aiSessionCancelCallbackData(e,t){const s=o.createHash("sha256").update(`${e}\0${t}`).digest("base64url").slice(0,16);return this.aiSessionCancelTokens.set(s,{instanceId:e,sessionId:t}),`task_handoff:cp_ai_cancel:${s}`}aiSessionQueueSteerCallbackData(e,t,s){return`task_handoff:cp_ai_steer:${this.aiSessionQueueActionToken({type:"steer",instanceId:e,sessionId:t,queueId:s})}`}aiSessionQueueDeleteMenuCallbackData(e,t){return`task_handoff:cp_ai_qdel_menu:${this.aiSessionQueueActionToken({type:"delete-menu",instanceId:e,sessionId:t})}`}aiSessionQueueDeleteItemCallbackData(e,t,s){return`task_handoff:cp_ai_qdel:${this.aiSessionQueueActionToken({type:"delete-item",instanceId:e,sessionId:t,queueId:s})}`}aiSessionQueueActionToken(e){const t=o.createHash("sha256").update([e.type,e.instanceId,e.sessionId,"queueId"in e?e.queueId:""].join("\0")).digest("base64url").slice(0,16);return this.aiSessionQueueActionTokens.set(t,e),t}}function Ao(e){return"object"==typeof e&&e&&"reply"in e?String(e.reply||""):""}function ko(e){return"object"==typeof e&&null!==e&&!0===e.accepted}function zo(e){const t="object"==typeof e&&e&&"replyMarkup"in e?e.replyMarkup:void 0;if(t&&"object"==typeof t&&!Array.isArray(t))return t}function To(e){const t=Array.isArray(e?.actionRows)?e.actionRows:void 0;return da(t||(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e]))}function Co(e){const t=Number(e?.message_id);return Number.isInteger(t)?t:void 0}function Eo(e,t,s){return[e,t,s||""].join(":")}function No(e,t,s){return[e,t,String(s)].join(":")}function _o(e,t,s){const n=`:${t}:${s}`;if(!e.endsWith(n))return;const i=e.slice(0,-n.length).split(":"),a=i[0]||"",r=i[1]||"",o=i.slice(2).join(":")||void 0;return a&&r?{instanceId:a,sessionId:r,turnId:o}:void 0}function xo(e){return"string"==typeof e?e:""}function Oo(e){return e instanceof Error?e.message:String(e)}function Ro(e,t){return Boolean(e&&"object"==typeof e&&"code"in e&&e.code===t)}function jo(e,t=160){const s=String(e||"").replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,Math.max(0,t-3))}...`}function Do(e){const t=$o(e.body.cardActionData||e.body.content),s=$o(t.cardPrivateData||e.body.cardPrivateData||e.body.content);return{outTrackId:xo(e.body.outTrackId),spaceId:xo(e.body.spaceId),conversationType:e.conversationType,hasCallbackData:Boolean(e.callbackData),callbackDataPreview:jo(e.callbackData,120),actionIds:[...qo(s.actionIdList),...qo(t.actionIdList),xo(s.actionId),xo(t.actionId),xo(e.body.actionId)].filter(Boolean).slice(0,5),cardActionDataKeys:Object.keys(t).sort(),privateDataKeys:Object.keys(s).sort(),paramKeys:Object.keys(e.params).sort(),hasSessionWebhook:Boolean(e.sessionWebhook)}}function Mo(e){if(e)return{id:e.id,providerTurnId:e.providerTurnId,source:e.source,status:e.status,phase:e.phase,revision:e.revision,userPrompt:jo(e.userPrompt,80),hasResponse:Boolean(e.lastMessage?.trim()||e.summary?.trim()),responsePreview:jo(e.lastMessage||e.summary,80),updatedAt:e.updatedAt,observedAt:e.observedAt}}function Po(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function $o(e){if(e&&"object"==typeof e&&!Array.isArray(e))return e;if("string"==typeof e&&e.trim())try{return Po(JSON.parse(e))}catch{return{}}return{}}function qo(e){return Array.isArray(e)?e.map(e=>xo(e)).filter(Boolean):[]}function Lo(e){const t=e.aiSession&&"session"in e.aiSession?e.aiSession:void 0,s=t?.session||(e.aiSession&&"id"in e.aiSession?e.aiSession:void 0);return{instanceId:e.instanceId||e.instance?.id||"",sessionId:e.aiSessionId||s?.id||e.binding?.activeAiSessionId||"",turnId:e.turnId||e.providerTurnId||t?.turnId||t?.providerTurnId||(s?Ko(s):""),sources:{resultTurnId:e.turnId,resultProviderTurnId:e.providerTurnId,actionTurnId:t?.turnId,actionProviderTurnId:t?.providerTurnId,sessionActiveTurnId:s?.activeTurnId,latestTurnId:s?ed(s)?.id:void 0}}}function Uo(e,t){const s=`${e.project?.name||e.projectId} / ${e.instance?.name||e.instanceId}`,n="approval"===e.kind?`/approve ${e.id}\n/deny ${e.id}\n/skip ${e.id}`:`/reply ${e.id} <message>`,i=`[${s}]\n${e.result}\n\n${n}`;return"approval"!==e.kind?{text:i}:{text:i,replyMarkup:da([[{text:"Allow",callbackData:t(e.id,"allow")},{text:"Skip",callbackData:t(e.id,"skip")},{text:"Deny",callbackData:t(e.id,"deny")}]])}}const Fo=5,Bo=48;function Ho(e){const{cancelCallbackData:t,queueSteerCallbackData:s,queueDeleteMenuCallbackData:n,permissionCallbackData:i,pendingApproval:a,session:r}=e,o="approval"===a?.kind?[[{text:"Allow",callbackData:i(a.id,"allow")},{text:"Skip",callbackData:i(a.id,"skip")},{text:"Deny",callbackData:i(a.id,"deny")}]]:[];if("running"!==r.status)return o;const d=(r.queue?.items||[]).filter(e=>"queued"===e.status).slice(0,Fo);return[...d.map((e,t)=>[{text:Wo(e.message,t),callbackData:s(e.id)}]),d.length?[{text:"Delete Queue",callbackData:n()},{text:"Cancel",callbackData:t}]:[{text:"Cancel",callbackData:t}]]}function Go(e,t){if("approval"===t.phase)return{id:`${e}:ai:${t.id}`,kind:"approval"}}function Wo(e,t){const s=`${t+1}. `,n=e.replace(/\s+/g," ").trim()||"Queued message",i=Math.max(8,Bo-s.length),a=Array.from(n);return`${s}${a.length>i?`${a.slice(0,Math.max(1,i-3)).join("")}...`:n}`}function Vo(e,t,...s){return[e,t,...s].join(":")}function Ko(e){const t=ed(e);return t?.id?t.id:xo(e.activeTurnId)}function Jo(e,t=""){const s=ed(e);return[t,s?.id||"",String(s?.revision??""),s?.status||e.status||"",s?.phase||e.phase||"",s?.lastMessage||"",s?.summary||"",s?.userPrompt||"",e.error||"",e.currentTool?.id||"",e.currentTool?.name||"",e.currentTool?.inputPreview||"",String(e.toolCallsSinceLastMessage??0),Qo(e)].join("\0")}function Qo(e){return(e.queue?.items||[]).map(e=>[e.id,e.status,e.message].join(":")).join("|")}function Yo(e,t){const s=function(e,t){let s="";t?"waiting"===t.status&&"approval"===t.phase&&t.summary?.trim()?s=t.summary.trim():t.lastMessage?.trim()?s=t.lastMessage.trim():t.summary?.trim()&&(s=t.summary.trim()):"waiting"===e.status&&"approval"===e.phase&&e.summary?.trim()?s=e.summary.trim():e.lastMessage?.trim()?s=e.lastMessage.trim():e.summary?.trim()?s=e.summary.trim():e.error?.trim()&&(s="failed"===e.status?`${e.agent} session failed:\n${e.error.trim()}`:e.error.trim());const n=function(e){if(e.currentTool?.name)return`Thinking... · ${e.currentTool.name}${e.currentTool.inputPreview?` · ${e.currentTool.inputPreview}`:""}`;const t=e.toolCallsSinceLastMessage??0;return t>0?`Thinking... · ${t} ${1===t?"tool":"tools"} completed`:""}(e);return[s,n&&!Zo(s,e)?n:""].filter(Boolean).join("\n\n")}(e,ed(e));return s?`${t}\n${s}`:""}function Xo(e,t){const s=[t.status,t.phase&&"unknown"!==t.phase?t.phase:void 0].filter(Boolean).join("/");return`${e} · ${t.agent||"ai"} ${s||"unknown"}`}function Zo(e,t){if("waiting"!==t.status||"approval"!==t.phase)return!1;const s=t.currentTool?.inputPreview?.trim();return Boolean(s&&e.includes(s))}function ed(e){return(e.turns||[]).filter(e=>e.id||e.userPrompt?.trim()||e.lastMessage?.trim()||e.summary?.trim()).at(-1)}class td{clients=new Set;listeners=new Set;seq=0;connect(e){e.topics=new Set(["*"]),this.clients.add(e),e.on("close",()=>{this.clients.delete(e)}),e.on("message",t=>{const s=function(e){try{const t=JSON.parse(String(e||"{}"));return t&&"object"==typeof t?t:void 0}catch{return}}(t);"subscribe"===s?.type&&(e.topics=new Set(s.topics?.length?s.topics:["*"]))})}publish(e,t,s={}){const n=this.createEvent(e,t,s),i=n.topic,a=JSON.stringify(n);for(const e of this.listeners)try{e(n)}catch{this.listeners.delete(e)}for(const t of this.clients)if(t.readyState===t.OPEN&&sd(t.topics,i,e))try{t.send(a)}catch{this.clients.delete(t)}return n}on(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}send(e,t,s,n={}){const i=this.createEvent(t,s,n);return e.send(JSON.stringify(i)),i}createEvent(e,t,s={}){return{v:1,id:`evt_${Date.now().toString(36)}_${Math.random().toString(16).slice(2)}`,seq:++this.seq,type:e,topic:s.topic||nn(e),createdAt:(new Date).toISOString(),payload:t,scope:s.scope}}}function sd(e,t,s){return!e||e.has("*")||e.has(t)||e.has(s)}const nd={"image/bmp":".bmp","image/gif":".gif","image/jpeg":".jpg","image/png":".png","image/webp":".webp"},id=r.z.object({instanceId:r.z.string().trim().min(1).max(120),sessionId:r.z.string().trim().min(1).max(120),kind:r.z.enum(["image","file"]),name:r.z.string().trim().min(1).max(240),mime:r.z.string().trim().min(1).max(120),data:r.z.string().min(1).max(31457280)}).strict();class ad{items=new Map;ttlMs;constructor(e=864e5){this.ttlMs=e}upload(e){this.pruneExpired();const n=id.parse(e),i=function(e,t){const s=/^data:([^;,]+);base64,(.+)$/i.exec(e.trim()),n=s?.[1]||t,i=s?.[2]||e;return{mime:n,buffer:Buffer.from(i,"base64")}}(n.data,n.mime),a=i.mime.toLowerCase(),r=nd[a]||"";if("image"===n.kind&&!r)throw new Error(`Unsupported image type: ${a}`);const d="image"===n.kind?i.buffer.length>w:i.buffer.length>=S;if(!i.buffer.length||d)throw new Error("image"===n.kind?"Image must be between 1 byte and 20971520 bytes.":"File must be smaller than 512000 bytes.");const c=`att_${o.randomUUID().replace(/-/g,"").slice(0,24)}`,l=new Date,p=new Date(l.getTime()+this.ttlMs),u=t.join(f.tmpdir(),"task-handoff-ai-session-attachments");s.mkdirSync(u,{recursive:!0});const m=function(e){return t.basename(e).replace(/[^\w.\- ()\u4e00-\u9fff]/g,"_").slice(0,160)||"attachment"}(n.name),g=t.join(u,`${c}-${m.endsWith(r)?m:`${m}${r}`}`);s.writeFileSync(g,i.buffer);const h={id:c,instanceId:n.instanceId,sessionId:n.sessionId,kind:n.kind,name:m,mime:a,size:i.buffer.length,source:{type:"inline",encoding:"base64",data:i.buffer.toString("base64")},path:g,createdAt:l.toISOString(),expiresAt:p.toISOString()};return this.items.set(c,h),this.publicAttachment(h)}resolveRefs(e,t,s){this.pruneExpired();const n=r.z.array(F).max(6).default([]).parse(e),i=n.map(e=>"runtime-path"===e.source.type?L.parse(e):this.requireAttachment(e,t,s));if(i.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>A)throw new Error("Inline attachments must be 41943040 bytes or less in total.");for(const e of n)"upload-ref"===e.source.type&&this.consumeAttachment(e.id);return i}requireAttachment(e,t,s){const n=this.items.get(e.id);if(!n)throw new Error(`AI session attachment not found or expired: ${e.id}`);if(e.kind&&e.kind!==n.kind)throw new Error(`AI session attachment kind mismatch: ${e.id}`);if(n.instanceId!==t||n.sessionId!==s)throw new Error(`AI session attachment scope mismatch: ${e.id}`);return L.parse({id:n.id,kind:n.kind,name:n.name,mime:n.mime,size:n.size,source:n.source})}consumeAttachment(e){const t=this.items.get(e);if(t){this.items.delete(e);try{s.unlinkSync(t.path)}catch{}}}publicAttachment(e){return{id:e.id,kind:e.kind,name:e.name,mime:e.mime,size:e.size,expiresAt:e.expiresAt}}pruneExpired(){const e=Date.now();for(const[t,n]of this.items)if(!(Date.parse(n.expiresAt)>e)){this.items.delete(t);try{s.unlinkSync(n.path)}catch{}}}}function rd(e){return e.readyState===(e.OPEN??1)}function od(e,t,s){e.close(function(e){if("number"==typeof e&&Number.isInteger(e))return e>=3e3&&e<=4999||e>=1e3&&e<=1014&&1004!==e&&1005!==e&&1006!==e?e:void 0}(t),function(e){const t=Buffer.isBuffer(e)?e.toString("utf8"):"string"==typeof e?e:"";if(Buffer.byteLength(t,"utf8")<=123)return t;let s="";for(const e of t){const t=`${s}${e}`;if(Buffer.byteLength(t,"utf8")>123)break;s=t}return s}(s)||void 0)}function dd(e,t){e.send(t.data,{binary:t.isBinary})}function cd(e,t){if(rd(e))for(const s of t.splice(0))dd(e,s)}class ld{attempt=0;timer;get pending(){return Boolean(this.timer)}get attempts(){return this.attempt}schedule(e,t={}){if(this.timer)return;const s=function(e,t=Math.random){const s=Math.min(3e4,1e3*2**Math.max(0,e));return Math.min(3e4,Math.round(s*(.75+.5*t())))}(this.attempt,t.random);this.attempt+=1;const n=t.setTimeoutFn||setTimeout;return this.timer=n(()=>{this.timer=void 0,e()},s),{attempt:this.attempt,delay:s}}reset(e=clearTimeout){this.timer&&e(this.timer),this.timer=void 0,this.attempt=0}cancel(e=clearTimeout){this.timer&&e(this.timer),this.timer=void 0}}const pd=r.z.object({nodeId:r.z.string().trim().min(1).max(120)}).strict();function ud(e,t){if(!t)throw Object.assign(new Error(`Node ${e.id} paired-HMAC auth is missing keyId.`),{statusCode:500,code:"NODE_AGENT_REMOTE_KEY_ID_MISSING"});return t}function md(){return`req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`}const gd=new Map;function hd(e,t){if("paired-hmac"!==e.auth.mode||!e.auth.keyId||!e.auth.secret){const e=new Error("Reverse tunnel requires paired-HMAC node auth.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_TUNNEL_REQUIRES_PAIRED_HMAC"}),e}const s=function(e){const t=new Map;for(const[s,n]of Object.entries(e)){const e=Array.isArray(n)?n.join(","):"string"==typeof n?n:void 0===n?"":String(n);t.set(s.toLowerCase(),e)}return{nodeId:t.get("x-taskhandoff-node-id")||"",keyId:t.get("x-taskhandoff-key-id")||"",timestamp:t.get("x-taskhandoff-timestamp")||"",nonce:t.get("x-taskhandoff-nonce")||"",bodySha256:t.get("x-taskhandoff-body-sha256")||"",signature:t.get("x-taskhandoff-signature")||""}}(t.headers);if(s.nodeId!==e.id)throw Object.assign(new Error("Invalid reverse tunnel node id."),{statusCode:401,code:"NODE_AGENT_TUNNEL_NODE_MISMATCH"});if(s.keyId!==e.auth.keyId)throw Object.assign(new Error("Invalid reverse tunnel key id."),{statusCode:401,code:"NODE_AGENT_TUNNEL_KEY_INVALID"});const n=Date.parse(s.timestamp);if(!Number.isFinite(n)||Math.abs(Date.now()-n)>sr)throw Object.assign(new Error("Reverse tunnel timestamp is outside the allowed window."),{statusCode:401,code:"NODE_AGENT_TUNNEL_TIMESTAMP_INVALID"});const i=Date.now()-sr;for(const[e,t]of gd)t<=i&&gd.delete(e);const a=`${e.id}:${s.keyId}:${s.nonce}`;if(!s.nonce||gd.has(a))throw Object.assign(new Error("Reverse tunnel nonce is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_NONCE_INVALID"});const r=nr("");if(s.bodySha256!==r)throw Object.assign(new Error("Reverse tunnel body hash is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_BODY_HASH_INVALID"});const d=t.url||`/api/node-agent/tunnel?nodeId=${encodeURIComponent(e.id)}`,c=ir(e.auth.secret,{keyId:s.keyId,method:"GET",pathWithQuery:d,timestamp:s.timestamp,nonce:s.nonce,bodySha256:r});if(!function(e,t){const s=Buffer.from(e,"hex"),n=Buffer.from(t,"hex");return s.length===n.length&&o.timingSafeEqual(s,n)}(s.signature,c))throw Object.assign(new Error("Reverse tunnel signature is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_SIGNATURE_INVALID"});gd.set(a,Date.now()+sr)}function fd(e){return e?e instanceof Headers?Object.fromEntries(e.entries()):Array.isArray(e)?Object.fromEntries(e.map(([e,t])=>[e,t])):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,String(t)])):{}}class Id{sockets=new Map;events;onStreamsHello;validateInstanceScope;validatedInstanceScopes=new Map;validatingInstanceScopes=new Map;instanceEventQueues=new Map;constructor(e,t={}){this.events=e,this.onStreamsHello=t.onStreamsHello,this.validateInstanceScope=t.validateInstanceScope}attach(e,t){const s=this.sockets.get(e);if(s){for(const t of s.pending.values())clearTimeout(t.timer),t.reject(new Error(`Reverse tunnel for node ${e} was replaced.`));for(const[e,t]of s.streams)s.streams.delete(e),t.downstream.close(1011,"Reverse tunnel was replaced.");for(const t of s.httpStreams.values())clearTimeout(t.timer),t.body?.destroy(new Error(`Reverse tunnel for node ${e} was replaced.`)),t.settled||t.reject(new Error(`Reverse tunnel for node ${e} was replaced.`))}const n=new Map;this.sockets.set(e,{socket:t,pending:n,streams:new Map,httpStreams:new Map}),t.on?.("close",()=>this.detach(e,t)),t.on?.("error",()=>this.detach(e,t))}detach(e,t){const s=this.sockets.get(e);if(s&&(!t||s.socket===t)){this.sockets.delete(e);for(const t of s.pending.values())clearTimeout(t.timer),t.reject(new Error(`Reverse tunnel for node ${e} disconnected.`));for(const[e,t]of s.streams)s.streams.delete(e),t.downstream.close(1011,"Reverse tunnel disconnected.");for(const t of s.httpStreams.values())clearTimeout(t.timer),t.body?.destroy(new Error(`Reverse tunnel for node ${e} disconnected.`)),t.settled||t.reject(new Error(`Reverse tunnel for node ${e} disconnected.`))}}handleMessage(e,t){if(this.handleNodeAgentEvent(e,t))return!0;if(this.handleWebSocketMessage(e,t))return!0;if("node-agent.response"!==t.type)return!1;const s="string"==typeof t.requestId?t.requestId:"",n=this.sockets.get(e),i=n?.pending.get(s);if(!i||!n)return!0;n.pending.delete(s),clearTimeout(i.timer);const a=Number(t.status)||502,r=t.headers&&"object"==typeof t.headers?t.headers:{},o="string"==typeof t.body?t.body:"";if(t.error&&"object"==typeof t.error){const e=t.error,s=new Error("string"==typeof e.message?e.message:`Reverse tunnel request failed with HTTP ${a}`);return Object.assign(s,{statusCode:a,code:"string"==typeof e.code?e.code:"NODE_AGENT_REVERSE_REQUEST_FAILED"}),i.reject(s),!0}return i.resolve(new Response(o,{status:a,headers:r})),!0}handleNodeAgentEvent(e,t){const s="string"==typeof t.type?t.type:"";if("node-agent.streams.hello"===s){const e="string"==typeof t.instanceId?t.instanceId:"",s=on.safeParse(t.payload);return e&&s.success&&this.onStreamsHello?.(e,s.data),!0}if(!s.startsWith("node-agent.event."))return!1;const n=t.event&&"object"==typeof t.event&&!Array.isArray(t.event)?t.event:void 0,i="string"==typeof n?.type?n.type:"";if(!i)return!0;const a="payload"in n?n.payload:{},r=n.scope&&"object"==typeof n.scope&&!Array.isArray(n.scope)?n.scope:{},o="string"==typeof t.instanceId?t.instanceId:void 0;if(i===Os){const t=xs.safeParse(a),s="string"==typeof r.instanceId?r.instanceId:"string"==typeof n.instanceId?n.instanceId:o;return!t.success||t.data.instanceId!==s||(this.enqueueValidatedInstanceEvent(e,t.data.instanceId,()=>{this.events?.publish(Os,t.data,{topic:"instances",scope:{...r,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(i===Vs){const t=Ks.safeParse(a),s="string"==typeof r.instanceId?r.instanceId:"string"==typeof n.instanceId?n.instanceId:o;return!t.success||t.data.instanceId!==s||(this.enqueueValidatedInstanceEvent(e,t.data.instanceId,()=>{this.events?.publish(Vs,t.data,{topic:"instances",scope:{...r,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(i===Ss||i===As){const t=i===Ss?zs.safeParse(a):Ts.safeParse(a),s="string"==typeof r.instanceId?r.instanceId:"string"==typeof n.instanceId?n.instanceId:o;return!t.success||t.data.instanceId!==s||(this.enqueueValidatedInstanceEvent(e,t.data.instanceId,()=>{this.events?.publish(i,t.data,{topic:"instances",scope:{...r,nodeId:e,instanceId:t.data.instanceId}})}),!0)}return this.events?.publish(i,a,{topic:"string"==typeof n.topic?n.topic:void 0,scope:{...r,nodeId:e,instanceId:"string"==typeof r.instanceId?r.instanceId:"string"==typeof n.instanceId?n.instanceId:o}}),!0}enqueueValidatedInstanceEvent(e,t,s){const n=`${e}:${t}`,i=(this.instanceEventQueues.get(n)||Promise.resolve()).then(async()=>{await this.isValidatedInstanceScope(e,t)&&s()});this.instanceEventQueues.set(n,i),i.finally(()=>{this.instanceEventQueues.get(n)===i&&this.instanceEventQueues.delete(n)})}async isValidatedInstanceScope(e,t){const s=`${e}:${t}`,n=Date.now();if((this.validatedInstanceScopes.get(s)||0)>n)return!0;const i=this.validatingInstanceScopes.get(s);if(i)return i;const a=(async()=>{let n=!1;try{n=Boolean(await(this.validateInstanceScope?.(e,t)))}catch{n=!1}return n&&this.validatedInstanceScopes.set(s,Date.now()+3e4),n})();return this.validatingInstanceScopes.set(s,a),a.finally(()=>{this.validatingInstanceScopes.get(s)===a&&this.validatingInstanceScopes.delete(s)})}async request(e,t,s={}){const n=this.sockets.get(e.id);if(!n){const t=new Error(`Reverse tunnel for node ${e.id} is not connected.`);throw Object.assign(t,{statusCode:503,code:"NODE_AGENT_REVERSE_TUNNEL_OFFLINE"}),t}const i=md(),a="string"==typeof s.body?s.body:void 0===s.body||null===s.body?void 0:String(s.body),r={type:"control-plane.request",requestId:i,route:t,init:{method:s.method||"GET",headers:fd(s.headers),body:a}};return new Promise((s,a)=>{const o=setTimeout(()=>{n.pending.delete(i);const s=new Error(`Reverse tunnel request to ${e.id}${t} timed out.`);Object.assign(s,{statusCode:504,code:"NODE_AGENT_REVERSE_REQUEST_TIMEOUT"}),a(s)},3e4);n.pending.set(i,{resolve:s,reject:a,timer:o}),n.socket.send(JSON.stringify(r))})}async requestStream(e,t,s={}){const n=this.sockets.get(e.id);if(!n)throw Object.assign(new Error(`Reverse tunnel for node ${e.id} is not connected.`),{statusCode:503,code:"NODE_AGENT_REVERSE_TUNNEL_OFFLINE"});const i=md(),a="string"==typeof s.body?s.body:void 0===s.body||null===s.body?void 0:String(s.body);return new Promise((r,o)=>{const d=setTimeout(()=>{n.httpStreams.delete(i),o(Object.assign(new Error(`Reverse tunnel stream to ${e.id}${t} timed out.`),{statusCode:504,code:"NODE_AGENT_REVERSE_STREAM_TIMEOUT"}))},3e4);n.httpStreams.set(i,{resolve:r,reject:o,timer:d,settled:!1}),n.socket.send(JSON.stringify({type:"control-plane.http.open",streamId:i,route:t,init:{method:s.method||"GET",headers:fd(s.headers),body:a}}))})}attachHttpStream(e,t,s){const n=this.sockets.get(e),i=n?.httpStreams.get(t);if(!n||!i)return s.close(1008,"Unknown reverse HTTP stream."),!1;i.socket=s;const a=e=>{clearTimeout(i.timer),n.httpStreams.delete(t),i.settled?i.body?.destroy(e):(i.settled=!0,i.reject(e))};return s.on("message",(e,r)=>{if(r)return i.body?void(i.body.write(Buffer.isBuffer(e)?e:Buffer.from(e))||(s.pause?.(),i.body.once("drain",()=>s.resume?.()))):a(new Error("Reverse HTTP stream received data before response headers."));let o;try{o=JSON.parse(String(e))}catch{return void a(new Error("Reverse HTTP stream sent invalid JSON control data."))}if("node-agent.http.head"===o.type){if(i.settled)return;clearTimeout(i.timer);const e=Number(o.status)||502,a=o.headers&&"object"==typeof o.headers?o.headers:{},r=new m.PassThrough({highWaterMark:65536});return i.body=r,i.settled=!0,r.once("close",()=>{n.httpStreams.delete(t)&&1===s.readyState&&s.close(1e3,"HTTP consumer closed.")}),void i.resolve(new Response(m.Readable.toWeb(r),{status:e,headers:a}))}if("node-agent.http.end"===o.type)return n.httpStreams.delete(t),i.body?.end(),void(i.settled||a(new Error("Reverse HTTP stream ended before response headers.")));"node-agent.http.error"===o.type&&a(new Error("string"==typeof o.message?o.message:"Reverse HTTP stream failed."))}),s.on("close",()=>{n.httpStreams.has(t)&&a(new Error("Reverse HTTP stream disconnected."))}),s.on("error",()=>a(new Error("Reverse HTTP stream failed."))),!0}proxyWebSocket(e,t,s,n,i={}){const a=e.id,r=this.sockets.get(a);if(!r)return void t.close(1011,"Reverse tunnel is not connected.");const o=md(),d=e=>{try{r.socket.send(JSON.stringify(e))}catch{r.streams.delete(o),t.close(1011,"Reverse tunnel send failed.")}},c={downstream:t,pendingFrames:[]};r.streams.set(o,c),d({type:"control-plane.websocket.open",streamId:o,route:s,protocols:Array.isArray(n)?n:n?[n]:[],headers:i}),t.on("message",(e,s)=>{const n=r.streams.get(o);if(n&&!n.upstream)return n.pendingFrames.length>=256?(r.streams.delete(o),t.close(1011,"Reverse websocket stream pending frame limit exceeded."),void d({type:"control-plane.websocket.close",streamId:o,code:1011,reason:"Reverse websocket stream pending frame limit exceeded."})):void n.pendingFrames.push({data:e,isBinary:Boolean(s)})}),t.on("close",(e,t)=>{const s=r.streams.get(o);s&&(r.streams.delete(o),s.upstream?.close("number"==typeof e?e:1e3,Buffer.isBuffer(t)?t.toString("utf8"):"string"==typeof t?t:""),d({type:"control-plane.websocket.close",streamId:o,code:"number"==typeof e?e:1e3,reason:Buffer.isBuffer(t)?t.toString("utf8"):"string"==typeof t?t:""}))}),t.on("error",()=>{const e=r.streams.get(o);e&&(r.streams.delete(o),e.upstream?.close(1011,"Downstream websocket failed."),d({type:"control-plane.websocket.close",streamId:o,code:1011,reason:"Downstream websocket failed."}))})}attachWebSocketStream(e,t,s){const n=this.sockets.get(e),i=n?.streams.get(t);if(!n||!i)return s.close(1008,"Unknown reverse websocket stream."),!1;i.upstream=s;for(const e of i.pendingFrames.splice(0))s.send(e.data,{binary:e.isBinary});return function(e,t,s={}){const n=s.pendingFrameLimit??256;let i=!1,a=rd(t);const r=[],o=[];let d;const c=()=>{d&&(clearTimeout(d),d=void 0)},l=(s,i)=>{if(s.length>=n)return od(e,1011,"WebSocket bridge pending frame limit exceeded."),void od(t,1011,"WebSocket bridge pending frame limit exceeded.");s.push(i)};s.upstreamOpenTimeoutMs&&!a&&(d=setTimeout(()=>{a||i||(od(e,1011,"Upstream websocket did not open."),od(t))},s.upstreamOpenTimeoutMs)),e.on("open",()=>cd(e,o)),t.on("open",()=>{c(),a=!0,s.onUpstreamOpen?.(),cd(t,r)}),e.on("message",(e,s=!1)=>{const n={data:e,isBinary:Boolean(s)};rd(t)?dd(t,n):l(r,n)}),t.on("message",(t,s=!1)=>{const n={data:t,isBinary:Boolean(s)};rd(e)?dd(e,n):l(o,n)}),t.on("close",(t,n)=>{c(),s.onUpstreamClose?.(t,n),(a||i||!s.onUpstreamCloseBeforeOpen?.())&&od(e,t,n)}),t.on("error",t=>{s.onUpstreamError?.(t),(a||i||!s.onUpstreamErrorBeforeOpen?.(t))&&od(e)}),e.on("close",(e,n)=>{c(),i=!0,s.onClientClose?.(e,n),od(t,e,n)}),e.on("error",e=>{c(),i=!0,s.onClientError?.(e),od(t,1011,"WebSocket bridge client failed.")})}(i.downstream,s,{pendingFrameLimit:256,onClientClose:()=>{n.streams.delete(t)},onClientError:()=>{n.streams.delete(t)},onUpstreamClose:()=>{n.streams.delete(t)},onUpstreamError:()=>{n.streams.delete(t)}}),!0}handleWebSocketMessage(e,t){const s="string"==typeof t.type?t.type:"";if(!s.startsWith("node-agent.websocket."))return!1;const n=this.sockets.get(e),i="string"==typeof t.streamId?t.streamId:"",a=n?.streams.get(i);return!a||("node-agent.websocket.close"===s?(n?.streams.delete(i),a.downstream.close("number"==typeof t.code?t.code:1e3,"string"==typeof t.reason?t.reason:""),!0):"node-agent.websocket.error"!==s||(n?.streams.delete(i),a.downstream.close(1011,"string"==typeof t.message?t.message:"Reverse websocket proxy failed."),!0))}}class yd{sockets=new Map;socketUrls=new Map;retries=new Map;service;tunnel;safetyIntervalMs;logger;reconnectAttempts=0;safetyReconciliations=0;timer;constructor(e,t,s={}){var n;this.service=e,this.tunnel=t,this.safetyIntervalMs=(n=s.safetyIntervalMs,Math.min(6e4,Math.max(3e4,n??45e3))),this.logger=s.logger}start(){this.sync(),this.timer=setInterval(()=>{this.safetyReconciliations+=1,this.sync()},this.safetyIntervalMs)}stop(){this.timer&&(clearInterval(this.timer),this.timer=void 0);for(const e of this.sockets.values())e.close();this.sockets.clear(),this.socketUrls.clear();for(const e of this.retries.values())e.timer.cancel();this.retries.clear()}syncNow(){this.sync()}diagnostics(){return{reconnectAttempts:this.reconnectAttempts,safetyReconciliations:this.safetyReconciliations,activeConnections:this.sockets.size,pendingRetries:[...this.retries.values()].filter(e=>e.timer.pending).length,safetyIntervalMs:this.safetyIntervalMs}}sync(){const e=new Set;for(const t of this.service.listNodes()){const s=this.nodeEventsUrl(t);if(!s)continue;e.add(t.id);const n=this.retries.get(t.id);n?.timer.pending&&n.url!==s&&(n.timer.cancel(),this.retries.delete(t.id)),this.socketUrls.get(t.id)!==s&&this.sockets.has(t.id)&&(this.sockets.get(t.id)?.close(),this.sockets.delete(t.id)),this.sockets.has(t.id)||this.retries.get(t.id)?.timer.pending||this.connect(t,s)}for(const[t,s]of this.sockets)if(!e.has(t)){s.close(),this.sockets.delete(t),this.socketUrls.delete(t);const e=this.retries.get(t);e?.timer.cancel(),this.retries.delete(t)}for(const[t,s]of this.retries)e.has(t)||(s.timer.cancel(),this.retries.delete(t),this.socketUrls.delete(t))}connect(e,t){const s=rr(t),n=s?void 0:new URL(t),i=function(e,t){const s=function(e){return"paired-hmac"===e.auth.mode&&e.auth.secret?{keyId:e.auth.keyId,secret:e.auth.secret}:void 0}(e);if(s)return ar({nodeId:e.id,keyId:ud(e,s.keyId),secret:s.secret,method:t.method,pathWithQuery:t.pathWithQuery});const n=function(e){if("local-ipc"===e.connectionMode||"local-loopback"===e.connectionMode)return"local-static-key"===e.auth.mode?e.auth.secret:void 0}(e);return n?{authorization:`Bearer ${n}`}:void 0}(e,{method:"GET",pathWithQuery:n?`${n.pathname}${n.search}`:"/api/node-agent/events"}),a=s?dr(s,"/events",void 0,i):new l.WebSocket(t,{headers:i});this.sockets.set(e.id,a),this.socketUrls.set(e.id,t),a.on("open",()=>{const s=this.retries.get(e.id);s?.timer.reset(),this.retries.set(e.id,{timer:s?.timer||new ld,url:t})}),a.on("message",t=>{let s;try{s=JSON.parse(String(t))}catch{return}if(s&&"object"==typeof s&&!Array.isArray(s)){const t=s,n="string"==typeof t.type&&t.type.startsWith("node-agent.")?t:{type:"node-agent.event.forwarded",event:t};this.tunnel.handleMessage(e.id,n)}}),a.on("close",()=>{this.sockets.get(e.id)===a&&(this.sockets.delete(e.id),this.socketUrls.delete(e.id),this.scheduleReconnect(e.id))}),a.on("error",()=>{a.close()})}scheduleReconnect(e){const t=this.service.listNodes().find(t=>t.id===e),s=t&&this.nodeEventsUrl(t);if(!t||!s)return;const n=this.retries.get(e)??{timer:new ld};if(n.timer.pending)return;n.url=s;const i=n.timer.schedule(()=>{this.sockets.has(e)||this.connect(t,s)});i&&(this.reconnectAttempts+=1,this.logger?.info?.({nodeId:e,url:s,attempt:i.attempt,delay:i.delay,reconnectAttempts:this.reconnectAttempts},"session-stream.node-connection.reconnect-scheduled")),this.retries.set(e,n)}nodeEventsUrl(e){const t=e.controlEndpoint||e.endpoint;if(t&&"reverse-wss"!==e.connectionMode){if(rr(t))return t;try{const e=new URL("/api/node-agent/events",t.replace(/\/$/,""));return e.protocol="https:"===e.protocol?"wss:":"ws:",e.toString()}catch{return}}}}function bd(e=function(){return process.env.TASK_HANDOFF_CONTROL_PLANE_DATA_DIR||t.join(f.homedir(),".config","task-handoff","control-plane")}()){const s=t.resolve(e);return{dataDir:s,settingsPath:t.join(s,"control-plane-settings.json"),projectsDir:t.join(s,"projects"),modelsDir:t.join(s,"models"),imagesDir:t.join(s,"images"),marketDir:t.join(s,"market"),marketCachePath:t.join(s,"market","catalog-cache.json"),marketStatePath:t.join(s,"market","catalog-state.json"),marketMigrationBackupPath:t.join(s,"market","legacy-image-migration-backup.json"),nodesDir:t.join(s,"nodes"),nodeRuntimesDir:t.join(s,"node-runtimes"),controlledInstancesDir:t.join(s,"controlled-instances"),chatSessionsDir:t.join(s,"chat-sessions"),chatBridgesDir:t.join(s,"chat-bridges"),triggersDir:t.join(s,"triggers"),nodeJoinInvitesDir:t.join(s,"node-join-invites"),authUsersDir:t.join(s,"auth-users"),authSessionsDir:t.join(s,"auth-sessions"),aiSessionUnreadDir:t.join(s,"ai-session-unread"),logsDir:t.join(s,"logs")}}class vd extends Error{code;lockPath;owner;constructor(e,t,s={}){const n=t?.pid?` pid ${t.pid}`:"";super(`${s.label||"Process"} is already running${n}. Lock: ${e}`),this.name="ProcessSingletonError",this.code=s.code||"PROCESS_ALREADY_RUNNING",this.lockPath=e,this.owner=t}}function wd(e){return t.join(e,"owner.json")}function Sd(e){return t.join(e,"initializing.json")}function Ad(e){return t.join(e,"recovering.json")}function kd(e){return Number.isInteger(e)&&Number(e)>0}function zd(e,t=process.platform){if(kd(e)){if("linux"===t)try{const t=s.readFileSync(`/proc/${e}/stat`,"utf8"),n=t.lastIndexOf(")");if(n<0)return;const i=t.slice(n+2).trim().split(/\s+/)[19];return i?`linux:${i}`:void 0}catch{return}if(["darwin","freebsd","openbsd","aix","sunos"].includes(t)){const s=y.spawnSync("ps",["-o","lstart=","-p",String(e)],{encoding:"utf8",timeout:1e3}),n=0===s.status?s.stdout.trim().replace(/\s+/g," "):"";return n?`${t}:${n}`:void 0}if("win32"===t){const t=`(Get-Process -Id ${e} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks`,s=y.spawnSync("powershell.exe",["-NoProfile","-NonInteractive","-Command",t],{encoding:"utf8",timeout:2e3}),n=0===s.status?s.stdout.trim():"";return/^\d+$/.test(n)?`win32:${n}`:void 0}}}const Td=function(e){try{const t=JSON.parse(s.readFileSync(wd(e),"utf8"));if(kd(t.pid)&&"string"==typeof t.startTime){const e=t;return{pid:e.pid,hostname:"string"==typeof e.hostname&&e.hostname?e.hostname:f.hostname(),component:e.component||"server-update",command:"string"==typeof e.command?e.command:"",acquiredAt:"string"==typeof e.createdAt&&Number.isFinite(Date.parse(e.createdAt))?e.createdAt:new Date(0).toISOString(),token:"string"==typeof e.token&&e.token?e.token:`legacy:${e.pid}:${e.startTime}`,startIdentity:e.startTime.startsWith("linux:")?e.startTime:`linux:${e.startTime}`}}if(!(kd(t.pid)&&"string"==typeof t.hostname&&t.hostname&&"string"==typeof t.command&&"string"==typeof t.acquiredAt&&Number.isFinite(Date.parse(t.acquiredAt))&&"string"==typeof t.token&&t.token))return;return t}catch{return}};function Cd(e){if(!kd(e))return!1;try{return process.kill(Number(e),0),!0}catch(e){return"EPERM"===e.code}}function Ed(e){if(!e||!Cd(e.pid))return!1;if(!e.startIdentity)return!0;const t=zd(e.pid);return void 0===t||t===e.startIdentity}function Nd(e,n){const i=t.join(e,`.owner-${n.token}.tmp`);s.writeFileSync(i,`${JSON.stringify(n,null,2)}\n`,{encoding:"utf8",mode:384,flag:"wx"});const a=_d(e);if(a?.token!==n.token)throw Od(i),new Error(`Process lock initialization was superseded: ${e}`);s.renameSync(i,wd(e)),Od(Sd(e))}function _d(e){try{return JSON.parse(s.readFileSync(Sd(e),"utf8"))}catch{return}}function xd(e){const t=_d(e);return kd(t?.pid)&&Ed({pid:t.pid,startIdentity:t.startIdentity})}function Od(e){try{s.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}function Rd(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,20)}function jd(e){try{const t=_d(e),n=Date.parse(t?.startedAt||"");return Number.isFinite(n)?Math.max(0,Date.now()-n):Math.max(0,Date.now()-s.statSync(e).mtimeMs)}catch{return 0}}function Dd(e,n){try{return s.writeFileSync(Ad(e),`${JSON.stringify({token:n,pid:process.pid,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),!0}catch(i){if("EEXIST"===i.code)return!!function(e){const n=Ad(e),i=Md(n),a=Date.parse(i?.startedAt||"");if(Cd(i?.pid)||Number.isFinite(a)&&Date.now()-a<2e3)return!1;const r=t.join(e,`.recovering-${o.randomUUID()}.stale`);try{s.renameSync(n,r)}catch(e){if(["ENOENT","EEXIST"].includes(e.code||""))return!1;throw e}const d=Md(r);if(d?.token===i?.token)return Od(r),!0;try{s.linkSync(r,n)}catch(e){if("EEXIST"!==e.code)throw e}finally{Od(r)}return!1}(e)&&Dd(e,n);if("ENOENT"===i.code)return!1;throw i}}function Md(e){try{return JSON.parse(s.readFileSync(e,"utf8"))}catch{return}}function Pd(e,t){return Md(Ad(e))?.token===t}function $d(e,t,s){if(function(e,t,s){return"system"===e.type||("chat-bridge"===e.type?function(e,t){return"ai-session"===t.type?["read","send-message","approve","interrupt"].includes(e):"instance"===t.type?["read","start","stop","restart"].includes(e):"read"===e}(t,s):"admin"===e.role||("viewer"===e.role?"read"===t:"operator"===e.role&&function(e,t){return"read"===e||("instance"===t.type?["create","update","delete","start","stop","restart","proxy"].includes(e):"ai-session"===t.type?["send-message","approve","interrupt"].includes(e):("trigger"===t.type||"runtime"===t.type)&&["create","update","delete"].includes(e))}(t,s)))}(e,t,s))return;const n=new Error(`Actor is not allowed to ${t} ${s.type}${s.id?` ${s.id}`:""}.`);throw Object.assign(n,{statusCode:403,code:"CONTROL_PLANE_FORBIDDEN"}),n}const qd=r.z.string().trim().min(1),Ld=r.z.object({id:qd}).strict(),Ud=r.z.object({id:qd,keyId:qd}).strict(),Fd=r.z.object({id:qd,runtimeId:qd}).strict(),Bd=r.z.object({id:qd,folderId:qd}).strict(),Hd=r.z.object({id:qd,sessionId:qd}).strict(),Gd=r.z.object({id:qd,sessionId:qd,queueId:qd}).strict(),Wd=r.z.object({configHash:qd}).strict(),Vd=r.z.object({id:qd,configHash:qd}).strict(),Kd=r.z.object({id:qd,sessionId:qd,configHash:qd}).strict(),Jd=r.z.object({path:r.z.string().optional(),depth:r.z.coerce.number().int().min(0).max(32).optional()}).strict(),Qd=r.z.object({ids:r.z.array(r.z.string().trim().min(1).max(120)).default([])}).strict(),Yd=r.z.object({nodeId:r.z.string().trim().min(1).max(120),modelId:r.z.string().trim().min(1).max(120).optional()}).strict(),Xd=r.z.object({source:r.z.object({channel:Xs,bridgeId:r.z.string().trim().min(1).max(120).optional(),chatSessionId:r.z.string().trim().min(1).max(240),userId:r.z.string().trim().max(240).optional()}).strict(),action:r.z.discriminatedUnion("type",[r.z.object({type:r.z.literal("ai-session"),index:r.z.number().int().nonnegative()}).strict(),r.z.object({type:r.z.literal("instance-app-menu"),instanceId:r.z.string().trim().min(1).max(120)}).strict(),r.z.object({type:r.z.literal("launch-app"),instanceId:r.z.string().trim().min(1).max(120),appId:r.z.string().trim().min(1).max(120)}).strict(),r.z.object({type:r.z.literal("pending-decision"),routeId:r.z.string().trim().min(1).max(120),decision:r.z.enum(["allow","deny","skip"])}).strict()])}).strict();function Zd(e){const t=Xd.parse(e);if(!t.action)throw new Error("Chat gateway action is required.");return{source:t.source,action:t.action}}const ec=r.z.object({path:r.z.string().trim().max(1e3).optional(),depth:r.z.coerce.number().int().min(0).max(2).optional()}).strict(),tc=r.z.object({id:r.z.string().trim().min(1),appId:r.z.string().trim().min(1)}).strict(),sc=r.z.object({id:r.z.string().trim().min(1),jobId:r.z.string().trim().min(1)}).strict();const nc=r.z.object({appId:r.z.string().trim().min(1).max(120).default("terminal-tty"),cwdFolderId:r.z.string().trim().min(1).max(120).optional(),options:r.z.record(r.z.string(),r.z.unknown()).default({})}).strict().default({appId:"terminal-tty",options:{}}),ic=r.z.object({title:r.z.string().trim().min(1).max(120)}).strict(),ac=r.z.object({}).strict();function rc(e){const{app:t,service:s,events:n,appSessionAggregator:i,aiSessionAggregator:a,aiSessionUnread:o,chatGateway:d,aiSessionAttachments:c,nodeAgentTunnel:l,nodeEventSubscriber:p,errorPayload:u}=e;!function({app:e,service:t,events:s}){e.get("/api/projects",async()=>({data:t.listProjects()})),e.post("/api/projects",async(e,n)=>{const i=t.createProject(e.body);return s.publish("project.created",{projectId:i.id}),n.code(201).send({data:i})}),e.get("/api/projects/:id",async e=>({data:t.requireProject(Ld.parse(e.params).id)})),e.patch("/api/projects/:id",async e=>{const n=t.updateProject(Ld.parse(e.params).id,e.body);return s.publish("project.updated",{projectId:n.id}),{data:n}}),e.delete("/api/projects/:id",async e=>{const n=Ld.parse(e.params).id,i=t.deleteProject(n);return s.publish("project.deleted",{projectId:n,deleted:i}),{data:{deleted:i}}}),e.get("/api/models",async()=>({data:await t.listFederatedModels()})),e.post("/api/models",async(e,n)=>{const i=await t.createModel(e.body);return s.publish("model.created",{modelId:i.id}),n.code(201).send({data:i})}),e.post("/api/models/reorder",async e=>{const n=Qd.parse(e.body),i=await t.reorderModels(n.ids);return s.publish("model.reordered",{ids:n.ids}),{data:i}}),e.get("/api/models/:id",async e=>({data:t.requireModel(Ld.parse(e.params).id)})),e.patch("/api/models/:id",async e=>{const n=await t.updateModel(Ld.parse(e.params).id,e.body);return s.publish("model.updated",{modelId:n.id}),{data:n}}),e.delete("/api/models/:id",async e=>{const n=Ld.parse(e.params).id,i=await t.deleteModel(n);return s.publish("model.deleted",{modelId:n,deleted:i}),{data:{deleted:i}}}),e.post("/api/nodes/:nodeId/models",async(e,n)=>{const{nodeId:i}=Yd.parse(e.params),a=await t.createNodeModel(i,e.body);return s.publish("model.created",{modelId:a.id,nodeId:i}),n.code(201).send({data:a})}),e.patch("/api/nodes/:nodeId/models/:modelId",async e=>{const{nodeId:n,modelId:i}=Yd.parse(e.params),a=await t.updateNodeModel(n,i,e.body);return s.publish("model.updated",{modelId:i,nodeId:n}),{data:a}}),e.delete("/api/nodes/:nodeId/models/:modelId",async e=>{const{nodeId:n,modelId:i}=Yd.parse(e.params),a=await t.deleteNodeModel(n,i);return s.publish("model.deleted",{modelId:i,nodeId:n,deleted:a.deleted}),{data:a}}),e.get("/api/market/catalog",async()=>({data:t.getMarketCatalog()})),e.post("/api/market/refresh",async()=>{const e=await t.refreshMarketCatalog();return s.publish("market.catalog.updated",{revision:e.catalog.revision,source:e.catalog.source}),{data:e}}),e.get("/api/image-options",async()=>({data:t.listImageOptions()})),e.get("/api/images",async()=>({data:t.listImages()})),e.post("/api/images",async(e,n)=>{const i=t.createImage(e.body);return s.publish("image.created",{imageId:i.id}),n.code(201).send({data:i})}),e.get("/api/images/:id",async e=>({data:t.requireImage(Ld.parse(e.params).id)})),e.patch("/api/images/:id",async e=>{const n=t.updateImage(Ld.parse(e.params).id,e.body);return s.publish("image.updated",{imageId:n.id}),{data:n}}),e.delete("/api/images/:id",async e=>{const n=Ld.parse(e.params).id,i=await t.deleteImage(n);return s.publish("image.deleted",{imageId:n,deleted:i}),{data:{deleted:i}}})}({app:t,service:s,events:n}),function({app:e,service:t,events:s,nodeAgentTunnel:n,nodeEventSubscriber:i,errorPayload:a}){const r=e=>{if(!e||"object"!=typeof e||Array.isArray(e)||!("target"in e))return;const t=e.target;if(!t||"object"!=typeof t||Array.isArray(t)||"controlled-instance"!==t.component)return;const s=new Error("Independent controlled-instance updates were retired; update the Node rollout instead.");throw Object.assign(s,{statusCode:404,code:"LEGACY_INSTANCE_UPDATE_RETIRED"}),s};e.get("/api/nodes",async()=>({data:t.listPublicNodes()})),e.post("/api/nodes/local/sync",async()=>{const e=await t.syncLocalNodeConnection();return i.syncNow(),s.publish("node.local-synced",{nodeId:e.id}),{data:t.requirePublicNode(e.id)}}),e.post("/api/nodes",async(e,n)=>{const a=await t.createNode(e.body);return i.syncNow(),s.publish("node.created",{nodeId:a.id}),n.code(201).send({data:t.requirePublicNode(a.id)})}),e.post("/api/nodes/:id/check",async e=>{const n=await t.checkNode(Ld.parse(e.params).id);return s.publish("node.checked",{nodeId:n.id}),{data:n}}),e.get("/api/nodes/:id/updates/jobs",async e=>({data:await t.listNodeUpdateJobs(Ld.parse(e.params).id)})),e.get("/api/nodes/:id/settings/external-listener",async e=>({data:await t.getLocalNodeExternalListener(Ld.parse(e.params).id)})),e.patch("/api/nodes/:id/settings/external-listener",async e=>{const n=Ld.parse(e.params).id,i=await t.updateLocalNodeExternalListener(n,e.body);return s.publish("node.external-listener.updated",{nodeId:n,bindScope:i.bindScope,port:i.port}),{data:i}}),e.post("/api/nodes/:id/updates/check",async e=>{r(e.body);const n=Ld.parse(e.params).id,i=await t.checkNodeUpdate(n,$t.parse(e.body));return s.publish("node.update.checked",{nodeId:n,availableVersion:i.availableVersion,impact:i.impact}),{data:i}}),e.post("/api/nodes/:id/updates/apply",async(e,n)=>{r(e.body);const i=Ld.parse(e.params).id,a=await t.applyNodeUpdate(i,Ut.parse(e.body));return s.publish("node.update.queued",{nodeId:i,updateJobId:a.id,desiredVersion:a.toVersion,impact:a.impact}),n.code(202).send({data:a})}),e.post("/api/nodes/:id/pairing/invites",async(e,n)=>{const i=Ld.parse(e.params),a=await t.createNodePairingInvite(i.id,e.body);return s.publish("node.pairing-invite.created",{nodeId:i.id}),n.code(201).send({data:a})}),e.post("/api/nodes/:id/remotes/connect",async e=>{const n=Ld.parse(e.params),i=await t.connectNodeToControlPlane(n.id,e.body);return s.publish("node.remote-connect.requested",{nodeId:n.id}),{data:i}}),e.get("/api/nodes/:id/remotes",async e=>{const s=Ld.parse(e.params);return{data:await t.listNodeRemoteControlPlanes(s.id)}}),e.delete("/api/nodes/:id/remotes/:keyId",async e=>{const n=Ud.parse(e.params),i=await t.deleteNodeRemoteControlPlane(n.id,n.keyId);return s.publish("node.remote-key.deleted",{nodeId:n.id,keyId:n.keyId}),{data:i}}),e.post("/api/node-join/invites",async(e,n)=>{const i=t.createNodeJoinInvite(e.body);return s.publish("node-join.invite.created",{inviteId:i.id}),n.code(201).send({data:i})}),e.post("/api/node-join/complete",async(e,n)=>{const a=t.completeNodeJoin(e.body);return i.syncNow(),s.publish("node.joined",{nodeId:a.id}),n.code(201).send({data:t.requirePublicNode(a.id)})}),e.get("/api/nodes/:id/runtimes",async e=>({data:await t.listNodeRuntimes(Ld.parse(e.params).id)})),e.post("/api/nodes/:id/runtimes",async(e,n)=>{const i=Ld.parse(e.params),a=await t.createNodeRuntime(i.id,e.body);return s.publish("node.runtime.created",{nodeId:i.id,runtimeId:a.id}),n.code(201).send({data:a})}),e.patch("/api/nodes/:id/runtimes/:runtimeId",async e=>{const n=Fd.parse(e.params),i=await t.updateNodeRuntime(n.id,n.runtimeId,e.body);return s.publish("node.runtime.updated",{nodeId:n.id,runtimeId:i.id}),{data:i}}),e.delete("/api/nodes/:id/runtimes/:runtimeId",async e=>{const n=Fd.parse(e.params),i=await t.deleteNodeRuntime(n.id,n.runtimeId);return s.publish("node.runtime.deleted",{nodeId:n.id,runtimeId:n.runtimeId}),{data:i}}),e.post("/api/nodes/:id/runtimes/:runtimeId/check",async e=>{const n=Fd.parse(e.params),i=await t.checkNodeRuntime(n.id,n.runtimeId);return s.publish("node.runtime.checked",{nodeId:n.id,runtimeId:i.id}),{data:i}}),e.get("/api/nodes/:id/docker/images",async e=>({data:await t.listNodeDockerImages(Ld.parse(e.params).id)})),e.get("/api/nodes/:id/image-options",async e=>({data:await t.listNodeImageAvailability(Ld.parse(e.params).id)})),e.get("/api/nodes/:id/local-folders",async e=>({data:await t.listNodeLocalFolders(Ld.parse(e.params).id)})),e.get("/api/nodes/:id/folders/tree",async e=>{const s=Ld.parse(e.params),n=Jd.parse(e.query);return{data:await t.listNodeFolderTree(s.id,{path:n.path,depth:n.depth})}}),e.post("/api/nodes/:id/local-folders",async(e,n)=>{const i=Ld.parse(e.params),a=await t.createNodeLocalFolder(i.id,e.body);return s.publish("node.local-folder.created",{nodeId:i.id,folderId:a.id}),n.code(201).send({data:a})}),e.delete("/api/nodes/:id/local-folders/:folderId",async e=>{const n=Bd.parse(e.params),i=await t.deleteNodeLocalFolder(n.id,n.folderId);return s.publish("node.local-folder.deleted",{nodeId:n.id,folderId:n.folderId}),{data:i}}),e.get("/api/nodes/:id",async e=>({data:t.requirePublicNode(Ld.parse(e.params).id)})),e.patch("/api/nodes/:id",async e=>{const n=t.updateNode(Ld.parse(e.params).id,e.body);return i.syncNow(),s.publish("node.updated",{nodeId:n.id}),{data:t.requirePublicNode(n.id)}}),e.delete("/api/nodes/:id",async e=>{const n=Ld.parse(e.params).id,a=t.deleteNode(n);return i.syncNow(),s.publish("node.deleted",{nodeId:n,deleted:a}),{data:{deleted:a}}}),e.get("/api/node-runtimes",async()=>{const e=await t.listNodeRuntimesWithDiagnostics();return{data:e.items,meta:{nodeErrors:e.nodeErrors}}}),function(e){const{app:t,service:s,nodeAgentTunnel:n,errorPayload:i}=e;t.get("/api/node-agent/tunnel",{websocket:!0},(e,t)=>{try{const i=pd.parse(t.query),a=s.requireNode(i.nodeId);hd(a,t),n.attach(a.id,e),e.send(JSON.stringify({type:"control-plane.hello",protocolVersion:He,nodeId:a.id,serverTime:(new Date).toISOString(),capabilities:{reverseTunnel:"request-response",httpResponseStreaming:!0,lifecycleCommands:!0,instanceApiProxy:!0}})),e.on("message",t=>{let s;try{s=JSON.parse(String(t))}catch{return void e.send(JSON.stringify({type:"control-plane.error",code:"INVALID_JSON"}))}const i=s&&"object"==typeof s?s:{};n.handleMessage(a.id,i)||("node-agent.ping"!==i.type?"node-agent.identify"!==i.type?e.send(JSON.stringify({type:"control-plane.error",code:"UNSUPPORTED_MESSAGE"})):e.send(JSON.stringify({type:"control-plane.identified",nodeId:a.id,serverTime:(new Date).toISOString()})):e.send(JSON.stringify({type:"control-plane.pong",nodeId:a.id,serverTime:(new Date).toISOString()})))})}catch(t){const s=i(t);e.send(JSON.stringify({type:"control-plane.error",code:s.code,message:s.message})),e.close(1008,s.message)}}),t.get("/api/node-agent/tunnel/streams/:streamId",{websocket:!0},(e,t)=>{try{const i=pd.parse(t.query),a=s.requireNode(i.nodeId);hd(a,t);const r=t.params.streamId;n.attachWebSocketStream(a.id,r,e)}catch(t){const s=i(t);e.close(1008,s.message)}}),t.get("/api/node-agent/tunnel/http-streams/:streamId",{websocket:!0},(e,t)=>{try{const i=pd.parse(t.query),a=s.requireNode(i.nodeId);hd(a,t);const r=t.params.streamId;n.attachHttpStream(a.id,r,e)}catch(t){const s=i(t);e.close(1008,s.message)}})}({app:e,service:t,nodeAgentTunnel:n,errorPayload:a})}({app:t,service:s,events:n,nodeAgentTunnel:l,nodeEventSubscriber:p,errorPayload:u}),function({app:e,service:t,events:s}){e.get("/api/controlled-instances",async()=>({data:await t.listControlledInstances()})),e.post("/api/controlled-instances",async(e,n)=>{const i=await t.createControlledInstance(e.body);return s.publish("instance.created",{instanceId:i.id}),n.code(201).send({data:i})}),e.get("/api/controlled-instances/:id",async e=>({data:await t.requireControlledInstance(Ld.parse(e.params).id)})),e.get("/api/controlled-instances/:id/metrics",async e=>({data:await t.instanceResourceMetrics(Ld.parse(e.params).id)})),e.get("/api/controlled-instances/:id/apps/management",async e=>({data:await t.instanceAppManagement(Ld.parse(e.params).id)})),e.post("/api/controlled-instances/:id/apps/:appId/install",async e=>{const s=tc.parse(e.params),n=Nt.parse(e.body||{});return{data:await t.requestInstanceAppOperation(s.id,s.appId,"install",n)}}),e.post("/api/controlled-instances/:id/apps/:appId/uninstall",async e=>{const s=tc.parse(e.params),n=Nt.parse(e.body||{});return{data:await t.requestInstanceAppOperation(s.id,s.appId,"uninstall",n)}}),e.get("/api/controlled-instances/:id/apps/jobs/:jobId",async e=>{const s=sc.parse(e.params);return{data:await t.instanceAppManagementJob(s.id,s.jobId)}}),e.patch("/api/controlled-instances/:id",async e=>{const n=await t.updateControlledInstance(Ld.parse(e.params).id,e.body);return s.publish("instance.updated",{instanceId:n.id}),{data:n}}),e.delete("/api/controlled-instances/:id",async e=>{const n=Ld.parse(e.params).id,i=await t.deleteControlledInstance(n);return s.publish("instance.deleted",{instanceId:n,deleted:i}),{data:{deleted:i}}}),e.post("/api/controlled-instances/:id/start",async e=>{const n=await t.startControlledInstance(Ld.parse(e.params).id);return s.publish("instance.started",{instanceId:n.id}),{data:n}}),e.post("/api/controlled-instances/:id/stop",async e=>{const n=await t.stopControlledInstance(Ld.parse(e.params).id);return s.publish("instance.stopped",{instanceId:n.id}),{data:n}}),e.post("/api/controlled-instances/:id/restart",async e=>{const n=await t.restartControlledInstance(Ld.parse(e.params).id);return s.publish("instance.restarted",{instanceId:n.id}),{data:n}}),e.post("/api/controlled-instances/:id/image-provisioning/retry",async e=>{const n=await t.retryControlledInstanceImageProvisioning(Ld.parse(e.params).id);return s.publish("instance.image-provisioning-retried",{instanceId:n.id}),{data:n}}),e.get("/api/controlled-instances/:id/config-sync",async e=>({data:await t.instanceConfigSyncState(Ld.parse(e.params).id)})),e.get("/api/controlled-instances/:id/config-sync/folders",async e=>({data:await t.listInstanceConfigSyncFolders(Ld.parse(e.params).id,ec.parse(e.query))})),e.post("/api/controlled-instances/:id/config-sync",async e=>{const n=Ld.parse(e.params).id,i=await t.syncInstanceConfigs(n,e.body);return s.publish("instance.config-synced",{instanceId:n}),{data:i}}),e.get("/api/instance-board",async()=>{const e=await t.boardWithDiagnostics();return{data:e.items,meta:{nodeErrors:e.nodeErrors}}})}({app:t,service:s,events:n}),function({app:e,service:t,events:s,appSessionAggregator:n,aiSessionAggregator:i,aiSessionUnread:a,aiSessionAttachments:o}){e.get("/api/app-sessions",async e=>{const t=r.z.object({refresh:r.z.string().optional(),includeTombstones:r.z.string().optional(),instanceId:r.z.string().trim().min(1).optional(),streamId:r.z.string().trim().min(1).optional(),sinceRevision:r.z.string().optional()}).parse(e.query||{});if(void 0!==t.sinceRevision){const e=Number(t.sinceRevision);if(!Number.isInteger(e)||e<0){const e=new Error("sinceRevision must be a non-negative integer.");throw Object.assign(e,{statusCode:400,code:"APP_SESSION_DELTA_INVALID"}),e}if(!t.streamId){const e=new Error("streamId is required with sinceRevision.");throw Object.assign(e,{statusCode:400,code:"APP_SESSION_DELTA_INVALID"}),e}return{data:await n.delta({instanceId:t.instanceId,streamId:t.streamId,sinceRevision:e})}}return{data:await n.list({refresh:"true"===t.refresh||"1"===t.refresh,includeTombstones:"true"===t.includeTombstones||"1"===t.includeTombstones})}}),e.post("/api/controlled-instances/:id/apps/sessions",async e=>{const n=Ld.parse(e.params),i=nc.parse(e.body),a=await t.launchAppSession(n.id,i.appId,{...i.options,...i.cwdFolderId?{cwdFolderId:i.cwdFolderId}:{}});return s.publish("instance.app-session.launched",{instanceId:n.id,sessionId:"string"==typeof a.id?a.id:void 0,appId:i.appId}),{data:a}}),e.post("/api/controlled-instances/:id/apps/sessions/:sessionId/stop",async e=>{const n=Hd.parse(e.params),i=await t.stopAppSession(n.id,n.sessionId);return s.publish("instance.app-session.stopped",{instanceId:n.id,sessionId:n.sessionId}),{data:i}}),e.patch("/api/controlled-instances/:id/apps/sessions/:sessionId",async e=>{const n=Hd.parse(e.params),i=ic.parse(e.body),a=await t.renameAppSession(n.id,n.sessionId,i.title);return s.publish("instance.app-session.renamed",{instanceId:n.id,sessionId:n.sessionId,title:i.title}),{data:a}}),e.get("/api/controlled-instances/:id/app-sessions",async e=>{const t=Ld.parse(e.params),s=(await n.list()).instances.find(e=>e.instanceId===t.id);return{data:s?.appSessions||{runningCount:0,problemCount:0,sessions:[],updatedAt:(new Date).toISOString()}}}),e.get("/api/controlled-instances/:id/ai-sessions/history",async e=>{const s=Ld.parse(e.params);return{data:await t.listAiSessionHistory(s.id)}}),e.get("/api/controlled-instances/:id/ai-sessions/history/:sessionId",async e=>{const s=Hd.parse(e.params);return{data:await t.getAiSessionHistoryDetail(s.id,s.sessionId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/resume",async e=>{const n=Hd.parse(e.params);ac.parse(e.body||{});const i=await t.resumeAiSession(n.id,n.sessionId);return s.publish("instance.ai-session.resumed",{instanceId:n.id,sessionId:i.aiSessionId,providerSessionId:i.providerSessionId,appSessionId:i.appSessionId,disposition:i.disposition}),{data:i}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/messages",async e=>{const n=Hd.parse(e.params),i=ie.parse(e.body||{}),a=o.resolveRefs(i.attachments,n.id,n.sessionId),r=await t.sendAiSessionMessage(n.id,n.sessionId,i.message,i.mode,a,i.references,i.permissionMode);return s.publish("instance.ai-session.message-sent",{instanceId:n.id,sessionId:n.sessionId}),{data:r}}),e.get("/api/controlled-instances/:id/ai-sessions/:sessionId/mentions",async e=>{const s=Hd.parse(e.params);return{data:await t.aiSessionMentionCatalog(s.id,s.sessionId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/mentions/files",async e=>{const s=Hd.parse(e.params),n=te.parse(e.body||{});return{data:await t.searchAiSessionMentionFiles(s.id,s.sessionId,n.query)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/commands",async e=>{const n=Hd.parse(e.params),i=W.parse(e.body||{}),a=await t.executeAiSessionCommand(n.id,n.sessionId,i);return s.publish("instance.ai-session.command-executed",{instanceId:n.id,sessionId:n.sessionId,command:i.command}),{data:a}}),e.get("/api/controlled-instances/:id/ai-sessions/:sessionId/queue",async e=>{const s=Hd.parse(e.params);return{data:await t.aiSessionQueue(s.id,s.sessionId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/queue/:queueId/steer",async e=>{const n=Gd.parse(e.params),i=await t.steerAiSessionQueuedMessage(n.id,n.sessionId,n.queueId);return s.publish("instance.ai-session.message-sent",{instanceId:n.id,sessionId:n.sessionId}),{data:i}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/queue/:queueId/retry",async e=>{const s=Gd.parse(e.params);return{data:await t.retryAiSessionQueuedMessage(s.id,s.sessionId,s.queueId)}}),e.delete("/api/controlled-instances/:id/ai-sessions/:sessionId/queue/:queueId",async e=>{const s=Gd.parse(e.params);return{data:await t.removeAiSessionQueuedMessage(s.id,s.sessionId,s.queueId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/interrupt",async e=>{const n=Hd.parse(e.params),i=await t.interruptAiSession(n.id,n.sessionId);return s.publish("instance.ai-session.interrupted",{instanceId:n.id,sessionId:n.sessionId}),{data:i}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/approval",async e=>{const n=Hd.parse(e.params),i=ae.parse(e.body||{}),a=await t.resolveAiSessionApproval(n.id,n.sessionId,i.decision);return s.publish("instance.ai-session.approval-resolved",{instanceId:n.id,sessionId:n.sessionId,decision:i.decision}),{data:a}}),e.get("/api/ai-sessions",async(e,t)=>{const s=e.query;if(void 0!==s.sinceRevision){const e=Number(s.sinceRevision);if(!Number.isInteger(e)||e<0)return t.code(400).send({error:"invalid_since_revision"});try{return s.streamId?{data:await i.delta({instanceId:s.instanceId,streamId:s.streamId,sinceRevision:e})}:t.code(400).send({error:"stream_id_required"})}catch(e){if("AI_SESSION_DELTA_INSTANCE_ID_REQUIRED"===e.message)return t.code(400).send({error:"instance_id_required"});throw e}}const n=await i.list({refresh:"true"===s.refresh||"1"===s.refresh});for(const e of n.instances)a.reconcile(e.instanceId,e.aiSessions);return{data:{...n,instances:n.instances.map(e=>({...e,aiSessions:a.decorate(e.instanceId,e.aiSessions)}))}}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/read",async(e,t)=>{const n=Hd.parse(e.params),o=r.z.object({sessionUpdatedAt:r.z.string().datetime()}).strict().parse(e.body||{}),d=(await i.list()).instances.find(e=>e.instanceId===n.id),c=d?.aiSessions.sessions.find(e=>e.id===n.sessionId);if(!d||!c)return t.code(404).send({error:{code:"AI_SESSION_NOT_FOUND",message:"AI session was not found."}});a.reconcile(n.id,d.aiSessions);const l=a.markRead(n.id,n.sessionId,o.sessionUpdatedAt);return l?(s.publish(C,l,{topic:"ai.sessions",scope:{instanceId:n.id,sessionId:n.sessionId}}),{data:l}):t.code(404).send({error:{code:"AI_SESSION_NOT_FOUND",message:"AI session was not found."}})})}({app:t,service:s,events:n,appSessionAggregator:i,aiSessionAggregator:a,aiSessionUnread:o,aiSessionAttachments:c}),function({app:e,service:t,events:s}){e.get("/api/triggers",async()=>({data:await t.listTriggers()})),e.post("/api/triggers",async(e,n)=>{const i=t.createTrigger(e.body);return s.publish("trigger.created",{configHash:i.configHash}),n.code(201).send({data:i})}),e.delete("/api/triggers/:configHash",async e=>{const n=Wd.parse(e.params),i=await t.deleteTrigger(n.configHash);return s.publish("trigger.deleted",{configHash:n.configHash}),{data:i}}),e.post("/api/triggers/:configHash/apply",async e=>{const n=Wd.parse(e.params),i=await t.applyTrigger(n.configHash,e.body||{});return s.publish("trigger.applied",{configHash:n.configHash}),{data:i}}),e.get("/api/controlled-instances/:id/triggers",async e=>{const s=Ld.parse(e.params);return{data:await t.listInstanceTriggers(s.id)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/triggers",async e=>{const n=Hd.parse(e.params),i=await t.bindAiSessionTrigger(n.id,n.sessionId,e.body||{});return s.publish("instance.ai-session.trigger-bound",{instanceId:n.id,sessionId:n.sessionId}),{data:i}}),e.delete("/api/controlled-instances/:id/ai-sessions/:sessionId/triggers/:configHash",async e=>{const n=Kd.parse(e.params),i=await t.unbindAiSessionTrigger(n.id,n.sessionId,n.configHash);return s.publish("instance.ai-session.trigger-unbound",{instanceId:n.id,sessionId:n.sessionId,configHash:n.configHash}),{data:i}}),e.post("/api/controlled-instances/:id/triggers/:configHash/run",async e=>{const n=Vd.parse(e.params),i=await t.runInstanceTrigger(n.id,n.configHash,e.body||{});return s.publish("instance.trigger.run",{instanceId:n.id,configHash:n.configHash}),{data:i}})}({app:t,service:s,events:n}),function({app:e,service:t,chatGateway:s}){e.get("/api/chat/sessions",async()=>({data:t.listChatSessions()})),e.get("/api/chat/sessions/:id",async e=>({data:t.requireChatSession(Ld.parse(e.params).id)})),e.get("/api/chat-gateway/status",async()=>({data:s.status()})),e.post("/api/chat-gateway/poll-ai-sessions",async()=>({data:await s.pollAiSessionsNow()})),e.get("/api/chat-gateway/bridges",async()=>({data:t.listChatBridges()})),e.post("/api/chat-gateway/bridges",async e=>({data:t.createChatBridge(e.body)})),e.patch("/api/chat-gateway/bridges/:id",async n=>{const i=Ld.parse(n.params).id,a=t.requireChatBridge(i),r=ia.parse(n.body),o=t.updateChatBridge(i,n.body),d="boolean"==typeof r.enabled?r.enabled:void 0;return e.log.info({component:"control-plane-chat-gateway",action:"bridge-patch",bridgeId:i,hasEnabled:void 0!==d,requestedEnabled:d,previousEnabled:a.enabled,updatedEnabled:t.requireChatBridge(i).enabled},"chat gateway bridge state changed"),void 0!==d&&d!==a.enabled&&(d?s.startBridge(i):s.stopBridge(i)),{data:o}}),e.post("/api/chat-gateway/bridges/:id/start",async n=>{const i=Ld.parse(n.params).id,a=t.requireChatBridge(i);return t.updateChatBridge(i,{enabled:!0}),e.log.info({component:"control-plane-chat-gateway",action:"bridge-start-request",bridgeId:i,previousEnabled:a.enabled,updatedEnabled:t.requireChatBridge(i).enabled},"chat gateway bridge state changed"),{data:s.startBridge(i)}}),e.post("/api/chat-gateway/bridges/:id/stop",async n=>{const i=Ld.parse(n.params).id,a=t.requireChatBridge(i);return t.updateChatBridge(i,{enabled:!1}),e.log.info({component:"control-plane-chat-gateway",action:"bridge-stop-request",bridgeId:i,previousEnabled:a.enabled,updatedEnabled:t.requireChatBridge(i).enabled},"chat gateway bridge state changed"),{data:s.stopBridge(i)}}),e.delete("/api/chat-gateway/bridges/:id",async e=>({data:{deleted:t.deleteChatBridge(Ld.parse(e.params).id)}})),e.post("/api/chat-gateway/messages",{bodyLimit:67108864},async e=>({data:await t.handleChatGatewayMessage(tn.parse(e.body))})),e.post("/api/chat-gateway/actions",async e=>({data:await t.handleChatGatewayAction(Zd(e.body))})),e.get("/api/pending-routes",async()=>({data:await t.listPendingRoutes()}))}({app:t,service:s,chatGateway:d})}const oc=new Set(["connection","content-length","keep-alive","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding","upgrade"]),dc=new Set(["content-encoding"]);function cc(e){return Object.fromEntries(Object.entries(e).filter(([e,t])=>!oc.has(e.toLowerCase())&&void 0!==t).map(([e,t])=>[e,Array.isArray(t)?t.join(", "):String(t)]))}function lc(e){return Object.fromEntries(Object.entries(cc(e)).filter(([e])=>{const t=e.toLowerCase();return"host"!==t&&"cookie"!==t&&"authorization"!==t&&(!t.startsWith("sec-websocket-")||"sec-websocket-origin"===t)}))}function pc(e){const t=e["sec-websocket-protocol"],s=(Array.isArray(t)?t.join(","):"string"==typeof t?t:"").split(",").map(e=>e.trim()).filter(Boolean);return s.length?s:void 0}function uc(e,t,s){for(const[s,n]of Object.entries(t.headers)){const t=s.toLowerCase();oc.has(t)||dc.has(t)||e.header(s,n)}if(!t.body)return e.code(t.status).send();const n=m.Readable.fromWeb(t.body);return e.raw.once("close",()=>n.destroy()),e.code(t.status).send(s?n.pipe(s):n)}function mc(e){return`/instances/${encodeURIComponent(e)}`}function gc(e,t,s,n){const i=function(e,t){const s=e.split("?",1)[0]||"/";return t.toLowerCase().includes("text/html")&&!s.startsWith("/api/")}(n,Object.entries(t.headers).find(([e])=>"content-type"===e.toLowerCase())?.[1]||"")?function(e){const t=[];let s=0,n=!1;const i=(i,a=!1)=>{if(n)return;const r=Buffer.concat(t,s).toString("utf8");!/<head(\s[^>]*)?>/i.exec(r)&&!a&&s<65536||(n=!0,i.push(function(e,t){if(e.includes("__TASK_HANDOFF_PUBLIC_BASE__"))return e;const s=`<script>window.__TASK_HANDOFF_PUBLIC_BASE__=${JSON.stringify(t)};<\/script>`;return/<head(\s[^>]*)?>/i.test(e)?e.replace(/<head(\s[^>]*)?>/i,e=>`${e}\n${s}`):`${s}\n${e}`}(r,e)),t.length=0,s=0)};return new m.Transform({transform(e,a,r){n?this.push(e):(t.push(Buffer.from(e)),s+=e.length,i(this)),r()},flush(e){i(this,!0),e()}})}(mc(s)):void 0;return uc(e,t,i)}function hc(e){return Buffer.isBuffer(e)?e:e instanceof Uint8Array?Buffer.from(e):e instanceof URLSearchParams?e.toString():"string"==typeof e?e:null==e?void 0:JSON.stringify(e)}function fc(e,t){if("string"!=typeof t||!e.hasContentTypeParser(t))try{e.addContentTypeParser(t,{parseAs:"buffer"},(e,t,s)=>s(null,t))}catch(e){if(e instanceof Error&&/Content type parser .* already present/i.test(e.message))return;throw e}}function Ic(e){return new URL(e,"http://control-plane.local").searchParams.get("token")||""}function yc(e,t,s){const n=encodeURIComponent(e),i=encodeURIComponent(t),a=s.vnc&&"object"==typeof s.vnc?s.vnc:{};return"kasmvnc"===("string"==typeof a.backend?a.backend:"")?`/apps/access/vnc/proxy/api/apps/sessions/${i}/web/?token=${n}`:`/apps/access/vnc/proxy/api/novnc/vnc.html?token=${n}&path=${encodeURIComponent(`apps/access/vnc/proxy/api/apps/sessions/${i}/vnc?token=${n}`)}&autoconnect=1&resize=scale`}class bc{snapshots=new Map;history=new Map;advertisedStreams=new Map;recoveries=new Map;listeners=new Set;bootstrap;logger;recoverDelta;recoverSnapshot;onRecoveredEvent;recoveryCount=0;counters={streamResets:0,gaps:0,deltaRecoveries:0,snapshotRecoveries:0,recoveryFailures:0};constructor(e){this.bootstrap=e.bootstrap,this.logger=e.logger,this.recoverDelta=e.recoverDelta,this.recoverSnapshot=e.recoverSnapshot,this.onRecoveredEvent=e.onRecoveredEvent}handleEvent(e){if("ai-session.message-delta"===e.type){const t=Ee.safeParse(e.payload);return t.success||this.logger?.warn?.({eventType:e.type,issues:t.error.issues,errorCode:"AI_SESSION_MESSAGE_DELTA_INVALID"},"ai-session.aggregator.message-delta.invalid"),!0}const t=e.type===k?ze:e.type===z?Te:e.type===T?Ce:void 0;if(!t)return!1;const s=t.safeParse(e.payload);return s.success?(e.type===k&&this.applySnapshot(s.data),e.type===z&&this.applyPatch(s.data),e.type===T&&this.applyRemoved(s.data),!0):(this.logger?.warn?.({eventType:e.type,issues:s.error.issues,errorCode:"AI_SESSION_EVENT_INVALID"},"ai-session.aggregator.event.invalid"),!0)}onSnapshot(e){return this.listeners.add(e),()=>this.listeners.delete(e)}applySnapshot(e){const t=this.advertisedStreams.get(e.meta.instanceId)?.streamId;return t&&t!==e.meta.streamId?(this.logger?.warn?.({instanceId:e.meta.instanceId,advertisedStreamId:t,streamId:e.meta.streamId,revision:e.meta.revision},"ai-session.aggregator.snapshot.obsolete-stream"),!1):this.apply({type:k,payload:e})}advertiseStream(e,t){return this.advertisedStreams.set(e,t),this.recoverStream(e,t.streamId,t.latestRevision).catch(s=>{this.counters.recoveryFailures+=1,this.logger?.warn?.({instanceId:e,streamId:t.streamId,error:s instanceof Error?s.message:String(s)},"ai-session.aggregator.recovery.failed")})}diagnostics(){return{...this.counters,recoveriesStarted:this.recoveryCount,activeRecoveries:this.recoveries.size}}removeInstance(e){this.snapshots.delete(e),this.history.delete(e),this.advertisedStreams.delete(e)}applyPatch(e){return this.apply({type:z,payload:e})}applyRemoved(e){return this.apply({type:T,payload:e})}async list(e={}){return(e.refresh||0===this.snapshots.size)&&await this.bootstrapFromInstances(),this.view()}async streamDescriptors(){return 0===this.snapshots.size&&await this.bootstrapFromInstances(),[...this.snapshots.entries()].map(([e,t])=>({topic:"ai.sessions",instanceId:e,streamId:t.streamId,latestRevision:t.revision,earliestRetainedRevision:this.history.get(e)?.[0]?.payload.meta.revision??t.revision+1}))}async delta(e){0===this.snapshots.size&&await this.bootstrapFromInstances();const t=this.resolveDeltaInstanceId(e.instanceId);this.pruneHistory(t);const s=this.snapshots.get(t)?.revision??0,n=this.snapshots.get(t)?.streamId||"unavailable",i=this.history.get(t)?.[0]?.payload.meta.revision??s+1;if(e.streamId!==n||e.sinceRevision>s)return Ne.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:!0,events:[]});if(e.sinceRevision===s)return Ne.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:!1,events:[]});const a=(this.history.get(t)??[]).filter(t=>t.payload.meta.revision>e.sinceRevision),r=!a.length||a[0].payload.meta.revision!==e.sinceRevision+1;return Ne.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:r,events:r?[]:a.map(({type:e,payload:t})=>({type:e,payload:t}))})}apply(e,t=!1){const{meta:s}=e.payload,n=this.advertisedStreams.get(s.instanceId)?.streamId;if(n&&n!==s.streamId)return this.logger?.warn?.({instanceId:s.instanceId,advertisedStreamId:n,streamId:s.streamId,revision:s.revision},"ai-session.aggregator.event.obsolete-stream"),!1;const i=this.recoveries.get(s.instanceId);if(i&&!t)return i.streamId===s.streamId&&(i.highWater=Math.max(i.highWater,s.revision)),!1;const a=this.snapshots.get(s.instanceId);e.type===k&&a&&a.streamId!==s.streamId&&(this.counters.streamResets+=1,this.logger?.info?.({instanceId:s.instanceId,previousStreamId:a.streamId,streamId:s.streamId,previousRevision:a.revision,revision:s.revision,traceId:s.traceId,resetCount:this.counters.streamResets},"ai-session.aggregator.stream-reset"));const r=function(e,t){const{meta:s}=t.payload;if(t.type===k)return e?.streamId===s.streamId&&s.revision===e.revision?{kind:"duplicate",projection:e}:e?.streamId===s.streamId&&s.revision<e.revision?{kind:"stale",projection:e}:{kind:"applied",projection:ve.parse({streamId:s.streamId,revision:s.revision,lastEventAt:s.generatedAt,snapshot:t.payload.snapshot})};if(!e||e.streamId!==s.streamId)return{kind:"snapshot-required",projection:e,streamId:s.streamId};if(s.revision===e.revision)return{kind:"duplicate",projection:e};if(s.revision<e.revision)return{kind:"stale",projection:e};if(s.revision!==e.revision+1||s.previousRevision!==e.revision)return{kind:"gap",projection:e,expectedRevision:e.revision+1,receivedRevision:s.revision};const n=new Map(e.snapshot.sessions.map(e=>[e.id,e]));if(t.type===z){for(const e of t.payload.upserted)n.set(e.id,e);for(const e of t.payload.removed)n.delete(e)}else for(const e of t.payload.sessionIds)n.delete(e);const i=[...n.values()],a=be.parse({runningCount:i.filter(e=>"running"===e.status).length,waitingCount:i.filter(e=>"waiting"===e.status).length,staleCount:e.snapshot.staleCount,sessions:i,updatedAt:s.generatedAt});return{kind:"applied",projection:ve.parse({streamId:s.streamId,revision:s.revision,lastEventAt:s.generatedAt,snapshot:a})}}(a,e);if("applied"!==r.kind)return this.logger?.warn?.({traceId:s.traceId,streamId:s.streamId,instanceId:s.instanceId,currentRevision:a?.revision,revision:s.revision,outcome:r.kind},`ai-session.aggregator.event.${r.kind}`),"gap"!==r.kind&&"snapshot-required"!==r.kind||("gap"===r.kind&&(this.counters.gaps+=1),this.recoverStream(s.instanceId,s.streamId,s.revision).catch(e=>{this.logger?.warn?.({instanceId:s.instanceId,streamId:s.streamId,revision:s.revision,error:e instanceof Error?e.message:String(e)},"ai-session.aggregator.recovery.failed")})),!1;const o=r.projection;this.snapshots.set(s.instanceId,o),this.rememberEvent(s.instanceId,e);const d={instanceId:s.instanceId,streamId:o.streamId,aiSessions:o.snapshot,revision:o.revision,lastEventAt:o.lastEventAt};for(const e of this.listeners)try{e(d)}catch{this.listeners.delete(e)}return this.logger?.info?.({traceId:s.traceId,streamId:s.streamId,instanceId:s.instanceId,revision:s.revision,reason:s.reason,sessionCount:o.snapshot.sessions.length},"ai-session.aggregator.event.accepted"),!0}async bootstrapFromInstances(){this.logger?.info?.({},"ai-session.aggregator.bootstrap");const e=new Map(this.snapshots),t=await this.bootstrap(),s=new Set(t.instances.map(e=>e.instanceId));for(const[t,n]of e)s.has(t)||this.snapshots.get(t)!==n||(this.snapshots.delete(t),this.history.delete(t),this.advertisedStreams.delete(t));for(const s of t.instances){const t=this.snapshots.get(s.instanceId);if(t!==e.get(s.instanceId))continue;const n=this.advertisedStreams.get(s.instanceId)?.streamId;n&&n!==s.streamId||t?.streamId===s.streamId&&t.revision>=s.revision||(t?.streamId!==s.streamId&&this.history.delete(s.instanceId),this.snapshots.set(s.instanceId,{streamId:s.streamId,snapshot:s.aiSessions,revision:s.revision,lastEventAt:s.lastEventAt}))}}recoverStream(e,t,s){const n=this.recoveries.get(e);if(n)return n.streamId===t?n.highWater=Math.max(n.highWater,s):(n.streamId=t,n.highWater=s),n.promise;if(!this.recoverDelta||!this.recoverSnapshot)return Promise.resolve();const i={promise:Promise.resolve(),streamId:t,highWater:s},a=++this.recoveryCount;return this.logger?.info?.({instanceId:e,highWater:s,recoveryNumber:a},"ai-session.aggregator.recovery.started"),i.promise=(async()=>{for(;;){const t=this.advertisedStreams.get(e);if(!t)return;i.streamId!==t.streamId&&(i.streamId=t.streamId,i.highWater=t.latestRevision);const s=this.snapshots.get(e);if(s&&s.streamId===t.streamId){if(s.revision<Math.max(t.latestRevision,i.highWater)){const t=await this.recoverDelta(e,s.streamId,s.revision);if(this.advertisedStreams.get(e)?.streamId!==t.streamId||i.streamId!==t.streamId)continue;if(t.syncRequired){const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const s={type:k,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`ais_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(s,!0)&&this.onRecoveredEvent?.(s),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:a,outcome:"snapshot-fallback"},"ai-session.aggregator.recovery.snapshot")}else{for(const e of t.events)this.apply(e,!0)&&this.onRecoveredEvent?.(e);this.counters.deltaRecoveries+=1,i.highWater=Math.max(i.highWater,t.latestRevision),this.logger?.info?.({instanceId:e,streamId:t.streamId,sinceRevision:t.sinceRevision,latestRevision:t.latestRevision,eventCount:t.events.length,recoveryNumber:a,outcome:"delta"},"ai-session.aggregator.recovery.delta")}}}else{const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const s={type:k,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`ais_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(s,!0)&&this.onRecoveredEvent?.(s),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:a,outcome:"snapshot"},"ai-session.aggregator.recovery.snapshot")}const n=this.snapshots.get(e),r=this.advertisedStreams.get(e);if(n&&r&&n.streamId===r.streamId&&n.revision>=Math.max(r.latestRevision,i.highWater))return void this.logger?.info?.({instanceId:e,streamId:n.streamId,revision:n.revision,recoveryNumber:a,outcome:"success"},"ai-session.aggregator.recovery.completed")}})().finally(()=>this.recoveries.delete(e)),this.recoveries.set(e,i),i.promise}rememberEvent(e,t){const s=this.history.get(e)??[],n=s.length&&s[0].payload.meta.streamId!==t.payload.meta.streamId?[]:s;n.push({...t,createdAtMs:Date.now()}),this.history.set(e,n),this.pruneHistory(e)}pruneHistory(e){const t=this.history.get(e);if(!t?.length)return;const s=t.filter(e=>e.createdAtMs>=Date.now()-36e5);s.length?this.history.set(e,s):this.history.delete(e)}resolveDeltaInstanceId(e){if(e)return e;const t=[...this.snapshots.keys()];if(1===t.length)return t[0];throw new Error("AI_SESSION_DELTA_INSTANCE_ID_REQUIRED")}view(){return{updatedAt:(new Date).toISOString(),instances:[...this.snapshots.entries()].map(([e,t])=>({instanceId:e,streamId:t.streamId,aiSessions:t.snapshot,revision:t.revision,lastEventAt:t.lastEventAt}))}}}const vc=r.z.object({id:r.z.string().trim().min(1),instanceId:r.z.string().trim().min(1).max(160),sessionId:r.z.string().trim().min(1).max(120),unread:r.z.boolean(),lastStatus:r.z.enum(["running","waiting","idle","failed"]),sessionUpdatedAt:r.z.string().datetime(),createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}).strict();class wc{records;bySession=new Map;listener;constructor(e,t={}){this.records=new hn(e.aiSessionUnreadDir,{schema:vc}),this.listener=t.onChanged}init(){this.records.init(),this.bySession.clear();for(const e of this.records.list())this.bySession.set(Ac(e.instanceId,e.sessionId),e)}reconcile(e,t){const s=new Set;for(const n of t.sessions)s.add(n.id),this.reconcileSession(e,n);for(const t of[...this.bySession.values()])t.instanceId!==e||s.has(t.sessionId)||this.deleteRecord(t)}decorate(e,t){return{...t,sessions:t.sessions.map(t=>({...t,unread:this.bySession.get(Ac(e,t.id))?.unread||!1}))}}markRead(e,t,s){const n=this.bySession.get(Ac(e,t));return n&&n.sessionUpdatedAt===s&&n.unread?Sc(this.store({...n,unread:!1,updatedAt:(new Date).toISOString()},!1)):n?Sc(n):void 0}removeInstance(e){for(const t of[...this.bySession.values()])t.instanceId===e&&this.deleteRecord(t)}reconcileSession(e,t){const s=Ac(e,t.id),n=this.bySession.get(s),i=(new Date).toISOString();if(!n)return void this.store({id:kc(e,t.id),instanceId:e,sessionId:t.id,unread:!1,lastStatus:t.status,sessionUpdatedAt:t.updatedAt,createdAt:i,updatedAt:i},!1);const a=function(e,t,s){return"running"!==s&&"waiting"!==s&&("running"===t||"waiting"===t||e)}(n.unread,n.lastStatus,t.status),r=a!==n.unread;(r||n.lastStatus!==t.status||n.sessionUpdatedAt!==t.updatedAt)&&this.store({...n,unread:a,lastStatus:t.status,sessionUpdatedAt:t.updatedAt,updatedAt:i},r)}store(e,t){const s=this.records.put(e);return this.bySession.set(Ac(s.instanceId,s.sessionId),s),t&&this.listener?.(Sc(s)),s}deleteRecord(e){this.records.delete(e.id),this.bySession.delete(Ac(e.instanceId,e.sessionId))}}function Sc(e){return _.parse({instanceId:e.instanceId,sessionId:e.sessionId,unread:e.unread,sessionUpdatedAt:e.sessionUpdatedAt,updatedAt:e.updatedAt})}function Ac(e,t){return`${e}\0${t}`}function kc(e,t){return`ais_unread_${o.createHash("sha256").update(Ac(e,t)).digest("hex").slice(0,32)}`}class zc{snapshots=new Map;history=new Map;advertisedStreams=new Map;recoveries=new Map;bootstrap;logger;recoverDelta;recoverSnapshot;onRecoveredEvent;recoveryCount=0;counters={streamResets:0,gaps:0,deltaRecoveries:0,snapshotRecoveries:0,recoveryFailures:0};constructor(e){this.bootstrap=e.bootstrap,this.logger=e.logger,this.recoverDelta=e.recoverDelta,this.recoverSnapshot=e.recoverSnapshot,this.onRecoveredEvent=e.onRecoveredEvent}handleEvent(e){const t=e.type===Ln?Qn:e.type===Un?Yn:e.type===Fn?Xn:void 0;if(!t)return!1;const s=t.safeParse(e.payload);return s.success?(e.type===Ln&&this.applySnapshot(s.data),e.type===Un&&this.applyPatch(s.data),e.type===Fn&&this.applyRemoved(s.data),!0):(this.logger?.warn?.({eventType:e.type,issues:s.error.issues,errorCode:"APP_SESSION_EVENT_INVALID"},"app-session.aggregator.event.invalid"),!0)}applySnapshot(e){const t=this.advertisedStreams.get(e.meta.instanceId)?.streamId;return t&&t!==e.meta.streamId?(this.logger?.warn?.({instanceId:e.meta.instanceId,advertisedStreamId:t,streamId:e.meta.streamId,revision:e.meta.revision},"app-session.aggregator.snapshot.obsolete-stream"),!1):this.apply({type:Ln,payload:e})}advertiseStream(e,t){return this.advertisedStreams.set(e,t),this.recoverStream(e,t.streamId,t.latestRevision).catch(s=>{this.counters.recoveryFailures+=1,this.logger?.warn?.({instanceId:e,streamId:t.streamId,error:s instanceof Error?s.message:String(s)},"app-session.aggregator.recovery.failed")})}diagnostics(){return{...this.counters,recoveriesStarted:this.recoveryCount,activeRecoveries:this.recoveries.size}}removeInstance(e){this.snapshots.delete(e),this.history.delete(e),this.advertisedStreams.delete(e)}applyPatch(e){return this.apply({type:Un,payload:e})}applyRemoved(e){return this.apply({type:Fn,payload:e})}async list(e={}){return(e.refresh||0===this.snapshots.size)&&await this.bootstrapFromInstances(),this.view({includeTombstones:e.includeTombstones})}async streamDescriptors(){return 0===this.snapshots.size&&await this.bootstrapFromInstances(),[...this.snapshots.entries()].map(([e,t])=>({topic:"app.sessions",instanceId:e,streamId:t.streamId,latestRevision:t.revision,earliestRetainedRevision:this.history.get(e)?.[0]?.payload.meta.revision??t.revision+1}))}async delta(e){0===this.snapshots.size&&await this.bootstrapFromInstances();const t=this.resolveDeltaInstanceId(e.instanceId);this.pruneHistory(t);const s=this.snapshots.get(t)?.revision??0,n=this.snapshots.get(t)?.streamId||"unavailable",i=this.history.get(t)?.[0]?.payload.meta.revision??s+1;if(e.streamId!==n||e.sinceRevision>s)return Zn.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:!0,events:[]});if(e.sinceRevision===s)return Zn.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:!1,events:[]});const a=(this.history.get(t)??[]).filter(t=>t.payload.meta.revision>e.sinceRevision),r=!a.length||a[0].payload.meta.revision!==e.sinceRevision+1;return Zn.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:r,events:r?[]:a.map(({type:e,payload:t})=>({type:e,payload:t}))})}apply(e,t=!1){const{meta:s}=e.payload,n=this.advertisedStreams.get(s.instanceId)?.streamId;if(n&&n!==s.streamId)return this.logger?.warn?.({instanceId:s.instanceId,advertisedStreamId:n,streamId:s.streamId,revision:s.revision},"app-session.aggregator.event.obsolete-stream"),!1;const i=this.recoveries.get(s.instanceId);if(i&&!t)return i.streamId===s.streamId&&(i.highWater=Math.max(i.highWater,s.revision)),!1;const a=this.snapshots.get(s.instanceId);e.type===Ln&&a&&a.streamId!==s.streamId&&(this.counters.streamResets+=1,this.logger?.info?.({instanceId:s.instanceId,previousStreamId:a.streamId,streamId:s.streamId,previousRevision:a.revision,revision:s.revision,traceId:s.traceId,resetCount:this.counters.streamResets},"app-session.aggregator.stream-reset"));const r=function(e,t){const{meta:s}=t.payload;if(t.type===Ln)return e?.streamId===s.streamId&&s.revision===e.revision?{kind:"duplicate",projection:e}:e?.streamId===s.streamId&&s.revision<e.revision?{kind:"stale",projection:e}:{kind:"applied",projection:Vn.parse({streamId:s.streamId,revision:s.revision,lastEventAt:s.generatedAt,snapshot:t.payload.snapshot})};if(!e||e.streamId!==s.streamId)return{kind:"snapshot-required",projection:e,streamId:s.streamId};if(s.revision===e.revision)return{kind:"duplicate",projection:e};if(s.revision<e.revision)return{kind:"stale",projection:e};if(s.revision!==e.revision+1||s.previousRevision!==e.revision)return{kind:"gap",projection:e,expectedRevision:e.revision+1,receivedRevision:s.revision};const n=[...e.snapshot.sessions];if(t.type===Un){const e=n.findIndex(e=>e.id===t.payload.session.id);e<0?n.push(t.payload.session):n[e]=t.payload.session}else{const e=n.findIndex(e=>e.id===t.payload.sessionId);e>=0&&n.splice(e,1),t.payload.tombstone&&n.push(t.payload.tombstone)}const i=ei(n,s.generatedAt);return{kind:"applied",projection:Vn.parse({streamId:s.streamId,revision:s.revision,lastEventAt:s.generatedAt,snapshot:i})}}(a,e);if("applied"!==r.kind)return this.logger?.warn?.({traceId:s.traceId,streamId:s.streamId,instanceId:s.instanceId,currentRevision:a?.revision,revision:s.revision,outcome:r.kind},`app-session.aggregator.event.${r.kind}`),"gap"!==r.kind&&"snapshot-required"!==r.kind||("gap"===r.kind&&(this.counters.gaps+=1),this.recoverStream(s.instanceId,s.streamId,s.revision).catch(e=>{this.logger?.warn?.({instanceId:s.instanceId,streamId:s.streamId,revision:s.revision,error:e instanceof Error?e.message:String(e)},"app-session.aggregator.recovery.failed")})),!1;const o=r.projection;return this.snapshots.set(s.instanceId,o),this.rememberEvent(s.instanceId,e),this.logger?.info?.({traceId:s.traceId,streamId:s.streamId,instanceId:s.instanceId,revision:s.revision,reason:s.reason,sessionCount:o.snapshot.sessions.length,runningCount:o.snapshot.runningCount,problemCount:o.snapshot.problemCount},"app-session.aggregator.event.accepted"),!0}async bootstrapFromInstances(){this.logger?.info?.({},"app-session.aggregator.bootstrap");const e=new Map(this.snapshots),t=await this.bootstrap(),s=new Set(t.instances.map(e=>e.instanceId));for(const[t,n]of e)s.has(t)||this.snapshots.get(t)!==n||(this.snapshots.delete(t),this.history.delete(t),this.advertisedStreams.delete(t));for(const s of t.instances){const t=this.snapshots.get(s.instanceId);if(t!==e.get(s.instanceId))continue;const n=this.advertisedStreams.get(s.instanceId)?.streamId;n&&n!==s.streamId||t?.streamId===s.streamId&&t.revision>=s.revision||(t?.streamId!==s.streamId&&this.history.delete(s.instanceId),this.snapshots.set(s.instanceId,{streamId:s.streamId,snapshot:s.appSessions,revision:s.revision,lastEventAt:s.lastEventAt}))}}recoverStream(e,t,s){const n=this.recoveries.get(e);if(n)return n.streamId===t?n.highWater=Math.max(n.highWater,s):(n.streamId=t,n.highWater=s),n.promise;if(!this.recoverDelta||!this.recoverSnapshot)return Promise.resolve();const i={promise:Promise.resolve(),streamId:t,highWater:s},a=++this.recoveryCount;return this.logger?.info?.({instanceId:e,highWater:s,recoveryNumber:a},"app-session.aggregator.recovery.started"),i.promise=(async()=>{for(;;){const t=this.advertisedStreams.get(e);if(!t)return;i.streamId!==t.streamId&&(i.streamId=t.streamId,i.highWater=t.latestRevision);const s=this.snapshots.get(e);if(s&&s.streamId===t.streamId){if(s.revision<Math.max(t.latestRevision,i.highWater)){const t=await this.recoverDelta(e,s.streamId,s.revision);if(this.advertisedStreams.get(e)?.streamId!==t.streamId||i.streamId!==t.streamId)continue;if(t.syncRequired){const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const s={type:Ln,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`aps_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(s,!0)&&this.onRecoveredEvent?.(s),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:a,outcome:"snapshot-fallback"},"app-session.aggregator.recovery.snapshot")}else{for(const e of t.events)this.apply(e,!0)&&this.onRecoveredEvent?.(e);this.counters.deltaRecoveries+=1,i.highWater=Math.max(i.highWater,t.latestRevision),this.logger?.info?.({instanceId:e,streamId:t.streamId,sinceRevision:t.sinceRevision,latestRevision:t.latestRevision,eventCount:t.events.length,recoveryNumber:a,outcome:"delta"},"app-session.aggregator.recovery.delta")}}}else{const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const s={type:Ln,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`aps_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(s,!0)&&this.onRecoveredEvent?.(s),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:a,outcome:"snapshot"},"app-session.aggregator.recovery.snapshot")}const n=this.snapshots.get(e),r=this.advertisedStreams.get(e);if(n&&r&&n.streamId===r.streamId&&n.revision>=Math.max(r.latestRevision,i.highWater))return void this.logger?.info?.({instanceId:e,streamId:n.streamId,revision:n.revision,recoveryNumber:a,outcome:"success"},"app-session.aggregator.recovery.completed")}})().finally(()=>this.recoveries.delete(e)),this.recoveries.set(e,i),i.promise}rememberEvent(e,t){const s=this.history.get(e)??[],n=s.length&&s[0].payload.meta.streamId!==t.payload.meta.streamId?[]:s;n.push({...t,createdAtMs:Date.now()}),this.history.set(e,n),this.pruneHistory(e)}pruneHistory(e){const t=this.history.get(e);if(!t?.length)return;const s=t.filter(e=>e.createdAtMs>=Date.now()-36e5);s.length?this.history.set(e,s):this.history.delete(e)}resolveDeltaInstanceId(e){if(e)return e;const t=[...this.snapshots.keys()];if(1===t.length)return t[0];throw new Error("APP_SESSION_DELTA_INSTANCE_ID_REQUIRED")}visibleSnapshot(e){return ei(e.sessions.filter(e=>ha("string"==typeof e.status?e.status:void 0)),e.updatedAt)}view(e={}){return{updatedAt:(new Date).toISOString(),instances:[...this.snapshots.entries()].map(([t,s])=>({instanceId:t,streamId:s.streamId,appSessions:e.includeTombstones?s.snapshot:this.visibleSnapshot(s.snapshot),revision:s.revision,lastEventAt:s.lastEventAt}))}}}function Tc(e){return e.raw[0].replace(/\\\$\{/g,"${")}class Cc{states=new Map;events;constructor(e){this.events=e}handle(e){if(e.type===Vs){const t=Ks.safeParse(e.payload);return void(t.success&&this.reconcileLifecycle(t.data))}if("instance.deleted"===e.type){const t=e.payload&&"object"==typeof e.payload&&"instanceId"in e.payload?String(e.payload.instanceId||""):"";return void(t&&this.deleteState(t))}if(e.type===Ss){const t=zs.safeParse(e.payload);return void(t.success&&this.applyOutput(t.data))}if(e.type===As){const t=Ts.safeParse(e.payload);t.success&&this.applyFinished(t.data)}}snapshots(){return[...this.states.values()].filter(e=>e.progress).map(e=>Cs.parse({...e.progress,terminalTail:e.terminalTail,...e.terminalTruncated?{terminalTruncated:!0}:{}}))}close(){for(const e of this.states.values())e.timer&&clearTimeout(e.timer);this.states.clear()}deleteState(e){const t=this.states.get(e);t?.timer&&clearTimeout(t.timer),this.states.delete(e)}reconcileLifecycle(e){const t=this.states.get(e.instanceId),s=e.imageProvisioning;t&&(!s||s.generation>t.identity.generation||s.generation===t.identity.generation&&"ready"===s.phase)&&this.deleteState(e.instanceId)}applyOutput(e){const t=this.states.get(e.instanceId);if(t&&e.generation<t.identity.generation)return;if(t&&e.generation===t.identity.generation&&!e.replay&&e.sequence<=t.sequence)return;const s=this.requireState(e);e.replay&&(s.terminal=new xc,s.terminalTail="",s.layers.clear(),s.terminalTruncated=!1),s.sequence=Math.max(s.sequence,e.sequence),s.observedAt=e.observedAt,s.terminal.write(e.data);const n=`${s.terminalTail}${e.data}`;s.terminalTruncated||=n.length>262144,s.terminalTail=n.slice(-262144),function(e,t){for(const s of t.split("\n")){const t=s.trim().match(/^([a-f0-9]{6,64}):\s+(.+)$/i);if(!t)continue;const[,n,i]=t,a=i.toLowerCase(),r=["pull complete","already exists","download complete","pulling fs layer","waiting","downloading","extracting"].find(e=>a.startsWith(e));if(!r)continue;const o=e.get(n)||{status:r},d=Nc(i);e.set(n,{...o,status:r,...d||{}})}}(s.layers,s.terminal.text()),s.progress=Ec(s),this.schedule(s)}applyFinished(e){const t=this.states.get(e.instanceId);if(t&&e.generation<t.identity.generation)return;if(t&&e.generation===t.identity.generation&&e.sequence<=t.sequence)return;const s=this.requireState(e);s.sequence=Math.max(s.sequence,e.sequence),s.observedAt=e.observedAt,s.outcome=e.outcome,s.progress=Ec(s),this.publish(s,!0)}requireState(e){const t=this.states.get(e.instanceId);if(t&&t.identity.generation===e.generation)return t;t?.timer&&clearTimeout(t.timer);const s={identity:{instanceId:e.instanceId,generation:e.generation,requestedReference:e.requestedReference},sequence:e.sequence,observedAt:e.observedAt,terminal:new xc,terminalTail:"",terminalTruncated:!1,layers:new Map};return this.states.set(e.instanceId,s),s}schedule(e){e.timer||(e.timer=setTimeout(()=>this.publish(e,!1),250),e.timer.unref?.())}publish(e,t){e.timer&&clearTimeout(e.timer),e.timer=void 0,e.progress&&(e.progress=Ec(e),this.events.publish("image.pull.progress",e.progress,{topic:"instances",scope:{instanceId:e.identity.instanceId}}),t&&(e.progress=Ec(e)))}}function Ec(e){const t=[...e.layers.values()],s=t.filter(e=>["pull complete","already exists"].includes(e.status)).length,n=t.filter(e=>"download complete"===e.status).length,i=t.filter(e=>"downloading"===e.status).length,a=t.filter(e=>"extracting"===e.status).length;let r=0,o=0;for(const e of t)e.total&&(o+=e.total,r+=["pull complete","already exists","download complete"].includes(e.status)?e.total:Math.min(e.current||0,e.total));const d="failed"===e.outcome?"failed":"succeeded"===e.outcome?"complete":a>0?"extracting":t.length>0?"pulling":"connecting",c=t.filter(e=>"already exists"!==e.status),l=c.length>0&&c.every(e=>Boolean(e.total)),p=l&&o>0?Math.max(0,Math.min(100,r/o*100)):void 0,u="failed"===d?"Image pull failed":"complete"===d?"Image pull complete":t.length?`${s}/${t.length} layers ready${void 0===p?"":` · ${Math.round(p)}%`}`:"Connecting to image registry";return Cs.parse({...e.identity,sequence:e.sequence,observedAt:e.observedAt,status:d,layers:{total:t.length,completed:s,downloaded:n,downloading:i,extracting:a},...l&&o>0?{bytes:{current:r,total:o},percent:p}:{},message:u})}function Nc(e){const t=e.match(/([\d.]+)\s*([kmgt]?i?b)\s*\/\s*([\d.]+)\s*([kmgt]?i?b)/i);if(t)return{current:_c(t[1],t[2]),total:_c(t[3],t[4])}}function _c(e,t){return Number(e)*1024**({b:0,kb:1,kib:1,mb:2,mib:2,gb:3,gib:3,tb:4,tib:4}[t.toLowerCase()]||0)}class xc{lines=[[]];row=0;col=0;pending="";write(e){const t=`${this.pending}${e}`;this.pending="";for(let e=0;e<t.length;){const s=t[e];if(""===s&&"["===t[e+1]){let s=e+2;for(;s<t.length&&!/[\x40-\x7e]/.test(t[s]);)s+=1;if(s>=t.length){this.pending=t.slice(e);break}this.csi(t.slice(e+2,s),t[s]),e=s+1;continue}"\r"===s?this.col=0:"\n"===s?(this.row+=1,this.col=0,this.ensureLine()):"\b"===s?this.col=Math.max(0,this.col-1):s>=" "&&(this.ensureLine(),this.lines[this.row][this.col]=s,this.col+=1),e+=1}this.trim()}text(){return this.lines.map(e=>e.join("").trimEnd()).join("\n")}csi(e,t){const s=e.replace(/^\?/,"").split(";").map(e=>Number(e||1)),n=s[0]||1;"A"===t?this.row=Math.max(0,this.row-n):"B"===t?(this.row+=n,this.ensureLine()):"C"===t?this.col+=n:"D"===t?this.col=Math.max(0,this.col-n):"G"===t?this.col=Math.max(0,n-1):"H"===t||"f"===t?(this.row=Math.max(0,n-1),this.col=Math.max(0,(s[1]||1)-1),this.ensureLine()):"K"===t?(this.ensureLine(),2===(s[0]||0)?this.lines[this.row]=[]:this.lines[this.row].splice(this.col)):"J"===t&&2===(s[0]||0)&&(this.lines=[[]],this.row=0,this.col=0)}ensureLine(){for(;this.lines.length<=this.row;)this.lines.push([])}trim(){if(this.lines.length<=500)return;const e=this.lines.length-500;this.lines.splice(0,e),this.row=Math.max(0,this.row-e)}}const Oc=r.z.object({code:r.z.string().trim().min(1),message:r.z.string(),details:r.z.record(r.z.string(),r.z.unknown()).optional(),retryable:r.z.boolean().optional()}).strict(),Rc=r.z.object({error:Oc}).strict();function jc(e){const t=process.env[e]?.trim();return t||void 0}const Dc=function(e,t=process.env,s=e){let n;return()=>{const i=t.TASK_HANDOFF_VERSION?.trim();return i||(n||=v(e,t,s),n)}}("@task-handoff/control-plane");function Mc(){return{component:"control-plane",packageName:"@task-handoff/control-plane",packageVersion:Dc(),protocolVersion:He,buildId:jc("TASK_HANDOFF_BUILD_ID"),builtAt:jc("TASK_HANDOFF_BUILT_AT"),gitCommit:jc("TASK_HANDOFF_GIT_COMMIT"),imageRef:jc("TASK_HANDOFF_IMAGE_REF"),imageDigest:jc("TASK_HANDOFF_IMAGE_DIGEST")}}const Pc={".css":"text/css; charset=utf-8",".html":"text/html; charset=utf-8",".ico":"image/x-icon",".js":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".map":"application/json; charset=utf-8",".png":"image/png",".svg":"image/svg+xml",".webp":"image/webp"};function $c(e,n){const i=t.resolve(e),a=t.resolve(i,n);if(a===i||a.startsWith(`${i}${t.sep}`))return s.existsSync(a)&&s.statSync(a).isFile()?a:void 0}function qc(e,n,i){const a=$c(n,i);return a?(e.header("cache-control","no-store"),e.type(Pc[t.extname(a).toLowerCase()]||"application/octet-stream"),e.send(s.createReadStream(a))):e.code(404).send({error:{code:"STATIC_FILE_NOT_FOUND",message:`${i} was not found in the Control Plane UI static directory.`}})}function Lc(e){if(e instanceof r.z.ZodError)return{statusCode:400,code:"VALIDATION_ERROR",message:e.issues.map(e=>`${e.path.join(".")||"body"}: ${e.message}`).join("; ")};const t=e&&"object"==typeof e?e:{};return{statusCode:"number"==typeof t.statusCode?t.statusCode:500,code:"string"==typeof t.code?t.code:"CONTROL_PLANE_ERROR",message:e instanceof Error?e.message:String(e),...t.details&&"object"==typeof t.details&&!Array.isArray(t.details)?{details:t.details}:{},..."boolean"==typeof t.retryable?{retryable:t.retryable}:{}}}async function Uc(e,t){if(!e.enabled())return{type:"system",reason:"auth-disabled"};const s=await e.userForSessionToken(t);return s?{type:"user",userId:s.id,role:s.role}:void 0}function Fc(e,t){const s=t.split("?")[0]||"/";if(!s.startsWith("/api/"))return;if(s.startsWith("/api/auth/")||"/api/health"===s||"/api/events"===s)return;const n=function(e){return"GET"===e||"HEAD"===e?"read":"POST"===e?"create":"PATCH"===e||"PUT"===e?"update":"DELETE"===e?"delete":"read"}(e);return s.startsWith("/api/control-plane/settings")?{action:"GET"===e?"read":"manage-settings",resource:{type:"control-plane-settings"}}:s.startsWith("/api/models")?{action:"GET"===e?"read":"manage-secrets",resource:{type:"model"}}:s.startsWith("/api/chat-gateway/bridges")?{action:"GET"===e?"read":"manage-secrets",resource:{type:"chat-bridge"}}:"/api/node-join/invites"===s?{action:"manage-node-auth",resource:{type:"node"}}:s.startsWith("/api/nodes")?/\/(runtimes|local-folders)(\/|$)/.test(s)?{action:n,resource:{type:"runtime"}}:{action:"GET"===e?"read":"manage-node-auth",resource:{type:"node"}}:s.startsWith("/api/controlled-instances")?s.includes("/ai-sessions/")?s.endsWith("/approval")?{action:"approve",resource:{type:"ai-session"}}:s.endsWith("/interrupt")?{action:"interrupt",resource:{type:"ai-session"}}:s.includes("/messages")||s.includes("/queue/")?{action:"send-message",resource:{type:"ai-session"}}:{action:"read",resource:{type:"ai-session"}}:s.includes("/start")?{action:"start",resource:{type:"instance"}}:s.includes("/stop")?{action:"stop",resource:{type:"instance"}}:s.includes("/restart")?{action:"restart",resource:{type:"instance"}}:/\/config-sync(?:\/|$)/.test(s)?{action:"update",resource:{type:"instance"}}:{action:n,resource:{type:"instance"}}:s.startsWith("/api/projects")?{action:n,resource:{type:"project"}}:s.startsWith("/api/images")||s.startsWith("/api/image-options")||s.startsWith("/api/market")?{action:n,resource:{type:"runtime"}}:s.startsWith("/api/triggers")?{action:n,resource:{type:"trigger"}}:{action:"read",resource:{type:"control-plane-settings"}}}async function Bc(e={}){const r=bd(e.dataDir),o=n({logger:e.logger??!0}),d=new fr(r,{...e.service,logger:o.log}),c=new xn(r,e.auth),l=new td,p=new Cc(l),u=new wc(r,{onChanged:e=>queueMicrotask(()=>l.publish(C,e,{topic:"ai.sessions",scope:{instanceId:e.instanceId,sessionId:e.sessionId}}))});u.init();const m=new bc({bootstrap:()=>d.bootstrapAiSessionsFromInstances(),logger:o.log,recoverDelta:(e,t,s)=>d.recoverAiSessionDelta(e,t,s),recoverSnapshot:e=>d.recoverAiSessionSnapshot(e),onRecoveredEvent:e=>l.publish(e.type,e.payload)});m.onSnapshot(e=>u.reconcile(e.instanceId,e.aiSessions));const g=new zc({bootstrap:()=>d.bootstrapAppSessionsFromInstances(),logger:o.log,recoverDelta:(e,t,s)=>d.recoverAppSessionDelta(e,t,s),recoverSnapshot:e=>d.recoverAppSessionSnapshot(e),onRecoveredEvent:e=>l.publish(e.type,e.payload)});d.setAppSessionSnapshotProvider(e=>g.list(e)),d.setAiSessionSnapshotProvider(e=>m.list(e)),l.on(e=>{if(p.handle(e),"instance.deleted"===e.type){const t=e.payload&&"object"==typeof e.payload&&"instanceId"in e.payload?String(e.payload.instanceId||""):"";t&&(g.removeInstance(t),m.removeInstance(t),u.removeInstance(t))}g.handleEvent(e),m.handleEvent(e)});const h=new ad,f=new Id(l,{validateInstanceScope:(e,t)=>d.nodeOwnsInstance(e,t),onStreamsHello:(e,t)=>{for(const s of t.streams)"ai.sessions"===s.topic&&m.advertiseStream(e,s),"app.sessions"===s.topic&&g.advertiseStream(e,s)}});d.setNodeAgentTransport(f),d.init(),await d.syncLocalNodeConnection().catch(()=>{}),c.init();const I=new So(d,e.service?.fetchImpl,{aiSessions:m,logger:o.log});I.startEnabled();const y=new yd(d,f,{safetyIntervalMs:Number(process.env.TASK_HANDOFF_EVENT_CONNECTION_SAFETY_INTERVAL_MS)||void 0,logger:o.log});y.start(),await o.register(i),await o.register(a),o.addHook("onClose",async()=>{p.close(),y.stop(),I.stopAll()});const b=(v=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(v||"").trim().toLowerCase()));var v;o.setErrorHandler((e,t,s)=>{const n=Lc(e);(b||n.statusCode>=500)&&(n.statusCode>=500?o.log.error.bind(o.log):o.log.warn.bind(o.log))({method:t.method,url:t.url,statusCode:n.statusCode,errorCode:n.code,error:n.message},"control plane request failed"),s.code(n.statusCode).send(Rc.parse({error:{code:n.code,message:n.message,...n.details?{details:n.details}:{},...void 0!==n.retryable?{retryable:n.retryable}:{}}}))}),o.addHook("preHandler",async(e,t)=>{if(!c.enabled()||function(e,t){const s=t.split("?")[0];return!("/api/health"!==s&&!s.startsWith("/api/auth/"))||"GET"===e&&"/install-node-agent.sh"===s||"POST"===e&&"/api/node-join/complete"===s||"/api/app-access/session"===s||!!s.startsWith("/apps/access/")||!("/favicon.ico"!==s&&!s.startsWith("/assets/"))||"GET"===e&&!s.startsWith("/api/")&&!s.startsWith("/instances/")}(e.method,e.url)){const t=await Uc(c,e.cookies[yn]),s=Fc(e.method,e.url);return void(s&&$d(t||{type:"system",reason:"auth-disabled"},s.action,s.resource))}const s=await Uc(c,e.cookies[yn]);if(!s)return t.code(401).send({error:{code:"CONTROL_PLANE_AUTH_REQUIRED",message:"Sign in to access the Control Plane."}});const n=Fc(e.method,e.url);n&&$d(s,n.action,n.resource)});const w=t.resolve(e.staticDir||process.env.TASK_HANDOFF_CONTROL_PLANE_STATIC_DIR||t.resolve(process.cwd(),"packages","control-plane-ui","dist"));return s.existsSync(w)&&(o.get("/assets/*",async(e,s)=>{const n=e.params["*"];return qc(s,w,t.join("assets",n||""))}),o.get("/favicon.ico",async(e,t)=>$c(w,"favicon.ico")?qc(t,w,"favicon.ico"):t.code(204).send())),o.get("/api/health",async()=>({data:{ok:!0,role:"control-plane",protocolVersion:He,build:Mc(),dataDir:r.dataDir,serverTime:(new Date).toISOString()}})),o.get("/api/session-streams/diagnostics",async()=>({data:{aiSessions:m.diagnostics(),appSessions:g.diagnostics(),nodeConnections:y.diagnostics()}})),o.get("/install-node-agent.sh",async(e,t)=>(t.header("cache-control","no-store"),t.type("text/x-shellscript; charset=utf-8"),Tc`#!/bin/sh
1
+ "use strict";var e=require("commander"),t=require("node:path"),s=require("node:fs"),n=require("fastify"),i=require("@fastify/cookie"),a=require("@fastify/websocket"),r=require("zod"),o=require("node:crypto"),d=require("node:util"),c=require("write-file-atomic"),l=require("ws"),p=require("node:http"),u=require("node:net"),m=require("node:stream"),g=require("telegramify-markdown"),h=require("node:fs/promises"),f=require("node:os"),I=require("dingtalk-stream"),y=require("node:child_process");function b(e,t){try{const n=JSON.parse(s.readFileSync(e,"utf8"));if(t&&n.name!==t)return;return"string"==typeof n.version&&n.version.trim()?n.version.trim():void 0}catch{return}}function v(e,s=process.env,n=e){const i=s.TASK_HANDOFF_VERSION?.trim();if(i)return i;const a=n.split("/").at(-1),r=[process.argv[1]?t.dirname(t.resolve(process.argv[1])):void 0,process.cwd()].filter(e=>Boolean(e));for(const s of new Set(r)){let n=s;for(;;){const s=b(t.join(n,"package.json"),e);if(s)return s;const i=t.dirname(n);if(i===n)break;n=i}}let o;for(const e of new Set(r)){let s=e;for(;;){if(a){const e=b(t.join(s,"packages",a,"package.json"),n);if(e)return e}o||=b(t.join(s,"package.json"));const e=t.dirname(s);if(e===s)break;s=e}}return o||"unknown"}const w=20971520,S=512e3,A=41943040,k="ai-session.snapshot",z="ai-session.patch",T="ai-session.removed",C="ai-session.unread.updated",E=r.z.string().trim().min(1).max(80),N=r.z.enum(["running","waiting","idle","failed"]),_=r.z.object({instanceId:r.z.string().trim().min(1).max(160),sessionId:r.z.string().trim().min(1).max(120),unread:r.z.boolean(),sessionUpdatedAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}).strict(),x=r.z.enum(["thinking","tool","editing","approval","responding","unknown"]),O=r.z.object({id:r.z.string().trim().min(1).max(240).optional(),kind:r.z.string().trim().min(1).max(80).optional(),name:r.z.string().trim().min(1).max(120),inputPreview:r.z.string().trim().max(500).optional(),startedAt:r.z.string().datetime().optional()}).strict(),R=r.z.enum(["pending-init","running","interrupted","completed","errored","shutdown","not-found"]),j=r.z.enum(["started","interacted","interrupted"]),D=r.z.object({threadId:r.z.string().trim().min(1).max(240),path:r.z.string().trim().max(4096).optional(),status:R,activity:j.optional(),message:r.z.string().trim().max(1e3).optional(),updatedAt:r.z.string().datetime()}).strict(),M=r.z.object({toolCalls:r.z.number().int().min(0).default(0),edits:r.z.number().int().min(0).default(0),approvals:r.z.number().int().min(0).default(0)}).strict().default({toolCalls:0,edits:0,approvals:0}),P=r.z.object({send:r.z.boolean().optional(),interrupt:r.z.boolean().optional(),approval:r.z.boolean().optional()}).strict(),$=r.z.enum(["image","file"]),q=r.z.object({id:r.z.string().trim().min(1).max(120),kind:$,name:r.z.string().trim().min(1).max(240),mime:r.z.string().trim().min(1).max(120),size:r.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER)}),L=r.z.union([q.extend({source:r.z.object({type:r.z.literal("inline"),encoding:r.z.literal("base64"),data:r.z.string().min(1).max(31457280)}).strict()}).strict().superRefine((e,t)=>{const s="image"===e.kind?w:S;(e.size<=0||("file"===e.kind?e.size>=s:e.size>s))&&t.addIssue({code:r.z.ZodIssueCode.custom,path:["size"],message:`Inline ${e.kind} must be between 1 byte and ${s} bytes.`})}),q.extend({source:r.z.object({type:r.z.literal("runtime-path"),path:r.z.string().trim().min(1).max(4096)}).strict()}).strict()]),U=q.extend({sourceType:r.z.enum(["inline","runtime-path"])}).strict(),F=r.z.union([r.z.object({id:r.z.string().trim().min(1).max(120),kind:$.optional(),source:r.z.object({type:r.z.literal("upload-ref")}).strict().default({type:"upload-ref"})}).strict(),r.z.object({id:r.z.string().trim().min(1).max(120),kind:$,name:r.z.string().trim().min(1).max(240),mime:r.z.string().trim().min(1).max(120),size:r.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),source:r.z.object({type:r.z.literal("runtime-path"),path:r.z.string().trim().min(1).max(4096)}).strict()}).strict()]),B=r.z.enum(["auto","queue","steer","immediate"]),H=r.z.enum(["ask","auto-review","full-access"]),G=r.z.enum(["review","rename","goal","compact"]),W=r.z.object({command:G,argument:r.z.string().trim().max(4e3).optional()}).strict().superRefine((e,t)=>{"rename"!==e.command||e.argument||t.addIssue({code:"custom",path:["argument"],message:"Rename requires a thread name."}),"review"!==e.command&&"compact"!==e.command||!e.argument||t.addIssue({code:"custom",path:["argument"],message:`${e.command} does not accept an argument.`})}),V=r.z.object({command:G,value:r.z.string().max(4e3).optional(),turnId:r.z.string().trim().min(1).max(240).optional()}).strict();r.z.enum(["skill","app","plugin"]);const K=r.z.object({name:r.z.string().trim().min(1).max(240),path:r.z.string().trim().min(1).max(4096)}),J=r.z.discriminatedUnion("kind",[K.extend({kind:r.z.literal("skill"),path:r.z.string().trim().min(1).max(4096).refine(e=>e.startsWith("/"),"Skill paths must be absolute.")}).strict(),K.extend({kind:r.z.literal("app"),path:r.z.string().trim().min(7).max(4096).regex(/^app:\/\/[^\s/]+$/,"App paths must use app://<id>.")}).strict(),K.extend({kind:r.z.literal("plugin"),path:r.z.string().trim().min(10).max(4096).regex(/^plugin:\/\/[^\s/]+$/,"Plugin paths must use plugin://<id>.")}).strict()]),Q=r.z.array(J).max(20).default([]),Y=r.z.enum(["plugin","skill","file","directory","app"]),X=r.z.object({kind:Y,name:r.z.string().trim().min(1).max(240),description:r.z.string().trim().max(1e3).optional(),path:r.z.string().trim().min(1).max(4096),icon:r.z.string().trim().max(4096).optional()}).strict().superRefine((e,t)=>{"skill"!==e.kind||e.path.startsWith("/")||t.addIssue({code:"custom",path:["path"],message:"Skill paths must be absolute."}),"app"!==e.kind||/^app:\/\/[^\s/]+$/.test(e.path)||t.addIssue({code:"custom",path:["path"],message:"App paths must use app://<id>."}),"plugin"!==e.kind||/^plugin:\/\/[^\s/]+$/.test(e.path)||t.addIssue({code:"custom",path:["path"],message:"Plugin paths must use plugin://<id>."}),"file"!==e.kind&&"directory"!==e.kind||!e.path.startsWith("/")&&!e.path.split(/[\\/]+/).includes("..")||t.addIssue({code:"custom",path:["path"],message:"File and directory paths must be relative to the session cwd."})}),Z=r.z.object({category:r.z.enum(["skills","plugins","apps","files"]),code:r.z.string().trim().min(1).max(160),message:r.z.string().trim().min(1).max(2e3)}).strict(),ee=r.z.object({sessionId:r.z.string().trim().min(1).max(120),providerSessionId:r.z.string().trim().min(1).max(240),cwd:r.z.string().trim().min(1).max(4096),candidates:r.z.array(X).max(1e3).default([]),diagnostics:r.z.array(Z).max(20).default([])}).strict(),te=r.z.object({query:r.z.string().trim().max(240).default("")}).strict(),se=r.z.object({sessionId:r.z.string().trim().min(1).max(120),cwd:r.z.string().trim().min(1).max(4096),query:r.z.string().trim().max(240),requestId:r.z.string().trim().min(1).max(160),candidates:r.z.array(X.refine(e=>"file"===e.kind||"directory"===e.kind,"File search only returns files and directories.")).max(200).default([]),complete:r.z.boolean().default(!1)}).strict(),ne=r.z.object({message:r.z.string().trim().min(1).max(2e4),mode:B.optional(),permissionMode:H.optional()});ne.extend({attachments:r.z.array(L).max(6).default([]),references:Q}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>A&&t.addIssue({code:r.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})});const ie=ne.extend({attachments:r.z.array(F).max(6).default([]),references:Q}).strict(),ae=r.z.object({decision:r.z.enum(["allow","deny","skip"])}).strict();r.z.object({queueIds:r.z.array(r.z.string().trim().min(1).max(120)).max(100)}).strict(),r.z.object({code:r.z.string().trim().min(1).max(160),message:r.z.string(),instanceId:r.z.string().trim().min(1).max(160).optional(),nodeId:r.z.string().trim().min(1).max(160).optional(),route:r.z.string().trim().min(1).max(4096).optional()}).strict();const re=r.z.object({id:r.z.string().trim().min(1).max(120),message:r.z.string().trim().min(1).max(2e4),attachments:r.z.array(U).max(6).default([]),references:Q,permissionMode:H.optional(),status:r.z.enum(["queued","sending","failed"]).default("queued"),createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime(),error:r.z.string().trim().max(4e3).optional()}).strict(),oe=r.z.object({pendingCount:r.z.number().int().min(0).default(0),items:r.z.array(re).max(100).default([])}).strict().default({pendingCount:0,items:[]}),de=r.z.enum(["control","realtime","adapter-snapshot","transcript-tail","transcript-scan","process-scan","app-session"]),ce=r.z.object({id:r.z.string().trim().min(1).max(240),status:r.z.enum(["running","completed"]),startedAt:r.z.string().datetime().optional(),completedAt:r.z.string().datetime().optional()}).strict(),le=r.z.object({id:r.z.string().trim().min(1).max(240),providerTurnId:r.z.string().trim().max(240).optional(),source:de.optional(),userPrompt:r.z.string().trim().optional(),status:r.z.enum(["queued","running","waiting","completed","failed"]).default("running"),phase:x.optional(),summary:r.z.string().trim().max(1e3).optional(),lastMessage:r.z.string().trim().optional(),lastMessageItemId:r.z.string().trim().max(240).optional(),contextCompactions:r.z.array(ce).max(20).optional(),revision:r.z.number().int().min(0).default(0),sourcePriority:r.z.number().int().min(0).max(100).optional(),snapshotVersion:r.z.number().int().min(0).optional(),observedAt:r.z.string().datetime().optional(),startedAt:r.z.string().datetime().optional(),updatedAt:r.z.string().datetime().optional(),completedAt:r.z.string().datetime().optional()}).strict(),pe=le.pick({id:!0,providerTurnId:!0,userPrompt:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,contextCompactions:!0,startedAt:!0,updatedAt:!0,completedAt:!0}).strict(),ue=r.z.object({id:r.z.string().trim().min(1).max(120),agent:E,appSessionId:r.z.string().trim().max(120).optional(),appId:r.z.string().trim().max(120).optional(),providerSessionId:r.z.string().trim().max(240).optional(),providerMeta:r.z.record(r.z.string(),r.z.unknown()).optional(),appBindingKeys:r.z.array(r.z.string().trim().min(1).max(240)).max(20).optional(),actions:P.optional(),activeTurnId:r.z.string().trim().max(240).optional(),title:r.z.string().trim().max(240).optional(),cwd:r.z.string().trim().max(4096).optional(),userPrompt:r.z.string().trim().optional(),turns:r.z.array(le).max(50).optional(),status:N.default("running"),phase:x.default("unknown"),summary:r.z.string().trim().max(1e3).optional(),lastMessage:r.z.string().trim().optional(),lastMessageItemId:r.z.string().trim().max(240).optional(),currentTool:O.optional(),toolCallsSinceLastMessage:r.z.number().int().min(0).default(0),subAgents:r.z.array(D).max(50).default([]),transcriptPath:r.z.string().trim().max(4096).optional(),transcriptSize:r.z.number().int().min(0).optional(),startedAt:r.z.string().datetime(),updatedAt:r.z.string().datetime(),completedAt:r.z.string().datetime().optional(),error:r.z.string().trim().max(4e3).optional(),counters:M,queue:oe}).strict(),me=r.z.object({session:ue,provider:r.z.string().trim().min(1).max(80),action:r.z.enum(["send","queue","steer","interrupt","approval"]),decision:r.z.enum(["allow","deny","skip"]).optional(),turnId:r.z.string().trim().min(1).max(240).optional(),providerTurnId:r.z.string().trim().min(1).max(240).optional(),queueId:r.z.string().trim().min(1).max(120).optional()}).strict(),ge=r.z.object({id:r.z.string().trim().min(1).max(120),agent:r.z.enum(["codex","claude"]),providerSessionId:r.z.string().trim().min(1).max(240),title:r.z.string().trim().max(240).optional(),userPrompt:r.z.string().trim().optional(),lastMessage:r.z.string().trim().optional(),cwd:r.z.string().trim().min(1).max(4096),lastActiveAt:r.z.string().datetime(),archivedAt:r.z.string().datetime()}).strict();r.z.object({schemaVersion:r.z.literal(1).default(1),items:r.z.array(ge).max(50).default([])}).strict();const he=r.z.object({items:r.z.array(ge).max(50).default([])}).strict(),fe=r.z.object({item:ge,turns:r.z.array(pe).max(50).default([])}).strict(),Ie=r.z.object({disposition:r.z.enum(["resumed","already-open"]),aiSessionId:r.z.string().trim().min(1).max(120),providerSessionId:r.z.string().trim().min(1).max(240),appSessionId:r.z.string().trim().min(1).max(120)}).strict(),ye=ue.pick({id:!0,agent:!0,appSessionId:!0,appId:!0,providerSessionId:!0,providerMeta:!0,appBindingKeys:!0,actions:!0,activeTurnId:!0,title:!0,cwd:!0,userPrompt:!0,turns:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,lastMessageItemId:!0,currentTool:!0,toolCallsSinceLastMessage:!0,subAgents:!0,queue:!0,startedAt:!0,updatedAt:!0,error:!0}),be=r.z.object({runningCount:r.z.number().int().min(0).default(0),waitingCount:r.z.number().int().min(0).default(0),staleCount:r.z.number().int().min(0).default(0),sessions:r.z.array(ye).default([]),updatedAt:r.z.string().datetime()}).strict(),ve=r.z.object({streamId:r.z.string().trim().min(1).max(240),revision:r.z.number().int().min(0),lastEventAt:r.z.string().datetime(),snapshot:be}).strict(),we=be.pick({runningCount:!0,waitingCount:!0,staleCount:!0,updatedAt:!0}).extend({idleCount:r.z.number().int().min(0).default(0),problemCount:r.z.number().int().min(0).default(0),revision:r.z.number().int().min(0).optional()});function Se(e,t={}){const s=e.sessions.filter(e=>"idle"===e.status).length,n=e.sessions.filter(e=>"failed"===e.status).length;return we.parse({runningCount:e.runningCount,waitingCount:e.waitingCount,staleCount:e.staleCount,idleCount:s,problemCount:n,updatedAt:e.updatedAt,revision:t.revision})}const Ae=r.z.enum(["provider-event","app-session-created","app-session-updated","app-session-deleted","discovery-scan","control-action","heartbeat-sync","startup"]),ke=r.z.object({streamId:r.z.string().trim().min(1).max(240),instanceId:r.z.string().trim().min(1).max(160),nodeId:r.z.string().trim().min(1).max(160).optional(),revision:r.z.number().int().min(0),previousRevision:r.z.number().int().min(0).optional(),traceId:r.z.string().trim().min(1).max(160),generatedAt:r.z.string().datetime(),reason:Ae}).strict(),ze=r.z.object({meta:ke,snapshot:be}).strict(),Te=r.z.object({meta:ke,upserted:r.z.array(ye).default([]),removed:r.z.array(r.z.string().trim().min(1).max(120)).default([])}).strict(),Ce=r.z.object({meta:ke,sessionIds:r.z.array(r.z.string().trim().min(1).max(120)).default([]),expiresAt:r.z.string().datetime()}).strict(),Ee=r.z.object({instanceId:r.z.string().trim().min(1).max(160),nodeId:r.z.string().trim().min(1).max(160).optional(),sessionId:r.z.string().trim().min(1).max(120),providerSessionId:r.z.string().trim().min(1).max(240),turnId:r.z.string().trim().min(1).max(240),itemId:r.z.string().trim().min(1).max(240),delta:r.z.string().min(1),generatedAt:r.z.string().datetime()}).strict(),Ne=r.z.object({streamId:r.z.string().trim().min(1).max(240),instanceId:r.z.string().trim().min(1).max(160),sinceRevision:r.z.number().int().min(0),latestRevision:r.z.number().int().min(0),earliestRetainedRevision:r.z.number().int().min(0),syncRequired:r.z.boolean().default(!1),events:r.z.array(r.z.discriminatedUnion("type",[r.z.object({type:r.z.literal(k),payload:ze}).strict(),r.z.object({type:r.z.literal(z),payload:Te}).strict(),r.z.object({type:r.z.literal(T),payload:Ce}).strict()])).default([])}).strict(),_e=r.z.object({source:de,sourcePriority:r.z.number().int().min(0).max(100).optional(),observedAt:r.z.string().datetime().optional(),snapshotVersion:r.z.number().int().min(0).optional()}).strict(),xe=_e.extend({type:r.z.literal("snapshot"),agent:r.z.string().trim().min(1).max(80),appSessionId:r.z.string().trim().max(120).optional(),appId:r.z.string().trim().max(120).optional(),providerSessionId:r.z.string().trim().max(240).optional(),providerMeta:r.z.record(r.z.string(),r.z.unknown()).optional(),appBindingKeys:r.z.array(r.z.string().trim().min(1).max(240)).max(20).optional(),actions:P.optional(),title:r.z.string().trim().max(240).optional(),cwd:r.z.string().trim().max(4096).optional(),activeTurnId:r.z.string().trim().max(240).optional(),userPrompt:r.z.string().trim().optional(),turns:r.z.array(le).max(50).optional(),status:N.optional(),phase:x.optional(),summary:r.z.string().trim().max(1e3).optional(),lastMessage:r.z.string().trim().optional(),lastMessageItemId:r.z.string().trim().max(240).optional(),currentTool:O.optional(),toolCallsSinceLastMessage:r.z.number().int().min(0).optional(),subAgents:r.z.array(D).max(50).optional(),transcriptPath:r.z.string().trim().max(4096).optional(),transcriptSize:r.z.number().int().min(0).optional(),replaceActivity:r.z.boolean().optional()}).strict(),Oe=_e.extend({type:r.z.literal("event"),sessionId:r.z.string().trim().min(1).max(120),kind:r.z.enum(["lifecycle","send-ack","turn-started","user-message","assistant-message","approval-requested","turn-completed","tool-activity","sub-agent-activity","context-compaction"]),activeTurnId:r.z.string().trim().max(240).optional(),providerTurnId:r.z.string().trim().max(240).optional(),userPrompt:r.z.string().trim().optional(),text:r.z.string().optional(),itemId:r.z.string().trim().max(240).optional(),status:N.optional(),phase:x.optional(),summary:r.z.string().trim().max(1e3).optional(),error:r.z.string().trim().max(4e3).optional(),currentTool:O.nullable().optional(),toolCallsSinceLastMessage:r.z.number().int().min(0).optional(),subAgents:r.z.array(D).max(50).optional(),contextCompaction:ce.optional(),counters:r.z.object({toolCalls:r.z.number().int().min(0).optional(),edits:r.z.number().int().min(0).optional(),approvals:r.z.number().int().min(0).optional()}).strict().optional()}).strict().superRefine((e,t)=>{if("context-compaction"===e.kind?e.contextCompaction||t.addIssue({code:"custom",path:["contextCompaction"],message:"context-compaction events require contextCompaction"}):void 0!==e.contextCompaction&&t.addIssue({code:"custom",path:["contextCompaction"],message:"contextCompaction is only valid for context-compaction events"}),"sub-agent-activity"===e.kind?e.subAgents||t.addIssue({code:"custom",path:["subAgents"],message:"sub-agent-activity events require subAgents"}):void 0!==e.subAgents&&t.addIssue({code:"custom",path:["subAgents"],message:"subAgents is only valid for sub-agent-activity events"}),"tool-activity"!==e.kind)return void 0!==e.currentTool&&t.addIssue({code:"custom",path:["currentTool"],message:"currentTool is only valid for tool-activity events"}),void(void 0!==e.toolCallsSinceLastMessage&&t.addIssue({code:"custom",path:["toolCallsSinceLastMessage"],message:"toolCallsSinceLastMessage is only valid for tool-activity events"}));"currentTool"in e||t.addIssue({code:"custom",path:["currentTool"],message:"tool-activity events require currentTool"}),void 0===e.toolCallsSinceLastMessage&&t.addIssue({code:"custom",path:["toolCallsSinceLastMessage"],message:"tool-activity events require toolCallsSinceLastMessage"})});r.z.discriminatedUnion("type",[xe,Oe]);const Re=r.z.union([r.z.object({type:r.z.literal("schedule"),scheduleKind:r.z.literal("interval").default("interval"),intervalMs:r.z.number().int().min(1e3)}).strict(),r.z.object({type:r.z.literal("schedule"),scheduleKind:r.z.literal("daily"),timeOfDay:r.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:r.z.string().trim().min(1).max(120)}).strict(),r.z.object({type:r.z.literal("schedule"),scheduleKind:r.z.literal("weekly"),weekdays:r.z.array(r.z.number().int().min(0).max(6)).min(1).max(7),timeOfDay:r.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:r.z.string().trim().min(1).max(120)}).strict()]),je=r.z.union([Re,r.z.object({type:r.z.literal("file-change"),roots:r.z.array(r.z.string().trim().min(1).max(4096)).min(1).max(50),globs:r.z.array(r.z.string().trim().min(1).max(500)).min(1).max(100),ignore:r.z.array(r.z.string().trim().min(1).max(500)).max(100).optional(),debounceMs:r.z.number().int().min(100).max(6e4).default(1500)}).strict(),r.z.object({type:r.z.literal("ai-session"),agent:r.z.string().trim().min(1).max(80).optional(),statuses:r.z.array(r.z.enum(["running","waiting","idle","failed"])).max(20).optional(),phases:r.z.array(r.z.enum(["thinking","tool","editing","approval","responding","unknown"])).max(20).optional()}).strict()]),De=r.z.object({promptTemplate:r.z.string().trim().min(1).max(2e4)}).strict(),Me=r.z.object({cooldownMs:r.z.number().int().min(0).max(864e5).optional(),maxConcurrentRuns:r.z.number().int().min(1).max(20).default(1),whenBusy:r.z.enum(["skip","queue"]).default("skip")}).strict(),Pe=r.z.object({configHash:r.z.string().trim().min(8).max(80),name:r.z.string().trim().min(1).max(160),description:r.z.string().trim().max(1e3).optional(),source:je,action:De,policy:Me.default({maxConcurrentRuns:1,whenBusy:"skip"}),createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}).strict(),$e=r.z.object({type:r.z.literal("ai-session"),aiSessionId:r.z.string().trim().min(1).max(160)}).strict(),qe=r.z.object({configHash:r.z.string().trim().min(8).max(80),deploymentId:r.z.string().trim().min(1).max(240).optional(),instanceId:r.z.string().trim().min(1).max(160),origin:r.z.enum(["control-plane","controlled-instance"]).default("controlled-instance"),enabled:r.z.boolean().default(!0),target:$e,localName:r.z.string().trim().min(1).max(160).optional(),createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}).strict(),Le=r.z.object({configHash:r.z.string().trim().min(8).max(80),deploymentId:r.z.string().trim().min(1).max(240).optional(),instanceId:r.z.string().trim().min(1).max(160),status:r.z.enum(["idle","running","disabled","error"]).default("idle"),lastTriggeredAt:r.z.string().datetime().optional(),lastCompletedAt:r.z.string().datetime().optional(),lastSkippedAt:r.z.string().datetime().optional(),lastError:r.z.string().trim().max(4e3).optional(),runCount:r.z.number().int().min(0).default(0),skippedCount:r.z.number().int().min(0).default(0)}).strict(),Ue=r.z.object({id:r.z.string().trim().min(1).max(160),configHash:r.z.string().trim().min(8).max(80),deploymentId:r.z.string().trim().min(1).max(240).optional(),instanceId:r.z.string().trim().min(1).max(160),eventType:r.z.enum(["manual","schedule","file-change","ai-session"]),status:r.z.enum(["started","completed","failed","skipped"]),target:$e,promptPreview:r.z.string().trim().max(500),eventSummary:r.z.string().trim().max(1e3).optional(),error:r.z.string().trim().max(4e3).optional(),startedAt:r.z.string().datetime(),completedAt:r.z.string().datetime().optional()}).strict(),Fe=r.z.object({schemaVersion:r.z.literal(1).default(1),configs:r.z.array(Pe).default([]),deployments:r.z.array(qe).default([]),runtime:r.z.array(Le).default([]),recentRuns:r.z.array(Ue).default([])}).strict();function Be(e){return Array.isArray(e)?e.map(Be):e&&"object"==typeof e?Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>[e,Be(t)])):e}const He="2026-07-30",Ge=r.z.string(),We=r.z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),Ve=r.z.string().datetime(),Ke=r.z.record(r.z.string(),r.z.string()).default({}),Je=r.z.record(r.z.string(),r.z.string()).default({}),Qe=r.z.string().regex(/^\d{4}-\d{2}-\d{2}$/,"Protocol version must use YYYY-MM-DD format."),Ye=/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/,Xe=/^sha256:([a-fA-F0-9]{64})$/,Ze=/^(?:localhost|[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?)(?::[1-9][0-9]{0,4})?$/,et=/^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*$/;function tt(e){const t=e.trim();if(!t||t.length>512||t.includes("://")||/\s/.test(t))throw new Error("Docker image reference must not contain a URL scheme or whitespace.");const s=t.indexOf("@");if(-1!==s&&s!==t.lastIndexOf("@"))throw new Error("Docker image reference contains more than one digest separator.");const n=-1===s?void 0:t.slice(s+1),i=-1===s?t:t.slice(0,s),a=n?Xe.exec(n):void 0;if(n&&!a)throw new Error("Docker image digest must use sha256 followed by 64 hexadecimal characters.");const r=i.lastIndexOf("/"),o=i.lastIndexOf(":"),d=o>r,c=d?i.slice(o+1):void 0,l=d?i.slice(0,o):i;if(c&&!Ye.test(c))throw new Error("Docker image tag is invalid.");if(!c&&!a)throw new Error("Docker image reference must include an explicit tag or sha256 digest.");const p=l.split("/");if(!p.length||p.some(e=>!e))throw new Error("Docker image repository path is invalid.");const u=p.length>1&&(p[0].includes(".")||p[0].includes(":")||"localhost"===p[0]),m=u?p.slice(1):p;if(u&&!Ze.test(p[0].toLowerCase()))throw new Error("Docker image registry host or port is invalid.");if(!m.length||m.some(e=>!et.test(e)))throw new Error("Docker image repository names must be lowercase and use valid separators.");const g=p.map((e,t)=>u&&0===t?e.toLowerCase():e).join("/"),h=a?`sha256:${a[1].toLowerCase()}`:void 0;return`${g}${c?`:${c}`:""}${h?`@${h}`:""}`}function st(e){const t=tt(e),s=t.indexOf("@"),n=-1===s?void 0:t.slice(s+1),i=-1===s?t:t.slice(0,s),a=i.lastIndexOf("/"),r=i.lastIndexOf(":"),o=r>a;return{reference:t,repository:o?i.slice(0,r):i,tag:o?i.slice(r+1):void 0,digest:n}}const nt=r.z.string().trim().min(1).max(512).transform((e,t)=>{try{return tt(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),r.z.NEVER}}),it=r.z.string().trim().transform((e,t)=>{const s=Xe.exec(e);return s?`sha256:${s[1].toLowerCase()}`:(t.addIssue({code:"custom",message:"Docker image digest must use sha256 followed by 64 hexadecimal characters."}),r.z.NEVER)}),at=r.z.object({codexModelHash:We.nullable().optional(),claudeModelHash:We.nullable().optional()}).strict().default({}),rt=r.z.object({imageId:We,tag:r.z.string().trim().min(1).max(128).regex(Ye).optional()}).strict(),ot={img_default:"market_taskhandoff_browser",img_codex:"market_taskhandoff_codex",img_ai:"market_taskhandoff_ai"};function dt(e,t){if(e&&"object"==typeof e&&!Array.isArray(e)){const t=e;return{imageId:"string"==typeof t.imageId&&ot[t.imageId]||t.imageId,..."string"==typeof t.tag?{tag:t.tag}:{}}}if("string"==typeof t)return{imageId:ot[t]||t}}function ct(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,{defaultImageId:s,...n}=t;return{...n,defaultImageSelection:dt(t.defaultImageSelection,s)}}const lt=r.z.object({id:We,name:r.z.string().trim().min(1).max(120),kind:r.z.enum(["tty","gui","web"]),source:r.z.enum(["builtin","custom"]),availability:r.z.enum(["available","missing-dependency"]),capabilities:r.z.object({automation:r.z.enum(["cdp"]).optional(),supportsCwdSelection:r.z.boolean().default(!1)}).strict().default({supportsCwdSelection:!1}),diagnosticCode:r.z.enum(["APP_EXECUTABLE_NOT_FOUND"]).optional()}).strict(),pt=r.z.object({code:r.z.enum(["APP_CATALOG_INVALID"]),message:r.z.string().trim().min(1).max(500)}).strict(),ut=r.z.object({items:r.z.array(lt).max(256),observedAt:Ve,issues:r.z.array(pt).max(32).default([])}).strict(),mt=r.z.enum(["linux","darwin","win32","freebsd","openbsd","aix","sunos","unknown"]),gt=r.z.enum(["x64","arm64","arm","ia32","ppc64","s390x","riscv64","unknown"]),ht=r.z.enum(["apt","dnf","brew","npm"]),ft=r.z.enum(["user","passwordless-sudo","root"]),It=r.z.object({platform:mt,arch:gt,installers:r.z.array(ht).max(8),privilege:ft,installerAccess:r.z.object({npmGlobalWritable:r.z.boolean().optional()}).strict().optional()}).strict(),yt=r.z.enum(["installed","not-installed","broken","unsupported"]),bt=r.z.enum(["recipe","bundled","external","none"]),vt=r.z.object({code:r.z.enum(["BUNDLED","EXTERNALLY_MANAGED","ALREADY_INSTALLED","NOT_INSTALLED","UNSUPPORTED_PLATFORM","INSTALLER_UNAVAILABLE","INSTALLER_NOT_WRITABLE","INSUFFICIENT_PRIVILEGE","OPERATION_IN_PROGRESS"]),message:r.z.string().trim().min(1).max(500)}).strict(),wt=r.z.object({id:We,name:r.z.string().trim().min(1).max(120),kind:r.z.enum(["tty","gui","web"]),description:r.z.string().trim().max(500).optional(),state:yt,managementSource:bt,version:r.z.string().trim().max(120).optional(),canInstall:r.z.boolean(),canUninstall:r.z.boolean(),installReason:vt.optional(),uninstallReason:vt.optional(),activeJobId:We.optional()}).strict(),St=r.z.enum(["install","uninstall"]),At=r.z.enum(["queued","running","succeeded","failed","cancelled","interrupted"]),kt=r.z.object({current:r.z.number().finite().nonnegative().optional(),total:r.z.number().finite().positive().optional(),unit:r.z.string().trim().min(1).max(40).optional()}).strict().refine(e=>void 0===e.total||void 0===e.current||e.current<=e.total,{message:"Progress current must not exceed total.",path:["current"]}),zt=r.z.object({executable:r.z.string().trim().min(1).max(4096),args:r.z.array(r.z.string().max(4096)).max(128)}).strict(),Tt=r.z.object({code:r.z.string().trim().min(1).max(120).regex(/^[a-z0-9][a-z0-9._-]*$/),message:r.z.string().trim().min(1).max(4096),retryable:r.z.boolean(),sessionIds:r.z.array(We).max(64).optional()}).strict(),Ct=r.z.object({id:We,requestId:We.optional(),appId:We,operation:St,state:At,phase:r.z.string().trim().min(1).max(120).optional(),progress:kt.optional(),command:zt.optional(),logTail:r.z.string().max(32768).optional(),logTruncated:r.z.boolean().optional(),error:Tt.optional(),requestedAt:Ve,startedAt:Ve.optional(),finishedAt:Ve.optional(),updatedAt:Ve}).strict(),Et=r.z.object({streamId:We,sequence:r.z.number().int().nonnegative(),capabilities:It,apps:r.z.array(wt).max(256),activeJobs:r.z.array(Ct).max(256),recentJobs:r.z.array(Ct).max(256),observedAt:Ve}).strict(),Nt=r.z.object({requestId:We.optional()}).strict(),_t=r.z.object({job:Ct}).strict();r.z.object({type:r.z.literal("app-management"),streamId:We,sequence:r.z.number().int().nonnegative(),observedAt:Ve,job:Ct.optional(),snapshot:Et.optional()}).strict().refine(e=>void 0!==e.job||void 0!==e.snapshot,{message:"An app management event requires a job or snapshot."});const xt=r.z.object({component:r.z.enum(["control-plane","node-agent","controlled-instance"]),packageName:r.z.string().trim().max(160).optional(),packageVersion:r.z.string().trim().max(80).optional(),protocolVersion:Ge.optional(),buildId:r.z.string().trim().max(160).optional(),builtAt:r.z.string().trim().max(120).optional(),gitCommit:r.z.string().trim().max(120).optional(),imageRef:r.z.string().trim().max(512).optional(),imageDigest:r.z.string().trim().max(240).optional()}).strict(),Ot=r.z.enum(["stable","beta","alpha"]),Rt=r.z.object({packageName:r.z.literal("@task-handoff/controlled-instance"),version:r.z.string().trim().min(1).max(80),platform:r.z.string().trim().min(1).max(40),arch:r.z.string().trim().min(1).max(40),formatVersion:r.z.number().int().positive(),launcherAbi:r.z.number().int().positive(),entrypoint:r.z.string().trim().min(1).max(512),sha256:r.z.string().regex(/^[a-f0-9]{64}$/)}).strict(),jt=r.z.object({code:r.z.enum(["INSTANCE_RUNTIME_VERSION_MISMATCH","INSTANCE_RUNTIME_ARTIFACT_UNAVAILABLE","INSTANCE_RUNTIME_ARTIFACT_INVALID","INSTANCE_RUNTIME_INSTALL_FAILED","INSTANCE_RUNTIME_RESTART_FAILED","INSTANCE_RUNTIME_VERIFICATION_FAILED","INSTANCE_BASE_RUNTIME_INCOMPATIBLE","NODE_UPDATE_PREFLIGHT_FAILED","NODE_UPDATE_FAILED","LEGACY_INSTANCE_UPDATE_RETIRED"]),message:r.z.string().trim().min(1).max(4096),expectedVersion:r.z.string().trim().min(1).max(80).optional(),actualVersion:r.z.string().trim().min(1).max(80).optional(),retryable:r.z.boolean().default(!1)}).strict(),Dt=r.z.object({desiredVersion:r.z.string().trim().min(1).max(80),actualVersion:r.z.string().trim().min(1).max(80).optional(),phase:r.z.enum(["pending","draining","installing","restarting","verifying","matched","failed"]),attempt:r.z.number().int().nonnegative().default(0),lastAttemptAt:Ve.optional(),matchedAt:Ve.optional(),error:jt.optional()}).strict(),Mt=r.z.object({runningInstanceCount:r.z.number().int().nonnegative(),stoppedInstanceCount:r.z.number().int().nonnegative(),activeInstanceCount:r.z.number().int().nonnegative(),restartInstanceCount:r.z.number().int().nonnegative(),runningInstanceIds:r.z.array(We).max(1024).default([]),stoppedInstanceIds:r.z.array(We).max(1024).default([]),activeInstanceIds:r.z.array(We).max(1024).default([])}).strict(),Pt=r.z.object({phase:r.z.enum(["queued","updating-node","restarting-node","converging-instances","succeeded","degraded","failed"]),desiredVersion:r.z.string().trim().min(1).max(80),nodeVersion:r.z.string().trim().min(1).max(80).optional(),expectedInstanceIds:r.z.array(We).max(1024).default([]),expectedInstanceCount:r.z.number().int().nonnegative(),matchedInstanceCount:r.z.number().int().nonnegative(),pendingInstanceCount:r.z.number().int().nonnegative(),failedInstanceCount:r.z.number().int().nonnegative(),deferredInstanceCount:r.z.number().int().nonnegative().default(0)}).strict(),$t=r.z.object({channel:Ot.default("stable")}).strict(),qt=r.z.object({source:r.z.literal("npm"),channel:Ot,currentVersion:r.z.string().trim().max(240).optional(),availableVersion:r.z.string().trim().min(1).max(240),artifactRef:r.z.string().trim().min(1).max(512).optional(),runtimeArtifacts:r.z.array(Rt).default([]),impact:Mt,updateAvailable:r.z.boolean(),supported:r.z.boolean().default(!0),reason:r.z.string().trim().max(2048).optional(),checkedAt:Ve,preflightToken:r.z.string().trim().min(16).max(240).optional()}).strict(),Lt=r.z.object({id:We,nodeId:We,source:r.z.literal("npm"),channel:Ot,fromVersion:r.z.string().trim().max(240).optional(),toVersion:r.z.string().trim().min(1).max(240),artifactRef:r.z.string().trim().min(1).max(512).optional(),runtimeArtifacts:r.z.array(Rt).default([]),impact:Mt,rollout:Pt,status:r.z.enum(["queued","updating-node","restarting-node","converging-instances","succeeded","degraded","failed"]),error:jt.optional(),startedAt:Ve.optional(),completedAt:Ve.optional(),createdAt:Ve,updatedAt:Ve}).strict(),Ut=r.z.object({channel:Ot,targetVersion:r.z.string().trim().min(1).max(240),preflightToken:r.z.string().trim().min(16).max(240)}).strict(),Ft=r.z.object({type:r.z.enum(["branch","tag","commit"]),name:r.z.string().trim().min(1).max(240).optional(),commit:r.z.string().trim().max(80).optional()}).strict(),Bt=r.z.object({type:r.z.enum(["none","ssh-key","https-token"]).default("none"),secretId:r.z.string().trim().min(1).max(120).optional()}).strict(),Ht=r.z.object({depth:r.z.number().int().positive().max(1e5).optional(),submodules:r.z.boolean().default(!1),lfs:r.z.boolean().default(!1),subdirectory:r.z.string().trim().max(240).default("")}).strict();r.z.object({id:We,name:r.z.string().trim().min(1).max(160),url:r.z.string().trim().min(1).max(2048),provider:r.z.string().trim().max(80).optional(),ref:Ft.default({type:"branch",name:"main"}),auth:Bt.default({type:"none"}),clone:Ht.default({submodules:!1,lfs:!1,subdirectory:""}),defaultImageSelection:rt.optional(),labels:Ke,createdAt:Ve,updatedAt:Ve}).strict();const Gt=r.z.object({id:We,nodeId:We,name:r.z.string().trim().min(1).max(160),path:r.z.string().trim().min(1).max(4096),defaultImageSelection:rt.optional(),labels:Ke,createdAt:Ve,updatedAt:Ve}).strict(),Wt=r.z.discriminatedUnion("type",[r.z.object({type:r.z.literal("git-repository"),repositoryId:We.optional(),url:r.z.string().trim().min(1).max(2048),provider:r.z.string().trim().max(80).optional(),ref:Ft.default({type:"branch",name:"main"}),auth:Bt.default({type:"none"}),clone:Ht.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),r.z.object({type:r.z.literal("git-template"),url:r.z.string().trim().min(1).max(2048),templateId:r.z.string().trim().min(1).max(120).optional(),ref:Ft.default({type:"branch",name:"main"}),auth:Bt.default({type:"none"}),clone:Ht.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),r.z.object({type:r.z.literal("local-folder"),localFolderId:We.optional(),path:r.z.string().trim().min(1).max(4096),ownerNodeId:We.optional()}).strict()]),Vt=r.z.object({mode:r.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]),path:r.z.string().trim().min(1).max(4096).default("/workspace"),volumeName:r.z.string().trim().max(240).optional(),readOnly:r.z.boolean().default(!1)}).strict(),Kt=r.z.enum(["codex","claude"]),Jt=r.z.object({id:We,name:r.z.string().trim().min(1).max(160),source:Wt,defaultImageSelection:rt.optional(),defaultNodeId:We.optional(),defaultRuntimeId:We.optional(),workspacePolicy:Vt,labels:Ke,createdAt:Ve,updatedAt:Ve}).strict(),Qt=r.z.object({id:We,name:r.z.string().trim().min(1).max(160),endpoint:r.z.string().trim().min(1).max(2048),key:r.z.string().trim().min(1).max(4096),model:r.z.string().trim().min(1).max(240),app:Kt,enabled:r.z.boolean().default(!0),order:r.z.number().int().min(0).max(1e6).default(0),labels:Ke,createdAt:Ve,updatedAt:Ve}).strict(),Yt=Qt.omit({key:!0}).extend({keyPreview:r.z.string().trim().min(1).max(160),keySet:r.z.boolean()}).strict(),Xt=Yt.extend({referenceCount:r.z.number().int().min(0)}).strict(),Zt=r.z.object({instanceId:We,codexModelHash:We.optional(),claudeModelHash:We.optional(),updatedAt:Ve}).strict(),es=r.z.discriminatedUnion("type",[r.z.object({type:r.z.literal("control-plane"),name:Qt.shape.name,enabled:Qt.shape.enabled,order:Qt.shape.order}).strict(),r.z.object({type:r.z.literal("node"),nodeId:We,name:Qt.shape.name,enabled:Qt.shape.enabled,order:Qt.shape.order,referenceCount:r.z.number().int().min(0)}).strict()]),ts=r.z.object({id:We,model:Yt,locations:r.z.array(es).min(1),referenceCount:r.z.number().int().min(0).default(0)}).strict(),ss=r.z.object({nodeId:We,code:r.z.string().trim().min(1).max(120),message:r.z.string().trim().min(1).max(2e3)}).strict(),ns=r.z.object({models:r.z.array(ts),nodeDiagnostics:r.z.array(ss).default([]),updatedAt:Ve}).strict();function is(e){const t={app:Kt.parse(e.app),endpoint:Qt.shape.endpoint.parse(e.endpoint),key:Qt.shape.key.parse(e.key),model:Qt.shape.model.parse(e.model)};return`mdl_${o.createHash("sha256").update(JSON.stringify(t)).digest("hex")}`}Qt.omit({id:!0,createdAt:!0,updatedAt:!0}).strict().partial().strict(),r.z.object({modelSelection:at,codexModelHash:We.optional(),claudeModelHash:We.optional()}).strict();const as=r.z.enum(["market","custom"]),rs=r.z.enum(["active","deprecated","yanked"]),os=r.z.discriminatedUnion("kind",[r.z.object({kind:r.z.literal("builtin"),key:r.z.string().trim().min(1).max(120)}).strict(),r.z.object({kind:r.z.literal("remote"),url:r.z.string().trim().url().max(2048),mediaType:r.z.enum(["image/png","image/jpeg","image/webp"]).optional(),digest:it.optional()}).strict()]),ds={kind:"builtin",key:"default-image-cover"},cs=r.z.array(r.z.string().trim().min(1).max(80)).default([]),ls=r.z.array(r.z.string().trim().min(1).max(120)).default([]),ps=r.z.record(r.z.string().trim().min(2).max(35),r.z.string().trim().min(1).max(4096)),us=r.z.object({os:r.z.string().trim().min(1).max(40),architecture:r.z.string().trim().min(1).max(40),digest:it.optional(),downloadSizeBytes:r.z.number().int().positive().optional(),unpackedSizeBytes:r.z.number().int().positive().optional()}).strict(),ms=r.z.object({name:r.z.string().trim().min(1).max(128).regex(Ye),version:r.z.string().trim().min(1).max(160).optional(),reference:nt,manifestDigest:it.optional(),publishedAt:Ve.optional(),platforms:r.z.array(us).default([]),status:rs.default("active")}).strict(),gs=r.z.object({id:We,publisher:r.z.string().trim().min(1).max(160),slug:r.z.string().trim().min(1).max(160),name:r.z.string().trim().min(1).max(160),description:r.z.string().trim().min(1).max(4096),localizedDescriptions:ps.optional(),cover:os.optional(),repository:r.z.string().trim().min(1).max(512),defaultTag:r.z.string().trim().min(1).max(128).regex(Ye),tags:r.z.array(ms).min(1),capabilities:cs,optionalApps:ls,defaultEnv:Je,labels:Ke,status:rs.default("active")}).strict().superRefine((e,t)=>{e.tags.some(t=>t.name===e.defaultTag)||t.addIssue({code:"custom",path:["defaultTag"],message:"Market defaultTag must identify one of the image tags."})}),hs=r.z.object({protocolVersion:Qe,catalogId:We,revision:r.z.string().trim().min(1).max(240),source:r.z.enum(["embedded","remote","cache"]),generatedAt:Ve,expiresAt:Ve.optional(),items:r.z.array(gs)}).strict(),fs=r.z.object({source:r.z.enum(["embedded","remote","cache"]),state:r.z.enum(["ready","refreshing","stale","failed"]),revision:r.z.string().trim().min(1).max(240),updatedAt:Ve,error:r.z.string().trim().max(4096).optional()}).strict(),Is=r.z.object({id:We,origin:r.z.literal("custom"),name:r.z.string().trim().min(1).max(160),description:r.z.string().trim().max(4096).optional(),localizedDescriptions:ps.optional(),cover:os.optional(),reference:nt,repository:r.z.string().trim().min(1).max(512),tag:r.z.string().trim().min(1).max(128).regex(Ye).optional(),pullPolicy:r.z.literal("if-not-present").default("if-not-present"),capabilities:cs,optionalApps:ls,defaultEnv:Je,labels:Ke,createdAt:Ve,updatedAt:Ve}).strict(),ys=r.z.object({name:r.z.string().trim().min(1).max(128).regex(Ye),version:r.z.string().trim().min(1).max(160).optional(),reference:nt,manifestDigest:it.optional(),status:rs}).strict(),bs=r.z.object({id:We,origin:as,name:r.z.string().trim().min(1).max(160),description:r.z.string().trim().max(4096).optional(),localizedDescriptions:ps.optional(),cover:os,repository:r.z.string().trim().min(1).max(512),tag:r.z.string().trim().min(1).max(128).regex(Ye).optional(),availableTags:r.z.array(ys),reference:nt,digest:it.optional(),downloadSizeBytes:r.z.number().int().positive().optional(),unpackedSizeBytes:r.z.number().int().positive().optional(),capabilities:cs,optionalApps:ls,defaultEnv:Je,labels:Ke,readOnly:r.z.boolean(),lifecycleStatus:rs.optional(),market:r.z.object({catalogId:We,catalogRevision:r.z.string().trim().min(1).max(240),publisher:r.z.string().trim().min(1).max(160),version:r.z.string().trim().min(1).max(160).optional()}).strict().optional()}).strict(),vs=r.z.object({id:We,origin:as,name:r.z.string().trim().min(1).max(160),description:r.z.string().trim().max(4096).optional(),localizedDescriptions:ps.optional(),cover:os.optional(),repository:r.z.string().trim().min(1).max(512),tag:r.z.string().trim().min(1).max(128).regex(Ye).optional(),requestedReference:nt,resolvedDigest:it.optional(),resolvedReference:nt.optional(),downloadSizeBytes:r.z.number().int().positive().optional(),pullPolicy:r.z.literal("if-not-present").default("if-not-present"),capabilities:cs,optionalApps:ls,defaultEnv:Je,labels:Ke,market:r.z.object({catalogId:We,catalogRevision:r.z.string().trim().min(1).max(240),publisher:r.z.string().trim().min(1).max(160),version:r.z.string().trim().min(1).max(160).optional()}).strict().optional(),createdAt:Ve,updatedAt:Ve}).strict(),ws=r.z.object({phase:r.z.enum(["checking-image","pulling-image","resolving-image","ready","failed"]),requestedReference:nt,generation:r.z.number().int().nonnegative().default(0),error:r.z.string().trim().max(4096).optional(),startedAt:Ve,updatedAt:Ve}).strict(),Ss="image.pull.terminal.output",As="image.pull.terminal.finished",ks=r.z.object({instanceId:We,generation:r.z.number().int().nonnegative(),requestedReference:nt,sequence:r.z.number().int().nonnegative(),observedAt:Ve}).strict(),zs=ks.extend({data:r.z.string().min(1).max(65536),replay:r.z.boolean().optional()}).strict(),Ts=ks.extend({outcome:r.z.enum(["succeeded","failed"])}).strict(),Cs=ks.extend({status:r.z.enum(["connecting","pulling","extracting","complete","failed"]),layers:r.z.object({total:r.z.number().int().nonnegative(),completed:r.z.number().int().nonnegative(),downloaded:r.z.number().int().nonnegative(),downloading:r.z.number().int().nonnegative(),extracting:r.z.number().int().nonnegative()}).strict(),bytes:r.z.object({current:r.z.number().finite().nonnegative(),total:r.z.number().finite().positive()}).strict().optional(),percent:r.z.number().finite().min(0).max(100).optional(),message:r.z.string().trim().min(1).max(500),terminalTail:r.z.string().max(262144).optional(),terminalTruncated:r.z.boolean().optional()}).strict();function Es(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const s=e,n=new Set(["id","origin","name","description","localizedDescriptions","cover","reference","repository","tag","image","registry","pullPolicy","capabilities","optionalApps","defaultEnv","labels","createdAt","updatedAt"]);for(const e of Object.keys(s))n.has(e)||t?.({imageId:"string"==typeof s.id?s.id:void 0,field:e});const i="string"==typeof s.id?s.id:void 0,a="string"==typeof s.image?s.image:void 0;void 0!==a&&t?.({imageId:i,field:"image"}),Object.prototype.hasOwnProperty.call(s,"registry")&&t?.({imageId:i,field:"registry"});let r,o="string"==typeof s.reference?s.reference:a;if(o)try{o=tt(o)}catch{if(!o.includes("@")&&o.lastIndexOf(":")<=o.lastIndexOf("/"))try{o=tt(`${o}:latest`),t?.({imageId:i,field:"reference:implicit-latest"})}catch{}}try{r=o?st(o):void 0}catch{}return{id:s.id,origin:"custom",name:s.name,description:s.description,localizedDescriptions:s.localizedDescriptions,cover:Ys(s.cover,["kind","key","url","mediaType","digest"]),reference:r?.reference??o,repository:r?.repository??s.repository,tag:r?.tag??s.tag,pullPolicy:s.pullPolicy??"if-not-present",capabilities:s.capabilities,optionalApps:s.optionalApps,defaultEnv:s.defaultEnv,labels:s.labels,createdAt:s.createdAt,updatedAt:s.updatedAt}}const Ns=r.z.object({id:We,name:r.z.string().trim().min(1).max(160),connectionMode:r.z.enum(["local-ipc","local-loopback","direct-http","reverse-wss"]).default("direct-http"),auth:r.z.object({mode:r.z.enum(["local-static-key","paired-hmac"]).default("local-static-key"),keyId:We.optional(),secret:r.z.string().trim().max(4096).optional(),pairedAt:Ve.optional(),pairing:r.z.object({status:r.z.enum(["pending","paired","expired"]).default("pending"),joinToken:r.z.string().trim().max(4096).optional(),expiresAt:Ve.optional()}).strict().optional()}).strict().default({mode:"local-static-key"}),endpoint:r.z.string().trim().max(2048).optional(),controlEndpoint:r.z.string().trim().max(2048).optional(),containerEndpoint:r.z.string().trim().max(2048).optional(),publicWebBase:r.z.string().trim().max(2048).optional(),status:r.z.enum(["unknown","online","offline","degraded"]).default("unknown"),health:r.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),capabilities:r.z.record(r.z.string(),r.z.unknown()).default({}),appInventory:ut.optional(),labels:Ke,lastSeenAt:Ve.optional(),createdAt:Ve,updatedAt:Ve}).strict(),_s=r.z.object({id:We,nodeId:We,name:r.z.string().trim().min(1).max(160),type:r.z.enum(["docker","kubernetes","local"]),status:r.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:r.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),capabilities:r.z.record(r.z.string(),r.z.unknown()).default({}),labels:Ke,createdAt:Ve,updatedAt:Ve}).strict(),xs=r.z.object({instanceId:We,runtimeKind:r.z.literal("docker"),state:r.z.enum(["pending","available","stopped","unavailable"]),sampledAt:Ve,cpu:r.z.object({usagePercent:r.z.number().nonnegative()}).strict().optional(),memory:r.z.object({usageBytes:r.z.number().nonnegative(),limitBytes:r.z.number().nonnegative().optional(),usagePercent:r.z.number().nonnegative().optional()}).strict().optional(),network:r.z.object({rxBytes:r.z.number().nonnegative(),txBytes:r.z.number().nonnegative()}).strict().optional(),blockIo:r.z.object({readBytes:r.z.number().nonnegative(),writeBytes:r.z.number().nonnegative()}).strict().optional(),pids:r.z.number().int().nonnegative().optional(),error:r.z.string().trim().max(2048).optional()}).strict(),Os="instance.metrics.snapshot",Rs=r.z.object({ok:r.z.boolean().optional(),role:r.z.string().optional(),nodeId:We.optional(),platform:mt.optional()}).passthrough(),js=r.z.object({bindScope:r.z.enum(["loopback","all-ipv4"]),port:r.z.number().int().min(1).max(65535)}).strict().extend({host:r.z.enum(["127.0.0.1","0.0.0.0"]),status:r.z.enum(["listening","error"]),source:r.z.enum(["bootstrap","persisted"]),error:r.z.string().trim().max(2048).optional()}).strict(),Ds=r.z.object({nodeId:We,joinToken:r.z.string().trim().min(1),expiresAt:Ve}).passthrough(),Ms=r.z.object({id:We,keyId:We,url:r.z.string().trim().min(1).max(2048).optional(),name:r.z.string().trim().min(1).max(160).optional(),pairedAt:Ve,updatedAt:Ve.optional(),active:r.z.boolean().optional(),current:r.z.boolean().optional()}).passthrough(),Ps=r.z.object({remote:r.z.object({id:We,url:r.z.string().trim().min(1).max(2048).optional(),keyId:We,pairedAt:Ve,active:r.z.boolean()}).passthrough(),tunnel:r.z.object({status:r.z.enum(["disabled","saved","connecting","connected","failed"]),error:r.z.string().trim().max(2048).optional()}).passthrough()}).passthrough(),$s=r.z.object({deleted:r.z.boolean().optional()}).passthrough(),qs=r.z.object({repository:r.z.string().trim().optional(),tag:r.z.string().trim().optional(),id:r.z.string().trim(),createdSince:r.z.string().trim().optional(),size:r.z.string().trim().optional(),sizeBytes:r.z.number().int().nonnegative().optional(),reference:r.z.string().trim().min(1),repoDigests:r.z.array(r.z.string().trim().min(1).max(512)).default([])}).passthrough(),Ls=r.z.object({image:bs,status:r.z.enum(["available","pull-required","unknown"]),localImage:qs.optional(),localSizeBytes:r.z.number().int().nonnegative().optional(),downloadSizeBytes:r.z.number().int().positive().optional(),unpackedSizeBytes:r.z.number().int().positive().optional(),error:r.z.string().trim().max(2048).optional()}).strict(),Us=r.z.lazy(()=>r.z.object({name:r.z.string().trim().min(1),path:r.z.string().trim().min(1).max(4096),children:r.z.array(Us)}).passthrough()),Fs=r.z.object({status:r.z.number().int().min(100).max(599).optional(),headers:r.z.record(r.z.string(),r.z.string()).optional(),bodyBase64:r.z.string().optional()}).passthrough(),Bs=r.z.object({strategy:r.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),web:r.z.string().trim().max(2048).optional(),api:r.z.string().trim().max(2048).optional(),vnc:r.z.string().trim().max(2048).optional(),tty:r.z.string().trim().max(2048).optional(),logs:r.z.string().trim().max(2048).optional(),status:r.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict(),Hs=r.z.object({strategy:r.z.enum(["control-plane-proxy","direct-port","node-proxy","kubernetes-ingress","kubernetes-port-forward"]).default("control-plane-proxy"),web:r.z.string().trim().max(2048).optional(),api:r.z.string().trim().max(2048).optional(),ws:r.z.string().trim().max(2048).optional(),status:r.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict().default({strategy:"control-plane-proxy",status:"unknown"}),Gs=r.z.object({mode:r.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]).optional(),status:r.z.enum(["unknown","pending","ready","failed"]).default("unknown"),path:r.z.string().trim().max(4096).optional(),resolvedCommit:r.z.string().trim().max(120).optional(),error:r.z.string().trim().max(2048).optional()}).passthrough(),Ws=r.z.object({id:We,name:r.z.string().trim().min(1).max(160),projectId:We.optional(),source:Wt,sourceSnapshot:r.z.record(r.z.string(),r.z.unknown()).default({}),modelSelection:at,nodeId:We,runtimeId:We,imageSelection:rt.optional(),imageSnapshot:vs.optional(),imageProvisioning:ws.optional(),stateRevision:r.z.number().int().min(0).default(0),processIncarnationId:r.z.string().trim().min(1).max(120).optional(),status:r.z.enum(["created","provisioning","starting","registering","registered","running","stopping","stopped","failed","unhealthy"]).default("created"),health:r.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),connectionStatus:r.z.enum(["unknown","online","offline","endpoint-unreachable"]).default("unknown"),agentStatus:r.z.enum(["unknown","online","offline"]).default("unknown"),targetStatus:r.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),uiAccessStatus:r.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),controlMode:r.z.enum(["standalone","controlled"]).default("controlled"),protocolVersion:Ge.optional(),instanceVersion:r.z.string().trim().max(80).optional(),build:xt.optional(),runtimeVersion:Dt.optional(),ready:r.z.boolean().default(!1),capabilities:r.z.record(r.z.string(),r.z.unknown()).default({}),appInventory:ut.optional(),config:r.z.object({autoImportAgentConfigs:r.z.boolean().default(!0),defaultCodexPermissionMode:H.default("ask")}).strict().default({autoImportAgentConfigs:!0,defaultCodexPermissionMode:"ask"}),workspace:Gs.default({status:"unknown"}),target:Bs.default({strategy:"direct-port",status:"unknown"}),access:Hs,apps:r.z.object({runningCount:r.z.number().int().min(0).default(0),problemCount:r.z.number().int().min(0).default(0),updatedAt:Ve.optional(),revision:r.z.number().int().min(0).optional()}).strict().default({runningCount:0,problemCount:0}),aiSessions:be.default(()=>({runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:(new Date).toISOString()})),triggers:r.z.object({enabledCount:r.z.number().int().min(0).default(0),runningCount:r.z.number().int().min(0).default(0),errorCount:r.z.number().int().min(0).default(0),configs:r.z.array(r.z.object({configHash:r.z.string().trim().min(1),config:Pe,deployments:r.z.array(qe).default([]),runtime:r.z.array(Le).default([])}).strict()).default([]),recentRuns:r.z.array(Ue).default([])}).strict().optional(),runtime:r.z.object({kind:r.z.string().trim().max(80).optional(),containerName:r.z.string().trim().max(240).optional(),containerId:r.z.string().trim().max(240).optional(),workspacePath:r.z.string().trim().max(4096).optional(),pid:r.z.number().int().positive().optional(),port:r.z.number().int().positive().max(65535).optional(),labels:r.z.record(r.z.string(),r.z.string()).default({})}).strict().default({labels:{}}),registrationToken:r.z.string().trim().min(1).max(240).optional(),lastHeartbeatAt:Ve.optional(),createdAt:Ve,updatedAt:Ve}).strict(),Vs="instance.lifecycle.snapshot",Ks=r.z.object({instanceId:Ws.shape.id,revision:r.z.number().int().min(0),updatedAt:Ve,status:Ws.shape.status.unwrap(),health:Ws.shape.health.unwrap(),connectionStatus:Ws.shape.connectionStatus.unwrap(),accessStatus:r.z.enum(["reachable","endpoint-unreachable"]),imageProvisioning:ws.optional(),workspace:Gs,runtime:Ws.shape.runtime.unwrap(),runtimeVersion:Dt.optional(),ready:r.z.boolean(),lastHeartbeatAt:Ve.optional()}).strict();function Js(e,t,s,n,i){if(!t||"object"!=typeof t||Array.isArray(t))return;let a=structuredClone(t);for(let t=0;t<20;t+=1){const t=e.safeParse(a);if(t.success)return t.data;let s=!1;for(const e of t.error.issues){if("unrecognized_keys"!==e.code)continue;const t=Qs(a,e.path);if(t&&"object"==typeof t&&!Array.isArray(t))for(const n of e.keys)Object.prototype.hasOwnProperty.call(t,n)&&(delete t[n],s=!0)}if(!s){const s=structuredClone(a);let n=!1;for(const e of t.error.issues){if(!e.path.length||"string"!=typeof e.path.at(-1))continue;const t=Qs(s,e.path.slice(0,-1)),i=e.path.at(-1);t&&"object"==typeof t&&!Array.isArray(t)&&Object.prototype.hasOwnProperty.call(t,i)&&(delete t[i],n=!0)}if(n){const t=e.safeParse(s);if(t.success)return t.data}break}}}function Qs(e,t){let s=e;for(const e of t){if(!s||"object"!=typeof s)return;s=s[e]}return s}function Ys(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const s=e;return Object.fromEntries(t.filter(e=>Object.prototype.hasOwnProperty.call(s,e)).map(e=>[e,s[e]]))}r.z.object({instanceId:We.optional(),projectId:We.optional(),source:Wt.optional(),nodeId:We.optional(),runtimeId:We.optional(),imageSelection:rt.optional(),instanceVersion:r.z.string().trim().max(80).optional(),protocolVersion:Ge,build:xt.optional(),controlMode:r.z.enum(["standalone","controlled"]).default("controlled"),capabilities:r.z.record(r.z.string(),r.z.unknown()).default({}),appInventory:ut.optional(),target:Bs.default({strategy:"direct-port",status:"unknown"}),workspace:Gs.default({status:"unknown"}),registrationToken:r.z.string().trim().max(240).optional(),processIncarnationId:r.z.string().trim().min(1).max(120).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==He||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol register payload requires appInventory."})}),r.z.object({status:Ws.shape.status.optional(),health:Ws.shape.health.optional(),protocolVersion:Ge,build:xt.optional(),capabilities:Ws.shape.capabilities.optional(),appInventory:ut.optional(),apps:Ws.shape.apps.optional(),aiSessions:Ws.shape.aiSessions.optional(),triggers:Ws.shape.triggers.optional(),workspace:Gs.optional(),target:Bs.partial().optional(),processIncarnationId:r.z.string().trim().min(1).max(120).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==He||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol heartbeat payload requires appInventory."})});const Xs=r.z.enum(["web","telegram","wechat","dingding"]),Zs=r.z.object({id:We,channel:Xs,name:r.z.string().trim().min(1).max(160),enabled:r.z.boolean().default(!1),token:r.z.string().trim().max(4096).optional(),defaultChatId:r.z.string().trim().max(240).optional(),allowedUserIds:r.z.array(r.z.string().trim().min(1).max(240)).default([]),pollIntervalMs:r.z.number().int().positive().max(6e4).default(3e3),settings:r.z.record(r.z.string(),r.z.unknown()).default({}),createdAt:Ve,updatedAt:Ve}).strict(),en=r.z.object({id:We,channel:Xs,bridgeId:We.optional(),chatSessionId:r.z.string().trim().min(1).max(240),userId:r.z.string().trim().max(240).optional(),activeProjectId:We.optional(),activeInstanceId:We.optional(),activeAiSessionId:r.z.string().trim().min(1).max(120).optional(),lastUsedAt:Ve,createdAt:Ve,updatedAt:Ve}).strict(),tn=r.z.object({source:r.z.object({type:r.z.literal("chat-gateway").default("chat-gateway"),channel:Xs,bridgeId:We.optional(),chatSessionId:r.z.string().trim().min(1).max(240),userId:r.z.string().trim().max(240).optional()}).strict(),message:r.z.object({text:r.z.string().trim().max(2e4).default(""),attachments:r.z.array(L).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>A&&t.addIssue({code:r.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})}),target:r.z.object({instanceId:We,aiSessionId:r.z.string().trim().min(1).max(120)}).strict().optional()}).strict(),sn=r.z.object({id:We,instanceId:We,projectId:We.optional(),aiSessionId:r.z.string().trim().min(1).max(120),providerSessionId:r.z.string().trim().max(240).optional(),result:r.z.string().default(""),timeoutMs:r.z.number().positive().optional(),source:r.z.string().trim().max(120).optional(),kind:r.z.enum(["task","approval"]).default("task"),status:r.z.enum(["pending","unreachable"]).default("pending"),error:r.z.string().trim().max(2048).optional(),lastSeenAt:Ve}).strict();function nn(e){return!1!==e.ready&&(!e.runtimeVersion||"matched"===e.runtimeVersion.phase)}function an(e){return e.startsWith("ai-session.")||e.startsWith("ai.session.")?"ai.sessions":e.startsWith("app-session.")?"app.sessions":e.startsWith("app.management")?"apps":e.startsWith("trigger.")?"triggers":e.startsWith("instance.")?"instances":e.startsWith("node.")?"nodes":e.startsWith("project.")?"projects":e.startsWith("model.")?"models":e.startsWith("image.")?"images":e.startsWith("market.")?"market":"system"}const rn=r.z.enum(["ai.sessions","app.sessions"]),on=r.z.object({topic:rn,instanceId:r.z.string().trim().min(1).max(160),streamId:r.z.string().trim().min(1).max(240),latestRevision:r.z.number().int().min(0),earliestRetainedRevision:r.z.number().int().min(0)}).strict(),dn=r.z.object({protocolVersion:r.z.literal(1),streams:r.z.array(on)}).strict();function cn(e){s.mkdirSync(e,{recursive:!0})}function ln(e,s){cn(t.dirname(e)),c.sync(e,`${JSON.stringify(s,null,2)}\n`,{encoding:"utf8"})}function pn(e,t){console.warn(JSON.stringify({message:e,...t}))}function un(e){return e&&"object"==typeof e&&"issues"in e?{issues:e.issues}:{error:e instanceof Error?e.message:String(e)}}function mn(e,t){const n=t.logger||pn;let i;try{i=JSON.parse(s.readFileSync(e,"utf8"))}catch(t){return void n("stored JSON could not be read",{filePath:e,...un(t)})}if(!t.schema)return i;const a=t.sanitize?t.sanitize(i):i,r=t.schema.safeParse(a);if(r.success)return r.data;const o="strip"in t.schema&&"function"==typeof t.schema.strip?t.schema.strip().safeParse(a):void 0;if(o?.success)return n("unknown stored fields were ignored",{filePath:e,issues:r.error.issues}),o.data;n("stored record failed validation and was isolated",{filePath:e,issues:r.error.issues})}function gn(e){return`${e}_${o.randomUUID().replace(/-/g,"").slice(0,20)}`}function hn(){return o.randomBytes(24).toString("base64url")}class fn{directory;options;constructor(e,t={}){this.directory=e,this.options=t}init(){cn(this.directory)}filePath(e){return t.join(this.directory,`${e}.json`)}list(){return this.init(),s.readdirSync(this.directory).filter(e=>e.endsWith(".json")).sort().flatMap(e=>{const s=mn(t.join(this.directory,e),this.options);return s?[s]:[]})}get(e){const t=this.filePath(e);return s.existsSync(t)?mn(t,this.options):void 0}put(e){const t=this.options.sanitize?this.options.sanitize(e):e,s=this.options.schema?this.options.schema.parse(t):t;return ln(this.filePath(s.id),s),s}patch(e,t){const s=this.get(e);if(!s)return;const n={...s,...t,id:s.id,createdAt:s.createdAt,updatedAt:(new Date).toISOString()};return this.put(n)}delete(e){const t=this.filePath(e);return!!s.existsSync(t)&&(s.unlinkSync(t),!0)}}class In{filePathValue;defaults;options;constructor(e,t,s={}){this.filePathValue=e,this.defaults=t,this.options=s}init(){cn(t.dirname(this.filePathValue)),s.existsSync(this.filePathValue)||this.put(this.defaults())}get(){return this.init(),s.existsSync(this.filePathValue)?mn(this.filePathValue,this.options)??this.defaults():this.defaults()}put(e){const t=this.options.sanitize?this.options.sanitize(e):e,s=this.options.schema?this.options.schema.parse(t):t;return ln(this.filePathValue,s),s}}const yn=d.promisify(o.scrypt),bn="task_handoff_cp_session",vn=r.z.enum(["disabled","password"]),wn=r.z.string().trim().min(1).max(80).regex(/^[a-zA-Z0-9_.@-]+$/),Sn=r.z.string().min(8).max(4096),An=r.z.object({username:wn,password:Sn}).strict(),kn=r.z.object({username:r.z.string().trim().min(1).max(80),password:r.z.string().min(1).max(4096)}).strict(),zn=r.z.object({id:r.z.string().trim().min(1),createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}),Tn=zn.extend({username:wn,passwordHash:r.z.string().trim().min(1),role:r.z.enum(["viewer","operator","admin"]),lastLoginAt:r.z.string().datetime().optional()}).strict(),Cn=zn.extend({userId:r.z.string().trim().min(1),tokenHash:r.z.string().trim().min(1),expiresAt:r.z.string().datetime(),lastSeenAt:r.z.string().datetime().optional()}).strict();function En(){return(new Date).toISOString()}function Nn(e){return o.createHash("sha256").update(e).digest("base64url")}async function _n(e){const t=o.randomBytes(16).toString("base64url");return`scrypt$${t}$${(await yn(e,t,64)).toString("base64url")}`}function xn(e){return{id:e.id,username:e.username,role:e.role,createdAt:e.createdAt,updatedAt:e.updatedAt,lastLoginAt:e.lastLoginAt}}class On{mode;users;sessions;constructor(e,t={}){this.mode=function(e={}){return vn.parse(e.mode||process.env.TASK_HANDOFF_CONTROL_PLANE_AUTH_MODE||"disabled")}(t),this.users=new fn(e.authUsersDir,{schema:Tn}),this.sessions=new fn(e.authSessionsDir,{schema:Cn})}init(){this.users.init(),this.sessions.init()}enabled(){return"password"===this.mode}state(){return{mode:this.mode,enabled:this.enabled(),requiresBootstrap:this.enabled()&&0===this.users.list().length}}async bootstrapAdmin(e){if(!this.enabled()){const e=new Error("Control Plane authentication is disabled.");throw Object.assign(e,{statusCode:400,code:"AUTH_DISABLED"}),e}if(this.users.list().length>0){const e=new Error("Control Plane admin user already exists.");throw Object.assign(e,{statusCode:409,code:"AUTH_BOOTSTRAP_ALREADY_DONE"}),e}const t=An.parse(e),s=En();return xn(this.users.put({id:gn("user"),username:t.username,passwordHash:await _n(t.password),role:"admin",createdAt:s,updatedAt:s}))}async login(e){if(!this.enabled()){const e=new Error("Control Plane authentication is disabled.");throw Object.assign(e,{statusCode:400,code:"AUTH_DISABLED"}),e}const t=kn.parse(e),s=this.users.list().find(e=>e.username===t.username);if(!s||!await async function(e,t){const[s,n,i]=t.split("$");if("scrypt"!==s||!n||!i)return!1;const a=await yn(e,n,64),r=Buffer.from(i,"base64url");return r.length===a.length&&o.timingSafeEqual(r,a)}(t.password,s.passwordHash)){const e=new Error("Invalid username or password.");throw Object.assign(e,{statusCode:401,code:"AUTH_LOGIN_FAILED"}),e}const n=En(),i=hn(),a=new Date(Date.now()+12096e5).toISOString(),r=this.sessions.put({id:gn("sess"),userId:s.id,tokenHash:Nn(i),expiresAt:a,createdAt:n,updatedAt:n,lastSeenAt:n});return this.users.put({...s,lastLoginAt:n,updatedAt:n}),{user:xn({...s,lastLoginAt:n,updatedAt:n}),sessionToken:`${r.id}.${i}`,expiresAt:a}}async userForSessionToken(e){if(!this.enabled())return;if(!e)return;const[t,s]=e.split(".");if(!t||!s)return;const n=this.sessions.get(t);if(!n||n.expiresAt<=En()||n.tokenHash!==Nn(s))return;const i=this.users.get(n.userId);return i?(this.sessions.put({...n,lastSeenAt:En(),updatedAt:En()}),xn(i)):void 0}async currentSession(e){const t=this.state(),s=await this.userForSessionToken(e);return{...t,authenticated:!this.enabled()||Boolean(s),user:s}}logout(e){const t=e?.split(".")[0];return t&&this.sessions.delete(t),{ok:!0}}}const Rn=r.z.enum(["import","export"]),jn=r.z.object({id:r.z.string().trim().min(1).max(120),label:r.z.string().trim().min(1).max(120),directoryName:r.z.string().trim().min(1).max(120)}).strict(),Dn=r.z.object({direction:Rn,programIds:r.z.array(r.z.string().trim().min(1).max(120)).min(1).max(20).refine(e=>new Set(e).size===e.length,"Config sync program ids must be unique."),workspaceFolder:r.z.string().trim().min(1).max(1e3)}).strict(),Mn=r.z.object({import:r.z.string().trim().min(1).max(1e3).default("."),export:r.z.string().trim().min(1).max(1e3).default(".")}).strict(),Pn=r.z.object({programs:r.z.array(jn),preferences:Mn}).strict(),$n=r.z.object({id:r.z.string().trim().min(1).max(120),type:r.z.enum(["file","dir"]),projectPath:r.z.string().trim().min(1).max(1e3),containerPath:r.z.string().trim().min(1).max(1e3),source:r.z.string().trim().min(1).max(4096),target:r.z.string().trim().min(1).max(4096),status:r.z.enum(["copied","skipped_missing_source","failed"]),error:r.z.string().trim().max(4096).optional()}).strict(),qn=r.z.object({preset:r.z.object({id:r.z.string().trim().min(1).max(120),label:r.z.string().trim().min(1).max(120),projectRoot:r.z.string().trim().min(1).max(1e3)}).strict(),direction:Rn,items:r.z.array($n).max(100)}).strict(),Ln=r.z.object({direction:Rn,workspaceFolder:r.z.string().trim().min(1).max(1e3),programs:r.z.array(qn).min(1).max(20)}).strict(),Un="app-session.snapshot",Fn="app-session.patch",Bn="app-session.removed",Hn=r.z.enum(["created","starting","running","stopping","stopped","exited","failed","closed","terminated","unknown"]),Gn=r.z.object({type:r.z.enum(["app-session","provider-session","adapter-key"]),id:r.z.string().trim().min(1).max(240),agent:r.z.string().trim().min(1).max(80).optional(),adapter:r.z.string().trim().min(1).max(80).optional(),key:r.z.string().trim().min(1).max(240).optional()}).strict(),Wn=r.z.record(r.z.string(),r.z.unknown()).and(r.z.object({id:r.z.string().trim().min(1).max(160),appId:r.z.string().trim().min(1).max(160).optional(),title:r.z.string().trim().max(240).optional(),kind:r.z.string().trim().max(80).optional(),status:Hn.default("unknown"),bindings:r.z.array(Gn).max(40).default([]),workspace:r.z.object({cwd:r.z.string().trim().max(4096).optional()}).strict().optional(),createdAt:r.z.string().datetime().optional(),updatedAt:r.z.string().datetime().optional()})),Vn=r.z.object({runningCount:r.z.number().int().min(0).default(0),problemCount:r.z.number().int().min(0).default(0),sessions:r.z.array(Wn).default([]),updatedAt:r.z.string().datetime()}).strict(),Kn=r.z.object({streamId:r.z.string().trim().min(1).max(240),revision:r.z.number().int().min(0),lastEventAt:r.z.string().datetime(),snapshot:Vn}).strict();Vn.pick({runningCount:!0,problemCount:!0,updatedAt:!0}).extend({revision:r.z.number().int().min(0).optional()});const Jn=r.z.enum(["app-session-created","app-session-updated","app-session-deleted","app-session-recovered","control-action","heartbeat-sync","startup"]),Qn=r.z.object({streamId:r.z.string().trim().min(1).max(240),instanceId:r.z.string().trim().min(1).max(160),nodeId:r.z.string().trim().min(1).max(160).optional(),revision:r.z.number().int().min(0),previousRevision:r.z.number().int().min(0).optional(),traceId:r.z.string().trim().min(1).max(160),generatedAt:r.z.string().datetime(),reason:Jn}).strict(),Yn=r.z.object({meta:Qn,snapshot:Vn}).strict(),Xn=r.z.object({meta:Qn,session:Wn}).strict(),Zn=r.z.object({meta:Qn,sessionId:r.z.string().trim().min(1).max(160),tombstone:Wn.optional(),expiresAt:r.z.string().datetime().optional()}).strict(),ei=r.z.object({streamId:r.z.string().trim().min(1).max(240),instanceId:r.z.string().trim().min(1).max(160),sinceRevision:r.z.number().int().min(0),latestRevision:r.z.number().int().min(0),earliestRetainedRevision:r.z.number().int().min(0),syncRequired:r.z.boolean().default(!1),events:r.z.array(r.z.discriminatedUnion("type",[r.z.object({type:r.z.literal(Un),payload:Yn}).strict(),r.z.object({type:r.z.literal(Fn),payload:Xn}).strict(),r.z.object({type:r.z.literal(Bn),payload:Zn}).strict()])).default([])}).strict();function ti(e,t=(new Date).toISOString()){const s=e.map(e=>si(e));return Vn.parse({runningCount:s.filter(e=>"running"===e.status).length,problemCount:s.filter(e=>"failed"===e.status).length,sessions:s,updatedAt:t})}function si(e){const t=ni("string"==typeof e.status?e.status:void 0),s="string"==typeof e.id?e.id:"",n=function(e){const t=di(e.workspace),s=di(e.tty),n=di(e.launch),i=di(e.ai),a=di(i.claude),r=ci(t.cwd)||ci(s.cwd)||ci(a.cwd)||ci(n.cwd);return r?{cwd:r}:void 0}(e),{workspace:i,...a}=e;return Wn.parse({...a,status:t,bindings:oi([...(r=e.bindings,Array.isArray(r)?r.flatMap(e=>{const t=Gn.safeParse(e);return t.success?[t.data]:[]}):[]),...s?[{type:"app-session",id:s}]:[],...ai(e)]),...n?{workspace:n}:{}});var r}function ni(e){const t=(e||"unknown").trim().toLowerCase();return"ended"===t?"closed":"terminating"===t?"stopping":Hn.options.includes(t)?t:"unknown"}function ii(e){return"app-session"===e.type?`app:${e.id}`:"provider-session"===e.type?`provider:${e.agent||""}:${e.id}`:`adapter:${e.adapter||""}:${e.key||e.id}`}function ai(e){const t=di(e.ai),s=di(t.claude),n=di(t.appServer),i=[],a=ci(s.short);a&&i.push({type:"adapter-key",adapter:"claude",agent:"claude",id:`short:${a}`,key:`short:${a}`});const r=ci(s.providerSessionId);r&&i.push({type:"provider-session",agent:"claude",id:r});const o=Array.isArray(t.threadIds)?t.threadIds.filter(e=>"string"==typeof e&&Boolean(e.trim())):[],d=ci(t.activeThreadId);for(const e of[...new Set([d,...o].filter(Boolean))])i.push({type:"provider-session",agent:"codex",id:e});const c=ci(n.socketPath);return c&&i.push({type:"adapter-key",adapter:"codex-app-server",agent:"codex",id:`socket:${c}`,key:`socket:${c}`}),i}function ri(e){const t="string"==typeof e.id?e.id:"",s=di(e.ai),n=ci(di(s.claude).short);return[t?`app:${t}`:"",n?`claude-short:${n}`:""]}function oi(e){const t=new Map;for(const s of e)t.set(ii(s),s);return[...t.values()]}function di(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function ci(e){return"string"==typeof e&&e.trim()?e.trim():""}function li(e=(new Date).toISOString()){return Vn.parse({runningCount:0,problemCount:0,sessions:[],updatedAt:e})}function pi(e,t,s,n){const i=n&&"object"==typeof n?n:{};return{nodeId:e.id,route:"string"==typeof i.route?i.route:t,method:s,code:"string"==typeof i.code?i.code:"NODE_AGENT_REQUEST_FAILED",message:n instanceof Error?n.message:String(n),statusCode:"number"==typeof i.statusCode?i.statusCode:void 0,issues:Array.isArray(i.issues)?i.issues:void 0}}class ui{requestImpl;logger;constructor(e){this.requestImpl=e.request,this.logger=e.logger}async request(e,t,s={}){const n=s.method||"GET";let i,a;try{i=await this.requestImpl(e,t,s)}catch(s){throw this.logger?.warn?.({nodeId:e.id,route:t,method:n,error:s instanceof Error?s.message:String(s),errorCode:"NODE_AGENT_TRANSPORT_FAILED"},"node agent transport failed"),s}try{a=await i.json()}catch(s){this.logger?.warn?.({nodeId:e.id,route:t,method:n,statusCode:i.status,error:s instanceof Error?s.message:String(s),errorCode:"NODE_AGENT_RESPONSE_INVALID_JSON"},"node agent response was not valid json");const a=new Error(`Node agent ${e.id} returned invalid JSON for ${t}.`);throw Object.assign(a,{statusCode:502,code:"NODE_AGENT_RESPONSE_INVALID_JSON",nodeId:e.id,route:t}),a}if(!i.ok){const s=new Error(a.error?.message||`Node agent request failed with HTTP ${i.status}`);throw Object.assign(s,{statusCode:i.status,code:a.error?.code||"NODE_AGENT_REQUEST_FAILED",nodeId:e.id,route:t}),this.logger?.warn?.({nodeId:e.id,route:t,method:n,statusCode:i.status,errorCode:a.error?.code||"NODE_AGENT_REQUEST_FAILED",error:s.message},"node agent request failed"),s}return this.logger?.info?.({nodeId:e.id,route:t,method:n,statusCode:i.status},"node agent request ok"),a.data??a}async requestSchema(e,t,s,n={}){return this.parse(e,t,s,await this.request(e,t,n),n.method||"GET")}parse(e,t,s,n,i="GET"){const a=s.safeParse(n);if(a.success)return a.data;this.logger?.warn?.({nodeId:e.id,route:t,method:i,errorCode:"NODE_AGENT_PROTOCOL_INVALID",issues:a.error.issues},"node agent protocol response did not match schema");const r=new Error(`Node agent ${e.id} returned invalid protocol data for ${t}.`);throw Object.assign(r,{statusCode:502,code:"NODE_AGENT_PROTOCOL_INVALID",nodeId:e.id,route:t,issues:a.error.issues}),r}}class mi{client;constructor(e){this.client=e}request(e,t,s={}){return this.client.request(e,t,s)}health(e){return this.client.requestSchema(e,"/health",Rs)}checkUpdate(e,t){return this.client.requestSchema(e,"/updates/check",qt,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}applyUpdate(e,t){return this.client.requestSchema(e,"/updates/apply",Lt,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}listUpdateJobs(e){return this.client.requestSchema(e,"/updates/jobs",r.z.array(Lt))}getExternalListener(e){return this.client.requestSchema(e,"/settings/external-listener",js)}updateExternalListener(e,t){return this.client.requestSchema(e,"/settings/external-listener",js,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}createPairingInvite(e,t={}){return this.client.requestSchema(e,"/pairing/invites",Ds,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t&&"object"==typeof t?t:{})})}connectRemote(e,t){return this.client.requestSchema(e,"/remotes/connect",Ps,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}listRemotes(e){return this.client.requestSchema(e,"/remotes",r.z.array(Ms))}deleteRemote(e,t){return this.client.requestSchema(e,`/remotes/${encodeURIComponent(t)}`,$s,{method:"DELETE"})}listDockerImages(e){return this.client.requestSchema(e,"/docker/images",r.z.array(qs))}listFolderTree(e,t={}){const s=new URLSearchParams;t.path&&s.set("path",t.path),void 0!==t.depth&&s.set("depth",String(t.depth));const n=s.toString();return this.client.requestSchema(e,"/folders/tree"+(n?`?${n}`:""),r.z.array(Us))}listLocalFolders(e){return this.client.requestSchema(e,"/local-folders",r.z.array(Gt))}createLocalFolder(e,t){return this.client.requestSchema(e,"/local-folders",Gt,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}async requireLocalFolder(e,t){const s=(await this.listLocalFolders(e)).find(e=>e.id===t);if(!s){const s=new Error(`Local folder ${t} was not found on node ${e.id}.`);throw Object.assign(s,{statusCode:404,code:"NODE_LOCAL_FOLDER_NOT_FOUND",nodeId:e.id,route:"/local-folders"}),s}return s}listRuntimes(e){return this.client.requestSchema(e,"/runtimes",r.z.array(_s))}createRuntime(e,t){return this.client.requestSchema(e,"/runtimes",_s,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}updateRuntime(e,t,s){const n=`/runtimes/${encodeURIComponent(t)}`;return this.client.requestSchema(e,n,_s,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}checkRuntime(e,t){const s=`/runtimes/${encodeURIComponent(t)}/check`;return this.client.requestSchema(e,s,_s,{method:"POST"})}deleteRuntime(e,t){return this.client.requestSchema(e,`/runtimes/${encodeURIComponent(t)}`,$s,{method:"DELETE"})}deleteLocalFolder(e,t){return this.client.requestSchema(e,`/local-folders/${encodeURIComponent(t)}`,$s,{method:"DELETE"})}listModels(e){return this.client.requestSchema(e,"/models",r.z.array(Xt))}createModel(e,t){return this.client.requestSchema(e,"/models",Xt,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}deployModel(e,t,s){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}/deploy`,Xt,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}updateModel(e,t,s){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}`,Xt,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}deleteModel(e,t){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}`,$s,{method:"DELETE"})}assignInstanceModels(e,t,s){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/model-assignment`,r.z.object({assignment:Zt,instance:Ws}).strict(),{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}async listFleetModels(e){return(await Promise.allSettled(e.map(async e=>({node:e,models:await this.listModels(e)})))).reduce((t,s,n)=>{if("fulfilled"===s.status)t.items.push(...s.value.models.map(e=>({nodeId:s.value.node.id,model:e})));else{const i=e[n];i&&t.nodeErrors.push(pi(i,"/models","GET",s.reason))}return t},{items:[],nodeErrors:[]})}async listFleetRuntimes(e){return(await Promise.allSettled(e.map(async e=>({node:e,runtimes:await this.listRuntimes(e)})))).reduce((t,s,n)=>{if("fulfilled"===s.status)t.items.push(...s.value.runtimes);else{const i=e[n];i&&t.nodeErrors.push(pi(i,"/runtimes","GET",s.reason))}return t},{items:[],nodeErrors:[]})}listInstances(e){return this.listInstancesWithDiagnostics(e).then(e=>e.items)}async listInstancesWithDiagnostics(e){return this.client.requestSchema(e,"/instances",r.z.array(r.z.unknown())).then(t=>t.reduce((t,s)=>{const n=this.normalizeInstance(e,s);return n.instance&&t.items.push(n.instance),n.error&&t.nodeErrors.push(n.error),t},{items:[],nodeErrors:[]}))}createInstance(e,t){return this.client.requestSchema(e,"/instances",Ws,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}updateInstance(e,t,s){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}`,Ws,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}deleteInstance(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/delete`,$s,{method:"POST"})}startInstance(e,t,s={}){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/start`,Ws,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}retryInstanceImageProvisioning(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/image-provisioning/retry`,Ws,{method:"POST"})}stopInstance(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/stop`,Ws,{method:"POST"})}restartInstance(e,t,s={}){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/restart`,Ws,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}instanceResourceMetrics(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/metrics`,xs)}proxyRawInstance(e,t,s){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/proxy/raw`,Fs,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}async listFleetInstances(e){return(await Promise.allSettled(e.map(async e=>({node:e,result:await this.listInstancesWithDiagnostics(e)})))).reduce((t,s,n)=>("fulfilled"===s.status?(t.items.push(...s.value.result.items),t.nodeErrors.push(...s.value.result.nodeErrors)):t.nodeErrors.push(pi(e[n],"/instances","GET",s.reason)),t),{items:[],nodeErrors:[]})}normalizeInstance(e,t){const s=(n=t,Ws.safeParse(function(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,s=new Set(Object.keys(Ws.shape)),n={};for(const[e,i]of Object.entries(t))s.has(e)&&(n[e]=i);if(t.capabilities&&"object"==typeof t.capabilities&&!Array.isArray(t.capabilities)){const e={...t.capabilities};Object.prototype.hasOwnProperty.call(e,"apps")&&delete e.apps,n.capabilities=e}else n.capabilities={};n.appInventory=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e;Object.keys(t).some(e=>!["observedAt","items","issues"].includes(e));const s={observedAt:t.observedAt,items:Array.isArray(t.items)?t.items.map(e=>{e&&"object"==typeof e&&!Array.isArray(e)&&Object.keys(e).some(e=>!["id","name","kind","source","availability","capabilities","diagnosticCode"].includes(e));const t=Ys(e,["id","name","kind","source","availability","diagnosticCode"]);return!t||"object"!=typeof t||Array.isArray(t)?t:{...t,capabilities:Ys(e.capabilities,["automation","supportsCwdSelection"])}}):t.items,issues:Array.isArray(t.issues)?t.issues.map(e=>Ys(e,["code","message"])):t.issues},n=ut.safeParse(s);return n.success?n.data:void 0}(t.appInventory,"string"==typeof t.id&&t.id),n.source=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,s=t.type,n=Wt.options.find(e=>e.shape.type.value===s);if(!n)return e;const i="local-folder"===s?["type","localFolderId","path","ownerNodeId"]:"git-template"===s?["type","url","templateId","ref","auth","clone"]:["type","repositoryId","url","provider","ref","auth","clone"],a=Ys(t,i);if("local-folder"!==s){const e=Js(Ft,Ys(t.ref,["type","name","commit"])),s=Js(Bt,Ys(t.auth,["type","secretId"])),n=Js(Ht,Ys(t.clone,["depth","submodules","lfs","subdirectory"]));e?a.ref=e:delete a.ref,s?a.auth=s:delete a.auth,n?a.clone=n:delete a.clone}const r=Js(n,a);return r?(Object.keys(t).some(e=>!i.includes(e)),r):a}(t.source,"string"==typeof t.id&&t.id),n.sourceSnapshot=t.sourceSnapshot&&"object"==typeof t.sourceSnapshot&&!Array.isArray(t.sourceSnapshot)?t.sourceSnapshot:{},n.build=Js(xt,t.build,0,0,"string"==typeof t.id&&t.id),n.aiSessions=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,s=Array.isArray(t.sessions)?t.sessions.flatMap((e,t)=>{const s=Js(ye,e);return s?[s]:[]}):[],n={runningCount:"number"==typeof t.runningCount&&Number.isInteger(t.runningCount)&&t.runningCount>=0?t.runningCount:0,waitingCount:"number"==typeof t.waitingCount&&Number.isInteger(t.waitingCount)&&t.waitingCount>=0?t.waitingCount:0,staleCount:"number"==typeof t.staleCount&&Number.isInteger(t.staleCount)&&t.staleCount>=0?t.staleCount:0,sessions:s,updatedAt:Ve.safeParse(t.updatedAt).success?t.updatedAt:(new Date).toISOString()};return Object.keys(t).some(e=>!["runningCount","waitingCount","staleCount","sessions","updatedAt"].includes(e)),be.parse(n)}(t.aiSessions,"string"==typeof t.id&&t.id),n.triggers=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e;Object.keys(t).some(e=>!["enabledCount","runningCount","errorCount","configs","recentRuns"].includes(e));const s=(e,t,s)=>Array.isArray(e)?e.flatMap((e,s)=>{const n=Js(t,e);return n?[n]:[]}):[],n=Array.isArray(t.configs)?t.configs.flatMap((e,t)=>{if(!e||"object"!=typeof e||Array.isArray(e))return[];const n=e;Object.keys(n).some(e=>!["configHash","config","deployments","runtime"].includes(e));const i=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,s=Ys(t,["configHash","name","description","createdAt","updatedAt"]);if(t.source&&"object"==typeof t.source&&!Array.isArray(t.source)){const e=t.source,n="schedule"===e.type?"interval"===e.scheduleKind?["type","scheduleKind","intervalMs"]:"daily"===e.scheduleKind?["type","scheduleKind","timeOfDay","timezone"]:["type","scheduleKind","weekdays","timeOfDay","timezone"]:"file-change"===e.type?["type","roots","globs","ignore","debounceMs"]:["type","agent","statuses","phases"];s.source=Ys(e,n)}return s.action=Ys(t.action,["promptTemplate"]),s.policy=Ys(t.policy,["cooldownMs","maxConcurrentRuns","whenBusy"]),Js(Pe,s)}(n.config);return"string"==typeof n.configHash&&i?[{configHash:n.configHash,config:i,deployments:s(n.deployments,qe),runtime:s(n.runtime,Le)}]:[]}):[];return{enabledCount:"number"==typeof t.enabledCount&&Number.isInteger(t.enabledCount)&&t.enabledCount>=0?t.enabledCount:0,runningCount:"number"==typeof t.runningCount&&Number.isInteger(t.runningCount)&&t.runningCount>=0?t.runningCount:0,errorCount:"number"==typeof t.errorCount&&Number.isInteger(t.errorCount)&&t.errorCount>=0?t.errorCount:0,configs:n,recentRuns:s(t.recentRuns,Ue)}}(t.triggers,"string"==typeof t.id&&t.id),n.apps=Js(Ws.shape.apps.unwrap(),Ys(t.apps,["runningCount","problemCount","updatedAt","revision"]),0,0,"string"==typeof t.id&&t.id)||{runningCount:0,problemCount:0},n.config=Js(Ws.shape.config.unwrap(),Ys(t.config,["autoImportAgentConfigs","defaultCodexPermissionMode"]),0,0,"string"==typeof t.id&&t.id)||{autoImportAgentConfigs:!0,defaultCodexPermissionMode:"ask"},n.modelSelection=Js(at.unwrap(),Ys(t.modelSelection,["codexModelHash","claudeModelHash"]),0,0,"string"==typeof t.id&&t.id)||{},n.imageSnapshot=function(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const s=e,n=Es(s),{reference:i,...a}=n,r=s.requestedReference??i;let o;try{o="string"==typeof r?st(r):void 0}catch{}const d={img_default:"market_taskhandoff_browser",img_codex:"market_taskhandoff_codex",img_ai:"market_taskhandoff_ai"},c="string"==typeof s.id?s.id:"string"==typeof t?t:void 0,l="market"===s.origin||c&&d[c]?"market":"custom";return void 0!==s.image||s.reference,{...a,id:c&&d[c]?d[c]:c,origin:l,repository:o?.repository??s.repository,tag:o?.tag??s.tag,requestedReference:o?.reference??r,resolvedDigest:s.resolvedDigest,resolvedReference:s.resolvedReference,downloadSizeBytes:s.downloadSizeBytes,market:"market"===l?Ys(s.market,["catalogId","catalogRevision","publisher","version"])??{catalogId:"task_handoff_embedded",catalogRevision:"legacy",publisher:"task-handoff"}:void 0}}(t.imageSnapshot,t.imageId,"string"==typeof t.id&&t.id);const i="string"==typeof t.imageId?{img_default:"market_taskhandoff_browser",img_codex:"market_taskhandoff_codex",img_ai:"market_taskhandoff_ai"}[t.imageId]||t.imageId:void 0,a=Ys(t.imageSelection,["imageId","tag"]);n.imageSelection=a||(i?{imageId:i,tag:n.imageSnapshot?.tag}:void 0),n.imageProvisioning=Js(ws,Ys(t.imageProvisioning,["phase","requestedReference","generation","error","startedAt","updatedAt"]),0,0,"string"==typeof t.id&&t.id),n.runtimeVersion=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,s=e=>{if("string"!=typeof e)return;const t=e.trim();return t&&t.length<=80?t:void 0},n=s(t.desiredVersion);if(!n)return;const i=new Set(Dt.shape.phase.options),a="string"==typeof t.phase?t.phase:"pending",r=i.has(a)?a:{idle:"pending",queued:"pending",checking:"pending",downloading:"installing",updating:"installing",completed:"matched",succeeded:"matched",ready:"matched",error:"failed"}[a]||"pending";let o;if(t.error&&"object"==typeof t.error&&!Array.isArray(t.error)){const e=t.error,n="string"==typeof e.message?e.message.trim().slice(0,4096):"";if(n){const t=new Set(jt.shape.code.options),i="string"==typeof e.code&&t.has(e.code)?e.code:"INSTANCE_RUNTIME_INSTALL_FAILED";e.code;const a=s(e.expectedVersion),r=s(e.actualVersion);o={code:i,message:n,...a?{expectedVersion:a}:{},...r?{actualVersion:r}:{},retryable:!0===e.retryable}}}"string"==typeof t.error&&t.error.trim()&&(o={code:"INSTANCE_RUNTIME_INSTALL_FAILED",message:t.error.trim().slice(0,4096),expectedVersion:n,...s(t.actualVersion)?{actualVersion:s(t.actualVersion)}:{},retryable:!1});const d=s(t.actualVersion),c=Ve.safeParse(t.lastAttemptAt),l=Ve.safeParse(t.matchedAt);return void 0!==t.lastAttemptAt&&c.success,void 0!==t.matchedAt&&l.success,{desiredVersion:n,...d?{actualVersion:d}:{},phase:r,attempt:"number"==typeof t.attempt&&Number.isInteger(t.attempt)&&t.attempt>=0?t.attempt:0,...c.success?{lastAttemptAt:c.data}:{},...l.success?{matchedAt:l.data}:{},...o?{error:o}:{}}}(t.runtimeVersion,"string"==typeof t.id&&t.id),n.workspace=Js(Gs,t.workspace,0,0,"string"==typeof t.id&&t.id)||{status:"unknown"},n.runtime=Js(Ws.shape.runtime.unwrap(),Ys(t.runtime,["kind","containerName","containerId","workspacePath","pid","port","labels"]),0,0,"string"==typeof t.id&&t.id)||{labels:{}},n.target=Js(Bs,Ys(t.target??t.endpoints,["strategy","web","api","vnc","tty","logs","status"]),0,0,"string"==typeof t.id&&t.id)||{strategy:"direct-port",status:"unknown"},n.access=Js(Hs,Ys(t.access,["strategy","web","api","ws","status"]),0,0,"string"==typeof t.id&&t.id)||{strategy:"control-plane-proxy",status:"unknown"};for(const[e,t]of Object.entries(Ws.shape))Object.prototype.hasOwnProperty.call(n,e)&&!t.safeParse(n[e]).success&&t.safeParse(void 0).success&&delete n[e];return n}(n)));var n;if(s.success)return s.data.protocolVersion&&s.data.protocolVersion!==He?(this.client.logger?.warn?.({nodeId:e.id,instanceId:s.data.id,expectedProtocolVersion:He,actualProtocolVersion:s.data.protocolVersion,errorCode:"PROTOCOL_VERSION_MISMATCH"},"node instance protocol version mismatch"),{instance:s.data}):{instance:s.data};const i=t&&"object"==typeof t?t:{},a="string"==typeof i.id?i.id:void 0;return this.client.logger?.warn?.({nodeId:e.id,instanceId:a,errorCode:"NODE_INSTANCE_PAYLOAD_INVALID",issues:s.error.issues.map(e=>({path:e.path.join("."),message:e.message}))},"node instance payload invalid"),{error:{nodeId:e.id,route:"/instances",method:"GET",code:"NODE_INSTANCE_PAYLOAD_INVALID",message:a?`Node instance ${a} payload is invalid.`:"Node instance payload is invalid.",statusCode:502,issues:s.error.issues}}}}function gi(){return(new Date).toISOString()}function hi(e){return e?e instanceof Headers?Object.fromEntries(e.entries()):Array.isArray(e)?Object.fromEntries(e.map(([e,t])=>[e,t])):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,String(t)])):{}}function fi(e){const t=String(e||"").split(":");if(3===t.length&&"ai"===t[1]&&t[0]&&t[2])return{instanceId:t[0],aiSessionId:t[2]}}function Ii(e){return e instanceof Error?e.message:String(e)}function yi(e,t){const s=new Error(t);throw Object.assign(s,{statusCode:404,code:e}),s}class bi{requireNode;nodeAgentRequest;nodeAgentStreamRequest;fetchImpl;constructor(e){this.requireNode=e.requireNode,this.nodeAgentRequest=e.nodeAgentRequest,this.nodeAgentStreamRequest=e.nodeAgentStreamRequest,this.fetchImpl=e.fetchImpl}async request(e,t,s={}){if(vi(e),!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=new Error(`Instance ${e.name} is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_UNREACHABLE"}),t}const n=this.requireNode(e.nodeId),i=await this.nodeAgentRequest(n,`/instances/${encodeURIComponent(e.id)}/proxy`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({path:`/api/${t.replace(/^\/+/,"")}`,method:s.method||"GET",headers:hi(s.headers),body:(a=s.body,"string"==typeof a?a:null==a?void 0:String(a))})});var a;const r=await i.json().catch(()=>({}));if(!i.ok){const s=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,s="string"==typeof t.code&&t.code.trim()?t.code:void 0,n="string"==typeof t.message&&t.message?t.message:void 0,i=t.details&&"object"==typeof t.details&&!Array.isArray(t.details)?t.details:void 0;return s||n?{code:s,message:n,details:i}:void 0}(r.error),n=s?.message||function(e){if(!e||"object"!=typeof e||Array.isArray(e))return"";const t=e.message;return"string"==typeof t?t:""}(r.error)||`Instance request failed with HTTP ${i.status}`,a=new Error(n);throw Object.assign(a,{statusCode:i.status,code:s?.code||"INSTANCE_REQUEST_FAILED",...s?.details?{details:s.details}:{},instanceId:e.id,nodeId:e.nodeId,route:t}),a}return r.data??r}async proxyHttp(e,t,s={}){if(vi(e),"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=new Error(`Instance ${e.name} is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_UNREACHABLE"}),t}if(!e.target.web){const t=new Error(`Instance ${e.name} web endpoint is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_WEB_UNREACHABLE"}),t}const n=this.requireNode(e.nodeId),i=await this.nodeAgentStreamRequest(n,`/instances/${encodeURIComponent(e.id)}/proxy/stream`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({path:t,method:s.method||"GET",headers:hi(s.headers),...(a=s.body,null==a?{}:"string"==typeof a?{body:a}:Buffer.isBuffer(a)?{bodyBase64:a.toString("base64")}:a instanceof Uint8Array?{bodyBase64:Buffer.from(a).toString("base64")}:{body:String(a)})})});var a;return{status:i.status||502,headers:Object.fromEntries(i.headers.entries()),body:i.body}}proxyWebSocket(e,t,s,n,i,a={}){if(vi(e),!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=new Error(`Instance ${e.name} web endpoint is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_WEB_UNREACHABLE"}),t}const r=this.requireNode(e.nodeId),o=n.startsWith("/")?n.slice(1):n;t.proxyWebSocket(r,s,`/instances/${encodeURIComponent(e.id)}/proxy/ws/${o}`,i,a)}async reportHeartbeat(e,t,s){if(!e.registrationToken)return;const n=this.requireNode(e.nodeId),i=`/instances/${encodeURIComponent(e.id)}/heartbeat`,a={method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.registrationToken}`},body:JSON.stringify(t)},r="reverse-wss"===n.connectionMode?await(s?.request(n,i,a)):await this.fetchImpl(`${(n.controlEndpoint||n.endpoint||"").replace(/\/$/,"")}/api/node-agent${i}`,a);if(!r)return;const o=await r.json().catch(()=>({}));if(!r.ok){const e=new Error(o.error?.message||`Instance heartbeat sync failed with HTTP ${r.status}`);throw Object.assign(e,{statusCode:r.status,code:"INSTANCE_HEARTBEAT_SYNC_FAILED"}),e}}}function vi(e){if(nn(e))return;const t=new Error(`Instance ${e.name} is not ready.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_NOT_READY",runtimePhase:e.runtimeVersion?.phase}),t}function wi(e){if(!e||"object"!=typeof e)return;const t=e,s=xt.safeParse(t.build),n=mt.safeParse(t.platform),i=gt.safeParse(t.arch);return{..."boolean"==typeof t.ok?{ok:t.ok}:{},..."string"==typeof t.role?{role:t.role}:{},..."string"==typeof t.nodeId?{nodeId:t.nodeId}:{},..."string"==typeof t.protocolVersion?{protocolVersion:t.protocolVersion}:{},...n.success?{platform:n.data}:{},...i.success?{arch:i.data}:{},...s.success?{build:s.data}:{},..."string"==typeof t.serverTime?{serverTime:t.serverTime}:{}}}function Si(e){const{agent:t,...s}=e.capabilities,{secret:n,...i}=e.auth,a=i.pairing?{...i.pairing,joinToken:void 0}:void 0,r=wi(t);return{...e,auth:a?{...i,pairing:a}:i,capabilities:r?{...s,agent:r}:s}}function Ai(e){return"local-folder"===e.type?{mode:"local-bind",path:"/workspace",readOnly:!1}:{mode:"git-clone",path:"/workspace",readOnly:!1}}function ki(e){return function(e){const{registrationToken:t,target:s,agentStatus:n,targetStatus:i,uiAccessStatus:a,...r}=e;return r}(function(e){const t=`/instances/${encodeURIComponent(e.id)}`,s={strategy:"control-plane-proxy",web:`${t}/`,api:`${t}/api`,ws:`${t}/api`,status:"online"===e.connectionStatus||"online"===e.agentStatus?"reachable":"endpoint-unreachable"};return{...e,access:s}}(e))}function zi(e){const{key:t,...s}=e;return{...s,keyPreview:(n=e.key,n.length<=8?"set":`${n.slice(0,4)}...${n.slice(-4)}`),keySet:!0};var n}class Ti{read(e){const t=new Map(e.projects.map(e=>[e.id,e])),s=new Map(e.images.map(e=>[e.id,e])),n=new Map(e.nodes.map(e=>[e.id,e])),i=new Map(e.runtimes.map(e=>[`${e.nodeId}:${e.id}`,e])),a=Date.now();return{items:e.instances.map(e=>function(e){const t=e.instance.lastHeartbeatAt?e.currentTime-Date.parse(e.instance.lastHeartbeatAt):void 0,s=void 0!==t&&t>3e4?"offline":e.instance.connectionStatus,n=ki(e.instance);return{...n,aiSessions:Se(n.aiSessions),connectionStatus:s,heartbeatAgeMs:t,project:e.project,image:e.image,node:e.node?Si(e.node):void 0,runtime:e.runtime,protocolCompatible:!e.instance.protocolVersion||e.instance.protocolVersion===He}}({instance:e,currentTime:a,project:e.projectId?t.get(e.projectId):void 0,image:e.imageSnapshot||(e.imageSelection?s.get(e.imageSelection.imageId):void 0),node:n.get(e.nodeId),runtime:i.get(`${e.nodeId}:${e.runtimeId}`)})),nodeErrors:e.nodeErrors||[]}}}const Ci=Pe.extend({id:Pe.shape.configHash}),Ei=r.z.object({name:Pe.shape.name,description:Pe.shape.description,source:je,action:De,policy:Me.partial().optional()}).strict(),Ni=r.z.object({instanceIds:r.z.array(Ws.shape.id).min(1),target:$e,enabled:r.z.boolean().optional()}).strict(),_i=r.z.object({configHash:Pe.shape.configHash,enabled:r.z.boolean().optional()}).strict();class xi{triggers;listInstances;requireInstance;instanceRequest;constructor(e){this.triggers=e.triggers,this.listInstances=e.listInstances,this.requireInstance=e.requireInstance,this.instanceRequest=e.instanceRequest}async listTriggers(){const e=await this.listInstances(),t=new Map;for(const e of this.triggers.list())t.set(e.configHash,{configHash:e.configHash,config:e,deploymentCount:0,enabledCount:0,runningCount:0,errorCount:0,ownedByControlPlane:!0,controlPlaneDeploymentCount:0,deployments:[],recentRuns:[]});for(const s of e){const e=s.triggers;for(const n of e?.configs||[]){const i=t.get(n.configHash)||{configHash:n.configHash,config:n.config,deploymentCount:0,enabledCount:0,runningCount:0,errorCount:0,ownedByControlPlane:!1,controlPlaneDeploymentCount:0,deployments:[],recentRuns:[]},a=(n.deployments||[]).map(e=>({instanceId:s.id,instanceName:s.name,deployment:e,runtime:(n.runtime||[]).find(t=>t.deploymentId?t.deploymentId===e.deploymentId:t.configHash===e.configHash)}));i.deploymentCount+=a.length,i.enabledCount+=a.filter(e=>Boolean(e.deployment.enabled)).length,i.controlPlaneDeploymentCount+=a.filter(e=>"control-plane"===e.deployment.origin).length,i.runningCount+=(n.runtime||[]).filter(e=>"running"===e.status).length,i.errorCount+=(n.runtime||[]).filter(e=>"error"===e.status).length,i.deployments.push(...a),i.recentRuns.push(...(e.recentRuns||[]).filter(e=>e.configHash===n.configHash).map(e=>({...e,instanceId:s.id,instanceName:s.name}))),t.set(n.configHash,i)}}return{updatedAt:(new Date).toISOString(),triggers:[...t.values()].sort((e,t)=>String(e.config.name||e.configHash).localeCompare(String(t.config.name||t.configHash)))}}createTrigger(e){const t=Ei.parse(e||{}),s=gi(),n={maxConcurrentRuns:t.policy?.maxConcurrentRuns??1,whenBusy:t.policy?.whenBusy??"skip",cooldownMs:t.policy?.cooldownMs},i=function(e){const t=Be({source:e.source,action:e.action,policy:Me.parse(e.policy||{})});return`trg_${o.createHash("sha256").update(JSON.stringify(t)).digest("hex").slice(0,24)}`}({source:t.source,action:t.action,policy:n}),a=Ci.parse({id:i,configHash:i,name:t.name,description:t.description,source:t.source,action:t.action,policy:n,createdAt:s,updatedAt:s});return this.triggers.put(a)}async deleteTrigger(e){const t=this.triggers.delete(e),s=await this.listInstances(),n=[];for(const t of s){const s=t.triggers?.configs?.find(t=>t.configHash===e);if(!s)continue;const i=(s.deployments||[]).filter(e=>"control-plane"===e.origin);if(!i.length)continue;const a=s.deployments.every(e=>"control-plane"===e.origin);for(const s of i)try{const i=s.deploymentId?`/triggers/${encodeURIComponent(e)}/deployments/${encodeURIComponent(s.deploymentId)}`:a?`/triggers/${encodeURIComponent(e)}`:void 0;if(!i){n.push({instanceId:t.id,configHash:e,deploymentId:s.deploymentId,deleted:!1,skipped:"missing-deployment-id"});continue}const r=await this.instanceRequest(t,i,{method:"DELETE"});if(n.push({instanceId:t.id,configHash:e,deploymentId:s.deploymentId,deleted:!0,data:r}),!s.deploymentId)break}catch(i){n.push({instanceId:t.id,configHash:e,deploymentId:s.deploymentId,deleted:!1,error:i instanceof Error?i.message:String(i)})}}return t||n.length||yi("TRIGGER_NOT_FOUND",`Trigger ${e} was not found.`),{configHash:e,deletedTemplate:t,results:n}}async applyTrigger(e,t){const s=this.requireTrigger(e),n=Ni.parse(t||{}),i=[];for(const e of n.instanceIds){const t=await this.requireInstance(e);i.push({instanceId:e,data:await this.instanceRequest(t,"/triggers",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:s.name,description:s.description,source:s.source,action:s.action,policy:s.policy,deployment:{origin:"control-plane",enabled:n.enabled??!0,target:n.target}})})})}return{configHash:e,results:i}}async listInstanceTriggers(e){const t=await this.requireInstance(e);return this.instanceRequest(t,"/triggers")}async bindAiSessionTrigger(e,t,s){const n=_i.parse(s||{}),i=this.requireTrigger(n.configHash),a=await this.requireInstance(e);return this.instanceRequest(a,"/triggers",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:i.name,description:i.description,source:i.source,action:i.action,policy:i.policy,deployment:{deploymentId:Oi(t,n.configHash),origin:"control-plane",enabled:n.enabled??!0,target:{type:"ai-session",aiSessionId:t}}})})}async unbindAiSessionTrigger(e,t,s){const n=await this.requireInstance(e),i=Oi(t,s);return this.instanceRequest(n,`/triggers/${encodeURIComponent(s)}/deployments/${encodeURIComponent(i)}`,{method:"DELETE"})}async runInstanceTrigger(e,t,s={}){const n=await this.requireInstance(e);return this.instanceRequest(n,`/triggers/${encodeURIComponent(t)}/run`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s||{})})}requireTrigger(e){const t=this.triggers.get(e);return t||yi("TRIGGER_NOT_FOUND",`Trigger ${e} was not found.`),t}}function Oi(e,t){return`ai_${Buffer.from(e).toString("base64url")}_${t}`}const Ri=r.z.object({id:Jt.shape.id.optional(),name:Jt.shape.name,source:Wt,defaultImageSelection:Jt.shape.defaultImageSelection,defaultNodeId:Jt.shape.defaultNodeId,defaultRuntimeId:Jt.shape.defaultRuntimeId,workspacePolicy:Vt.optional(),labels:Jt.shape.labels.optional()}).strict(),ji=Ri.omit({id:!0}).partial().strict();function Di(e){return 1===Array.from(e).length&&!/[\p{L}\p{N}\s]/u.test(e)&&"/"!==e&&"\\"!==e}const Mi=r.z.string().refine(Di,"Mention trigger must be one printable non-alphanumeric character other than / or \\.");function Pi(e){return 1===Array.from(e).length&&!/[\p{L}\p{N}\s]/u.test(e)&&"\\"!==e}const $i=r.z.string().refine(Pi,"Command trigger must be one printable non-alphanumeric character other than \\."),qi=r.z.object({publicBaseUrl:r.z.string().trim().max(2048).optional(),updateChannel:Ot.default("stable"),mentionTrigger:Mi.default("@"),commandTrigger:$i.default("/")}).strict().refine(e=>e.mentionTrigger!==e.commandTrigger,{path:["commandTrigger"],message:"Command and mention triggers must be different."}),Li=r.z.object({publicBaseUrl:r.z.string().trim().max(2048).optional(),updateChannel:Ot.optional(),mentionTrigger:Mi.optional(),commandTrigger:$i.optional()}).strict();function Ui(e){if(!e||"object"!=typeof e||Array.isArray(e))return{};const t={...e};return"string"==typeof t.mentionTrigger&&Di(t.mentionTrigger)||(t.mentionTrigger="@"),"string"==typeof t.commandTrigger&&Pi(t.commandTrigger)&&t.commandTrigger!==t.mentionTrigger||(t.commandTrigger="/"),t}const Fi=r.z.object({id:Is.shape.id.optional(),name:Is.shape.name,description:Is.shape.description,cover:Is.shape.cover,reference:Is.shape.reference,pullPolicy:Is.shape.pullPolicy.optional(),capabilities:r.z.array(r.z.string().trim().min(1).max(80)).optional(),optionalApps:r.z.array(r.z.string().trim().min(1).max(120)).optional(),defaultEnv:Is.shape.defaultEnv.optional(),labels:Is.shape.labels.optional()}).strict(),Bi=Fi.omit({id:!0}).partial().strict();class Hi{options;constructor(e){this.options=e}getSettings(){return qi.parse(this.options.settings.get())}updateSettings(e){const t=Li.parse(e),s=qi.parse({...this.getSettings(),...t,..."publicBaseUrl"in t?{publicBaseUrl:t.publicBaseUrl?.trim()||void 0}:{}});return this.options.settings.put(s)}seedDefaults(){}listProjects(){return this.options.projects.list().map(e=>this.normalizeProjectRecord(e))}getProject(e){const t=this.options.projects.get(e);return t?this.normalizeProjectRecord(t):void 0}createProject(e){const t=Ri.parse(e),s=gi(),n=Wt.parse(t.source);return this.options.projects.put(Jt.parse({...t,id:t.id||gn("proj"),source:n,defaultImageSelection:t.defaultImageSelection||{imageId:"market_taskhandoff_browser"},defaultNodeId:t.defaultNodeId||this.options.defaultNodeId(),defaultRuntimeId:t.defaultRuntimeId||"runtime_local_docker",workspacePolicy:Vt.parse(t.workspacePolicy||Ai(n)),labels:t.labels||{},createdAt:s,updatedAt:s}))}updateProject(e,t){const s=ji.parse(t),n=this.requireProject(e),i=s.source?Wt.parse(s.source):n.source;return this.options.projects.put(Jt.parse({...n,...s,id:e,source:i,workspacePolicy:s.workspacePolicy?Vt.parse(s.workspacePolicy):n.workspacePolicy,createdAt:n.createdAt,updatedAt:gi()}))}deleteProject(e){return this.options.projects.delete(e)}requireProject(e){const t=this.getProject(e);return t||yi("PROJECT_NOT_FOUND",`Project ${e} was not found.`),t}listImages(){return this.options.images.list()}getMarketCatalog(){return{catalog:this.options.market.getCatalog(),status:this.options.market.getStatus()}}listImageOptions(e){return[...this.options.market.getCatalog().items.map(t=>this.marketImageOption(t,void 0,e,!0)),...this.listImages().map(e=>this.customImageOption(e))]}resolveImageSelection(e,t){const s=this.options.market.getCatalog().items.find(t=>t.id===e.imageId);if(s)return this.marketImageOption(s,e.tag,t);const n=this.options.images.get(e.imageId);if(n||yi("IMAGE_NOT_FOUND",`Image ${e.imageId} was not found.`),e.tag&&e.tag!==n.tag)throw Object.assign(new Error(`Custom image ${n.id} cannot override its stored tag.`),{statusCode:400,code:"CUSTOM_IMAGE_TAG_OVERRIDE"});return this.customImageOption(n)}createImage(e){const t=Fi.parse(e),s=gi(),n=st(t.reference);return this.options.images.put(Is.parse({...t,id:t.id||gn("img"),origin:"custom",reference:n.reference,repository:n.repository,tag:n.tag,pullPolicy:t.pullPolicy||"if-not-present",capabilities:t.capabilities||[],optionalApps:t.optionalApps||[],defaultEnv:t.defaultEnv||{},labels:t.labels||{},createdAt:s,updatedAt:s}))}updateImage(e,t){const s=Bi.parse(t),n=this.requireImage(e),i=st(s.reference||n.reference);return this.options.images.put(Is.parse({...n,...s,id:e,origin:"custom",reference:i.reference,repository:i.repository,tag:i.tag,createdAt:n.createdAt,updatedAt:gi()}))}deleteImage(e){return this.options.images.delete(e)}requireImage(e){if(this.options.market.getCatalog().items.some(t=>t.id===e))throw Object.assign(new Error(`Market image ${e} is read-only.`),{statusCode:403,code:"MARKET_IMAGE_READ_ONLY"});const t=this.options.images.get(e);return t||yi("IMAGE_NOT_FOUND",`Image ${e} was not found.`),t}normalizeProjectRecord(e){const t=function(e){if(e&&"object"==typeof e&&!Array.isArray(e)){const t={...ct(e)};return"defaultRuntimeId"in t||(t.defaultRuntimeId="runtime_local_docker"),Jt.parse(t)}return Jt.parse(e)}(e);return t!==e&&this.options.projects.put(t),t}customImageOption(e){return bs.parse({id:e.id,origin:"custom",name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:e.cover||ds,repository:e.repository,tag:e.tag,availableTags:e.tag?[{name:e.tag,reference:e.reference,status:"active"}]:[],reference:e.reference,capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:e.defaultEnv,labels:e.labels,readOnly:!1})}marketImageOption(e,t,s,n=!1){if("yanked"===e.status&&!n)throw Object.assign(new Error(`Market image ${e.id} is no longer selectable.`),{statusCode:409,code:"MARKET_IMAGE_YANKED"});const i=t||e.defaultTag,a=e.tags.find(e=>e.name===i);if(!a)throw Object.assign(new Error(`Market image ${e.id} does not provide tag ${i}.`),{statusCode:400,code:"MARKET_IMAGE_TAG_NOT_FOUND"});if("yanked"===a.status&&!n)throw Object.assign(new Error(`Market image tag ${e.id}:${i} is no longer selectable.`),{statusCode:409,code:"MARKET_IMAGE_TAG_YANKED"});const r=a.platforms.find(e=>!(s?.os&&e.os!==s.os||s?.architecture&&e.architecture!==s.architecture)),o=this.options.market.getCatalog();return bs.parse({id:e.id,origin:"market",name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:e.cover||ds,repository:e.repository,tag:a.name,availableTags:e.tags.map(e=>({name:e.name,version:e.version,reference:e.reference,manifestDigest:e.manifestDigest,status:e.status})),reference:a.reference,digest:r?.digest||a.manifestDigest,downloadSizeBytes:r?.downloadSizeBytes,unpackedSizeBytes:r?.unpackedSizeBytes,capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:e.defaultEnv,labels:e.labels,readOnly:!0,lifecycleStatus:"yanked"===a.status||"yanked"===e.status?"yanked":"deprecated"===a.status||"deprecated"===e.status?"deprecated":"active",market:{catalogId:o.catalogId,catalogRevision:o.revision,publisher:e.publisher,version:a.version}})}}function Gi(e){const t=st(e.reference),s=t.tag||"latest";return{id:e.id,publisher:"task-handoff",slug:e.slug,name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:ds,repository:t.repository,defaultTag:s,tags:[{name:s,reference:t.reference,platforms:[],status:"active"}],capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:{},labels:{"task-handoff.image.kind":"controlled-instance","task-handoff.image.profile":e.slug},status:"active"}}function Wi(){const e=(t=process.env.TASK_HANDOFF_BUILT_AT)&&!Number.isNaN(Date.parse(t))?new Date(t).toISOString():(new Date).toISOString();var t;return hs.parse({protocolVersion:"2026-07-29",catalogId:"task_handoff_embedded",revision:process.env.TASK_HANDOFF_VERSION||"0.0.1",source:"embedded",generatedAt:e,items:[Gi({id:"market_taskhandoff_codex",slug:"codex",name:"TaskHandoff Codex",description:"Minimal Codex runtime with terminal and Codex.",localizedDescriptions:{"zh-CN":"最小 Codex 运行环境,包含终端和 Codex。"},reference:process.env.TASK_HANDOFF_CONTROLLED_CODEX_IMAGE||"huadream/task-handoff-controlled-codex:latest",capabilities:["terminal","codex"],optionalApps:["terminal-tty"]}),Gi({id:"market_taskhandoff_ai",slug:"ai",name:"TaskHandoff Codex + Claude",description:"AI development runtime with Codex, Claude, and terminal.",localizedDescriptions:{"zh-CN":"AI 开发运行环境,包含 Codex、Claude 和终端。"},reference:process.env.TASK_HANDOFF_CONTROLLED_AI_IMAGE||"huadream/task-handoff-controlled-ai:latest",capabilities:["terminal","codex","claude"],optionalApps:["terminal-tty"]}),Gi({id:"market_taskhandoff_browser",slug:"browser",name:"TaskHandoff Browser",description:"Full runtime with Codex, Claude, Chromium, VNC, and code-server.",localizedDescriptions:{"zh-CN":"完整运行环境,包含 Codex、Claude、Chromium、VNC 和 code-server。"},reference:process.env.TASK_HANDOFF_CONTROLLED_BROWSER_IMAGE||"huadream/task-handoff-controlled-browser:latest",capabilities:["browser","terminal","gui-terminal","vscode-web","codex","claude"],optionalApps:["chromium","terminal-tty","gui-terminal","vscode-web"]})]})}class Vi{async loadCatalog(){return Wi()}}class Ki{snapshot;status;constructor(e=Wi()){this.snapshot=hs.parse(e),this.status=fs.parse({source:this.snapshot.source,state:"ready",revision:this.snapshot.revision,updatedAt:this.snapshot.generatedAt})}getCatalog(){return this.snapshot}getStatus(){return this.status}acceptCandidate(e){const t=hs.safeParse(e);return t.success?(this.snapshot=t.data,this.status=fs.parse({source:t.data.source,state:"ready",revision:t.data.revision,updatedAt:(new Date).toISOString()}),{accepted:!0,snapshot:t.data}):(this.status=fs.parse({...this.status,state:"failed",updatedAt:(new Date).toISOString(),error:t.error.message}),{accepted:!1,error:t.error})}recordRefreshFailure(e){return this.status=fs.parse({...this.status,state:this.snapshot?"stale":"failed",updatedAt:(new Date).toISOString(),error:e instanceof Error?e.message:String(e)}),this.snapshot}async refresh(e){try{const t=await e.loadCatalog();return this.acceptCandidate(t)}catch(e){return this.recordRefreshFailure(e),{accepted:!1,error:e}}}}class Ji{records=new Map;options;constructor(e){this.options=e}create(e){const t=o.randomBytes(this.options.tokenBytes).toString("base64url"),s={...e,token:t,expiresAt:new Date(Date.now()+(e.ttlMs||this.options.ttlMs)).toISOString()};return this.records.set(t,s),s}resolve(e,t){const s=this.records.get(e);if(!s)throw this.invalidError();if(s.expiresAt<=(new Date).toISOString())throw this.records.delete(e),this.invalidError();if(t&&!t(s))throw this.invalidError();return s}invalidError(){const e=new Error(this.options.invalidMessage);return Object.assign(e,{statusCode:this.options.invalidStatusCode||400,code:this.options.invalidCode}),e}}class Qi{requireInstance;listAppSessions;tokens=new Ji({invalidMessage:"App access link is invalid or expired.",invalidCode:"APP_ACCESS_TOKEN_INVALID",invalidStatusCode:401,tokenBytes:24,ttlMs:9e5});constructor(e){this.requireInstance=e.requireInstance,this.listAppSessions=e.listAppSessions}createToken(e){return this.tokens.create({instanceId:e.instanceId,sessionId:e.sessionId,mode:e.mode,ttlMs:e.ttlMs})}resolveToken(e,t){return this.tokens.resolve(e,e=>!t||e.mode===t)}async proxyTarget(e,t,s=""){const n=this.resolveToken(e,t),i=await this.requireInstance(n.instanceId),a=await this.listAppSessions(),r=a.instances.find(e=>e.instanceId===n.instanceId)?.appSessions.sessions.find(e=>Zi(e.id)===n.sessionId);if(!r){const e=new Error("App session was not found.");throw Object.assign(e,{statusCode:404,code:"APP_SESSION_NOT_FOUND"}),e}return{access:n,instance:i,session:r,path:Yi(t,n.sessionId,r,s)}}}function Yi(e,t,s,n=""){return"tty"===e?`/api/apps/sessions/${encodeURIComponent(t)}/tty`:"vnc"===e?n?function(e,t){const s=`/${t.replace(/^\/+/,"")}`,n=s.split("?",1)[0]||"/",i=encodeURIComponent(e);if(!["/api/novnc/",`/api/apps/sessions/${i}/web/`,`/api/apps/sessions/${i}/vnc`,`/api/apps/sessions/${i}/novnc/`].some(e=>n===e.replace(/\/$/,"")||n.startsWith(e))){const e=new Error("App access token cannot be used for this path.");throw Object.assign(e,{statusCode:403,code:"APP_ACCESS_PATH_FORBIDDEN"}),e}return s}(t,n):Zi(Xi(s.vnc).noVncPath)||`/api/apps/sessions/${encodeURIComponent(t)}/web/`:Zi(Xi(s.web).webPath)||`/api/apps/sessions/${encodeURIComponent(t)}/web/`}function Xi(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function Zi(e){return"string"==typeof e&&e.trim()?e.trim():void 0}const ea={allow:"a",deny:"d",skip:"s"};function ta(e){return o.createHash("sha256").update(e).digest("base64url").slice(0,Math.ceil(16))}class sa{tokens=new Ji({invalidMessage:"Chat action expired.",invalidCode:"CHAT_ACTION_TOKEN_INVALID",tokenBytes:9,ttlMs:6e5});create(e){return this.tokens.create({type:e.type,instanceId:"instanceId"in e?e.instanceId:void 0,appId:"appId"in e?e.appId:void 0,routeId:"routeId"in e?e.routeId:void 0,decision:"decision"in e?e.decision:void 0,ttlMs:e.ttlMs})}resolve(e,t){return this.tokens.resolve(e,e=>!t||e.type===t)}pendingDecisionCallbackData(e,t){return`task_handoff:cp_p:v1:${ea[t]}:${ta(e)}`}}function na(e){const{token:t,...s}=e;return{...s,settings:aa(e.settings),tokenSet:Boolean(e.token)}}function ia(e,t){if(!t)return e;const s={...e};for(const[e,n]of Object.entries(t))void 0!==n&&""!==n&&(s[e]=n);return s}function aa(e){const t={...e};return"string"==typeof t.clientSecret&&t.clientSecret&&(delete t.clientSecret,t.clientSecretSet=!0),t}const ra=r.z.object({name:Zs.shape.name.optional(),channel:Zs.shape.channel.optional(),enabled:r.z.boolean().optional(),token:Zs.shape.token,defaultChatId:Zs.shape.defaultChatId,allowedUserIds:r.z.array(r.z.string().trim().min(1).max(240)).optional(),pollIntervalMs:Zs.shape.pollIntervalMs.optional(),settings:Zs.shape.settings.optional()}).strict(),oa=ra.extend({channel:Zs.shape.channel,name:Zs.shape.name.optional()});class da{chatBridges;chatSessions;constructor(e){this.chatBridges=e.chatBridges,this.chatSessions=e.chatSessions}list(){return this.chatBridges.list().map(na)}require(e){const t=this.chatBridges.get(e);return t||yi("CHAT_BRIDGE_NOT_FOUND",`Chat bridge ${e} was not found.`),t}create(e){const t=oa.parse(e),s=gi(),n=Zs.parse({...t,id:gn(`chat_${t.channel}`),name:t.name||(i=t.channel,a=this.chatBridges.list().filter(e=>e.channel===t.channel).length+1,`${"telegram"===i?"Telegram":"wechat"===i?"WeChat":"dingding"===i?"DingDing":"Web"} ${a}`),enabled:t.enabled??!1,allowedUserIds:t.allowedUserIds||[],pollIntervalMs:t.pollIntervalMs||3e3,settings:ia({},t.settings),createdAt:s,updatedAt:s});var i,a;return this.chatBridges.put(n),na(n)}update(e,t){const s=ra.parse(t),n=this.require(e),i=ia(n.settings,s.settings),a=Zs.parse({...n,...s,id:e,channel:n.channel,name:s.name||n.name,settings:i,updatedAt:gi()});return this.chatBridges.put(a),na(a)}delete(e){const t=this.chatBridges.delete(e);if(t)for(const t of this.chatSessions.list())t.bridgeId===e&&this.chatSessions.delete(t.id);return t}}class ca{chatSessions;constructor(e){this.chatSessions=e.chatSessions}list(){return this.chatSessions.list()}require(e){const t=this.chatSessions.get(e);return t||yi("CHAT_SESSION_NOT_FOUND",`Chat session ${e} was not found.`),t}upsert(e){const t=gi(),s=(n=e.channel,i=e.chatSessionId,`${e.bridgeId||n}:${i.replace(/[^a-zA-Z0-9_.:-]/g,"_")}`);var n,i;const a=this.chatSessions.get(s),r=en.parse({...a||{},...e,id:s,channel:e.channel,bridgeId:e.bridgeId,chatSessionId:e.chatSessionId,lastUsedAt:t,createdAt:a?.createdAt||t,updatedAt:t});return this.chatSessions.put(r),r}}function la(e){return{inline_keyboard:e.map(e=>e.map(e=>({text:e.text,...e.url?{url:e.url}:{callback_data:e.callbackData||""}})))}}function pa(e,t=40){return la(e.slice(0,t).map(e=>[e]))}function ua(e){return Buffer.from(e,"utf8").toString("base64url")}function ma(e){const t=ha(e).inline_keyboard,s=Array.isArray(t)?t:[],n=[];for(const e of s)if(Array.isArray(e))for(const t of e){const e=ha(t),s=String(e.callback_data||""),i=String(e.text||"").trim();i&&s&&n.push({text:i,id:`th_cb_${ua(s)}`})}return n.slice(0,40)}function ga(e,t){const s=String(e||"").replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,Math.max(0,t-3))}...`}function ha(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}const fa=new Set(["stopped","failed","exited","closed","terminated"]);function Ia(e){return!function(e){return fa.has(ni(e))}(e)}function ya(e){return(e.aiSessions?.sessions||[]).filter(t=>function(e,t){const s=(e.apps?.sessions||[]).filter(ka);if(t.appSessionId&&s.some(e=>e.id===t.appSessionId))return!0;const n=new Set(t.appBindingKeys||[]);return Boolean(n.size&&s.some(e=>function(e){return[...si(e).bindings.map(ii),...ri(e)].filter(Boolean)}(e).some(e=>n.has(e))))}(e,t))}function ba(e){return"waiting"===e.status?4:"failed"===e.status?3:"running"===e.status?2:"idle"===e.status?1:0}function va(e){return[e.cwd||"",e.agent||"",e.providerSessionId||e.id].join("\0")}function wa(e){const t=e.session.title||e.session.userPrompt||e.session.summary||e.session.lastMessage||"",s=t?` - ${ga(t,18)}`:"";return`${e.instance.name} - ${e.session.agent} ${e.session.status}${s}`}function Sa(e,t=(e,t)=>`task_handoff:approval:${e.id}:${t}`){return pa(e.flatMap(e=>"approval"!==e.kind?[]:[{text:ga(`Allow ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"allow")},{text:ga(`Skip ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"skip")},{text:ga(`Deny ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"deny")}]))}function Aa(e){const t=`${e.project?.name||e.projectId} / ${e.instance?.name||e.instanceId}`,s="approval"===e.kind?`/approve ${e.id} | /deny ${e.id} | /skip ${e.id}`:`/reply ${e.id} <message>`;return`[${t}] ${e.kind} ${e.id}: ${e.result}\n${s}`}function ka(e){const t=e&&"object"==typeof e&&!Array.isArray(e)?e:{};return Ia("string"==typeof t.status?t.status:void 0)}class za{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async handleAiSessionsCommand(e){const t=await this.targets.aiSessionChoices(e),s=t.find(e=>e.active),n=t.map(e=>function(e){const t=e.session.userPrompt||e.session.summary||e.session.lastMessage||"",s=e.active?"* ":"",n=e.session.phase&&"unknown"!==e.session.phase?`/${e.session.phase}`:"";return`${s}${e.index+1}. ${e.projectName} / ${e.instance.name} - ${e.session.agent} - ${e.session.status}${n} - ${e.session.id}${t?` - ${t}`:""}`}(e));return{accepted:!0,routed:!1,binding:e,instances:t.map(e=>e.instance),reply:t.length?["Select AI session",`Current ${s?`${s.projectName} / ${s.instance.name} / ${s.session.id}`:await this.targets.renderChatTarget(e)}, total ${t.length}`,"",...n].join("\n"):"No AI sessions found.",replyMarkup:t.length?pa(t.map(e=>({text:`${e.active?"✓ ":""}${wa(e)}`,callbackData:`task_handoff:cp_session:${e.index}`}))):void 0}}async handleAiSessionCommand(e,t){const s=String(t[0]||"").trim();if(!s)return this.handleAiSessionsCommand(e);const n=await this.targets.findAiSession(s);if(!n)return{accepted:!1,routed:!1,binding:e,reply:`AI session ${s} was not found. Try /sessions.`};const i=n.instance.projectId?this.deps.getProject(n.instance.projectId):void 0;return{accepted:!0,routed:!1,binding:this.deps.upsertChatSession({...e,activeProjectId:i?.id||e.activeProjectId,activeInstanceId:n.instance.id,activeAiSessionId:n.session.id}),instance:n.instance,aiSession:n.session,reply:`Active AI session: ${n.instance.name} / ${n.session.agent} ${n.session.id}`}}async handleAiSessionIndexAction(e,t){const s=(await this.targets.aiSessionChoices(e)).find(e=>e.index===t);if(!s)return{accepted:!1,routed:!1,binding:e,reply:"AI session was not found. Use /session to refresh the list.",replyMarkup:void 0};const n=this.deps.upsertChatSession({...e,activeProjectId:s.projectId||e.activeProjectId,activeInstanceId:s.instance.id,activeAiSessionId:s.session.id}),i=await this.handleAiSessionsCommand(n);return{...i,accepted:!0,binding:n,instance:s.instance,aiSession:s.session,message:`selected ${s.session.id}`,reply:[`Current chat is bound to AI session ${s.session.id}`,"",i.reply].join("\n")}}async handleAiSessionInterruptCommand(e){if(!e.activeInstanceId||!e.activeAiSessionId)return{accepted:!1,routed:!1,binding:e,reply:"No active AI session is selected. Use /sessions and /session <id> first."};const t=await this.deps.requireControlledInstance(e.activeInstanceId,!0),s=await this.deps.interruptAiSession(t.id,e.activeAiSessionId);return{accepted:!0,routed:!0,binding:e,instance:ki(t),aiSession:s,reply:`Interrupt sent to ${t.name} / ${e.activeAiSessionId}.`}}}function Ta(e){return e.providerTurnId||e.turnId||""}function Ca(e){return Ia(Oa(e.status))}function Ea(e,t){const s=Oa(t.appId)||"App",n=Oa(t.title).trim();if(n&&n!==s)return n;const i=function(e,t){for(const s of e.appInventory?.items||[])if(s.id===t)return s.name;return""}(e,s);return i||s}function Na(e){const t=function(e){return"online"!==e.connectionStatus?[]:(e.appInventory?.items||[]).filter(e=>"available"===e.availability)}(e),s=new Map;for(const e of t){const t=Ra(e);t&&!s.has(t.id)&&s.set(t.id,t)}return[...s.values()]}function _a(e,t,s,n){const i=Oa(t.id)?function(e,t){return`/apps/access/${xa(e)}?token=${encodeURIComponent(t())}`}(t,n):`/instances/${encodeURIComponent(e.id)}/`,a=function(e,t){const s=Oa(t);if(s&&new RegExp("^https?://","i").test(s))return new URL(e,s.endsWith("/")?s:s+"/").toString()}(i,s);return{text:a||i,url:a}}function xa(e){const t=Oa(e.kind);return"gui"===t?"vnc":"web"===t?"web":"tty"}function Oa(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function Ra(e){if("string"==typeof e&&e.trim())return{id:e,label:ja(e)};const t=function(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}(e),s=Oa(t.id).trim();return s?{id:s,label:Oa(t.name).trim()||ja(s)}:void 0}function ja(e){return{"terminal-tty":"Terminal","gui-terminal":"GUI Terminal",chromium:"Chromium",browser:"Browser","vscode-web":"VS Code"}[e]||e.replace(/[-_]+/g," ").replace(/\b\w/g,e=>e.toUpperCase())}class Da{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async handleInstancesCommand(e){const t=await this.targets.instancesForBinding(e);return{accepted:!0,routed:!1,binding:e,instances:t,reply:(s=t.length,0===s?"No controlled instances are registered yet.":`Instances: ${s}. Tap an instance to create an app.`),replyMarkup:t.length?pa(t.map(e=>{return{text:ga(e.name||e.id,56),callbackData:(t=this.deps.createChatActionToken({type:"instance-app-menu",instanceId:e.id}).token,`task_handoff:cp_i:${t}`)};var t})):void 0};var s}async handleInstanceAppMenuAction(e,t){const s=(await this.targets.instancesForBinding(e)).find(e=>e.id===t);if(!s)return{accepted:!1,routed:!1,binding:e,reply:"Instance not found."};const n=Na(s);return{accepted:!0,routed:!1,binding:e,instance:s,message:`Select an app for ${s.name}.`,reply:n.length?`New app for ${s.name}`:`No launchable apps for ${s.name}.`,replyMarkup:n.length?pa(n.map(e=>{return{text:ga(e.label,56),callbackData:(t=this.deps.createChatActionToken({type:"launch-app",instanceId:s.id,appId:e.id}).token,`task_handoff:cp_a:${t}`)};var t})):void 0}}async handleLaunchAppAction(e,t,s){const n=(await this.targets.instancesForBinding(e)).find(e=>e.id===t);if(!n)return{accepted:!1,routed:!1,binding:e,reply:"Instance not found."};const i=Na(n).find(e=>e.id===s);return i?{accepted:!0,routed:!1,binding:e,instance:n,session:await this.deps.launchAppSession(n.id,i.id),message:`${i.label} launched`,reply:`Launched ${i.label} on ${n.name}.`}:{accepted:!1,routed:!1,binding:e,reply:`App ${s} is not launchable for ${n.name}.`}}async handleAppsCommand(e){const t=await this.deps.boardAsync(),s=new Map(t.map(e=>[e.id,e])),n=(await this.deps.listAppSessions()).instances.flatMap(e=>{const t=s.get(e.instanceId);return t?e.appSessions.sessions.filter(Ca).filter(e=>Boolean(Oa(e.id))).map(e=>({instance:t,session:e,link:_a(t,e,this.deps.controlPlanePublicBaseUrl(),()=>this.deps.createAppAccessToken({instanceId:t.id,sessionId:Oa(e.id)||"",mode:xa(e)}).token)})):[]}),i=n.filter(e=>e.link.url).map(e=>({text:ga(`${e.instance.name} · ${Ea(e.instance,e.session)}`,56),url:e.link.url}));return{accepted:!0,routed:!1,binding:e,instances:t,reply:(a=n.length,r=i.length,0===a?"No app sessions found.":0===r?`App sessions: ${a}. Set the control plane public URL in Settings > Appearance to open them from chat.`:`App sessions: ${a}. Tap a button to open.`),replyMarkup:i.length?pa(i):void 0};var a,r}}class Ma{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async forwardChatMessage(e,t){const s=t.target?.instanceId||e.activeInstanceId||"",n=t.target?.aiSessionId||e.activeAiSessionId,i=await this.deps.requireControlledInstance(s,!0),a=i.projectId?this.deps.requireProject(i.projectId):void 0;if("online"!==i.connectionStatus){const e=new Error(`Instance ${i.name} is not reachable.`);throw Object.assign(e,{statusCode:409,code:"CHAT_TARGET_UNREACHABLE"}),e}if(n){const s=await this.trySendAiSessionMessage(e,i.id,n,t.message.text,t.message.attachments,!t.target);return s.ok?this.sentResult(e,a,i,n,s.aiSession):this.failedSendResult(s.binding,a,i,s.reply)}const r=await this.targets.defaultAiSessionIdForInstance(i.id);if(r){const s=this.deps.upsertChatSession({...e,activeAiSessionId:r}),n=await this.trySendAiSessionMessage(s,i.id,r,t.message.text,t.message.attachments);return n.ok?this.sentResult(s,a,i,r,n.aiSession):this.failedSendResult(n.binding,a,i,n.reply)}return{accepted:!1,routed:!1,binding:e,project:a||void 0,instance:ki(i),reply:`No active AI session is selected for ${i.name}. Use /sessions and /session <id> first.`}}async trySendAiSessionMessage(e,t,s,n,i=[],a=!0){try{return{ok:!0,binding:e,aiSession:await this.deps.sendAiSessionMessage(t,s,n||"请查看附件。",void 0,i)}}catch(t){return function(e){return"AI_SESSION_NOT_FOUND"===(e&&"object"==typeof e?e:{}).code}(t)?{ok:!1,binding:a?this.deps.upsertChatSession({...e,activeAiSessionId:void 0}):e,reply:`The selected AI session ${s} no longer exists. Use /session to choose an active AI session.`}:{ok:!1,binding:e,reply:`Failed to send to AI session ${s}: ${Ii(t)}`}}}failedSendResult(e,t,s,n){return{accepted:!1,routed:!1,binding:e,project:t||void 0,instance:ki(s),reply:n}}sentResult(e,t,s,n,i){return{accepted:!0,routed:!0,binding:e,project:t||void 0,instance:ki(s),aiSession:i,instanceId:s.id,aiSessionId:n,turnId:(a=i,a.turnId||a.providerTurnId||a.session.activeTurnId||""),providerTurnId:Ta(i),reply:`Sent to ${t?.name||s.sourceSnapshot.name||"Source"} / ${s.name} / ${n}.`};var a}}class Pa{deps;constructor(e){this.deps=e}async handlePendingCommand(e){const t=await this.deps.listPendingRoutes(),s=e.activeInstanceId?t.filter(t=>t.instanceId===e.activeInstanceId):t;return{accepted:!0,routed:!1,binding:e,pending:s,reply:s.length?s.map(Aa).join("\n"):"No pending routes.",replyMarkup:s.length?Sa(s,(e,t)=>this.deps.pendingDecisionCallbackData(e.id,t)):void 0}}async handlePendingDecisionCommand(e,t,s){const n=fi(t[0]);return n?n.aiSessionId?{accepted:!0,routed:!0,binding:e,pending:n,aiSession:await this.deps.resolveAiSessionApproval(n.instanceId,n.aiSessionId,s),reply:`${s} sent for ${n.instanceId}:${n.aiSessionId}.`}:{accepted:!1,routed:!1,binding:e,pending:n,reply:"Only AI session pending routes are supported here."}:{accepted:!1,routed:!1,binding:e,reply:`Usage: /${"allow"===s?"approve":s} <instance-id:pending-id>`}}async handlePendingReplyCommand(e,t){const s=fi(t[0]),n=t.slice(1).join(" ").trim();return s&&n?s.aiSessionId?{accepted:!0,routed:!0,binding:e,pending:s,aiSession:await this.deps.sendAiSessionMessage(s.instanceId,s.aiSessionId,n),reply:`Reply sent for ${s.instanceId}:${s.aiSessionId}.`}:{accepted:!1,routed:!1,binding:e,pending:s,reply:"Only AI session pending routes are supported here."}:{accepted:!1,routed:!1,binding:e,reply:"Usage: /reply <instance-id:pending-id> <message>"}}}class $a{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async handleUseCommand(e,t){if(0===t.length)return{accepted:!0,routed:!1,binding:e,instances:await this.targets.instancesForBinding(e),reply:await this.targets.renderUseHelp(e)};if(t.length>=2){const s=this.targets.resolveProject(t[0]),n=await this.targets.resolveInstance(t[1],s.id),i=this.deps.upsertChatSession({...e,activeProjectId:s.id,activeInstanceId:n.id,activeAiSessionId:await this.targets.defaultAiSessionIdForInstance(n.id)});return{accepted:!0,routed:!1,binding:i,reply:await this.targets.renderTargetSelected(i)}}const s=t[0],n=await this.targets.findInstance(s);if(n){const t=n.projectId?this.deps.requireProject(n.projectId):void 0,s=this.deps.upsertChatSession({...e,activeProjectId:t?.id,activeInstanceId:n.id,activeAiSessionId:await this.targets.defaultAiSessionIdForInstance(n.id)});return{accepted:!0,routed:!1,binding:s,reply:await this.targets.renderTargetSelected(s)}}const i=this.targets.resolveProject(s),a=(await this.deps.boardAsync()).filter(e=>e.projectId===i.id),r=a.filter(e=>"running"===e.status&&"online"===e.connectionStatus);if(1===r.length){const t=this.deps.upsertChatSession({...e,activeProjectId:i.id,activeInstanceId:r[0].id,activeAiSessionId:await this.targets.defaultAiSessionIdForInstance(r[0].id)});return{accepted:!0,routed:!1,binding:t,reply:await this.targets.renderTargetSelected(t)}}return{accepted:!0,routed:!1,binding:this.deps.upsertChatSession({...e,activeProjectId:i.id,activeInstanceId:void 0,activeAiSessionId:void 0}),instances:a,reply:`${i.name} selected. Choose an instance with /use ${i.id} <instance-id-or-name>.\n${this.targets.renderInstanceLines(a)}`}}async handleProjectCommand(e,t){if(0===t.length)return{accepted:!1,routed:!1,binding:e,reply:"Usage: /project <project-id-or-name>"};const s=this.targets.resolveProject(t[0]),n=e.activeInstanceId?await this.deps.requireControlledInstance(e.activeInstanceId).catch(()=>{}):void 0,i=this.deps.upsertChatSession({...e,activeProjectId:s.id,activeInstanceId:n?.projectId===s.id?n.id:void 0,activeAiSessionId:n?.projectId===s.id?e.activeAiSessionId:void 0});return{accepted:!0,routed:!1,binding:i,reply:`Active project: ${s.name}${i.activeInstanceId?"":". Choose an instance with /use."}`}}}class qa{deps;constructor(e){this.deps=e}async instancesForBinding(e){const t=await this.deps.boardAsync();return e.activeProjectId?t.filter(t=>t.projectId===e.activeProjectId):t}async fullInstancesForBinding(e){const t=await this.deps.listNodeInstances();return e.activeProjectId?t.filter(t=>t.projectId===e.activeProjectId):t}async renderUseHelp(e){return`Active target: ${await this.renderChatTarget(e)}\nUse /instances to list targets, then /use <instance-id-or-name>. Use /sessions to choose an AI session when there is more than one.`}async renderChatStatus(e){const t=e.activeInstanceId?(await this.deps.boardAsync()).find(t=>t.id===e.activeInstanceId):void 0;return t?`Active target: ${await this.renderChatTarget(e)}\nStatus: ${t.status}, connection: ${t.connectionStatus}`:`Active target: ${await this.renderChatTarget(e)}`}async renderChatHelp(e){return["TaskHandoff chat commands:","/help - show this help","/status or /target - show the active project, instance, and AI session","/instances - list controlled instances","/apps - list app sessions","/use <instance-id-or-name> - choose an active instance","/use <project-id-or-name> <instance-id-or-name> - choose a project and instance","/project <project-id-or-name> - narrow instance selection to a project","/sessions - list all AI sessions","/session <ai-session-id> - choose an active AI session","/pending - list waiting approvals","/approve <instance-id:pending-id>, /deny <...>, /skip <...> - answer approvals","/reply <instance-id:pending-id> <message> - reply to a waiting session","/cancel - interrupt the active AI session","","Send normal text after selecting a target; if there is only one AI session, it is selected automatically.",`Current target: ${await this.renderChatTarget(e)}`].join("\n")}renderInstanceLines(e){return e.map(e=>`${e.name} (${e.id}) - ${e.status}/${e.connectionStatus}`).join("\n")}async renderChatTarget(e){const t=e.activeProjectId?this.deps.getProject(e.activeProjectId):void 0,s=e.activeInstanceId?await this.deps.requireControlledInstance(e.activeInstanceId).catch(()=>{}):void 0,n=e.activeAiSessionId||"none";return`${t?.name||e.activeProjectId||"none"} / ${s?.name||e.activeInstanceId||"none"} / ${n}`}async renderTargetSelected(e){const t=await this.renderChatTarget(e);return e.activeAiSessionId?`Active target: ${t}`:`Active target: ${t}\nNo AI session selected yet. Use /sessions and /session <id>, or send a message once exactly one session is available.`}async defaultAiSessionIdForInstance(e){const t=await this.aiSessionSnapshotForInstance(e),s=(await this.deps.boardAsync()).find(t=>t.id===e),n=(await this.deps.listAppSessions()).instances.find(t=>t.instanceId===e),i=s?{...s,apps:{...s.apps,sessions:n?.appSessions.sessions||[]}}:void 0,a=t&&s?ya({...i,aiSessions:t}):t?.sessions||[];return 1===a.length?a[0].id:void 0}async aiSessionChoices(e){return(await this.boardInstancesForBinding({},{refresh:!0})).flatMap(t=>ya(t).map(s=>({index:0,projectId:t.projectId,projectName:t.project?.name||("string"==typeof t.sourceSnapshot.name?t.sourceSnapshot.name:"Source"),instance:t,session:s,active:e.activeInstanceId===t.id&&e.activeAiSessionId===s.id}))).sort((e,t)=>{const s=ba(t.session)-ba(e.session);return s||(String(e.instance.name||"").localeCompare(String(t.instance.name||""))||e.instance.id.localeCompare(t.instance.id)||va(e.session).localeCompare(va(t.session)))}).map((e,t)=>({...e,index:t}))}async boardInstancesForBinding(e,t={}){const s=await this.deps.boardAsync(),n=await this.deps.listAiSessions(t),i=await this.deps.listAppSessions(t),a=new Map(n.instances.map(e=>[e.instanceId,e.aiSessions])),r=new Map(i.instances.map(e=>[e.instanceId,e.appSessions])),o=s.map(e=>{return{...e,apps:{...e.apps,sessions:r.get(e.id)?.sessions||[]},aiSessions:a.get(e.id)||(t=e.aiSessions.updatedAt,{runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:t})};var t});return e.activeProjectId?o.filter(t=>t.projectId===e.activeProjectId):o}async findAiSession(e){const t=await this.boardInstancesForBinding({},{refresh:!0});for(const s of t){const t=(s.aiSessions?.sessions||[]).find(t=>t.id===e||t.providerSessionId===e);if(t)return{instance:s,session:t}}}async aiSessionSnapshotForInstance(e){const t=await this.deps.listAiSessions();return t.instances.find(t=>t.instanceId===e)?.aiSessions}resolveProject(e){const t=this.deps.getProject(e)||this.deps.listProjects().find(t=>t.name===e);return t||yi("PROJECT_NOT_FOUND",`Project ${e} was not found.`),t}async resolveInstance(e,t){const s=await this.findInstance(e,t);return s||yi("CONTROLLED_INSTANCE_NOT_FOUND",`Controlled instance ${e} was not found.`),s}async findInstance(e,t){return(await this.deps.listNodeInstances()).find(s=>!(s.id!==e&&s.name!==e||t&&s.projectId!==t))}}class La{deps;targets;sender;appCommands;pendingCommands;aiSessionCommands;targetCommands;constructor(e){this.deps=e,this.targets=new qa(e),this.sender=new Ma(e,this.targets),this.appCommands=new Da(e,this.targets),this.pendingCommands=new Pa(e),this.aiSessionCommands=new za(e,this.targets),this.targetCommands=new $a(e,this.targets)}async handleChatGatewayMessage(e){const t=tn.parse(e),s=this.deps.upsertChatSession({channel:t.source.channel,bridgeId:t.source.bridgeId,chatSessionId:t.source.chatSessionId,userId:t.source.userId}),n=t.message.text.trim();return n.startsWith("/")?this.handleChatCommand(s,n):n||t.message.attachments.length?s.activeInstanceId?this.sender.forwardChatMessage(s,t):{accepted:!1,routed:!1,binding:s,reply:await this.targets.renderUseHelp(s)}:{accepted:!1,routed:!1,binding:s,reply:""}}async handleChatGatewayAction(e){const t=this.deps.upsertChatSession({channel:e.source.channel,bridgeId:e.source.bridgeId,chatSessionId:e.source.chatSessionId,userId:e.source.userId});return"ai-session"===e.action.type?this.aiSessionCommands.handleAiSessionIndexAction(t,e.action.index):"instance-app-menu"===e.action.type?this.appCommands.handleInstanceAppMenuAction(t,e.action.instanceId):"launch-app"===e.action.type?this.appCommands.handleLaunchAppAction(t,e.action.instanceId,e.action.appId):"pending-decision"===e.action.type?this.pendingCommands.handlePendingDecisionCommand(t,[e.action.routeId],e.action.decision):{accepted:!1,routed:!1,binding:t,reply:"Unknown action."}}async handleChatCommand(e,t){const[s="",...n]=t.split(/\s+/),i=s.replace(/@[\w_]+$/,"");switch(i.toLowerCase()){case"/help":case"/start":return{accepted:!0,routed:!1,binding:e,reply:await this.targets.renderChatHelp(e)};case"/sessions":return this.aiSessionCommands.handleAiSessionsCommand(e);case"/session":return this.aiSessionCommands.handleAiSessionCommand(e,n);case"/cancel":case"/interrupt":return this.aiSessionCommands.handleAiSessionInterruptCommand(e);case"/pending":return this.pendingCommands.handlePendingCommand(e);case"/approve":return this.pendingCommands.handlePendingDecisionCommand(e,n,"allow");case"/deny":return this.pendingCommands.handlePendingDecisionCommand(e,n,"deny");case"/skip":return this.pendingCommands.handlePendingDecisionCommand(e,n,"skip");case"/reply":return this.pendingCommands.handlePendingReplyCommand(e,n);case"/instances":return this.appCommands.handleInstancesCommand(e);case"/apps":return this.appCommands.handleAppsCommand(e);case"/use":return this.targetCommands.handleUseCommand(e,n);case"/project":return this.targetCommands.handleProjectCommand(e,n);case"/status":case"/target":return{accepted:!0,routed:!1,binding:e,instance:e.activeInstanceId?(await this.deps.boardAsync()).find(t=>t.id===e.activeInstanceId):void 0,reply:await this.targets.renderChatStatus(e)};default:return{accepted:!1,routed:!1,binding:e,reply:`Unknown command: ${i||s}. Try /help.`}}}}const Ua=r.z.object({id:r.z.string().trim().min(1),preferences:Mn,createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}).strict();function Fa(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,s=t.preferences&&"object"==typeof t.preferences&&!Array.isArray(t.preferences)?t.preferences:{};return{id:t.id,preferences:{import:"string"==typeof s.import&&s.import.trim()?s.import:".",export:"string"==typeof s.export&&s.export.trim()?s.export:"."},createdAt:t.createdAt,updatedAt:t.updatedAt}}function Ba(){return Mn.parse({})}function Ha(e){return/^[a-z]:[\\/]/i.test(e)||/^[\\/]{2}[^\\/]+[\\/]+[^\\/]+/.test(e)}function Ga(e){return Ha(e)?t.win32:t.posix}const Wa=r.z.object({mode:r.z.enum(["local-static-key","paired-hmac"]).default("local-static-key"),keyId:Ns.shape.id.optional(),secret:r.z.string().trim().max(4096).optional(),pairedAt:Ns.shape.createdAt.optional(),pairing:r.z.object({status:r.z.enum(["pending","paired","expired"]).default("pending"),joinToken:r.z.string().trim().max(4096).optional(),expiresAt:Ns.shape.createdAt.optional()}).strict().optional()}).strict(),Va=r.z.object({autoImportAgentConfigs:r.z.boolean().optional(),defaultCodexPermissionMode:H.optional()}).strict(),Ka=r.z.object({id:Ws.shape.id.optional(),name:Ws.shape.name.optional(),projectId:Ws.shape.projectId,source:Wt.optional(),sourceSnapshot:r.z.record(r.z.string(),r.z.unknown()).optional(),nodeId:Ws.shape.nodeId.optional(),runtimeId:Ws.shape.runtimeId.optional(),imageSelection:Ws.shape.imageSelection,config:Va.optional(),modelSelection:Ws.shape.modelSelection.optional(),start:r.z.boolean().default(!1)}).strict(),Ja=r.z.object({name:Ws.shape.name.optional(),config:Va.optional(),modelSelection:Ws.shape.modelSelection.optional()}).strict(),Qa=r.z.object({name:Qt.shape.name,endpoint:Qt.shape.endpoint,key:Qt.shape.key,model:Qt.shape.model,app:Qt.shape.app,enabled:Qt.shape.enabled.optional(),order:Qt.shape.order.optional(),labels:Qt.shape.labels.optional()}).strict(),Ya=Qa.partial().strict(),Xa=r.z.object({id:Ns.shape.id.optional(),name:Ns.shape.name,connectionMode:Ns.shape.connectionMode.optional(),auth:Wa.optional(),joinToken:r.z.string().trim().min(1).max(4096).optional(),endpoint:Ns.shape.endpoint,controlEndpoint:Ns.shape.controlEndpoint,containerEndpoint:Ns.shape.containerEndpoint,publicWebBase:Ns.shape.publicWebBase,status:r.z.enum(["unknown","online","offline","degraded"]).optional(),health:r.z.enum(["unknown","ok","degraded","failed"]).optional(),capabilities:Ns.shape.capabilities.optional(),labels:Ns.shape.labels.optional(),lastSeenAt:Ns.shape.lastSeenAt}).strict(),Za=r.z.object({name:Ns.shape.name.optional(),connectionMode:Ns.shape.connectionMode.unwrap().optional(),auth:Wa.optional(),endpoint:Ns.shape.endpoint,controlEndpoint:Ns.shape.controlEndpoint,containerEndpoint:Ns.shape.containerEndpoint,publicWebBase:Ns.shape.publicWebBase,status:Ns.shape.status.unwrap().optional(),health:Ns.shape.health.unwrap().optional(),capabilities:Ns.shape.capabilities.unwrap().optional(),labels:Ns.shape.labels.unwrap().optional(),lastSeenAt:Ns.shape.lastSeenAt}).strict(),er=r.z.object({controlPlaneUrl:r.z.string().trim().url().max(2048),joinToken:r.z.string().trim().min(1).max(4096),controlPlaneName:r.z.string().trim().min(1).max(160).optional(),activate:r.z.boolean().optional()}).strict(),tr=r.z.object({nodeName:r.z.string().trim().min(1).max(160).optional(),expiresInMs:r.z.number().int().positive().max(36e5).optional()}).strict();function sr(e){return{enabledCount:e.deployments.filter(e=>e.enabled).length,runningCount:e.runtime.filter(e=>"running"===e.status).length,errorCount:e.runtime.filter(e=>"error"===e.status).length,configs:e.configs.map(t=>({configHash:t.configHash,config:t,deployments:e.deployments.filter(e=>e.configHash===t.configHash),runtime:e.runtime.filter(e=>e.configHash===t.configHash)})),recentRuns:e.recentRuns.slice(0,20)}}const nr="TASK_HANDOFF_NODE_AGENT_V1",ir=6e4;function ar(e=""){return o.createHash("sha256").update(e).digest("hex")}function rr(e,t){return o.createHmac("sha256",e).update(function(e){return[nr,e.keyId,e.method.toUpperCase(),e.pathWithQuery,e.timestamp,e.nonce,e.bodySha256].join("\n")}(t)).digest("hex")}function or(e){const t=e.timestamp||(new Date).toISOString(),s=e.nonce||o.randomBytes(18).toString("base64url"),n=ar(e.body||""),i=rr(e.secret,{keyId:e.keyId,method:e.method,pathWithQuery:e.pathWithQuery,timestamp:t,nonce:s,bodySha256:n});return{"x-taskhandoff-node-id":e.nodeId,"x-taskhandoff-key-id":e.keyId,"x-taskhandoff-timestamp":t,"x-taskhandoff-nonce":s,"x-taskhandoff-body-sha256":n,"x-taskhandoff-signature":i}}function dr(e){if(e.startsWith("ipc://"))return decodeURIComponent(e.slice(6))}function cr(e,t=process.platform){if(!function(e,t=process.platform){if("win32"===t)return e.startsWith("\\\\.\\pipe\\");const n=s.statSync(e),i=process.getuid?.();return void 0===i||n.uid===i}(e,t)){const e=new Error("Local node agent IPC socket is not owned by the current user.");throw Object.assign(e,{statusCode:403,code:"NODE_AGENT_IPC_OWNER_INVALID"}),e}}function lr(e,t,s,n={}){const i=`/api/node-agent${t}`,a={createConnection:()=>u.connect(e),headers:{host:"task-handoff-node-agent.local",...n}};return s&&(Array.isArray(s)?s.length>0:s.trim().length>0)?new l.WebSocket(`ws://task-handoff-node-agent.local${i}`,s,a):new l.WebSocket(`ws://task-handoff-node-agent.local${i}`,a)}const pr=r.z.object({id:r.z.string().trim().min(1),tokenHash:r.z.string().trim().min(1),expiresAt:r.z.string().datetime(),nodeName:r.z.string().trim().min(1).max(160).optional(),createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}).strict(),ur="task-handoff.control-plane.local",mr="task-handoff.control-plane.builtin";function gr(e){return"true"===e.labels[ur]}function hr(e){return"true"===e.labels[mr]}function fr(e){const t=function(e){return"paired-hmac"===e.auth.mode?e.auth.keyId:void 0}(e);if(!t){const t=new Error(`Node ${e.id} paired-HMAC auth is missing keyId.`);throw Object.assign(t,{statusCode:500,code:"NODE_AGENT_REMOTE_KEY_ID_MISSING"}),t}return t}function Ir(e,t){const s=function(e){return"paired-hmac"===e.auth.mode?e.auth.secret:void 0}(e);if(s)return or({nodeId:e.id,keyId:fr(e),secret:s,method:t.method,pathWithQuery:t.pathWithQuery,body:t.body});const n=function(e){if("local-ipc"===e.connectionMode||"local-loopback"===e.connectionMode)return"local-static-key"===e.auth.mode?e.auth.secret:void 0}(e);return n?{authorization:`Bearer ${n}`}:{}}class yr{projects;models;images;nodes;chatSessions;chatBridges;triggers;nodeJoinInvites;configSyncPreferences;settings;paths;fetchImpl;dockerCommandRunner;logger;appAccessService;chatActionTokenService=new sa;nodeAgentTransport;nodeAgentGateway;controlledInstanceGateway;instanceBoardReader=new Ti;controlPlaneTriggerService;catalogService;marketCatalogService;chatBridgeService;chatSessionService;chatSessionRuntime;aiSessionSnapshotProvider;appSessionSnapshotProvider;constructor(e,s={}){var n;this.paths=e,this.fetchImpl=s.fetchImpl||fetch,this.dockerCommandRunner=s.dockerCommandRunner,this.nodeAgentTransport=s.nodeAgentTransport,this.logger=(n=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(n||"").trim().toLowerCase())?s.logger:void 0);const i=new ui({request:(e,t,s)=>this.nodeAgentFetch(e,t,s),logger:this.logger});this.nodeAgentGateway=new mi(i),this.controlledInstanceGateway=new bi({requireNode:e=>this.requireNode(e),nodeAgentRequest:(e,t,s)=>this.nodeAgentFetch(e,t,s),nodeAgentStreamRequest:(e,t,s)=>this.nodeAgentTransportFor(e).requestStream(e,t,s),fetchImpl:this.fetchImpl});const a=e=>({schema:e,logger:(e,t)=>this.logWarn(t,e)});this.projects=new fn(e.projectsDir,{...a(Jt),sanitize:ct}),this.models=new fn(e.modelsDir,a(Qt)),this.images=new fn(e.imagesDir,{...a(Is),sanitize:e=>Es(e,e=>this.logWarn(e,"legacy image profile field was migrated"))}),this.nodes=new fn(e.nodesDir,a(Ns)),this.chatSessions=new fn(e.chatSessionsDir,a(en)),this.chatBridges=new fn(e.chatBridgesDir,a(Zs)),this.triggers=new fn(e.triggersDir,a(Ci)),this.nodeJoinInvites=new fn(e.nodeJoinInvitesDir,a(pr)),this.configSyncPreferences=new fn(t.join(e.dataDir,"config-sync-preferences"),{...a(Ua),sanitize:Fa}),this.settings=new In(e.settingsPath,()=>qi.parse({}),{...a(qi),sanitize:Ui}),this.marketCatalogService=new Ki,this.catalogService=new Hi({projects:this.projects,images:this.images,market:this.marketCatalogService,settings:this.settings,defaultNodeId:()=>this.defaultNodeId()}),this.controlPlaneTriggerService=new xi({triggers:this.triggers,listInstances:()=>this.listNodeInstances(),requireInstance:async e=>this.requireControlledInstance(e,!0),instanceRequest:(e,t,s)=>this.instanceRequest(e,t,s)}),this.appAccessService=new Qi({requireInstance:async e=>this.requireControlledInstance(e,!0),listAppSessions:()=>this.listAppSessions()}),this.chatBridgeService=new da({chatBridges:this.chatBridges,chatSessions:this.chatSessions}),this.chatSessionService=new ca({chatSessions:this.chatSessions}),this.chatSessionRuntime=new La({upsertChatSession:e=>this.upsertChatSession(e),boardAsync:()=>this.boardAsync(),listAiSessions:e=>this.listAiSessions(e),listAppSessions:e=>this.listAppSessions(e),listNodeInstances:()=>this.listNodeInstances(),listPendingRoutes:()=>this.listPendingRoutes(),pendingDecisionCallbackData:(e,t)=>this.pendingDecisionCallbackData(e,t),createChatActionToken:e=>this.createChatActionToken(e),createAppAccessToken:e=>this.createAppAccessToken(e),controlPlanePublicBaseUrl:()=>this.getSettings().publicBaseUrl||process.env.TASK_HANDOFF_CONTROL_PLANE_URL||process.env.TASK_HANDOFF_PUBLIC_BASE||process.env.TASK_HANDOFF_CONTROL_PLANE_PUBLIC_URL,requireControlledInstance:(e,t)=>this.requireControlledInstance(e,t),requireProject:e=>this.requireProject(e),getProject:e=>this.catalogService.getProject(e),listProjects:()=>this.catalogService.listProjects(),resolveAiSessionApproval:(e,t,s)=>this.resolveAiSessionApproval(e,t,s),sendAiSessionMessage:(e,t,s,n,i)=>this.sendAiSessionMessage(e,t,s,n,i),interruptAiSession:(e,t)=>this.interruptAiSession(e,t),launchAppSession:(e,t,s)=>this.launchAppSession(e,t,s)})}logInfo(e,t){this.logger?.info?.(e,t)}logWarn(e,t){this.logger?.warn?.(e,t)}logError(e,t){this.logger?.error?.(e,t)}setNodeAgentTransport(e){this.nodeAgentTransport=e}setAiSessionSnapshotProvider(e){this.aiSessionSnapshotProvider=e}async listAiSessions(e={}){return this.aiSessionSnapshotProvider?this.aiSessionSnapshotProvider(e):this.bootstrapAiSessionsFromInstances()}setAppSessionSnapshotProvider(e){this.appSessionSnapshotProvider=e}async listAppSessions(e={}){return this.appSessionSnapshotProvider?this.appSessionSnapshotProvider(e):this.bootstrapAppSessionsFromInstances()}init(){this.projects.init(),this.models.init(),this.images.init(),this.nodes.init(),this.chatSessions.init(),this.chatBridges.init(),this.triggers.init(),this.nodeJoinInvites.init(),this.settings.init(),this.migrateLegacyImageCatalog(),this.seedDefaults()}migrateLegacyImageCatalog(){const e=Object.keys(ot),n=[];for(const t of e){const e=this.images.filePath(t);if(s.existsSync(e))try{n.push({id:t,filePath:e,record:JSON.parse(s.readFileSync(e,"utf8"))})}catch(e){return this.writeMarketMigrationState("failed",{code:"LEGACY_IMAGE_BACKUP_READ_FAILED",legacyId:t,error:Ii(e)}),void this.logWarn({legacyId:t,error:Ii(e)},"legacy embedded image migration was not applied")}}if(!n.length)return;const i=s.readdirSync(this.paths.projectsDir).filter(e=>e.endsWith(".json")).sort().map(e=>t.join(this.paths.projectsDir,e)),a=[];for(const e of i)try{const t=JSON.parse(s.readFileSync(e,"utf8")),n=Jt.parse(ct(t));a.push({filePath:e,raw:t,migrated:n})}catch(t){return this.writeMarketMigrationState("failed",{code:"PROJECT_REFERENCE_VALIDATION_FAILED",filePath:e,error:Ii(t)}),void this.logWarn({filePath:e,error:Ii(t)},"legacy embedded image migration was not applied")}const r=a.find(({migrated:t})=>{const s=t.defaultImageSelection?.imageId;return!!s&&e.includes(s)});if(r)this.writeMarketMigrationState("failed",{code:"PROJECT_REFERENCE_MIGRATION_INCOMPLETE",filePath:r.filePath});else{s.mkdirSync(this.paths.marketDir,{recursive:!0}),c.sync(this.paths.marketMigrationBackupPath,`${JSON.stringify({version:He,createdAt:gi(),images:n.map(({id:e,record:t})=>({id:e,record:t})),projects:a.map(({filePath:e,raw:s})=>({file:t.basename(e),record:s}))},null,2)}\n`,{encoding:"utf8"});for(const{migrated:e}of a)this.projects.put(e);for(const{id:e}of n)this.images.delete(e),this.logWarn({legacyId:e,marketId:ot[e]},"embedded image profile migrated to Market");this.writeMarketMigrationState("complete",{code:"LEGACY_IMAGE_MIGRATION_COMPLETE",migratedImageIds:n.map(({id:e})=>e),backupPath:this.paths.marketMigrationBackupPath})}}writeMarketMigrationState(e,t){s.mkdirSync(this.paths.marketDir,{recursive:!0}),c.sync(this.paths.marketStatePath,`${JSON.stringify({status:e,updatedAt:gi(),...t},null,2)}\n`,{encoding:"utf8"})}getSettings(){return this.catalogService.getSettings()}updateSettings(e){return this.catalogService.updateSettings(e)}seedDefaults(){return this.catalogService.seedDefaults()}listProjects(){return this.catalogService.listProjects()}defaultNodeId(){return this.nodes.list().find(gr)?.id||this.nodes.list()[0]?.id}async inspectNodeAgent(e,t){const s="/health",n=t?Ir(t,{method:"GET",pathWithQuery:`/api/node-agent${s}`}):{},i=await this.fetchNodeAgentEndpoint(e,s,{headers:n}),a=await i.json().catch(()=>({}));if(!i.ok){const t=new Error(a.error?.message||`Node agent health check failed with HTTP ${i.status}`);throw Object.assign(t,{statusCode:i.status,code:"NODE_AGENT_HEALTH_FAILED"}),this.logWarn({nodeEndpoint:e,statusCode:i.status,errorCode:"NODE_AGENT_HEALTH_FAILED"},"node agent health check failed"),t}const r="string"==typeof a.data?.nodeId&&a.data.nodeId.trim()?a.data.nodeId.trim():void 0;if(!r){const t=new Error("Node agent health response did not include nodeId.");throw Object.assign(t,{statusCode:502,code:"NODE_AGENT_ID_MISSING"}),this.logWarn({nodeEndpoint:e,payload:a.data,errorCode:"NODE_AGENT_ID_MISSING"},"node agent health response missing node id"),t}a.data?.protocolVersion!==He&&this.logWarn({nodeId:r,nodeEndpoint:e,expectedProtocolVersion:He,actualProtocolVersion:a.data?.protocolVersion,build:a.data?.build,errorCode:"PROTOCOL_VERSION_MISMATCH"},"node agent protocol version mismatch");const o=xt.safeParse(a.data?.build);return this.logInfo({nodeId:r,nodeEndpoint:e,protocolVersion:a.data.protocolVersion,build:o.success?o.data:a.data?.build},"node agent health check ok"),{nodeId:r,data:a.data||{}}}async fetchNodeAgentEndpoint(e,t,s={}){const n=dr(e);return n?(cr(n),async function(e,t,s={}){const n=s.method||"GET",i="string"==typeof s.body||s.body instanceof Buffer?s.body:void 0===s.body||null===s.body?void 0:String(s.body),a=new Headers(s.headers||{});return void 0===i||a.has("content-length")||a.set("content-length",String(Buffer.byteLength(i))),new Promise((r,o)=>{const d=p.request({socketPath:e,method:n,path:`/api/node-agent${t}`,headers:Object.fromEntries(a.entries())},e=>{const t=e.statusCode||500;r(new Response("HEAD"!==n&&204!==t&&205!==t&&304!==t?m.Readable.toWeb(e):null,{status:t,headers:e.headers}))});d.on("error",o);const c=()=>d.destroy(new DOMException("The operation was aborted.","AbortError"));s.signal?.aborted?c():s.signal?.addEventListener("abort",c,{once:!0}),d.once("close",()=>s.signal?.removeEventListener("abort",c)),void 0!==i&&d.write(i),d.end()})}(n,t,s)):this.fetchImpl(`${e.replace(/\/$/,"")}/api/node-agent${t}`,s)}async completeNodeAgentPairing(e,t){const s=await this.fetchImpl(`${e.replace(/\/$/,"")}/api/node-agent/pairing/complete`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({joinToken:t.joinToken,controlPlaneName:t.controlPlaneName||"Control Plane",controlPlaneUrl:t.controlPlaneUrl})}),n=await s.json().catch(()=>({}));if(!s.ok){const e=new Error(n.error?.message||`Node agent pairing failed with status ${s.status}.`);throw Object.assign(e,{statusCode:s.status,code:"NODE_AGENT_PAIRING_FAILED"}),e}if("string"!=typeof n.data?.nodeId||!n.data.nodeId.trim()||"string"!=typeof n.data?.keyId||!n.data.keyId.trim()||"string"!=typeof n.data?.secret||!n.data.secret.trim()){const e=new Error("Node agent pairing response did not include nodeId, keyId, and secret.");throw Object.assign(e,{statusCode:502,code:"NODE_AGENT_PAIRING_RESPONSE_INVALID"}),e}return{nodeId:n.data.nodeId.trim(),keyId:n.data.keyId.trim(),secret:n.data.secret.trim(),pairedAt:"string"==typeof n.data.pairedAt?n.data.pairedAt:gi()}}async syncLocalNodeConnection(){const e={[ur]:"true",[mr]:"true"},t=process.env.TASK_HANDOFF_NODE_AGENT_TOKEN?.trim()||void 0,s=process.env.TASK_HANDOFF_NODE_AGENT_CONTROL_ENDPOINT||process.env.TASK_HANDOFF_NODE_AGENT_ENDPOINT||"http://127.0.0.1:8091",n=function(e){return dr(e)?"local-ipc":"local-loopback"}(s),i=Ns.parse({id:"node_local_probe",name:"Local Node Probe",connectionMode:n,auth:{mode:"local-static-key",secret:t},labels:e,createdAt:gi(),updatedAt:gi()}),a=await this.inspectNodeAgent(s,i),r=gi(),o=this.nodes.get(a.nodeId),d=this.nodes.list().filter(e=>gr(e)&&e.id!==a.nodeId);for(const e of d)this.nodes.delete(e.id);const c=Ns.parse({...o||{},id:a.nodeId,name:o?.name||"Local Node",connectionMode:n,auth:{mode:"local-static-key",secret:t},endpoint:s,controlEndpoint:s,containerEndpoint:process.env.TASK_HANDOFF_NODE_AGENT_CONTAINER_URL,publicWebBase:process.env.TASK_HANDOFF_INSTANCE_PUBLIC_WEB_BASE,status:"online",health:"ok",capabilities:{...o?.capabilities||{},agent:wi(a.data)},labels:{...o?.labels||{},...e},createdAt:o?.createdAt||r,updatedAt:r});return this.nodes.put(c)}listModels(){return this.listAllModels().map(zi)}async listFederatedModels(){const e=this.listNodes(),t=await this.nodeAgentGateway.listFleetModels(e),s=new Map;for(const e of this.listAllModels())s.set(e.id,{id:e.id,model:zi(e),locations:[{type:"control-plane",name:e.name,enabled:e.enabled,order:e.order}],referenceCount:0});for(const{nodeId:e,model:n}of t.items){const{referenceCount:t,...i}=n,a=s.get(n.id);a?(a.locations.push({type:"node",nodeId:e,name:n.name,enabled:n.enabled,order:n.order,referenceCount:n.referenceCount}),a.referenceCount+=n.referenceCount):s.set(n.id,{id:n.id,model:i,locations:[{type:"node",nodeId:e,name:n.name,enabled:n.enabled,order:n.order,referenceCount:n.referenceCount}],referenceCount:n.referenceCount})}return ns.parse({models:[...s.values()].sort((e,t)=>e.model.order-t.model.order||e.model.name.localeCompare(t.model.name)),nodeDiagnostics:t.nodeErrors.map(e=>({nodeId:e.nodeId,code:e.code,message:e.message})),updatedAt:gi()})}listAllModels(){return this.models.list().map(e=>this.normalizeModelRecord(e)).sort((e,t)=>e.order-t.order||e.createdAt.localeCompare(t.createdAt)||e.id.localeCompare(t.id))}async createModel(e){const t=Qa.parse(e),s=gi(),n=this.nextModelOrder(),i=is(t),a=this.models.get(i),r=Qt.parse({...t,id:i,enabled:t.enabled??!0,order:t.order??n,labels:t.labels||{},createdAt:a?.createdAt||s,updatedAt:s});return zi(this.models.put(r))}async updateModel(e,t){const s=Ya.parse(t),n=this.requireModelSecret(e),i=Qt.parse({...n,...s,key:s.key?.trim()?s.key:n.key,createdAt:n.createdAt,updatedAt:gi()}),a=is(i);return zi(this.models.put(Qt.parse({...i,id:a})))}async deleteModel(e){return this.requireModelSecret(e),this.models.delete(e)}async reorderModels(e){const t=[...new Set(e)],s=new Map(this.models.list().map(e=>[e.id,e]));for(const e of t)s.has(e)||yi("MODEL_NOT_FOUND",`Model ${e} was not found.`);return t.forEach((e,t)=>{const n=s.get(e);this.models.put(Qt.parse({...n,order:100*(t+1),updatedAt:gi()}))}),this.listModels()}createNodeModel(e,t){return this.nodeAgentGateway.createModel(this.requireNode(e),t)}updateNodeModel(e,t,s){return this.nodeAgentGateway.updateModel(this.requireNode(e),t,s)}deleteNodeModel(e,t){return this.nodeAgentGateway.deleteModel(this.requireNode(e),t)}createProject(e){return this.catalogService.createProject(e)}updateProject(e,t){return this.catalogService.updateProject(e,t)}deleteProject(e){return this.catalogService.deleteProject(e)}listImages(){return this.catalogService.listImages()}getMarketCatalog(){return this.catalogService.getMarketCatalog()}async refreshMarketCatalog(){return await this.marketCatalogService.refresh(new Vi),this.getMarketCatalog()}listImageOptions(){return this.catalogService.listImageOptions()}createImage(e){return this.catalogService.createImage(e)}async listNodeDockerImages(e){const t=this.requireNode(e);return this.nodeAgentGateway.listDockerImages(t)}async listNodeImageAvailability(e){const t=this.requireNode(e),s=t.capabilities.agent&&"object"==typeof t.capabilities.agent?t.capabilities.agent:{},n={os:"string"==typeof s.platform?s.platform:void 0,architecture:"string"==typeof s.arch?"x64"===s.arch?"amd64":s.arch:void 0},i=this.catalogService.listImageOptions(n);try{const e=await this.nodeAgentGateway.listDockerImages(t);return i.map(t=>{const s=e.find(e=>e.reference===t.reference||!!t.digest&&e.repoDigests.some(e=>e.endsWith(`@${t.digest}`))||e.repoDigests.includes(t.reference));return Ls.parse({image:t,status:s?"available":"pull-required",localImage:s,localSizeBytes:s?.sizeBytes,downloadSizeBytes:s?void 0:t.downloadSizeBytes,unpackedSizeBytes:s?void 0:t.unpackedSizeBytes})})}catch(e){const t=e instanceof Error?e.message:String(e);return i.map(e=>Ls.parse({image:e,status:"unknown",error:t}))}}async listNodeLocalFolders(e){const t=this.requireNode(e);return this.nodeAgentGateway.listLocalFolders(t)}async listNodeFolderTree(e,t={}){const s=this.requireNode(e);return this.nodeAgentGateway.listFolderTree(s,t)}async createNodeLocalFolder(e,t){const s=this.requireNode(e);return this.nodeAgentGateway.createLocalFolder(s,t)}async deleteNodeLocalFolder(e,t){const s=this.requireNode(e);return this.nodeAgentGateway.deleteLocalFolder(s,t)}async checkNode(e){const t=this.requireNode(e);try{const e=await this.nodeAgentGateway.health(t),s=e&&"object"==typeof e?e:{},n="string"==typeof s.nodeId&&s.nodeId.trim()?s.nodeId.trim():t.id,i=Ns.parse({...t,id:n,status:"online",health:"ok",capabilities:"object"==typeof e&&e?{...t.capabilities,agent:wi(e)}:t.capabilities,lastSeenAt:gi(),updatedAt:gi()});return n!==t.id&&this.nodes.delete(t.id),this.nodes.put(i),{id:i.id,status:"online",checkedAt:gi(),agent:e}}catch(e){const s=Ns.parse({...t,status:"offline",health:"failed",updatedAt:gi()});return this.nodes.put(s),{id:t.id,status:"offline",checkedAt:gi(),error:e instanceof Error?e.message:String(e)}}}async checkNodeUpdate(e,t){return this.nodeAgentGateway.checkUpdate(this.requireNode(e),t)}async applyNodeUpdate(e,t){return this.nodeAgentGateway.applyUpdate(this.requireNode(e),t)}async listNodeUpdateJobs(e){return this.nodeAgentGateway.listUpdateJobs(this.requireNode(e))}async getLocalNodeExternalListener(e){return this.nodeAgentGateway.getExternalListener(this.requireLocalListenerNode(e))}async updateLocalNodeExternalListener(e,t){return this.nodeAgentGateway.updateExternalListener(this.requireLocalListenerNode(e),t)}async createNodePairingInvite(e,t={}){const s=this.requireNode(e);return this.nodeAgentGateway.createPairingInvite(s,t)}requireLocalListenerNode(e){const t=this.requireNode(e);if(!hr(t)||!gr(t)){const e=new Error("Node agent TCP listener settings are available only for the built-in local node.");throw Object.assign(e,{statusCode:403,code:"LOCAL_NODE_LISTENER_ONLY"}),e}return t}pruneNodeJoinInvites(){const e=Date.now();for(const t of this.nodeJoinInvites.list())Date.parse(t.expiresAt)<=e&&this.nodeJoinInvites.delete(t.id)}createNodeJoinInvite(e={}){this.pruneNodeJoinInvites();const t=tr.parse(e&&"object"==typeof e?e:{}),s=gi(),n=hn(),i={id:gn("node_join"),tokenHash:o.createHash("sha256").update(n).digest("hex"),expiresAt:new Date(Date.now()+(t.expiresInMs||6e5)).toISOString(),...t.nodeName?{nodeName:t.nodeName}:{},createdAt:s,updatedAt:s};return this.nodeJoinInvites.put(i),{id:i.id,joinToken:n,expiresAt:i.expiresAt}}completeNodeJoin(e){this.pruneNodeJoinInvites();const t=r.z.object({joinToken:r.z.string().trim().min(1).max(4096),nodeId:Ns.shape.id,nodeName:Ns.shape.name.optional(),keyId:r.z.string().trim().min(1).max(160),secret:r.z.string().trim().min(1).max(4096),pairedAt:r.z.string().datetime().optional()}).parse(e),s=o.createHash("sha256").update(t.joinToken).digest("hex"),n=this.nodeJoinInvites.list().find(e=>e.tokenHash===s&&Date.parse(e.expiresAt)>Date.now());if(!n){const e=new Error("Node join token is invalid or expired.");throw Object.assign(e,{statusCode:401,code:"NODE_JOIN_TOKEN_INVALID"}),e}this.nodeJoinInvites.delete(n.id);const i=gi();if(this.nodes.get(t.nodeId)){const e=new Error(`Node ${t.nodeId} already exists in this control-plane.`);throw Object.assign(e,{statusCode:409,code:"NODE_JOIN_NODE_ALREADY_EXISTS"}),e}const a=Ns.parse({id:t.nodeId,name:t.nodeName||n.nodeName||t.nodeId,connectionMode:"reverse-wss",auth:{mode:"paired-hmac",keyId:t.keyId,secret:t.secret,pairedAt:t.pairedAt||i,pairing:{status:"paired"}},status:"unknown",health:"unknown",capabilities:{},labels:{},createdAt:i,updatedAt:i});return this.nodes.put(a)}async connectNodeToControlPlane(e,t){const s=this.requireNode(e),n=er.parse(t);return this.nodeAgentGateway.connectRemote(s,n)}async listNodeRemoteControlPlanes(e){const t=this.requireNode(e);return this.nodeAgentGateway.listRemotes(t)}async deleteNodeRemoteControlPlane(e,t){const s=this.requireNode(e);return this.nodeAgentGateway.deleteRemote(s,t)}updateImage(e,t){return this.catalogService.updateImage(e,t)}async deleteImage(e){this.catalogService.requireImage(e);const t=this.listProjects().find(t=>t.defaultImageSelection?.imageId===e);if(t)throw Object.assign(new Error(`Image ${e} is the default for project ${t.name}.`),{statusCode:409,code:"IMAGE_IN_USE"});const s=(await Promise.all(this.listNodes().map(e=>this.nodeAgentGateway.listLocalFolders(e)))).flat().find(t=>t.defaultImageSelection?.imageId===e);if(s)throw Object.assign(new Error(`Image ${e} is the default for local folder ${s.name}.`),{statusCode:409,code:"IMAGE_IN_USE"});const n=(await this.listNodeInstances()).find(t=>t.imageSelection?.imageId===e);if(n)throw Object.assign(new Error(`Image ${e} is used by instance ${n.name}.`),{statusCode:409,code:"IMAGE_IN_USE"});return this.catalogService.deleteImage(e)}listNodes(){return this.nodes.list()}listPublicNodes(){return this.listNodes().map(Si)}async createNode(e){const t=Xa.parse(e),s=gi(),n=t.connectionMode||"direct-http";let i="auth"in(e&&"object"==typeof e&&!Array.isArray(e)?e:{})?t.auth:{mode:"local-ipc"===n||"local-loopback"===n?"local-static-key":"paired-hmac"};if(("direct-http"===n||"reverse-wss"===n)&&"paired-hmac"!==i.mode){const e=new Error("Remote node connections require paired-HMAC authentication.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_REMOTE_REQUIRES_PAIRED_HMAC"}),e}const a=t.controlEndpoint||t.endpoint;if(!a&&"reverse-wss"!==n){const e=new Error("Node agent direct HTTP mode requires an endpoint.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_ENDPOINT_REQUIRED"}),e}const r=t.joinToken&&a?await this.completeNodeAgentPairing(a,{joinToken:t.joinToken}):void 0;if(r&&(i={mode:"paired-hmac",keyId:r.keyId,secret:r.secret,pairedAt:r.pairedAt,pairing:{status:"paired"}}),("direct-http"===n||"reverse-wss"===n)&&!i.secret){const e=new Error("Remote node connections require a paired node secret or join token.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_REMOTE_SECRET_REQUIRED"}),e}if(("direct-http"===n||"reverse-wss"===n)&&!i.keyId){const e=new Error("Remote node connections require a paired key id.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_REMOTE_KEY_ID_REQUIRED"}),e}const o=t.id||r?.nodeId||gn("node"),d=Ns.parse({id:o,name:t.name,connectionMode:n,auth:i,labels:t.labels||{},createdAt:s,updatedAt:s}),c="reverse-wss"===n&&(t.id||r?.nodeId)?void 0:await this.inspectNodeAgent(a||"",d),l=t.id||r?.nodeId||c?.nodeId||o;if(!l){const e=new Error("Node id is required.");throw Object.assign(e,{statusCode:400,code:"NODE_ID_REQUIRED"}),e}if(t.id&&c?.nodeId&&c.nodeId!==t.id){const e=new Error(`Node agent id ${c.nodeId} does not match requested node id ${t.id}.`);throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_ID_MISMATCH"}),e}const{joinToken:p,...u}=t,m=Ns.parse({...u,id:l,connectionMode:n,auth:i,endpoint:a,controlEndpoint:a,status:t.status||(c?"online":"unknown"),health:t.health||(c?"ok":"unknown"),capabilities:{...t.capabilities||{},...c?{agent:wi(c.data)}:{}},labels:t.labels||{},createdAt:s,updatedAt:s});return this.nodes.put(m)}updateNode(e,t){const s=Za.parse(t),n=this.requireNode(e),i=Ns.parse({...n,...s,id:e,createdAt:n.createdAt,updatedAt:gi()});return this.nodes.put(i)}deleteNode(e){if(hr(this.requireNode(e))){const e=new Error("The built-in local node connection cannot be deleted.");throw Object.assign(e,{statusCode:400,code:"LOCAL_NODE_CANNOT_BE_DELETED"}),e}return this.nodes.delete(e)}async listNodeRuntimes(e){const t=e?[this.requireNode(e)]:this.listNodes();return e?this.nodeAgentGateway.listRuntimes(t[0]):(await this.nodeAgentGateway.listFleetRuntimes(t)).items}async listNodeRuntimesWithDiagnostics(){return this.nodeAgentGateway.listFleetRuntimes(this.listNodes())}async createNodeRuntime(e,t){const s=this.requireNode(e);return this.nodeAgentGateway.createRuntime(s,t)}async updateNodeRuntime(e,t,s){const n=this.requireNode(e);return this.nodeAgentGateway.updateRuntime(n,t,s)}async deleteNodeRuntime(e,t){const s=this.requireNode(e);return this.nodeAgentGateway.deleteRuntime(s,t)}async checkNodeRuntime(e,t){const s=this.requireNode(e);return this.nodeAgentGateway.checkRuntime(s,t)}async listControlledInstances(){return(await this.listNodeInstances()).map(ki)}async listControlledInstancesWithDiagnostics(){const e=await this.listNodeInstancesWithDiagnostics();return{items:e.items.map(ki),nodeErrors:e.nodeErrors}}async instanceResourceMetrics(e){const t=await this.requireNodeInstance(e);return this.nodeAgentGateway.instanceResourceMetrics(this.requireNode(t.nodeId),t.id)}async nodeOwnsInstance(e,t){const s=this.requireNode(e);return(await this.nodeAgentGateway.listInstances(s)).some(s=>s.id===t&&s.nodeId===e)}async createControlledInstance(e){const t=Ka.parse(e),s=t.projectId?this.requireProject(t.projectId):void 0,n=t.runtimeId||s?.defaultRuntimeId||"runtime_local_docker",i=t.nodeId||s?.defaultNodeId||this.defaultNodeId();if(!i){const e=new Error("At least one node connection is required before creating an instance.");throw Object.assign(e,{statusCode:400,code:"NODE_REQUIRED"}),e}const a=this.requireNode(i),r=await this.requireNodeRuntimeOnNode(a.id,n),o="boolean"==typeof r.capabilities.requiresImage?r.capabilities.requiresImage:"local"!==r.type,d=t.imageSelection||(o?s?.defaultImageSelection:void 0),c=d?this.catalogService.resolveImageSelection(d):void 0;if(o&&!c){const e=new Error(`Runtime ${r.name} requires an image.`);throw Object.assign(e,{statusCode:400,code:"RUNTIME_IMAGE_REQUIRED"}),e}let l=t.source||s?.source,p=t.sourceSnapshot||s||{};if(!l){const e=new Error("Instance source is required.");throw Object.assign(e,{statusCode:400,code:"INSTANCE_SOURCE_REQUIRED"}),e}if("local"===r.type&&"local-folder"!==l.type){const e=new Error("Localhost runtime currently supports local folder sources only.");throw Object.assign(e,{statusCode:400,code:"LOCAL_RUNTIME_REQUIRES_LOCAL_FOLDER"}),e}if("local-folder"===l.type){if(l.ownerNodeId&&l.ownerNodeId!==a.id){const e=new Error(`Local folder ${l.path} belongs to node ${l.ownerNodeId}, not ${a.id}.`);throw Object.assign(e,{statusCode:400,code:"LOCAL_FOLDER_REQUIRES_OWNER_NODE"}),e}if(l.localFolderId){const e=await this.requireNodeLocalFolder(a,l.localFolderId);l={...l,localFolderId:e.id,ownerNodeId:e.nodeId,path:e.path},p=e}else{const e=await this.nodeAgentGateway.createLocalFolder(a,{name:"string"==typeof p.name?p.name:"Local folder",path:l.path,defaultImageSelection:c?{imageId:c.id,tag:c.tag}:void 0});l={...l,localFolderId:e.id,ownerNodeId:e.nodeId,path:e.path},p=e}}const u=await this.prepareInstanceModels(a,t.modelSelection||{}),m=gi(),g=c?{id:c.id,origin:c.origin,name:c.name,description:c.description,localizedDescriptions:c.localizedDescriptions,cover:c.cover,repository:c.repository,tag:c.tag,requestedReference:c.reference,resolvedDigest:c.digest,resolvedReference:c.digest?`${c.repository}@${c.digest}`:void 0,downloadSizeBytes:c.downloadSizeBytes,pullPolicy:"if-not-present",capabilities:c.capabilities,optionalApps:c.optionalApps,defaultEnv:c.defaultEnv,labels:c.labels,market:c.market,createdAt:m,updatedAt:m}:void 0,h=await this.nodeAgentGateway.createInstance(a,{id:t.id,name:t.name,runtimeId:n,...c&&g?{imageSelection:{imageId:c.id,tag:c.tag},image:g}:{},projectId:s?.id,source:l,sourceSnapshot:p,config:t.config,modelSelection:{}});let f=h;try{f=(await this.nodeAgentGateway.assignInstanceModels(a,h.id,u)).instance}catch(e){throw await this.nodeAgentGateway.deleteInstance(a,h.id).catch(()=>{}),e}let I={status:"not-requested"};if(t.start)try{f=await this.nodeAgentGateway.startInstance(a,f.id),I={status:"started"}}catch(e){I={status:"failed",error:br(e,"INSTANCE_START_FAILED")},f=await this.nodeAgentGateway.listInstances(a).then(e=>e.find(e=>e.id===f.id)||f).catch(()=>f)}return{...ki(f),registrationToken:f.registrationToken,startOutcome:I}}async updateControlledInstance(e,t){const s=Ja.parse(t),n=await this.requireNodeInstance(e),i=this.requireNode(n.nodeId),{modelSelection:a,...r}=s;let o=Object.keys(r).length?await this.nodeAgentGateway.updateInstance(i,e,r):n;if(a){const t=await this.prepareInstanceModels(i,a);o=(await this.nodeAgentGateway.assignInstanceModels(i,e,t)).instance}return ki(o)}async deleteControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId),n=await this.nodeAgentGateway.deleteInstance(s,e);return this.configSyncPreferences.delete(e),Boolean(n.deleted)}async startControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId);return await this.ensureInstanceModelAssignment(t),ki(await this.nodeAgentGateway.startInstance(s,e))}async stopControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId);return ki(await this.nodeAgentGateway.stopInstance(s,e))}async restartControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId);return await this.ensureInstanceModelAssignment(t),ki(await this.nodeAgentGateway.restartInstance(s,e))}async retryControlledInstanceImageProvisioning(e){const t=await this.requireNodeInstance(e);return ki(await this.nodeAgentGateway.retryInstanceImageProvisioning(this.requireNode(t.nodeId),e))}async boardAsync(){return(await this.boardWithDiagnostics()).items}async boardWithDiagnostics(){const e=await this.listNodeRuntimesWithDiagnostics(),t=await this.listNodeInstancesWithDiagnostics();return this.instanceBoardReader.read({projects:this.listProjects(),images:this.listImageOptions(),nodes:this.nodes.list(),runtimes:e.items,instances:t.items,nodeErrors:[...e.nodeErrors,...t.nodeErrors]})}async bootstrapAiSessionsFromInstances(){const e=await this.listNodeInstances(),t=await Promise.all(e.map(async e=>{if(!nn(e)||!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus)return{streamId:`bootstrap:${e.id}:ai.sessions`,revision:0,lastEventAt:e.aiSessions.updatedAt,snapshot:e.aiSessions};try{return ve.parse(await this.instanceRequest(e,"/ai-sessions/state"))}catch(t){return this.logWarn({instanceId:e.id,error:Ii(t),errorCode:"AI_SESSION_LIVE_REFRESH_FAILED"},"control plane failed to refresh live AI sessions"),{streamId:`bootstrap:${e.id}:ai.sessions`,revision:0,lastEventAt:e.aiSessions.updatedAt,snapshot:e.aiSessions}}}));return{updatedAt:(new Date).toISOString(),instances:e.map((e,s)=>({instanceId:e.id,streamId:t[s].streamId,aiSessions:t[s].snapshot,revision:t[s].revision,lastEventAt:t[s].lastEventAt}))}}async bootstrapAppSessionsFromInstances(){const e=await this.listNodeInstances(),t=await Promise.all(e.map(async e=>{if(!nn(e)||!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=li();return{streamId:`bootstrap:${e.id}:app.sessions`,revision:0,lastEventAt:t.updatedAt,snapshot:t}}try{return Kn.parse(await this.instanceRequest(e,"/apps/sessions/state"))}catch(t){this.logWarn({instanceId:e.id,error:Ii(t),errorCode:"APP_SESSION_LIVE_REFRESH_FAILED"},"control plane failed to refresh live app sessions");const s=li();return{streamId:`bootstrap:${e.id}:app.sessions`,revision:0,lastEventAt:s.updatedAt,snapshot:s}}}));return{updatedAt:(new Date).toISOString(),instances:e.map((e,s)=>({instanceId:e.id,streamId:t[s].streamId,appSessions:t[s].snapshot,revision:t[s].revision,lastEventAt:t[s].lastEventAt}))}}async recoverAiSessionDelta(e,t,s){const n=await this.requireControlledInstance(e,!0),i=new URLSearchParams({streamId:t,sinceRevision:String(s)});return Ne.parse(await this.instanceRequest(n,`/ai-sessions?${i}`))}async recoverAiSessionSnapshot(e){const t=await this.requireControlledInstance(e,!0);return ve.parse(await this.instanceRequest(t,"/ai-sessions/state"))}async recoverAppSessionDelta(e,t,s){const n=await this.requireControlledInstance(e,!0),i=new URLSearchParams({streamId:t,sinceRevision:String(s)});return ei.parse(await this.instanceRequest(n,`/apps/sessions?${i}`))}async recoverAppSessionSnapshot(e){const t=await this.requireControlledInstance(e,!0);return Kn.parse(await this.instanceRequest(t,"/apps/sessions/state"))}async listTriggers(){return this.controlPlaneTriggerService.listTriggers()}createTrigger(e){return this.controlPlaneTriggerService.createTrigger(e)}async deleteTrigger(e){return this.controlPlaneTriggerService.deleteTrigger(e)}async applyTrigger(e,t){return this.controlPlaneTriggerService.applyTrigger(e,t)}async listInstanceTriggers(e){return this.controlPlaneTriggerService.listInstanceTriggers(e)}async bindAiSessionTrigger(e,t,s){return this.controlPlaneTriggerService.bindAiSessionTrigger(e,t,s)}async unbindAiSessionTrigger(e,t,s){return this.controlPlaneTriggerService.unbindAiSessionTrigger(e,t,s)}async runInstanceTrigger(e,t,s={}){return this.controlPlaneTriggerService.runInstanceTrigger(e,t,s)}listChatSessions(){return this.chatSessionService.list()}createAppAccessToken(e){return this.appAccessService.createToken(e)}resolveAppAccessToken(e,t){return this.appAccessService.resolveToken(e,t)}createChatActionToken(e){return this.chatActionTokenService.create(e)}resolveChatActionToken(e,t){return this.chatActionTokenService.resolve(e,t)}pendingDecisionCallbackData(e,t){return this.chatActionTokenService.pendingDecisionCallbackData(e,t)}async appAccessProxyTarget(e,t,s=""){return this.appAccessService.proxyTarget(e,t,s)}listChatBridges(){return this.chatBridgeService.list()}requireChatBridge(e){return this.chatBridgeService.require(e)}createChatBridge(e){return this.chatBridgeService.create(e)}updateChatBridge(e,t){return this.chatBridgeService.update(e,t)}deleteChatBridge(e){return this.chatBridgeService.delete(e)}requireChatSession(e){return this.chatSessionService.require(e)}upsertChatSession(e){return this.chatSessionService.upsert(e)}async handleChatGatewayMessage(e){return this.chatSessionRuntime.handleChatGatewayMessage(e)}async handleChatGatewayAction(e){return this.chatSessionRuntime.handleChatGatewayAction(e)}async handleChatCommand(e,t){return this.chatSessionRuntime.handleChatCommand(e,t)}async listPendingRoutes(){const e=gi(),t=[],s=await this.listAiSessions(),n=new Map(s.instances.map(e=>[e.instanceId,e.aiSessions])),i=await this.nodeAgentGateway.listFleetInstances(this.listNodes());for(const s of i.items){if("online"!==s.connectionStatus&&"online"!==s.agentStatus||!s.target.web)continue;const i=s.projectId?this.catalogService.getProject(s.projectId):void 0;for(const a of n.get(s.id)?.sessions||[]){if("waiting"!==a.status)continue;const n=sn.parse({id:`${s.id}:ai:${a.id}`,instanceId:s.id,projectId:s.projectId,aiSessionId:a.id,providerSessionId:a.providerSessionId,result:a.summary||a.currentTool?.inputPreview||a.lastMessage||`${a.agent} is waiting for approval`,source:a.agent,kind:"approval",status:"pending",lastSeenAt:e});t.push({...n,project:i,instance:ki(s)})}}return t}async listAiSessionInstanceNames(){return(await this.nodeAgentGateway.listFleetInstances(this.listNodes())).items.map(e=>({id:e.id,name:e.name}))}async resolveAiSessionApproval(e,t,s){const n=await this.requireControlledInstance(e,!0);return me.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/approval`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({decision:s})}))}async listAiSessionHistory(e){const t=await this.requireControlledInstance(e,!0);return he.parse(await this.instanceRequest(t,"/ai-sessions/history"))}async getAiSessionHistoryDetail(e,t){const s=await this.requireControlledInstance(e,!0);return fe.parse(await this.instanceRequest(s,`/ai-sessions/history/${encodeURIComponent(t)}`))}async resumeAiSession(e,t){const s=await this.requireControlledInstance(e,!0),n=Ie.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/resume`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})}));return await Promise.all([this.listAppSessions({refresh:!0}),this.listAiSessions({refresh:!0})]),n}async launchAppSession(e,t="terminal-tty",s={}){const n=await this.requireControlledInstance(e,!0);if("online"!==n.connectionStatus&&"online"!==n.agentStatus){const e=new Error(`Instance ${n.name} is still starting. Wait for it to connect before launching apps.`);throw Object.assign(e,{statusCode:409,code:"INSTANCE_NOT_CONNECTED"}),e}const i=await this.resolveAppLaunchOptions(n,s),a=await this.instanceRequest(n,"/apps/sessions",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({appId:t,...i})});return await this.listAppSessions({refresh:!0}),a}async stopAppSession(e,t){const s=await this.requireControlledInstance(e,!0),n=await this.instanceRequest(s,`/apps/sessions/${encodeURIComponent(t)}/stop`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});return await this.listAppSessions({refresh:!0}),n}async instanceAppManagement(e){const t=await this.requireControlledInstance(e,!0);try{return function(e){try{return Et.parse(e)}catch(e){if(e instanceof r.z.ZodError){const e=new Error("This controlled instance does not support managed app operations.");throw Object.assign(e,{statusCode:409,code:"INSTANCE_APP_MANAGEMENT_UNSUPPORTED"}),e}throw e}}(await this.instanceRequest(t,"/apps/management"))}catch(e){const t=e&&"object"==typeof e?e:{};if(e instanceof r.z.ZodError||404===t.statusCode){const e=new Error("This controlled instance does not support managed app operations.");throw Object.assign(e,{statusCode:409,code:"INSTANCE_APP_MANAGEMENT_UNSUPPORTED"}),e}throw e}}async requestInstanceAppOperation(e,t,s,n={}){const i=await this.requireControlledInstance(e,!0);return _t.parse(await this.instanceRequest(i,`/apps/${encodeURIComponent(t)}/${s}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)}))}async instanceAppManagementJob(e,t){const s=await this.requireControlledInstance(e,!0);return _t.parse(await this.instanceRequest(s,`/apps/jobs/${encodeURIComponent(t)}`))}async renameAppSession(e,t,s){const n=await this.requireControlledInstance(e,!0),i=await this.instanceRequest(n,`/apps/sessions/${encodeURIComponent(t)}`,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({title:s})});return await this.listAppSessions({refresh:!0}),i}async sendAiSessionMessage(e,t,s,n,i=[],a=[],r){!function(e){if(e.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)<=A)return;const t=new Error("Inline attachments must be 41943040 bytes or less in total.");throw Object.assign(t,{statusCode:400,code:"AI_SESSION_ATTACHMENTS_TOO_LARGE"}),t}(i);const o=await this.requireControlledInstance(e,!0);i.some(e=>"runtime-path"===e.source.type)&&function(e,t){if("local"===t||!e.some(e=>"runtime-path"===e.source.type))return;const s=new Error("Runtime path attachments are currently available only for Local Runtime instances.");throw Object.assign(s,{statusCode:400,code:"AI_SESSION_RUNTIME_PATH_UNSUPPORTED"}),s}(i,(await this.requireNodeRuntimeOnNode(o.nodeId,o.runtimeId)).type);const d=o.aiSessions.sessions.find(e=>e.id===t),c=r||("codex"===d?.agent?o.config.defaultCodexPermissionMode:void 0),l={message:s,...n?{mode:n}:{},...i.length?{attachments:i}:{},...a.length?{references:a}:{},...c?{permissionMode:c}:{}};return me.parse(await this.instanceRequest(o,`/ai-sessions/${encodeURIComponent(t)}/messages`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(l)}))}async aiSessionMentionCatalog(e,t){const s=await this.requireControlledInstance(e,!0);return ee.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/mentions`))}async searchAiSessionMentionFiles(e,t,s){const n=await this.requireControlledInstance(e,!0);return se.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/mentions/files`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:s})}))}async executeAiSessionCommand(e,t,s){const n=await this.requireControlledInstance(e,!0);return V.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/commands`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)}))}async aiSessionQueue(e,t){const s=await this.requireControlledInstance(e,!0);return oe.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/queue`))}async steerAiSessionQueuedMessage(e,t,s){const n=await this.requireControlledInstance(e,!0);return me.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/queue/${encodeURIComponent(s)}/steer`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})}))}async retryAiSessionQueuedMessage(e,t,s){const n=await this.requireControlledInstance(e,!0);return ue.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/queue/${encodeURIComponent(s)}/retry`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})}))}async removeAiSessionQueuedMessage(e,t,s){const n=await this.requireControlledInstance(e,!0);return ue.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/queue/${encodeURIComponent(s)}`,{method:"DELETE"}))}async interruptAiSession(e,t){const s=await this.requireControlledInstance(e,!0);return me.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/interrupt`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})}))}async proxyInstanceHttp(e,t,s={}){const n=await this.requireControlledInstance(e,!0);return this.controlledInstanceGateway.proxyHttp(n,t,s)}async proxyInstanceWebSocket(e,t,s,n,i={}){const a=await this.requireControlledInstance(e,!0);this.controlledInstanceGateway.proxyWebSocket(a,this.nodeAgentTransportFor(this.requireNode(a.nodeId)),t,s,n,i)}async instanceConfigSyncState(e){const t=await this.requireControlledInstance(e,!0),s=r.z.array(jn).parse(await this.instanceRequest(t,"/config-sync/programs"));return Pn.parse({programs:s,preferences:this.configSyncPreferences.get(e)?.preferences||Ba()})}async listInstanceConfigSyncFolders(e,t={}){const s=await this.requireControlledInstance(e,!0),n=new URLSearchParams;return t.path&&n.set("path",t.path),void 0!==t.depth&&n.set("depth",String(t.depth)),r.z.array(Us).parse(await this.instanceRequest(s,"/config-sync/folders"+(n.size?`?${n}`:"")))}async syncInstanceConfigs(e,t){const s=Dn.parse(t),n=await this.requireControlledInstance(e,!0);if("export"===s.direction&&"local-folder"!==n.source.type){const e=new Error("Exporting instance config is only available for local folder projects.");throw Object.assign(e,{statusCode:400,code:"CONFIG_SYNC_EXPORT_REQUIRES_LOCAL_PROJECT"}),e}const i=function(e){const t=function(e){const t=[];for(const s of e.split("/"))if(s&&"."!==s)if(".."===s){if(!t.length)return"..";t.pop()}else t.push(s);return t.join("/")||"."}(e.trim().replace(/\\/g,"/")||".");if(t.startsWith("/")||".."===t||t.startsWith("../")){const e=new Error("Config sync folder must stay inside the instance workspace.");throw Object.assign(e,{statusCode:400,code:"CONFIG_SYNC_FOLDER_INVALID"}),e}return t||"."}(s.workspaceFolder),a={...s,workspaceFolder:i},r=Ln.parse(await this.instanceRequest(n,"/config-sync",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a)})),o=gi(),d=this.configSyncPreferences.get(e);return this.configSyncPreferences.put({id:e,preferences:{...d?.preferences||Ba(),[s.direction]:i},createdAt:d?.createdAt||o,updatedAt:o}),r}async instanceRequest(e,t,s={}){return this.controlledInstanceGateway.request(e,t,s)}async reportInstanceHeartbeat(e,t){await this.controlledInstanceGateway.reportHeartbeat(e,t,this.nodeAgentTransport)}async nodeAgentFetch(e,t,s={}){return this.nodeAgentTransportFor(e).request(e,t,s)}nodeAgentTransportFor(e){if("reverse-wss"===e.connectionMode){if(!this.nodeAgentTransport){const e=new Error("Reverse node agent transport is not available.");throw Object.assign(e,{statusCode:503,code:"NODE_AGENT_REVERSE_TRANSPORT_UNAVAILABLE"}),e}return this.nodeAgentTransport}return this.directNodeAgentTransport()}directNodeAgentTransport(){const e=async(e,t,s={})=>{const n=e.controlEndpoint||e.endpoint;if(!n){const e=new Error("Node agent direct HTTP mode requires an endpoint.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_ENDPOINT_REQUIRED"}),e}const i=s.method||"GET",a="string"==typeof s.body||s.body instanceof Buffer?s.body:void 0===s.body||null===s.body?void 0:String(s.body),r=Ir(e,{method:i,pathWithQuery:`/api/node-agent${t}`,body:a||""});return this.fetchNodeAgentEndpoint(n,t,{...s,body:a,headers:{...s.headers||{},...r}})};return{request:e,requestStream:e,proxyWebSocket:(e,t,s,n,i={})=>{const a=e.controlEndpoint||e.endpoint;if(!a)throw Object.assign(new Error("Node agent direct HTTP mode requires an endpoint."),{statusCode:400,code:"NODE_AGENT_ENDPOINT_REQUIRED"});const r=dr(a),o=`/api/node-agent${s}`,d=Ir(e,{method:"GET",pathWithQuery:o});if(r){cr(r);const e=lr(r,s,n,{...i,...d});return e.on("open",()=>{t.on("message",(t,s)=>e.readyState===l.WebSocket.OPEN&&e.send(s?t:String(t))),t.on("close",()=>e.close()),t.on("error",()=>e.close())}),e.on("message",(e,s)=>{t.readyState===l.WebSocket.OPEN&&t.send(s?e:e.toString())}),e.on("close",()=>t.close()),void e.on("error",()=>t.close(1011,"Instance websocket proxy failed."))}const c=new URL(o,a.replace(/\/$/,""));c.protocol="https:"===c.protocol?"wss:":"ws:";const p=n?new l.WebSocket(c.toString(),n,{headers:{...i,...d}}):new l.WebSocket(c.toString(),{headers:{...i,...d}});p.on("open",()=>{t.on("message",(e,t)=>p.readyState===l.WebSocket.OPEN&&p.send(t?e:String(e))),t.on("close",()=>p.close()),t.on("error",()=>p.close())}),p.on("message",(e,s)=>{t.readyState===l.WebSocket.OPEN&&t.send(s?e:e.toString())}),p.on("close",()=>t.close()),p.on("error",()=>t.close(1011,"Instance websocket proxy failed."))}}}async listNodeInstances(){return(await this.listNodeInstancesWithDiagnostics()).items}async listNodeInstancesWithDiagnostics(){const e=await this.nodeAgentGateway.listFleetInstances(this.listNodes());return{items:await Promise.all(e.items.map(e=>this.withFreshTriggerSnapshot(e))),nodeErrors:e.nodeErrors}}async withFreshTriggerSnapshot(e){if(!nn(e)||"online"!==e.connectionStatus&&"online"!==e.agentStatus)return e;try{const t=Fe.parse(await this.instanceRequest(e,"/triggers"));return Ws.parse({...e,triggers:sr(t)})}catch{return e}}async requireNodeInstance(e){for(const t of this.listNodes())try{const s=(await this.nodeAgentGateway.listInstances(t)).find(t=>t.id===e);if(s)return s}catch{}yi("CONTROLLED_INSTANCE_NOT_FOUND",`Controlled instance ${e} was not found.`)}async requireNodeRuntimeOnNode(e,t){const s=(await this.listNodeRuntimes(e)).find(e=>e.id===t);return s||yi("NODE_RUNTIME_NOT_FOUND",`Runtime ${t} was not found on node ${e}.`),s}async requireNodeLocalFolder(e,t){return this.nodeAgentGateway.requireLocalFolder(e,t)}async resolveAppLaunchOptions(e,t){const s="string"==typeof t.cwdFolderId?t.cwdFolderId.trim():"",n={...t};if(delete n.cwdFolderId,!s)return n;const i=this.requireNode(e.nodeId),a=await this.requireNodeLocalFolder(i,s);if(a.nodeId!==e.nodeId){const t=new Error(`Local folder ${a.id} belongs to node ${a.nodeId}, not ${e.nodeId}.`);throw Object.assign(t,{statusCode:400,code:"LOCAL_FOLDER_REQUIRES_INSTANCE_NODE"}),t}const r=await this.requireNodeRuntimeOnNode(e.nodeId,e.runtimeId);return{...n,cwd:this.appLaunchCwdForFolder(e,a,r)}}appLaunchCwdForFolder(e,s,n){if("local"===n.type)return Ga(i=s.path).resolve(i);var i;if("local-folder"!==e.source.type){const e=new Error("App cwd folder selection is only supported for local-folder instances.");throw Object.assign(e,{statusCode:400,code:"APP_CWD_REQUIRES_LOCAL_FOLDER_SOURCE"}),e}const a=function(e,t){if(Ha(e)!==Ha(t))return;const s=Ga(e),n=s.resolve(e),i=s.resolve(t),a=s.relative(n,i);return a?".."===a||a.startsWith(`..${s.sep}`)||s.isAbsolute(a)?void 0:a.split(s.sep).filter(Boolean):[]}(e.source.path,s.path);if(!a){const t=new Error(`Local folder ${s.path} is outside the instance workspace ${e.source.path}.`);throw Object.assign(t,{statusCode:400,code:"APP_CWD_OUTSIDE_WORKSPACE"}),t}const r=e.runtime.workspacePath||e.workspace.path||Ai(e.source).path||"/workspace";return a.length?t.posix.join(r,...a):r}async prepareInstanceModels(e,t){const s=await this.nodeAgentGateway.listModels(e),n={...null===t.codexModelHash?{codexModelHash:null}:t.codexModelHash?.trim()?{codexModelHash:t.codexModelHash.trim()}:{},...null===t.claudeModelHash?{claudeModelHash:null}:t.claudeModelHash?.trim()?{claudeModelHash:t.claudeModelHash.trim()}:{}},i=async(t,n)=>{if(null===n)return;const i=n?this.listAllModels().find(e=>e.id===n):this.listAllModels().find(e=>e.enabled&&e.app===t);if(i){if(i.app!==t)throw Object.assign(new Error(`Model ${i.id} is not a ${t} model.`),{statusCode:400,code:"MODEL_APP_MISMATCH"});if(!i.enabled)throw Object.assign(new Error(`Model ${i.id} is disabled.`),{statusCode:400,code:"MODEL_DISABLED"});return await this.nodeAgentGateway.deployModel(e,i.id,i),i.id}if(!n)return;const a=s.find(e=>e.id===n);if(a||yi("MODEL_NOT_FOUND",`Model ${n} was not found on control-plane or node ${e.id}.`),a.app!==t)throw Object.assign(new Error(`Model ${n} is not a ${t} model.`),{statusCode:400,code:"MODEL_APP_MISMATCH"});if(!a.enabled)throw Object.assign(new Error(`Model ${n} is disabled.`),{statusCode:400,code:"MODEL_DISABLED"});return a.id};return{modelSelection:n,codexModelHash:await i("codex",n.codexModelHash),claudeModelHash:await i("claude",n.claudeModelHash)}}async ensureInstanceModelAssignment(e){const t=this.requireNode(e.nodeId),s=await this.prepareInstanceModels(t,e.modelSelection);return this.nodeAgentGateway.assignInstanceModels(t,e.id,s)}nextModelOrder(){return this.models.list().reduce((e,t)=>Math.max(e,t.order),0)+100}requireProject(e){return this.catalogService.requireProject(e)}requireModel(e,t=!1){const s=this.models.get(e);s||yi("MODEL_NOT_FOUND",`Model ${e} was not found.`);const n=this.normalizeModelRecord(s);return t?n:zi(n)}requireModelSecret(e){const t=this.models.get(e);return t||yi("MODEL_NOT_FOUND",`Model ${e} was not found.`),this.normalizeModelRecord(t)}normalizeModelRecord(e){const t=function(e){if(e&&"object"==typeof e&&!Array.isArray(e)){const t={...e};if(!("app"in t)){const e=Array.isArray(t.apps)?t.apps:[];t.app=e.includes("claude")&&!e.includes("codex")?"claude":"codex"}return delete t.apps,Qt.parse(t)}return Qt.parse(e)}(e);return t!==e&&this.models.put(t),t}requireImage(e){return this.catalogService.requireImage(e)}requireNode(e){const t=this.nodes.get(e);return t||yi("NODE_NOT_FOUND",`Node ${e} was not found.`),t}requirePublicNode(e){return Si(this.requireNode(e))}async requireControlledInstance(e,t=!1){const s=await this.requireNodeInstance(e);return t?s:ki(s)}}function br(e,t){const s=(n=e)&&"object"==typeof n&&!Array.isArray(n)?n:{};var n;return{code:"string"==typeof s.code&&s.code.trim()?s.code.trim():t,message:e instanceof Error&&e.message.trim()?e.message:"The operation failed."}}function vr(e){return g(String(e),"escape").replace(/\n+$/,"")}function wr(e){const[t,...s]=String(e).split("\n");return[`*${vr(t)}*`,...s.map(vr)].join("\n")}function Sr(e,t=4e3){const s=String(e),n=wr(s);if(n.length<=t)return n;const[i,...a]=s.split("\n"),r=a.join("\n"),o="...\n";let d=i;for(;wr(`${d}\n${o}`).length>t&&d.length>0;)d=d.slice(0,Math.floor(.8*d.length));let c=0,l=r.length,p=0,u=wr(`${d}\n${o}`);for(;c<=l;){const e=Math.floor((c+l)/2),s=wr(`${d}\n${o}${r.slice(-e)}`);s.length<=t?(u=s,p=e,c=e+1):l=e-1}const m=r.slice(-p),g=m.indexOf("\n");return g>=0&&g<200&&(u=wr(`${d}\n${o}${m.slice(g+1)}`)),u}function Ar(e){return String(e).replace(/\\([_*\[\]()~`>#+\-=|{}.!\\])/g,"$1")}async function kr(e,t,s,n,i={}){if(!t.token)return;const a=s=>e(`https://api.telegram.org/bot${t.token}/sendMessage`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)}),r=function(e,t={}){const s=String(e),n=t.rawMarkdownV2?e=>e:vr;if(n(s).length<=4e3)return[s];const i=[];let a=s;for(;a;){let e=1,t=a.length,s=1;for(;e<=t;){const i=Math.floor((e+t)/2);n(a.slice(0,i)).length<=4e3?(s=i,e=i+1):t=i-1}if(s<a.length){const e=a.slice(0,s),t=Math.max(e.lastIndexOf("\n"),e.lastIndexOf(" "));t>=Math.floor(.6*s)&&(s=t+1)}i.push(a.slice(0,s).trimEnd()),a=a.slice(s).trimStart()}return i.filter(Boolean)}(n,i);let o;for(const[e,t]of r.entries())o=await Tr(a,"sendMessage",t,{chat_id:s,...0===e&&i.replyToMessageId?{reply_to_message_id:i.replyToMessageId,allow_sending_without_reply:!0}:{},...e===r.length-1&&i.replyMarkup?{reply_markup:i.replyMarkup}:{}},i);return o}async function zr(e,t,s){if(!t.token||!s)throw new Error("Telegram file id is required.");const n=await e(`https://api.telegram.org/bot${t.token}/getFile`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({file_id:s})}),i=await n.json().catch(()=>({}));if(!n.ok||!1===i.ok||!i.result?.file_path)throw new Error(i.description||`Telegram getFile failed with HTTP ${n.status}`);return`https://api.telegram.org/file/bot${t.token}/${i.result.file_path}`}async function Tr(e,t,s,n,i){const a=i.parseMode||"MarkdownV2",r=i.rawMarkdownV2?String(s):vr(s),o=await e({...n,text:r,disable_web_page_preview:!0,parse_mode:a}),d=await o.json().catch(()=>({}));if(o.ok&&!1!==d.ok)return d.result;if("editMessageText"===t&&Cr(d.description))return;if(i.rawMarkdownV2&&"MarkdownV2"===a){const i=await e({...n,text:Ar(s),disable_web_page_preview:!0,parse_mode:"Markdown"}),a=await i.json().catch(()=>({}));if(i.ok&&!1!==a.ok)return a.result;if("editMessageText"===t&&Cr(a.description))return}const c=await e({...n,text:i.rawMarkdownV2?Ar(s):String(s),disable_web_page_preview:!0}),l=await c.json().catch(()=>({}));if(c.ok&&!1!==l.ok)return l.result;if("editMessageText"!==t||!Cr(l.description))throw new Error(l.description||d.description||`Telegram ${t} failed with HTTP ${c.status}`)}function Cr(e){return"string"==typeof e&&e.toLowerCase().includes("message is not modified")}function Er(e,t){if("string"==typeof e&&e.trim())return e.trim();_r(`Chat action token is missing ${t}.`)}function Nr(e){if("allow"===e||"deny"===e||"skip"===e)return e;_r("Chat action token has an invalid decision.")}function _r(e){const t=new Error(e);throw Object.assign(t,{statusCode:400,code:"CHAT_ACTION_TOKEN_INVALID"}),t}function xr(e){return Buffer.from(e,"base64url").toString("utf8")}function Or(e){return String(e||"").split(/\r?\n/,1)[0].replace(/^#+\s*/,"").trim().slice(0,20)||"消息通知"}function Rr(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,"string"==typeof t?t:JSON.stringify(t)]))}function jr(e){return`dtv1.card//IM_GROUP.${e}`}function Dr(e){return`dtv1.card//IM_ROBOT.${e}`}function Mr(e,t){return Yr(t.biz_conversation_id).trim()||Yr(e.spaceId).replace(/^dtv1\.card\/\/(?:IM_GROUP|IM_SINGLE|IM_ROBOT)\./,"")}function Pr(e,t={}){const s=Yr(e).trim().toUpperCase();return"2"===s||"IM_GROUP"===s?"IM_GROUP":"1"===s||"IM_SINGLE"===s?t.robotMessage?"IM_ROBOT":"IM_SINGLE":"IM_ROBOT"===s?"IM_ROBOT":void 0}function $r(e){if(e.startsWith("task_handoff:"))return e;if(!e.startsWith("th_cb_"))return"";try{return Buffer.from(e.slice(6),"base64url").toString("utf8")}catch{return""}}function qr(e,t,s,n,i){const a=Mr(n,i);return{cardUpdateOptions:{updateCardDataByKey:!0,updatePrivateDataByKey:!1},cardData:{cardParamMap:Rr({type:"actions",title:"TaskHandoff",description:e,list:ma(t),biz_out_track_id:n.outTrackId||i.biz_out_track_id||"",biz_conversation_id:a,biz_sender_id:i.biz_sender_id||n.userId||"",biz_session_webhook:i.biz_session_webhook||"",biz_conversation_type:i.biz_conversation_type||"",biz_step:s,error_msg:""})}}}function Lr(e,t,s){const n=[];return Ur(n,s),Ur(n,t),Ur(n,e),n.find(e=>$r(e))||""}function Ur(e,t){for(const s of["actionIdList","actionIds"]){const n=t[s];Array.isArray(n)&&e.push(...n.map(e=>Yr(e)).filter(Boolean))}for(const s of["actionId","action_id","id","actionValue"]){const n=Yr(t[s]);n&&e.push(n)}}async function Fr(e,t,s,n){n?.("info",{target:"sessionWebhook",hasSessionWebhook:Boolean(t),textPreview:Kr(s)},"dingding webhook send requested");const i=await e(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({msgtype:"markdown",markdown:{title:Or(s),text:s}})}),a=await i.json().catch(()=>{});if(n?.(i.ok?"info":"warn",{target:"sessionWebhook",status:i.status,response:Vr(a)},"dingding webhook send completed"),!i.ok)throw new Error(`DingDing webhook failed with HTTP ${i.status}`)}async function Br(e){const t=ma(e.replyMarkup);if(!t.length&&!e.forceCard)return e.sessionWebhook&&await Fr(e.fetchImpl,e.sessionWebhook,e.text,e.runtime.onLog),e.sessionWebhook?{delivered:!0}:void 0;const s=e.bridge.settings||{},n=Yr(s.robotCode),i=Yr(s.cardTemplateId)||"13fc6717-12e4-43ed-8533-111a310d4995.schema",a=Yr(s.cardCallbackRouteKey)||"bi_workflow_ticket",r=e.senderId||e.runtime.senderIds.get(e.chatId)||Yr(s.senderId),o=(d=e.runtime,c=e.chatId,d.conversationTypes?.get(c)||"IM_GROUP");var d,c;if(!n||!i||!r){if(e.sessionWebhook&&!e.forceCard)return await Fr(e.fetchImpl,e.sessionWebhook,e.text,e.runtime.onLog),{delivered:!0};throw new Error("DingDing card target is incomplete.")}const l=`task_handoff_cp_${Date.now()}_${Math.random().toString(16).slice(2)}`,p=Rr({type:"actions",title:e.title||Or(e.text),description:e.text,list:t,biz_out_track_id:l,biz_step:e.step||"task_handoff_actions",biz_conversation_id:e.chatId,biz_sender_id:r,biz_session_webhook:e.sessionWebhook||e.runtime.chatWebhooks.get(e.chatId)||"",biz_conversation_type:o,error_msg:""}),u=function(e,t,s,n){return"IM_SINGLE"===e?{openSpaceId:(i=t,`dtv1.card//IM_SINGLE.${i}`),imSingleOpenSpaceModel:{},imSingleOpenDeliverModel:{extension:{}}}:"IM_ROBOT"===e?{openSpaceId:Dr(s),imRobotOpenSpaceModel:{supportForward:!1},imRobotOpenDeliverModel:{extension:{},robotCode:n,spaceType:"IM_ROBOT"}}:{openSpaceId:jr(t),imGroupOpenSpaceModel:{supportForward:!1},imGroupOpenDeliverModel:{extension:{},robotCode:n}};var i}(o,e.chatId,r,n);return e.runtime.onLog?.("info",{target:"card",chatId:e.chatId,senderId:r,conversationType:o,openSpaceId:u.openSpaceId,hasSessionWebhook:Boolean(e.sessionWebhook||e.runtime.chatWebhooks.get(e.chatId)),hasReplyMarkup:Boolean(e.replyMarkup),actionCount:t.length,textPreview:Kr(e.text)},"dingding card send requested"),await Hr(e.fetchImpl,e.bridge,e.runtime,"POST","/v1.0/card/instances/createAndDeliver",{userId:r,cardTemplateId:i,outTrackId:l,callbackType:"STREAM",callbackRouteKey:a,cardData:{cardParamMap:p},userIdType:Number(s.cardUserIdType||1),...u}),{delivered:!0,outTrackId:l}}async function Hr(e,t,s,n,i,a){const r=await async function(e,t,s){if(s.accessToken&&s.accessToken.expiresAt>Date.now()+6e4)return s.accessToken.value;const n=Yr(t.settings.clientSecret);if(!t.token||!n)throw new Error("DingDing client id/secret is not configured.");const i=await e("https://api.dingtalk.com/v1.0/oauth2/accessToken",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({appKey:t.token,appSecret:n})}),a=await i.json().catch(()=>({}));if(!i.ok||!a.accessToken)throw new Error(`DingDing access token failed with HTTP ${i.status}`);const r=Number(a.expireIn||7200);return s.accessToken={value:a.accessToken,expiresAt:Date.now()+1e3*Math.max(60,r-120)},s.accessToken.value}(e,t,s);s.onLog?.("info",{method:n,path:i,body:Gr(a)},"dingding api request");const o=await e(`https://api.dingtalk.com${i}`,{method:n,headers:{"content-type":"application/json","x-acs-dingtalk-access-token":r},body:JSON.stringify(a)}),d=await o.json().catch(()=>{});if(s.onLog?.(o.ok?"info":"warn",{method:n,path:i,status:o.status,response:Vr(d)},"dingding api response"),!o.ok)throw new Error(`DingDing API failed with HTTP ${o.status}`);const c=Jr(d),l=c.code??c.errcode,p=c.success;if(void 0!==l&&"0"!==String(l)||!1===p)throw new Error(Yr(c.message||c.errmsg)||`DingDing API returned ${String(l||"failure")}`);const u=function(e){const t=Jr(Jr(e).result).deliverResults;if(!Array.isArray(t))return"";const s=t.find(e=>!1===Jr(e).success);if(!s)return"";const n=Jr(s),i=Yr(n.spaceId),a=Yr(n.errorMsg||n.message||n.errmsg)||"delivery failed";return i?`DingDing card delivery failed for ${i}: ${a}`:`DingDing card delivery failed: ${a}`}(d);if(u)throw new Error(u);return o}function Gr(e){const t=Jr(e),s=Jr(t.cardData),n=Jr(s.cardParamMap);return{openSpaceId:Yr(t.openSpaceId),outTrackId:Yr(t.outTrackId),userId:Yr(t.userId),userIdType:t.userIdType,callbackType:Yr(t.callbackType),callbackRouteKey:Yr(t.callbackRouteKey),hasImGroupTarget:Boolean(t.imGroupOpenSpaceModel||t.imGroupOpenDeliverModel),hasImSingleTarget:Boolean(t.imSingleOpenSpaceModel||t.imSingleOpenDeliverModel),hasImRobotTarget:Boolean(t.imRobotOpenSpaceModel||t.imRobotOpenDeliverModel),imGroupOpenDeliverModel:Wr(t.imGroupOpenDeliverModel),imSingleOpenDeliverModel:Wr(t.imSingleOpenDeliverModel),imRobotOpenDeliverModel:Wr(t.imRobotOpenDeliverModel),bizConversationId:Yr(n.biz_conversation_id),bizConversationType:Yr(n.biz_conversation_type),bizSenderId:Yr(n.biz_sender_id),hasBizSessionWebhook:Boolean(Yr(n.biz_session_webhook))}}function Wr(e){const t=Jr(e);if(Object.keys(t).length)return{hasExtension:Boolean(t.extension),hasRobotCode:Boolean(Yr(t.robotCode)),spaceType:Yr(t.spaceType),recipients:Array.isArray(t.recipients)?t.recipients.length:void 0,atUserIds:t.atUserIds&&"object"==typeof t.atUserIds?Object.keys(t.atUserIds).length:void 0}}function Vr(e){const t=Jr(e),s=Jr(t.result);return{code:t.code??t.errcode,message:Kr(t.message||t.errmsg,240),success:t.success,requestId:t.requestId,result:Object.keys(s).length?s:void 0}}function Kr(e,t=160){const s=String(e||"").replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,Math.max(0,t-3))}...`}function Jr(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function Qr(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:"string"==typeof e&&e.trim()?Xr(e):{}}function Yr(e){return"string"==typeof e?e:""}function Xr(e){try{return Jr(JSON.parse(String(e||"{}")))}catch{return{}}}function Zr(e){return JSON.stringify(ma(e))}function eo(e){return e instanceof Error?e.message:String(e)}function to(e){const t=Number(function(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}(e).message_id);return Number.isInteger(t)&&t>0?t:void 0}function so(e){return Array.isArray(e?.actionRows)?e.actionRows:(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e])}function no(e,t){const s=so(e),n=so(t);return s.length===n.length&&s.every((e,t)=>{const s=n[t];return e.length===s.length&&e.every((e,t)=>{const n=s[t];return e.text===n?.text&&e.callbackData===n.callbackData})})}class io{entries;updateIntervalMs;send;edit;deleteMessage;messageIdFromResult;retryAfterMs;isMessageTooLong;onLog;constructor(e){this.entries=e.entries||new Map,this.updateIntervalMs=e.updateIntervalMs||1e3,this.send=e.send,this.edit=e.edit,this.deleteMessage=e.delete,this.messageIdFromResult=e.messageIdFromResult||to,this.retryAfterMs=e.retryAfterMs,this.isMessageTooLong=e.isMessageTooLong,this.onLog=e.onLog}remember(e,t,s,n,i,a){const r=this.entries.get(e);return r&&(clearTimeout(r.timer),r.timer=void 0,r.pendingText=void 0),this.entries.set(e,{lastText:s,lastUpdateAt:Date.now(),messageId:t,route:n,options:i,lastOptions:i,renderOptions:a}),r}rekey(e,t){if(e===t)return this.entries.has(e);const s=this.entries.get(e);if(!s||this.entries.has(t))return!1;if(clearTimeout(s.timer),s.timer=void 0,this.entries.delete(e),this.entries.set(t,s),s.pendingText){const e=s.rateLimitedUntil?Math.max(0,s.rateLimitedUntil-Date.now()):Math.max(0,this.updateIntervalMs-(Date.now()-s.lastUpdateAt));this.scheduleFlush(t,e)}return!0}scheduleFlush(e,t){const s=this.entries.get(e);s&&!s.timer&&(s.timer=setTimeout(()=>{s.timer=void 0;const t=s.pendingText;s.pendingText=void 0,t&&this.applyUpdate(e,t,s.route,s.options)},t))}markRateLimited(e,t,s,n){const i=this.entries.get(e);i&&(i.rateLimitedUntil=Date.now()+s,i.pendingText=t,i.route=n||i.route,this.scheduleFlush(e,s))}async applyUpdate(e,t,s,n){let i=this.entries.get(e);const a=i,r=s||i?.route,o=n||i?.options;try{if(i&&(i.route=r,i.options=o),i?.pending&&await i.pending,i=this.entries.get(e),a&&i!==a)return!1;if(i?.messageId){if(i.lastText===t&&no(i.lastOptions,o))return!0;const s=Date.now()-(i.lastUpdateAt||0);return s<this.updateIntervalMs?(i.pendingText=t,i.route=r||i.route,i.options=o||i.options,this.scheduleFlush(e,this.updateIntervalMs-s),!0):(await this.edit(i.messageId,t,r||i.route,o),i.lastText=t,i.lastOptions=o,i.lastUpdateAt=Date.now(),i.rateLimitedUntil=void 0,!0)}const s={lastText:t,lastUpdateAt:Date.now(),route:r,options:o,lastOptions:o};return s.pending=this.send(t,r,o).then(e=>{const t=this.messageIdFromResult(e);t&&(s.messageId=t)}).finally(()=>{s.pending=void 0}),this.entries.set(e,s),await s.pending,this.entries.get(e)===s&&(s.rateLimitedUntil=void 0,Boolean(s.messageId))}catch(s){const n=this.retryAfterMs?.(s);return n&&this.markRateLimited(e,t,n,r),this.onLog?.(`Telegram progress update skipped: ${eo(s)}`),!1}}update(e,t,s,n){const i=String(t||"").trim();if(!i)return;const a=this.entries.get(e);if(a&&(a.route=s||a.route,a.options=n||a.options),a?.lastText===i&&no(a.lastOptions,n||a.options)&&!a.pending&&!a.pendingText)return;const r=Date.now();if(a?.rateLimitedUntil){if(a.rateLimitedUntil>r)return a.pendingText=i,void this.scheduleFlush(e,a.rateLimitedUntil-r);a.rateLimitedUntil=void 0}if(a?.pending&&!a.messageId)return a.pendingText=i,void this.scheduleFlush(e,this.updateIntervalMs);if(!a||!a.messageId)return void this.applyUpdate(e,i,s,n);const o=Date.now()-(a.lastUpdateAt||0);if(o>=this.updateIntervalMs)return clearTimeout(a.timer),a.timer=void 0,a.pendingText=void 0,void this.applyUpdate(e,i,s||a.route,n||a.options);a.pendingText=i,this.scheduleFlush(e,this.updateIntervalMs-o)}async finish(e,t,s,n,i){const a=this.entries.get(e);if(!a)return!1;try{if(clearTimeout(a.timer),a.timer=void 0,a.pendingText=void 0,a.pending&&await a.pending,this.entries.delete(e),!a.messageId)return!1;const r=n||a.options;return a.lastText===t&&no(a.lastOptions,r)||await this.edit(a.messageId,t,s||a.route,r,i||a.renderOptions),!0}catch(t){return this.entries.delete(e),this.isMessageTooLong?.(t)?this.onLog?.("Telegram progress finish too long; sending result as multiple messages"):this.onLog?.(`Telegram progress finish skipped: ${eo(t)}`),!1}}async delete(e,t){const s=this.entries.get(e);if(!s)return!1;try{return clearTimeout(s.timer),s.timer=void 0,s.pendingText=void 0,s.pending&&await s.pending,this.entries.delete(e),!(!this.deleteMessage||!s.messageId)&&(await this.deleteMessage(s.messageId,t||s.route),!0)}catch(t){return this.entries.delete(e),this.onLog?.(`Telegram progress delete skipped: ${eo(t)}`),!1}}async flush(e){const t=this.entries.get(e);if(t?.pending&&await t.pending,t?.timer){clearTimeout(t.timer),t.timer=void 0;const s=t.pendingText;t.pendingText=void 0,s&&await this.applyUpdate(e,s,t.route,t.options)}}async wait(e){await function(e){return new Promise(t=>setTimeout(t,e))}(e)}}const ao=20971520;function ro(e){const t=[],s=[...e.photo||[]].filter(e=>e.file_id).sort((e,t)=>Number(t.file_size||t.width||0)-Number(e.file_size||e.width||0))[0];return s?.file_id&&t.push({fileId:s.file_id,fileName:"telegram-photo.jpg",fileSize:s.file_size}),e.document?.file_id&&String(e.document.mime_type||"").startsWith("image/")&&t.push({fileId:e.document.file_id,fileName:e.document.file_name,fileSize:e.document.file_size}),t}async function oo(e,s,n="telegram-image",i){if(i&&i>ao)throw new Error(`image is too large: ${i} bytes`);const a=await e.getFileLink(s),r=String(a),o=await(e.fetchFile||fetch)(r);if(!o.ok)throw new Error(`download failed with HTTP ${o.status}`);const d=Number(o.headers.get("content-length")||0);if(d>ao)throw new Error(`image is too large: ${d} bytes`);const c=t.extname(n)||".jpg";let l=c;try{l=t.extname(new URL(r).pathname)||c}catch{l=c}const p=t.join(f.tmpdir(),"task-handoff-images");await h.mkdir(p,{recursive:!0});const u=l.replace(/[^.\w-]/g,"")||".jpg",m=t.join(p,`telegram-${Date.now()}-${Math.random().toString(16).slice(2)}${u}`),g=Buffer.from(await o.arrayBuffer());if(g.length>ao)throw new Error(`image is too large: ${g.length} bytes`);return await h.writeFile(m,g),m}class co{store;constructor(e){this.store=new io({updateIntervalMs:e.updateIntervalMs,send:(t,s,n)=>e.send(e.requireBridge(s?.bridgeId||""),s?.chatId||"",Sr(t),{rawMarkdownV2:!0,replyMarkup:ho(n)}),edit:(t,s,n,i)=>e.edit(e.requireBridge(n?.bridgeId||""),n?.chatId||"",t,Sr(s),{rawMarkdownV2:!0,replyMarkup:ho(i)}),delete:(t,s)=>e.deleteMessage(e.requireBridge(s?.bridgeId||""),s?.chatId||"",t),onLog:e.onLog})}get entries(){return this.store.entries}remember(e,t,s,n){return this.store.remember(e,t,s,n)}rekey(e,t){return this.store.rekey(e,t)}delete(e){return this.store.delete(e)}async applyUpdate(e){return this.store.applyUpdate(e.key,e.text,{bridgeId:e.bridge.id,chatId:e.chatId},{actionRows:e.actionRows})}}async function lo(e){return(await async function(e,t,s,n){const i=[];for(const a of ro(s))try{const s=await oo({getFileLink:s=>zr(e,t,s),fetchFile:e},a.fileId,a.fileName,a.fileSize);i.push(s)}catch(e){n?.({fileId:a.fileId,fileName:a.fileName,error:e})}return i}(e.fetchImpl,e.bridge,e.message,e.onImageDownloadError)).map((e,n)=>{const i=s.readFileSync(e);return{id:`tg_${o.randomUUID().replace(/-/g,"").slice(0,24)}`,kind:"image",name:t.basename(e)||`telegram-image-${n+1}`,mime:po(e),size:i.length,source:{type:"inline",encoding:"base64",data:i.toString("base64")}}})}function po(e){const s=t.extname(e).toLowerCase();return".png"===s?"image/png":".webp"===s?"image/webp":".gif"===s?"image/gif":".bmp"===s?"image/bmp":"image/jpeg"}function uo(e){const t=Number(e?.message_id);return Number.isInteger(t)?t:void 0}function mo(e){return e&&"object"==typeof e?e:{}}function go(e){return"string"==typeof e?e:""}function ho(e){const t=Array.isArray(e?.actionRows)?e.actionRows:void 0;return la(t||(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e]))}function fo(e){const t=Array.isArray(e.item_list)?e.item_list:[];for(const e of t){const t=Io(e);if(1!==t.type)continue;const s=yo(Io(t.text_item).text).trim();if(s)return s}return""}function Io(e){return e&&"object"==typeof e?e:{}}function yo(e){return"string"==typeof e?e:""}function bo(e){const{fetchImpl:t,bridge:s,dingdingRuntime:n}=e;return"telegram"===s.channel?function(e,t){return{bridge:t,send:async(s,n,i={})=>{const a=await kr(e,t,s,n,{replyMarkup:i.replyMarkup,replyToMessageId:i.replyToMessageId,rawMarkdownV2:i.rawMarkdownV2,parseMode:i.parseMode});return{provider:"telegram",raw:a,messageId:Number(mo(a).message_id)||void 0}}}}(t,s):"dingding"===s.channel?function(e,t,s){return{bridge:t,send:async(n,i,a={})=>{const r=a.sessionWebhook||s?.chatWebhooks.get(n)||Yr(t.settings.sessionWebhook);if(a.replyMarkup&&s){const o=await Br({fetchImpl:e,bridge:t,runtime:s,chatId:n,text:i,replyMarkup:a.replyMarkup,sessionWebhook:r,senderId:a.senderId||s.senderIds.get(n)||Yr(t.settings.senderId)});if(o?.delivered)return{provider:"dingding",interactionId:o.outTrackId}}if(r)return await Fr(e,r,i,s?.onLog),{provider:"dingding"}}}}(t,s,n):"wechat"===s.channel?function(e,t){return{bridge:t,send:async(s,n,i={})=>{const a=i.contextToken||yo(t.settings.contextToken);if(a)return await async function(e,t,s,n,i){const a=yo(t.settings.baseUrl)||"https://ilinkai.weixin.qq.com",r=await e(`${a}/ilink/bot/sendmessage`,{method:"POST",headers:{"content-type":"application/json",AuthorizationType:"ilink_bot_token",Authorization:`Bearer ${t.token}`,"X-WECHAT-UIN":String(Math.floor(Math.random()*2**31))},body:JSON.stringify({msg:{from_user_id:"",to_user_id:s,client_id:o.randomUUID(),message_type:2,message_state:2,context_token:n,item_list:[{type:1,text_item:{text:i}}]},base_info:{channel_version:"1.0.2"}})}),d=await r.json().catch(()=>({}));if(!r.ok||d.errcode)throw new Error(yo(d.errmsg)||`WeChat send failed with HTTP ${r.status}`)}(e,t,s,a,n),{provider:"wechat"}}}}(t,s):{bridge:s,send:async()=>{}}}class vo{entries=new Map;fetchImpl;generations=new Map;constructor(e){this.fetchImpl=e}clear(){for(const e of new Set([...this.generations.keys(),...[...this.entries.values()].map(e=>e.bridgeId)]))this.clearBridge(e)}clearBridge(e){this.generations.set(e,this.generation(e)+1),this.clearEntries(t=>t.bridgeId===e)}generation(e){return this.generations.get(e)||0}isCurrent(e,t){return this.generation(e)===t}clearEntries(e){for(const[t,s]of this.entries)e(s)&&(s.timer&&clearTimeout(s.timer),s.cancelPending?.(new Error("DingDing progress update was cancelled.")),this.entries.delete(t))}async applyUpdate(e,t){const s=this.generation(e.bridge.id),n=Zr(e.replyMarkup);let i=this.entries.get(e.key);if(i?.pending&&await i.pending,!this.isCurrent(e.bridge.id,s))return!1;if(i=this.entries.get(e.key),i?.outTrackId){if(i.lastText===e.text&&i.lastActionsFingerprint===n)return!0;const a=Date.now()-(i.lastUpdateAt||0);return a<1e3?(i.pendingText=e.text,i.pendingReplyMarkup=e.replyMarkup,i.timer||(i.pending=new Promise((n,r)=>{i.cancelPending=r,i.timer=setTimeout(()=>{i.timer=void 0,i.cancelPending=void 0;const a=i.pendingText,o=i.pendingReplyMarkup;if(i.pendingText=void 0,i.pendingReplyMarkup=void 0,!a)return n(void 0);this.updateExistingEntry(e,t,i,a,o,Zr(o),s).then(n,r)},1e3-a)}).finally(()=>{i.pending=void 0,i.timer=void 0,i.cancelPending=void 0})),await i.pending,this.isCurrent(e.bridge.id,s)&&this.entries.has(e.key)):(await this.updateExistingEntry(e,t,i,e.text,e.replyMarkup,n,s),this.isCurrent(e.bridge.id,s))}const a={bridgeId:e.bridge.id,lastText:e.text,lastActionsFingerprint:n,lastUpdateAt:Date.now()};return a.pending=Br({fetchImpl:this.fetchImpl,bridge:e.bridge,runtime:t,chatId:e.chatId,text:e.text,replyMarkup:e.replyMarkup,sessionWebhook:t.chatWebhooks.get(e.chatId)||wo(e.bridge.settings.sessionWebhook),senderId:t.senderIds.get(e.chatId)||wo(e.bridge.settings.senderId),title:"TaskHandoff 执行中",step:"progress",forceCard:!0}).then(t=>{this.isCurrent(e.bridge.id,s)&&(t?.outTrackId&&(a.outTrackId=t.outTrackId),t?.delivered&&(a.delivered=!0,a.lastUpdateAt=Date.now()))}).catch(t=>{throw this.isCurrent(e.bridge.id,s)&&this.entries.delete(e.key),t}).finally(()=>{a.pending=void 0}),this.entries.set(e.key,a),await a.pending,this.isCurrent(e.bridge.id,s)&&Boolean(a.delivered)}async updateExistingEntry(e,t,s,n,i,a,r){s.outTrackId&&(await async function(e){await Hr(e.fetchImpl,e.bridge,e.runtime,"PUT","/v1.0/card/instances",{outTrackId:e.outTrackId,cardData:{cardParamMap:Rr({type:"actions",title:e.title,description:e.text,list:ma(e.replyMarkup),biz_step:e.step,error_msg:""})},cardUpdateOptions:{updateCardDataByKey:!0,updatePrivateDataByKey:!1},userIdType:Number(e.bridge.settings.cardUserIdType||1)})}({fetchImpl:this.fetchImpl,bridge:e.bridge,runtime:t,outTrackId:s.outTrackId,text:n,replyMarkup:i,title:"TaskHandoff 执行中",step:"progress"}),this.isCurrent(e.bridge.id,r)&&(s.lastText=n,s.lastActionsFingerprint=a,s.lastUpdateAt=Date.now()))}}function wo(e){return"string"==typeof e?e:""}function So(e){const t=new I.DWClient({clientId:e.clientId,clientSecret:e.clientSecret,ua:"task-handoff-control-plane",keepAlive:!1,autoReconnect:!1});let s,n=!1;return{async connect(){n=!1,await t.getEndpoint(),await t._connect();const e=t.socket;e?.once("close",()=>{n||s?.(new Error("DingDing stream connection closed."))})},disconnect(){n=!0,t.disconnect()},onDisconnect(e){s=e},getConfig:()=>t.getConfig(),registerCallbackListener(e,s){t.registerCallbackListener(e,s)},socketCallBackResponse(e,s){t.socketCallBackResponse(e,s)}}}class Ao{runtimes=new Map;connected=new Set;reconnectTimers=new Map;progress;options;createClient;constructor(e){this.options=e,this.createClient=e.createClient||So,this.progress=new vo(e.fetchImpl)}has(e){return this.runtimes.has(e)}isRunning(e){return this.connected.has(e)}get(e){return this.runtimes.get(e)}ids(){return this.runtimes.keys()}start(e){const t=(s=e.settings?.clientSecret,"string"==typeof s?s:"");var s;if(!e.token||!t)return this.options.onError(e.id,"DingDing client id/secret is not configured."),!1;const n={client:this.createClient({clientId:e.token,clientSecret:t}),chatWebhooks:new Map,senderIds:new Map,conversationTypes:new Map,onLog:(t,s,n)=>{this.options.logger["warn"===t?"warn":"info"]({bridgeId:e.id,...s},n)}};return n.client.onDisconnect?.(t=>{this.runtimes.get(e.id)===n&&this.handleConnectionFailure(e,n,t)}),n.client.registerCallbackListener(I.TOPIC_ROBOT,t=>{this.options.onRobotMessage(e,n,t).catch(t=>{this.options.onError(e.id,t)})}),n.client.registerCallbackListener(I.TOPIC_CARD,t=>{const s=t;this.options.onCardCallback(e,n,s).then(e=>n.client.socketCallBackResponse(s.headers.messageId,e||{})).catch(t=>{this.options.onError(e.id,t),n.client.socketCallBackResponse(s.headers.messageId,{})})}),this.options.clearError(e.id),this.runtimes.set(e.id,n),this.connect(e,n),!0}connect(e,t){this.reconnectTimers.delete(e.id),this.connected.add(e.id),t.client.connect().then(()=>{this.runtimes.get(e.id)===t&&(this.connected.add(e.id),this.options.clearError(e.id))}).catch(s=>{this.runtimes.get(e.id)===t&&this.handleConnectionFailure(e,t,s)})}handleConnectionFailure(e,t,s){if(this.connected.delete(e.id),this.progress.clearBridge(e.id),this.options.onError(e.id,s||"DingDing stream connection failed."),this.reconnectTimers.has(e.id))return;const n=setTimeout(()=>{this.runtimes.get(e.id)===t&&this.connect(e,t)},this.options.reconnectDelayMs??1e3);n.unref?.(),this.reconnectTimers.set(e.id,n)}stop(e){const t=this.reconnectTimers.get(e);t&&clearTimeout(t),this.reconnectTimers.delete(e),this.connected.delete(e);const s=this.runtimes.get(e);if(s){try{s.client.disconnect()}catch{}this.runtimes.delete(e)}this.progress.clearBridge(e)}stopAll(){for(const e of[...this.runtimes.keys()])this.stop(e);this.progress.clear()}applyUpdate(e){const t=this.runtimes.get(e.bridge.id);return t?this.progress.applyUpdate(e,t):Promise.resolve(!1)}applyProgressUpdate(e){return this.applyUpdate(e)}}class ko{service;fetchImpl;logger;bridgeTimers=new Map;dingdingBridges;bridgeErrors=new Map;telegramOffsets=new Map;seenTelegramUpdates=new Set;wechatCursors=new Map;announcedPending=new Set;deliveredAiSessionFingerprints=new Map;aiSessionCancelTokens=new Map;aiSessionQueueActionTokens=new Map;telegramMessageAggregates=new Map;telegramMessageAggregateEndTokens=new Map;telegramProgressMessageTargets=new Map;aiSessionInstanceNamesCache;aiSessionInstanceNamesPending;aiSessionPendingRoutesCache;aiSessionPendingRoutesPending;telegramProgress;stopAiSessionListener;constructor(e,t=fetch,s={}){var n;this.service=e,this.fetchImpl=t,this.logger=(n=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(n||"").trim().toLowerCase())?s.logger:void 0),this.telegramProgress=new co({updateIntervalMs:s.telegramProgressUpdateIntervalMs,requireBridge:e=>this.service.requireChatBridge(e),send:(e,t,s,n={})=>this.sendTelegramMessage(e,t,s,n),edit:(e,t,s,n,i={})=>this.editTelegramMessage(e,t,s,n,i),deleteMessage:(e,t,s)=>this.deleteTelegramMessage(e,t,s),onLog:e=>this.logWarn({message:e},"telegram progress store warning")}),this.dingdingBridges=new Ao({fetchImpl:this.fetchImpl,createClient:s.createDingdingClient,logger:{info:(e,t)=>this.logInfo(e,t),warn:(e,t)=>this.logWarn(e,t)},onRobotMessage:(e,t,s)=>this.handleDingdingRobotMessage(e,t,s),onCardCallback:(e,t,s)=>this.handleDingdingCardCallback(e,t,s),onError:(e,t)=>this.bridgeErrors.set(e,jo(t)),clearError:e=>this.bridgeErrors.delete(e)}),this.stopAiSessionListener=s.aiSessions?.onSnapshot(e=>{this.deliverAiSessionSnapshot(e.instanceId,e.aiSessions).catch(t=>{this.logWarn({instanceId:e.instanceId,snapshotRevision:e.revision,error:jo(t)},"chat ai session snapshot delivery failed")})})}logInfo(e,t){this.logger?.info?.({component:"control-plane-chat-gateway",...e},t)}logWarn(e,t){this.logger?.warn?.({component:"control-plane-chat-gateway",...e},t)}updateChatBridge(e,t){this.service.updateChatBridge?.(e,t)}startEnabled(){this.logAiSessionDelivery({stage:"runtime-start-enabled",bridgeCount:this.service.listChatBridges().length,enabledBridgeCount:this.service.listChatBridges().filter(e=>e.enabled).length});for(const e of this.service.listChatBridges())e.enabled&&this.startBridge(e.id)}startBridge(e){if(this.bridgeTimers.has(e)||this.dingdingBridges.has(e))return this.logAiSessionDelivery({stage:"bridge-start-skipped-running",bridgeId:e}),this.status();const t=this.service.requireChatBridge(e);return this.logAiSessionDelivery({stage:"bridge-start",bridgeId:t.id,channel:t.channel,enabled:t.enabled,hasToken:Boolean(t.token)}),"wechat"===t.channel?this.startPollingBridge(t,e=>this.pollWechat(e)):"dingding"===t.channel?this.startDingdingBridge(t):"telegram"!==t.channel?(this.bridgeErrors.set(e,`${t.channel} bridge is not supported in the control plane.`),this.status()):t.token?this.startPollingBridge(t,e=>this.pollTelegram(e)):(this.bridgeErrors.set(e,"Telegram token is not configured."),this.status())}stopBridge(e){const t=this.bridgeTimers.get(e);return t&&(clearInterval(t),this.bridgeTimers.delete(e)),this.dingdingBridges.stop(e),this.status()}stopAll(){for(const e of new Set([...this.bridgeTimers.keys(),...this.dingdingBridges.ids()]))this.stopBridge(e);this.dingdingBridges.stopAll();for(const e of this.telegramMessageAggregates.values())e.timer&&clearTimeout(e.timer);this.telegramMessageAggregates.clear(),this.telegramMessageAggregateEndTokens.clear(),this.stopAiSessionListener?.(),this.stopAiSessionListener=void 0}async pollBridgeNow(e){const t=this.service.requireChatBridge(e);return"telegram"===t.channel?(await this.pollTelegram(t),this.status()):"wechat"===t.channel?(await this.pollWechat(t),this.status()):("dingding"===t.channel||this.bridgeErrors.set(e,`${t.channel} bridge is not supported in the control plane.`),this.status())}async pollAiSessionsNow(){return await this.pollAiSessionUpdates(),this.status()}status(){const e=this.service.listChatBridges().map(e=>({id:e.id,channel:e.channel,name:e.name,running:this.bridgeTimers.has(e.id)||this.dingdingBridges.isRunning(e.id),tokenSet:Boolean(e.tokenSet),defaultChatId:e.defaultChatId,lastUpdateId:this.telegramOffsets.get(e.id),error:this.bridgeErrors.get(e.id)}));return{running:e.some(e=>e.running),bridges:e}}startPollingBridge(e,t){if(!e.token)return this.bridgeErrors.set(e.id,`${e.channel} token is not configured.`),this.status();this.bridgeErrors.delete(e.id);const s=setInterval(()=>{const s=this.service.requireChatBridge(e.id);s.enabled?t(s).catch(t=>this.bridgeErrors.set(e.id,t instanceof Error?t.message:String(t))):this.stopBridge(e.id)},e.pollIntervalMs);if(this.bridgeTimers.set(e.id,s),this.logAiSessionDelivery({stage:"bridge-poll-started",bridgeId:e.id,channel:e.channel,intervalMs:e.pollIntervalMs}),e.enabled){const s=this.service.requireChatBridge(e.id);t(s).catch(t=>this.bridgeErrors.set(e.id,t instanceof Error?t.message:String(t)))}return this.status()}startDingdingBridge(e){return this.dingdingBridges.start(e),this.status()}async handleDingdingRobotMessage(e,t,s){const n=function(e){const t=Xr(e),s=Yr(t.conversationId),n=Yr(Jr(t.text).content).trim();if(!s||!n)return;const i=Yr(t.senderStaffId||t.senderId);return{chatId:s,userId:i||void 0,senderId:i||void 0,sessionWebhook:Yr(t.sessionWebhook)||void 0,conversationType:Pr(t.conversationType,{robotMessage:!0}),text:n,raw:t}}(s.data);if(t.client.socketCallBackResponse(s.headers.messageId,{}),!n||!this.dingdingAllowed(e,n.senderId))return void this.logWarn({bridgeId:e.id,hasEvent:Boolean(n),senderId:n?.senderId,allowed:n?this.dingdingAllowed(e,n.senderId):void 0},"dingding robot message ignored");const{chatId:i,text:a,senderId:r,sessionWebhook:o}=n;o&&t.chatWebhooks.set(i,o),n.conversationType&&t.conversationTypes.set(i,n.conversationType);const d=o||t.chatWebhooks.get(i)||Ro(e.settings.sessionWebhook);let c;r&&t.senderIds.set(i,r),e.defaultChatId||this.updateChatBridge(e.id,{defaultChatId:i}),this.logInfo({bridgeId:e.id,chatId:i,senderId:r,conversationType:n.conversationType,hasSessionWebhook:Boolean(o),hasCachedSessionWebhook:Boolean(t.chatWebhooks.get(i)),hasResolvedSessionWebhook:Boolean(d),textPreview:Mo(a)},"dingding chat gateway message received");try{c=await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"dingding",bridgeId:e.id,chatSessionId:i,userId:r||void 0},message:{text:a,attachments:[]}})}catch(e){if(d)return void await Fr(this.fetchImpl,d,`Failed to handle message: ${jo(e)}`,t.onLog);throw e}const l=zo(c);if(l){const s=Co(c);if(await this.sendDingdingRoutedAiSessionProgress(c,e,t,i,l,{replyMarkup:s,sessionWebhook:d,senderId:r,sourcePrompt:a}))return;const n=bo({fetchImpl:this.fetchImpl,bridge:e,dingdingRuntime:t}),o=await n.send(i,l,{replyMarkup:s,sessionWebhook:d,senderId:r});if(o)return void this.logInfo({bridgeId:e.id,chatId:i,senderId:r,provider:o.provider,interactionId:o.interactionId,routed:!0===qo(c).routed,hasReplyMarkup:Boolean(s),replyPreview:Mo(l)},"dingding chat gateway reply sent");this.logWarn({bridgeId:e.id,chatId:i,senderId:r,hasReplyMarkup:Boolean(s),hasSessionWebhook:Boolean(d),hasRobotCode:Boolean(Ro(e.settings.robotCode)),replyPreview:Mo(l)},"dingding chat gateway reply skipped")}}async sendDingdingRoutedAiSessionProgress(e,t,s,n,i,a={}){if(!0!==e.routed)return!1;const r=Fo(e),{instanceId:o,sessionId:d,turnId:c}=r;if(!o||!d||!c)return this.logWarn({bridgeId:t.id,chatId:n,instanceId:o,sessionId:d,turnId:c,turnIdSources:r.sources,routed:e.routed},"dingding ai session progress card not started"),!1;const l=Jo(o,d,c,t.id,n);return!!await this.dingdingBridges.applyProgressUpdate({bridge:t,key:l,chatId:n,text:i,replyMarkup:a.replyMarkup})&&(this.deliveredAiSessionFingerprints.delete(l),this.logInfo({bridgeId:t.id,chatId:n,instanceId:o,sessionId:d,turnId:c,key:l,hasSessionWebhook:Boolean(a.sessionWebhook),hasSenderId:Boolean(a.senderId),sourcePrompt:Mo(a.sourcePrompt,80),textPreview:Mo(i)},"dingding ai session progress card started"),!0)}async handleDingdingCardCallback(e,t,s){const n=function(e){const t=Xr(e),s=Qr(t.cardActionData||t.content),n=Qr(s.cardPrivateData||t.cardPrivateData||t.content),i=Qr(n.params||s.params||t.params),a=Yr(t.userId),r=Yr(i.biz_sender_id);return{userId:a||void 0,chatId:Mr(t,i),senderId:a||void 0,deliverySenderId:r||void 0,sessionWebhook:Yr(i.biz_session_webhook)||void 0,conversationType:Pr(i.biz_conversation_type||t.conversationType||t.conversation_type),callbackData:$r(Lr(t,s,n)),body:t,params:i}}(s.data);if(!this.dingdingAllowed(e,n.userId))return qr("not authorized",void 0,"unauthorized",n.body,n.params);const i=n.chatId||Ro(e.defaultChatId),a=n.deliverySenderId||n.senderId,r=n.sessionWebhook;let o;i&&r&&t.chatWebhooks.set(i,r),i&&n.conversationType&&t.conversationTypes.set(i,n.conversationType),i&&a&&t.senderIds.set(i,a);try{o=await this.parseChatGatewayCallbackAction(n.callbackData)}catch(e){if(Do(e,"CHAT_PENDING_ACTION_STALE"))return qr(jo(e),void 0,"approval_stale",n.body,n.params);throw e}if(!o)return this.logWarn({bridgeId:e.id,chatId:i,userId:n.userId,senderId:a,...Po(n)},"dingding card callback action unsupported"),qr("Unsupported action",void 0,"unsupported",n.body,n.params);this.logInfo({bridgeId:e.id,chatId:i,userId:n.userId,senderId:a,actionType:o.type,...Po(n)},"dingding card callback action parsed");const d=await this.service.handleChatGatewayAction({source:{channel:"dingding",bridgeId:e.id,chatSessionId:i,userId:n.userId||void 0},action:o});return"pending-decision"===o.type&&To(d)?qr(`${o.decision} sent`,void 0,"approval_done",n.body,n.params):qr(zo(d)||Ro(d.message)||"updated",Co(d),"updated",n.body,n.params)}async pollWechat(e){if(!e.token)return;const t=e.settings||{},s=this.wechatCursors.get(e.id)||Ro(t.updatesBuf),n=await async function(e){const t=yo((e.bridge.settings||{}).baseUrl)||"https://ilinkai.weixin.qq.com",s=await e.fetchImpl(`${t}/ilink/bot/getupdates`,{method:"POST",headers:{"content-type":"application/json",AuthorizationType:"ilink_bot_token",Authorization:`Bearer ${e.bridge.token}`,"X-WECHAT-UIN":String(Math.floor(Math.random()*2**31))},body:JSON.stringify({get_updates_buf:e.cursor,base_info:{channel_version:"1.0.2"}})}),n=await s.json().catch(()=>({}));if(!s.ok)throw new Error(`HTTP ${s.status}`);const i=Number(n.errcode??n.ret??0);if(0!==i)throw new Error(yo(n.errmsg||n.msg)||`chat bridge API returned ${i}`);const a=[];for(const e of Array.isArray(n.msgs)?n.msgs:[]){const t=Io(e);if(1!==t.message_type)continue;const s=yo(t.from_user_id),n=fo(t);s&&n&&a.push({chatId:s,text:n,contextToken:yo(t.context_token)})}return{cursor:yo(n.get_updates_buf),messages:a}}({fetchImpl:this.fetchImpl,bridge:e,cursor:s});n.cursor&&(this.wechatCursors.set(e.id,n.cursor),this.updateChatBridge(e.id,{settings:{updatesBuf:n.cursor}}));for(const s of n.messages){const{chatId:n,contextToken:i,text:a}=s;if(e.defaultChatId||this.updateChatBridge(e.id,{defaultChatId:n,settings:{contextToken:i}}),e.defaultChatId&&e.defaultChatId!==n)continue;const r=zo(await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"wechat",bridgeId:e.id,chatSessionId:n},message:{text:a,attachments:[]}}));if(r){const s=bo({fetchImpl:this.fetchImpl,bridge:e});await s.send(n,r,{contextToken:i||Ro(t.contextToken)})}}}async pollPendingRoutes(){const e=await this.service.listPendingRoutes().catch(()=>[]);for(const t of e){if(this.announcedPending.has(t.id))continue;const e=Bo(t,(e,t)=>this.service.pendingDecisionCallbackData(e,t)),s=t.aiSessionId?this.service.listChatSessions().filter(e=>e.activeInstanceId===t.instanceId&&e.activeAiSessionId===t.aiSessionId):[],n=new Set;for(const t of s)t.bridgeId&&await this.sendToChatBinding(t,e).then(e=>{e&&n.add(t.id)});(0!==n.size||await this.sendToDefaultBridgeChats(e))&&this.announcedPending.add(t.id)}}async sendToDefaultBridgeChats(e){let t=!1;for(const s of this.service.listChatBridges()){if(!s.enabled||!s.defaultChatId)continue;let n=!1;await this.sendViaBridge(s,s.defaultChatId,e).catch(e=>{this.bridgeErrors.set(s.id,e instanceof Error?e.message:String(e)),n=!0})?t=!0:n||this.bridgeErrors.set(s.id,"Chat bridge message was not delivered.")}return t}async sendToChatBinding(e,t){if(!e.bridgeId)return!1;const s=this.service.requireChatBridge(e.bridgeId);if(!s.enabled)return!1;const n=await this.sendViaBridge(s,e.chatSessionId,t).catch(e=>{this.bridgeErrors.set(s.id,e instanceof Error?e.message:String(e))});return Boolean(n)}async sendViaBridge(e,t,s){const n="string"==typeof s?s:s.text,i="string"==typeof s?void 0:s.replyMarkup;return bo({fetchImpl:this.fetchImpl,bridge:e,dingdingRuntime:this.dingdingBridges.get(e.id)}).send(t,n,{replyMarkup:i})}async pollTelegram(e){if(!e.token)return;const t=Number(e.settings?.telegramLastUpdateId),s=this.telegramOffsets.get(e.id)||(Number.isInteger(t)?t:void 0),n=await async function(e){const t=new URLSearchParams({timeout:"0",...e.offset?{offset:String(e.offset+1)}:{}}),s=await e.fetchImpl(`https://api.telegram.org/bot${e.bridge.token}/getUpdates?${t.toString()}`),n=await s.json().catch(()=>({}));if(!s.ok||!1===n.ok){if(409===s.status||/terminated by other getUpdates request/i.test(String(n.description||"")))return{updates:[],conflict:n.description||"Telegram getUpdates conflict: another bot instance is polling this token."};throw new Error(n.description||`Telegram getUpdates failed with HTTP ${s.status}`)}const i=[];for(const t of n.result||[]){const s=Number(t.update_id),n=Number.isInteger(s)?s:void 0,a=mo(t.callback_query);if(Object.keys(a).length>0){i.push({updateId:n,callbackQuery:a});continue}const r=mo(t.message||t.edited_message);if(0===Object.keys(r).length)continue;const o=mo(r.chat),d=mo(r.from),c=void 0!==o.id?String(o.id):e.bridge.defaultChatId,l=void 0!==d.id?String(d.id):void 0;if(!c)continue;const p=go(r.text??r.caption),u=ro(r);(p||0!==u.length)&&i.push({updateId:n,chatId:c,userId:l,text:p,rawText:p,message:r,messageId:uo(r),replyToMessageId:uo(mo(r.reply_to_message)),quoteText:go(mo(r.quote).text),autoBegin:1===u.length&&!p.trim(),hasSingleImageWithoutText:1===u.length&&!p.trim()})}return{updates:i}}({fetchImpl:this.fetchImpl,bridge:e,offset:s});if(n.conflict)this.bridgeErrors.set(e.id,n.conflict);else for(const t of n.updates){if(void 0!==t.updateId){const s=`${e.id}:${t.updateId}`;if(this.seenTelegramUpdates.has(s)||t.updateId<=(this.telegramOffsets.get(e.id)||0))continue;this.seenTelegramUpdates.add(s),this.telegramOffsets.set(e.id,t.updateId),this.updateChatBridge(e.id,{settings:{...e.settings,telegramLastUpdateId:t.updateId}})}if("callbackQuery"in t){const s=t.callbackQuery;await this.handleTelegramCallback(e,s).catch(async t=>{const n=qo(s.message),i=qo(n.chat),a=void 0!==i.id?String(i.id):e.defaultChatId;this.bridgeErrors.set(e.id,jo(t)),a&&await this.sendTelegramMessage(e,a,`Action failed: ${jo(t)}`).catch(t=>{this.bridgeErrors.set(e.id,jo(t))})});continue}if(!this.telegramAllowed(e,t.userId))continue;const s=await lo({fetchImpl:this.fetchImpl,bridge:e,message:t.message,onImageDownloadError:({fileId:t,fileName:s,error:n})=>{this.logWarn({bridgeId:e.id,fileId:t,fileName:s,error:jo(n)},"telegram image download skipped")}}),n=t.rawText.trim();(n||s.length)&&(this.logInfo({bridgeId:e.id,chatId:t.chatId,messageId:t.messageId,replyToMessageId:t.replyToMessageId,hasQuote:Boolean(t.quoteText),textPreview:Mo(n)},"telegram chat gateway message received"),await this.handleTelegramIncomingText(e,t.chatId,t.userId,n,s,{sourceMessageId:t.messageId,replyToMessageId:t.replyToMessageId,quoteText:t.quoteText},{autoBegin:t.autoBegin}))}}async handleTelegramIncomingText(e,t,s,n,i=[],a={},r={}){const o=n.trim(),d=o.split(/\s+/,1)[0]?.toLowerCase()||"",c=_o(e.id,t,s),l=this.telegramMessageAggregates.get(c);return r.autoBegin&&!l?(this.startTelegramAggregate(c,e,t,s,a),this.enqueueTelegramAggregate(c,e,t,s,n,i,a,!0),void await this.sendTelegramMessage(e,t,"Image received. Continue sending messages, then send /end when done.",{replyToMessageId:a.sourceMessageId,replyMarkup:la([[{text:"/end",callbackData:this.telegramAggregateEndCallbackData(c)}]])})):"/begin"===d?(this.startTelegramAggregate(c,e,t,s,a),void await this.sendTelegramMessage(e,t,"Started collecting messages. Send /end when done.",{replyToMessageId:a.sourceMessageId,replyMarkup:la([[{text:"/end",callbackData:this.telegramAggregateEndCallbackData(c)}]])})):void("/end"!==d?l?.explicit?this.enqueueTelegramAggregate(c,e,t,s,n,i,a,!0):this.isImmediateTelegramCommand(o)?await this.dispatchTelegramGatewayMessage(e,t,s,n,i,a):this.enqueueTelegramAggregate(c,e,t,s,n,i,a,!1):await this.flushTelegramAggregate(c,"manual"))}isImmediateTelegramCommand(e){return e.trim().startsWith("/")}startTelegramAggregate(e,t,s,n,i={}){const a=this.telegramMessageAggregates.get(e);a?.timer&&clearTimeout(a.timer),this.telegramMessageAggregates.set(e,{bridgeId:t.id,chatId:s,userId:n,texts:[],attachments:[],context:i,explicit:!0})}enqueueTelegramAggregate(e,t,s,n,i,a,r,o){const d=this.telegramMessageAggregates.get(e);d?.timer&&clearTimeout(d.timer);const c=d||{bridgeId:t.id,chatId:s,userId:n,texts:[],attachments:[],context:r,explicit:o};var l,p;c.bridgeId=t.id,c.chatId=s,c.userId=n,c.context=(l=c.context,p=r,{sourceMessageId:l?.sourceMessageId??p.sourceMessageId,replyToMessageId:l?.replyToMessageId??p.replyToMessageId,quoteText:l?.quoteText||p.quoteText}),c.explicit=c.explicit||o,i.trim()&&c.texts.push(i),c.attachments.push(...a),c.explicit?c.timer=void 0:c.timer=setTimeout(()=>{this.flushTelegramAggregate(e,"timer")},1e3),this.telegramMessageAggregates.set(e,c)}async flushTelegramAggregate(e,t){const s=this.telegramMessageAggregates.get(e);if(!s)return!1;s.timer&&clearTimeout(s.timer),this.telegramMessageAggregates.delete(e);const n=this.service.requireChatBridge(s.bridgeId),i=s.texts.join("\n\n").trim();return i||s.attachments.length?(await this.dispatchTelegramGatewayMessage(n,s.chatId,s.userId,i,s.attachments,s.context),!0):("timer"!==t&&await this.sendTelegramMessage(n,s.chatId,"No messages collected.",{replyToMessageId:s.context?.sourceMessageId}),!1)}async handleTelegramAggregateEndCallback(e,t,s,n,i){const a=this.telegramMessageAggregateEndTokens.get(n)||"";if(a!==_o(e.id,t,i))return void await this.answerTelegramCallback(e,s,"This collection is not active here");const r=await this.flushTelegramAggregate(a,"callback");await this.answerTelegramCallback(e,s,r?"Collected message sent":"No messages collected")}telegramAggregateEndCallbackData(e){const t=o.createHash("sha256").update(e).digest("base64url").slice(0,16);return this.telegramMessageAggregateEndTokens.set(t,e),`task_handoff:cp_msg_end:${t}`}async dispatchTelegramGatewayMessage(e,t,s,n,i=[],a={}){try{const r=void 0!==a.replyToMessageId?this.telegramReplyAiSessionTarget(e.id,t,a.replyToMessageId):void 0,o=function(e,t){const s=String(t||"").trim(),n=e.trim();return s?[`引用:${s}`,n].filter(Boolean).join("\n"):n}(n,a.quoteText),d=await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"telegram",bridgeId:e.id,chatSessionId:t,userId:s},message:{text:o,attachments:i},...r?{target:{instanceId:r.instanceId,aiSessionId:r.sessionId}}:{}}),c=zo(d),l=Co(d);if(c){const s=await this.sendTelegramMessage(e,t,c,{replyMarkup:l,replyToMessageId:a.sourceMessageId});this.logInfo({bridgeId:e.id,chatId:t,sourceMessageId:a.sourceMessageId,replyToMessageId:a.replyToMessageId,replyTargetInstanceId:r?.instanceId,replyTargetSessionId:r?.sessionId,sentMessageId:Number(qo(s).message_id)||void 0,routed:!0===qo(d).routed,replyPreview:Mo(c)},"telegram chat gateway reply sent"),await this.rememberTelegramProgressMessage(d,e,t,s,c)}}catch(s){this.bridgeErrors.set(e.id,jo(s)),await this.sendTelegramMessage(e,t,`Failed to handle message: ${jo(s)}`,{replyToMessageId:a.sourceMessageId}).catch(t=>{this.bridgeErrors.set(e.id,jo(t))})}}async handleTelegramCallback(e,t){const s=Ro(t.data),n=qo(t.from),i=qo(t.message),a=qo(i.chat),r=void 0!==a.id?String(a.id):e.defaultChatId,o=void 0!==n.id?String(n.id):void 0;if(!r||!this.telegramAllowed(e,o))return;const d=s.match(/^task_handoff:cp_ai_cancel:([^:]+)$/);if(d){const s=this.aiSessionCancelTokens.get(d[1]);return s?void await this.handleTelegramAiSessionCancelCallback(e,r,Ro(t.id),s.instanceId,s.sessionId,o,No(i)):void await this.answerTelegramCallback(e,Ro(t.id),"This AI session action expired")}const c=s.match(/^task_handoff:cp_ai_steer:([^:]+)$/);if(c){const s=this.aiSessionQueueActionTokens.get(c[1]);return s&&"steer"===s.type?void await this.handleTelegramAiSessionQueueSteerCallback(e,r,Ro(t.id),s.instanceId,s.sessionId,s.queueId,o,No(i)):void await this.answerTelegramCallback(e,Ro(t.id),"This AI session action expired")}const l=s.match(/^task_handoff:cp_ai_qdel_menu:([^:]+)$/);if(l){const s=this.aiSessionQueueActionTokens.get(l[1]);return s&&"delete-menu"===s.type?void await this.handleTelegramAiSessionQueueDeleteMenuCallback(e,r,Ro(t.id),s.instanceId,s.sessionId,o,No(i)):void await this.answerTelegramCallback(e,Ro(t.id),"This AI session action expired")}const p=s.match(/^task_handoff:cp_ai_qdel:([^:]+)$/);if(p){const s=this.aiSessionQueueActionTokens.get(p[1]);return s&&"delete-item"===s.type?void await this.handleTelegramAiSessionQueueDeleteItemCallback(e,r,Ro(t.id),No(i),s.instanceId,s.sessionId,s.queueId,o):void await this.answerTelegramCallback(e,Ro(t.id),"This AI session action expired")}const u=s.match(/^task_handoff:cp_msg_end:([^:]+)$/);if(u)return void await this.handleTelegramAggregateEndCallback(e,r,Ro(t.id),u[1],o);let m;try{m=await this.parseChatGatewayCallbackAction(s)}catch(s){if(Do(s,"CHAT_PENDING_ACTION_STALE")){await this.answerTelegramCallback(e,Ro(t.id),jo(s));const n=No(i);return void(void 0!==n&&await this.deleteTelegramStandaloneApprovalMessage(e,r,n))}throw s}if(!m)return void await this.answerTelegramCallback(e,Ro(t.id),"Unsupported action");const g=await this.service.handleChatGatewayAction({source:{channel:"telegram",bridgeId:e.id,chatSessionId:r,userId:o},action:m});if("pending-decision"===m.type&&To(g)){await this.answerTelegramCallback(e,Ro(t.id),"");const s=No(i);return void(void 0!==s&&await this.deleteTelegramStandaloneApprovalMessage(e,r,s))}await this.answerTelegramCallback(e,Ro(t.id),Ro(g.message)||"updated");const h=zo(g),f=Co(g),I=i.message_id;h&&"number"==typeof I?await this.editTelegramMessage(e,r,I,h,{replyMarkup:f}):h&&await this.sendTelegramMessage(e,r,h,{replyMarkup:f})}async deleteTelegramStandaloneApprovalMessage(e,t,s){[...this.telegramProgress.entries.values()].find(n=>n.messageId===s&&n.route?.bridgeId===e.id&&n.route?.chatId===t)||await this.deleteTelegramMessage(e,t,s).catch(n=>{this.logWarn({bridgeId:e.id,chatId:t,messageId:s,error:jo(n)},"telegram callback message delete failed")})}async handleTelegramAiSessionCancelCallback(e,t,s,n,i,a,r){if(this.telegramAiSessionActionAllowed(e,t,n,i,r))try{await this.service.interruptAiSession(n,i),await this.answerTelegramCallback(e,s,"Interrupt sent"),this.logInfo({bridgeId:e.id,chatId:t,instanceId:n,sessionId:i,userId:a},"telegram ai session cancel sent")}catch(t){this.bridgeErrors.set(e.id,jo(t)),await this.answerTelegramCallback(e,s,`Interrupt failed: ${Mo(jo(t),120)}`)}else await this.answerTelegramCallback(e,s,"This chat is not bound to that AI session")}async handleTelegramAiSessionQueueSteerCallback(e,t,s,n,i,a,r,o){if(this.telegramAiSessionActionAllowed(e,t,n,i,o))try{await this.service.steerAiSessionQueuedMessage(n,i,a),await this.answerTelegramCallback(e,s,"Steered queued message"),this.logInfo({bridgeId:e.id,chatId:t,instanceId:n,sessionId:i,queueId:a,userId:r},"telegram ai session queued message steered")}catch(t){this.bridgeErrors.set(e.id,jo(t)),await this.answerTelegramCallback(e,s,`Steer failed: ${Mo(jo(t),120)}`)}else await this.answerTelegramCallback(e,s,"This chat is not bound to that AI session")}async handleTelegramAiSessionQueueDeleteMenuCallback(e,t,s,n,i,a,r){if(this.telegramAiSessionActionAllowed(e,t,n,i,r))try{const r=qo(await this.service.aiSessionQueue(n,i)),o=(Array.isArray(r.items)?r.items:[]).map(e=>qo(e)).filter(e=>"queued"===Ro(e.status)&&Ro(e.id));if(!o.length)return void await this.answerTelegramCallback(e,s,"Queue is empty");const d=o.slice(0,Ho).map((e,t)=>[{text:Ko(Ro(e.message),t),callbackData:this.aiSessionQueueDeleteItemCallbackData(n,i,Ro(e.id))}]);await this.sendTelegramMessage(e,t,"Delete queued message",{replyMarkup:la(d)}),await this.answerTelegramCallback(e,s,"Select a queued message"),this.logInfo({bridgeId:e.id,chatId:t,instanceId:n,sessionId:i,itemCount:o.length,userId:a},"telegram ai session queue delete menu sent")}catch(t){this.bridgeErrors.set(e.id,jo(t)),await this.answerTelegramCallback(e,s,`Queue menu failed: ${Mo(jo(t),120)}`)}else await this.answerTelegramCallback(e,s,"This chat is not bound to that AI session")}async handleTelegramAiSessionQueueDeleteItemCallback(e,t,s,n,i,a,r,o){if(this.telegramAiSessionActionAllowed(e,t,i,a,n))try{await this.service.removeAiSessionQueuedMessage(i,a,r),await this.answerTelegramCallback(e,s,"Queued message deleted"),Number.isInteger(n)&&await this.deleteTelegramMessage(e,t,n).catch(s=>{this.logWarn({bridgeId:e.id,chatId:t,instanceId:i,sessionId:a,queueId:r,messageId:n,error:jo(s)},"telegram ai session queue delete menu message delete failed")}),this.logInfo({bridgeId:e.id,chatId:t,instanceId:i,sessionId:a,queueId:r,userId:o},"telegram ai session queued message deleted")}catch(t){this.bridgeErrors.set(e.id,jo(t)),await this.answerTelegramCallback(e,s,`Delete failed: ${Mo(jo(t),120)}`)}else await this.answerTelegramCallback(e,s,"This chat is not bound to that AI session")}telegramAiSessionBindingActive(e,t,s,n){return this.service.listChatSessions().some(i=>i.bridgeId===e.id&&i.chatSessionId===t&&i.activeInstanceId===s&&i.activeAiSessionId===n)}telegramAiSessionActionAllowed(e,t,s,n,i){if(this.telegramAiSessionBindingActive(e,t,s,n))return!0;if(!Number.isInteger(i))return!1;const a=this.telegramReplyAiSessionTarget(e.id,t,i);return Boolean(a&&a.instanceId===s&&a.sessionId===n)}async parseChatGatewayCallbackAction(e){const t=function(e){const t=e.match(/^task_handoff:cp_p:v1:([ads]):([A-Za-z0-9_-]{16})$/);if(!t)return;const s=Object.entries(ea).find(([,e])=>e===t[1])?.[0];return"allow"===s||"deny"===s||"skip"===s?{routeFingerprint:t[2],decision:s}:void 0}(e);if(t){const e=(await this.service.listPendingRoutes()).filter(e=>"approval"===e.kind&&"pending"===e.status&&ta(e.id)===t.routeFingerprint);if(1!==e.length){const e=new Error("This approval is no longer pending.");throw Object.assign(e,{statusCode:409,code:"CHAT_PENDING_ACTION_STALE"}),e}return{type:"pending-decision",routeId:e[0].id,decision:t.decision}}return function(e,t){const s=e.match(/^task_handoff:cp_session:(\d+)$/);if(s)return{type:"ai-session",index:Number(s[1])};const n=e.match(/^task_handoff:cp_i:([^:]+)$/)||e.match(/^task_handoff:cp_instance_apps:([^:]+)$/);if(n)return n[0].startsWith("task_handoff:cp_i:")?{type:"instance-app-menu",instanceId:Er(t(n[1],"instance-app-menu").instanceId,"instanceId")}:{type:"instance-app-menu",instanceId:xr(n[1])};const i=e.match(/^task_handoff:cp_a:([^:]+)$/)||e.match(/^task_handoff:cp_launch_app:([^:]+):([^:]+)$/);if(i){if(i[0].startsWith("task_handoff:cp_a:")){const e=t(i[1],"launch-app");return{type:"launch-app",instanceId:Er(e.instanceId,"instanceId"),appId:Er(e.appId,"appId")}}return{type:"launch-app",instanceId:xr(i[1]),appId:xr(i[2])}}const a=e.match(/^task_handoff:cp_p:([^:]+)$/);if(a){const e=t(a[1],"pending-decision");return{type:"pending-decision",routeId:Er(e.routeId,"routeId"),decision:Nr(e.decision)}}const r=e.match(/^task_handoff:approval:([^:]+:ai:[^:]+):(allow|deny|skip)$/);return r?{type:"pending-decision",routeId:r[1],decision:r[2]}:void 0}(e,(e,t)=>this.service.resolveChatActionToken(e,t))}dingdingAllowed(e,t){const s=String(t||"").trim();return!!s&&(0===e.allowedUserIds.length?(e.allowedUserIds=[s],this.updateChatBridge(e.id,{allowedUserIds:e.allowedUserIds}),this.logInfo({bridgeId:e.id,userId:s},"dingding user bound"),!0):e.allowedUserIds.includes(s))}telegramAllowed(e,t){const s=String(t||"").trim();return!!s&&(0===e.allowedUserIds.length?(e.allowedUserIds=[s],this.updateChatBridge(e.id,{allowedUserIds:e.allowedUserIds}),this.logInfo({bridgeId:e.id,userId:s},"telegram user bound"),!0):e.allowedUserIds.includes(s))}async answerTelegramCallback(e,t,s){return async function(e,t,s,n){t.token&&s&&await e(`https://api.telegram.org/bot${t.token}/answerCallbackQuery`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({callback_query_id:s,text:n.slice(0,200)})})}(this.fetchImpl,e,t,s)}async editTelegramMessage(e,t,s,n,i={}){return async function(e,t,s,n,i,a={}){if(t.token)return Tr(s=>e(`https://api.telegram.org/bot${t.token}/editMessageText`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)}),"editMessageText",i,{chat_id:s,message_id:n,...a.replyMarkup?{reply_markup:a.replyMarkup}:{}},a)}(this.fetchImpl,e,t,s,n,i)}async sendTelegramMessage(e,t,s,n={}){return kr(this.fetchImpl,e,t,s,n)}async deleteTelegramMessage(e,t,s){return async function(e,t,s,n){if(!t.token||!Number.isInteger(n))return;const i=await e(`https://api.telegram.org/bot${t.token}/deleteMessage`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({chat_id:s,message_id:n})}),a=await i.json().catch(()=>({}));if(!i.ok||!1===a.ok)throw new Error(a.description||`Telegram deleteMessage failed with HTTP ${i.status}`)}(this.fetchImpl,e,t,s)}async rememberTelegramProgressMessage(e,t,s,n,i){if(!0!==e.routed)return;const a=Fo(e),{instanceId:r,sessionId:o,turnId:d}=a,c=Number(qo(n).message_id);if(!(r&&o&&d&&Number.isInteger(c)))return void this.logWarn({bridgeId:t.id,chatId:s,instanceId:r,sessionId:o,turnId:d,turnIdSources:a.sources,messageId:Number.isInteger(c)?c:void 0,routed:e.routed},"telegram ai session progress message not remembered");const l=Jo(r,o,d,t.id,s),p=this.telegramProgress.remember(l,c,i,{bridgeId:t.id,chatId:s});this.telegramProgressMessageTargets.set(xo(t.id,s,c),{instanceId:r,sessionId:o,turnId:d,messageId:c}),this.deliveredAiSessionFingerprints.delete(l),this.logInfo({bridgeId:t.id,chatId:s,instanceId:r,sessionId:o,turnId:d,turnIdSources:a.sources,key:l,messageId:c,progressKeys:this.telegramProgressKeysFor(r,o,t.id,s),textPreview:Mo(i)},"telegram ai session progress message remembered"),"queue"===function(e){return e.aiSession&&"action"in e.aiSession?e.aiSession.action:void 0}(e)&&p?.messageId&&p.messageId!==c&&(this.telegramProgressMessageTargets.delete(xo(t.id,s,p.messageId)),await this.deleteTelegramMessage(t,s,p.messageId).catch(e=>{this.logWarn({bridgeId:t.id,chatId:s,instanceId:r,sessionId:o,turnId:d,replacedMessageId:p.messageId,replacementMessageId:c,error:jo(e)},"telegram queued progress message replacement cleanup failed")}))}telegramReplyAiSessionTarget(e,t,s){const n=this.telegramProgressMessageTargets.get(xo(e,t,s));if(n)return n;for(const[n,i]of this.telegramProgress.entries)if(i.messageId===s&&i.route?.bridgeId===e&&i.route?.chatId===t){const i=Oo(n,e,t);if(i){const n={...i,messageId:s};return this.telegramProgressMessageTargets.set(xo(e,t,s),n),n}}}async pollAiSessionUpdates(){this.logAiSessionDelivery({stage:"poll-tick"});const e=await this.service.listAiSessions({refresh:!0}).catch(()=>{});if(!e)return void this.logAiSessionDelivery({stage:"poll-no-snapshot"});const t=new Map(e.instances.map(e=>[e.instanceId,e.aiSessions]));this.logAiSessionDelivery({stage:"poll-snapshot",instanceCount:t.size,instances:[...t.entries()].map(([e,t])=>({instanceId:e,sessionCount:t.sessions?.length||0,runningCount:t.runningCount,waitingCount:t.waitingCount,updatedAt:t.updatedAt}))});const s=await this.aiSessionInstanceNames();for(const[e,n]of t)await this.deliverAiSessionSnapshot(e,n,s)}async deliverAiSessionSnapshot(e,t,s){const n=Array.isArray(t.sessions)?t.sessions:[];if(this.pruneAiSessionDeliveryState(e,new Set(n.map(e=>e.id))),0===n.length)return;const i=s||await this.aiSessionInstanceNames(),a=await this.aiSessionPendingApprovalRoutes(e);await this.deliverOwnedTelegramProgress(e,n,i,a),await this.deliverActiveBindingTelegramProgress(e,n,i,a)}pruneAiSessionDeliveryState(e,t){for(const[s,n]of this.telegramProgress.entries){const i=n.route?.bridgeId&&n.route.chatId?this.telegramProgressMessageTargets.get(xo(n.route.bridgeId,n.route.chatId,n.messageId||0))||Oo(s,n.route.bridgeId,n.route.chatId):void 0;i&&i.instanceId===e&&!t.has(i.sessionId)&&(n.timer&&clearTimeout(n.timer),this.telegramProgress.entries.delete(s),this.deliveredAiSessionFingerprints.delete(s),n.route?.bridgeId&&n.route.chatId&&n.messageId&&this.telegramProgressMessageTargets.delete(xo(n.route.bridgeId,n.route.chatId,n.messageId)))}}async deliverActiveBindingTelegramProgress(e,t,s,n){for(const i of this.service.listChatSessions()){if(!i.bridgeId||i.activeInstanceId!==e||!i.activeAiSessionId)continue;const a=t.find(e=>e.id===i.activeAiSessionId);if(!a)continue;const r=ed(s.get(e)||e,a),o=Zo(a,r);if(!o){this.logAiSessionDelivery({stage:"skip-empty-text",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,status:a.status,phase:a.phase,activeTurnId:a.activeTurnId,latestTurn:$o(sd(a))});continue}const d=Qo(a);if(!d){this.logAiSessionDelivery({stage:"skip-empty-turn-id",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,status:a.status,phase:a.phase,activeTurnId:a.activeTurnId,latestTurn:$o(sd(a))});continue}if(this.listTelegramOwnedProgressEntries(e).some(e=>e.route.bridgeId===i.bridgeId&&e.route.chatId===i.chatSessionId&&e.owner.sessionId===a.id&&e.owner.turnId===d))continue;const c=Jo(e,a.id,d,i.bridgeId||"",i.chatSessionId),l=Yo(a,r);if(!l||this.deliveredAiSessionFingerprints.get(c)===l){this.logAiSessionDelivery({stage:"skip-duplicate-fingerprint",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,hasFingerprint:Boolean(l),fingerprintMatched:this.deliveredAiSessionFingerprints.get(c)===l,latestTurn:$o(sd(a)),textPreview:Mo(o)});continue}const p=Wo({cancelCallbackData:this.aiSessionCancelCallbackData(e,a.id),queueSteerCallbackData:t=>this.aiSessionQueueSteerCallbackData(e,a.id,t),queueDeleteMenuCallbackData:()=>this.aiSessionQueueDeleteMenuCallbackData(e,a.id),permissionCallbackData:(e,t)=>this.service.pendingDecisionCallbackData(e,t),pendingApproval:n.get(a.id)||Vo(e,a),session:a});if(this.logAiSessionDelivery({stage:"attempt",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,relatedProgressKeys:this.telegramProgressKeysFor(e,a.id,i.bridgeId||"",i.chatSessionId),hasProgressEntry:this.telegramProgress.entries.has(c),status:a.status,phase:a.phase,activeTurnId:a.activeTurnId,latestTurn:$o(sd(a)),textPreview:Mo(o)}),await this.updateChatAiSessionProgress(i,o,c,p).catch(t=>(this.bridgeErrors.set(i.bridgeId||"",t instanceof Error?t.message:String(t)),this.logWarn({bridgeId:i.bridgeId,chatId:i.chatSessionId,instanceId:e,sessionId:a.id,turnId:d,key:c,error:jo(t)},"chat ai session progress update failed"),!1)))this.logAiSessionDelivery({stage:"delivered",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,textPreview:Mo(o)}),this.deliveredAiSessionFingerprints.set(c,l);else{if("telegram"===this.service.requireChatBridge(i.bridgeId).channel){const t=this.telegramProgress.entries.get(c),s=this.service.requireChatBridge(i.bridgeId);this.logAiSessionDelivery({stage:t?"telegram-update-not-delivered":"telegram-no-progress-entry",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,relatedProgressKeys:this.telegramProgressKeysFor(e,a.id,i.bridgeId||"",i.chatSessionId),bridgeEnabled:s.enabled,bridgeChannel:s.channel,progressMessageId:t?.messageId,progressLastText:Mo(t?.lastText),progressHasPending:Boolean(t?.pending),latestTurn:$o(sd(a)),textPreview:Mo(o)});continue}await this.sendToChatBinding(i,{text:o}).catch(e=>(this.bridgeErrors.set(i.bridgeId||"",e instanceof Error?e.message:String(e)),!1))&&(this.logAiSessionDelivery({stage:"fallback-sent",instanceId:e,sessionId:a.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,textPreview:Mo(o)}),this.deliveredAiSessionFingerprints.set(c,l))}}}async deliverOwnedTelegramProgress(e,t,s,n){for(const i of this.listTelegramOwnedProgressEntries(e)){const a=this.service.requireChatBridge(i.route.bridgeId);if("telegram"!==a.channel||!a.enabled)continue;const r=this.matchTelegramOwnedProgressSession(i,t);if(!r)continue;const o={bridgeId:i.route.bridgeId,chatSessionId:i.route.chatId},d=ed(s.get(e)||e,r),c=Zo(r,d),l=Qo(r);if(!c||!l){this.logAiSessionDelivery({stage:"remembered-skip-turn-or-text",key:i.key,parsedTurnId:i.owner.turnId,currentTurnId:l,hasText:Boolean(c),instanceId:e,sessionId:r.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId});continue}if(i.owner.turnId&&l!==i.owner.turnId){this.logAiSessionDelivery({stage:"remembered-skip-different-active-turn",key:i.key,ownerTurnId:i.owner.turnId,currentTurnId:l,instanceId:e,sessionId:r.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId});continue}const p=l===i.owner.turnId?i.key:Jo(e,r.id,l,i.route.bridgeId,i.route.chatId);if(p!==i.key&&this.telegramProgress.entries.has(p))continue;if(p!==i.key&&!this.rekeyTelegramOwnedProgress(i,p,{instanceId:e,sessionId:r.id,turnId:l}))continue;const u=Yo(r,d);if(!u||this.deliveredAiSessionFingerprints.get(p)===u){this.logAiSessionDelivery({stage:"remembered-skip-fingerprint",key:i.key,hasFingerprint:Boolean(u),fingerprintMatched:this.deliveredAiSessionFingerprints.get(p)===u,instanceId:e,sessionId:r.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId});continue}const m=Wo({cancelCallbackData:this.aiSessionCancelCallbackData(e,r.id),queueSteerCallbackData:t=>this.aiSessionQueueSteerCallbackData(e,r.id,t),queueDeleteMenuCallbackData:()=>this.aiSessionQueueDeleteMenuCallbackData(e,r.id),permissionCallbackData:(e,t)=>this.service.pendingDecisionCallbackData(e,t),pendingApproval:n.get(r.id)||Vo(e,r),session:r}),g=await this.updateChatAiSessionProgress(o,c,p,m).catch(e=>(this.bridgeErrors.set(o.bridgeId||"",e instanceof Error?e.message:String(e)),!1));this.logAiSessionDelivery({stage:g?"remembered-delivered":"remembered-not-delivered",key:p,instanceId:e,sessionId:r.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId,textPreview:Mo(c)}),g&&(this.telegramProgressMessageTargets.set(xo(i.route.bridgeId,i.route.chatId,i.messageId),{instanceId:e,sessionId:r.id,turnId:l,messageId:i.messageId}),this.deliveredAiSessionFingerprints.set(p,u))}}rekeyTelegramOwnedProgress(e,t,s){return!!this.telegramProgress.rekey(e.key,t)&&(this.deliveredAiSessionFingerprints.delete(e.key),this.telegramProgressMessageTargets.set(xo(e.route.bridgeId,e.route.chatId,e.messageId),{...s,messageId:e.messageId}),!0)}logAiSessionDelivery(e){this.logInfo(e,"telegram ai session delivery flow")}telegramProgressKeysFor(e,t,s,n){const i=`${e}:${t}:`,a=`:${s}:${n}`;return[...this.telegramProgress.entries.keys()].filter(e=>e.startsWith(i)&&e.endsWith(a))}listTelegramOwnedProgressEntries(e){const t=[];for(const[s,n]of this.telegramProgress.entries){if(!n.route?.bridgeId||!n.route.chatId||!Number.isInteger(n.messageId))continue;const i=this.telegramProgressMessageTargets.get(xo(n.route.bridgeId,n.route.chatId,n.messageId))||Oo(s,n.route.bridgeId,n.route.chatId);i&&i.instanceId===e&&t.push({key:s,route:{bridgeId:n.route.bridgeId,chatId:n.route.chatId},messageId:n.messageId,owner:{...i,messageId:n.messageId}})}return t}matchTelegramOwnedProgressSession(e,t){const s=t.find(t=>t.id===e.owner.sessionId);if(s)return s;if(e.owner.turnId){const s=t.find(t=>t.activeTurnId===e.owner.turnId||sd(t)?.id===e.owner.turnId||sd(t)?.providerTurnId===e.owner.turnId);if(s)return s}}async updateChatAiSessionProgress(e,t,s,n){if(!e.bridgeId)return this.logAiSessionDelivery({stage:"chat-update-skip-missing-bridge",chatId:e.chatSessionId,key:s}),!1;const i=this.service.requireChatBridge(e.bridgeId);if(!i.enabled)return this.logAiSessionDelivery({stage:"chat-update-skip-bridge-state",bridgeId:e.bridgeId,chatId:e.chatSessionId,key:s,bridgeEnabled:i.enabled,bridgeChannel:i.channel}),!1;if("dingding"===i.channel){if(!this.dingdingBridges.get(i.id))return!1;const a=await this.dingdingBridges.applyProgressUpdate({bridge:i,key:s,chatId:e.chatSessionId,text:t,replyMarkup:Eo({actionRows:n})});return a&&this.logInfo({bridgeId:e.bridgeId,chatId:e.chatSessionId,key:s,textPreview:Mo(t)},"dingding ai session progress updated"),a}if("telegram"!==i.channel)return!1;const a=this.telegramProgress.entries.get(s);if(!await this.telegramProgress.applyUpdate({bridge:i,key:s,chatId:e.chatSessionId,text:t,actionRows:n})){const t=this.telegramProgress.entries.get(s);return this.logAiSessionDelivery({stage:"telegram-update-returned-false",bridgeId:e.bridgeId,chatId:e.chatSessionId,key:s,progressMessageId:a?.messageId||t?.messageId,progressLastText:Mo(a?.lastText||t?.lastText),progressHasPending:Boolean(a?.pending||t?.pending)}),!1}return this.logInfo({bridgeId:e.bridgeId,chatId:e.chatSessionId,key:s,messageId:a?.messageId||this.telegramProgress.entries.get(s)?.messageId,textPreview:Mo(t)},a?"telegram ai session progress edited":"telegram ai session progress sent"),!0}async aiSessionInstanceNames(){return this.aiSessionInstanceNamesCache&&this.aiSessionInstanceNamesCache.expiresAt>Date.now()?this.aiSessionInstanceNamesCache.value:(this.aiSessionInstanceNamesPending||(this.aiSessionInstanceNamesPending=(this.service.listAiSessionInstanceNames?.()||this.service.boardAsync()).catch(()=>[]).then(e=>{const t=new Map(e.map(e=>[e.id,e.name||e.id]));return this.aiSessionInstanceNamesCache={expiresAt:Date.now()+3e4,value:t},t}).finally(()=>{this.aiSessionInstanceNamesPending=void 0})),this.aiSessionInstanceNamesPending)}async aiSessionPendingApprovalRoutes(e){let t=this.aiSessionPendingRoutesCache?.expiresAt&&this.aiSessionPendingRoutesCache.expiresAt>Date.now()?this.aiSessionPendingRoutesCache.value:void 0;return t||(this.aiSessionPendingRoutesPending||=this.service.listPendingRoutes().catch(()=>[]).then(e=>(this.aiSessionPendingRoutesCache={expiresAt:Date.now()+1e3,value:e},e)).finally(()=>{this.aiSessionPendingRoutesPending=void 0}),t=await this.aiSessionPendingRoutesPending),new Map(t.filter(t=>t.instanceId===e&&t.aiSessionId&&"approval"===t.kind).map(e=>[e.aiSessionId||"",e]))}aiSessionCancelCallbackData(e,t){const s=o.createHash("sha256").update(`${e}\0${t}`).digest("base64url").slice(0,16);return this.aiSessionCancelTokens.set(s,{instanceId:e,sessionId:t}),`task_handoff:cp_ai_cancel:${s}`}aiSessionQueueSteerCallbackData(e,t,s){return`task_handoff:cp_ai_steer:${this.aiSessionQueueActionToken({type:"steer",instanceId:e,sessionId:t,queueId:s})}`}aiSessionQueueDeleteMenuCallbackData(e,t){return`task_handoff:cp_ai_qdel_menu:${this.aiSessionQueueActionToken({type:"delete-menu",instanceId:e,sessionId:t})}`}aiSessionQueueDeleteItemCallbackData(e,t,s){return`task_handoff:cp_ai_qdel:${this.aiSessionQueueActionToken({type:"delete-item",instanceId:e,sessionId:t,queueId:s})}`}aiSessionQueueActionToken(e){const t=o.createHash("sha256").update([e.type,e.instanceId,e.sessionId,"queueId"in e?e.queueId:""].join("\0")).digest("base64url").slice(0,16);return this.aiSessionQueueActionTokens.set(t,e),t}}function zo(e){return"object"==typeof e&&e&&"reply"in e?String(e.reply||""):""}function To(e){return"object"==typeof e&&null!==e&&!0===e.accepted}function Co(e){const t="object"==typeof e&&e&&"replyMarkup"in e?e.replyMarkup:void 0;if(t&&"object"==typeof t&&!Array.isArray(t))return t}function Eo(e){const t=Array.isArray(e?.actionRows)?e.actionRows:void 0;return la(t||(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e]))}function No(e){const t=Number(e?.message_id);return Number.isInteger(t)?t:void 0}function _o(e,t,s){return[e,t,s||""].join(":")}function xo(e,t,s){return[e,t,String(s)].join(":")}function Oo(e,t,s){const n=`:${t}:${s}`;if(!e.endsWith(n))return;const i=e.slice(0,-n.length).split(":"),a=i[0]||"",r=i[1]||"",o=i.slice(2).join(":")||void 0;return a&&r?{instanceId:a,sessionId:r,turnId:o}:void 0}function Ro(e){return"string"==typeof e?e:""}function jo(e){return e instanceof Error?e.message:String(e)}function Do(e,t){return Boolean(e&&"object"==typeof e&&"code"in e&&e.code===t)}function Mo(e,t=160){const s=String(e||"").replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,Math.max(0,t-3))}...`}function Po(e){const t=Lo(e.body.cardActionData||e.body.content),s=Lo(t.cardPrivateData||e.body.cardPrivateData||e.body.content);return{outTrackId:Ro(e.body.outTrackId),spaceId:Ro(e.body.spaceId),conversationType:e.conversationType,hasCallbackData:Boolean(e.callbackData),callbackDataPreview:Mo(e.callbackData,120),actionIds:[...Uo(s.actionIdList),...Uo(t.actionIdList),Ro(s.actionId),Ro(t.actionId),Ro(e.body.actionId)].filter(Boolean).slice(0,5),cardActionDataKeys:Object.keys(t).sort(),privateDataKeys:Object.keys(s).sort(),paramKeys:Object.keys(e.params).sort(),hasSessionWebhook:Boolean(e.sessionWebhook)}}function $o(e){if(e)return{id:e.id,providerTurnId:e.providerTurnId,source:e.source,status:e.status,phase:e.phase,revision:e.revision,userPrompt:Mo(e.userPrompt,80),hasResponse:Boolean(e.lastMessage?.trim()||e.summary?.trim()),responsePreview:Mo(e.lastMessage||e.summary,80),updatedAt:e.updatedAt,observedAt:e.observedAt}}function qo(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function Lo(e){if(e&&"object"==typeof e&&!Array.isArray(e))return e;if("string"==typeof e&&e.trim())try{return qo(JSON.parse(e))}catch{return{}}return{}}function Uo(e){return Array.isArray(e)?e.map(e=>Ro(e)).filter(Boolean):[]}function Fo(e){const t=e.aiSession&&"session"in e.aiSession?e.aiSession:void 0,s=t?.session||(e.aiSession&&"id"in e.aiSession?e.aiSession:void 0);return{instanceId:e.instanceId||e.instance?.id||"",sessionId:e.aiSessionId||s?.id||e.binding?.activeAiSessionId||"",turnId:e.turnId||e.providerTurnId||t?.turnId||t?.providerTurnId||(s?Qo(s):""),sources:{resultTurnId:e.turnId,resultProviderTurnId:e.providerTurnId,actionTurnId:t?.turnId,actionProviderTurnId:t?.providerTurnId,sessionActiveTurnId:s?.activeTurnId,latestTurnId:s?sd(s)?.id:void 0}}}function Bo(e,t){const s=`${e.project?.name||e.projectId} / ${e.instance?.name||e.instanceId}`,n="approval"===e.kind?`/approve ${e.id}\n/deny ${e.id}\n/skip ${e.id}`:`/reply ${e.id} <message>`,i=`[${s}]\n${e.result}\n\n${n}`;return"approval"!==e.kind?{text:i}:{text:i,replyMarkup:la([[{text:"Allow",callbackData:t(e.id,"allow")},{text:"Skip",callbackData:t(e.id,"skip")},{text:"Deny",callbackData:t(e.id,"deny")}]])}}const Ho=5,Go=48;function Wo(e){const{cancelCallbackData:t,queueSteerCallbackData:s,queueDeleteMenuCallbackData:n,permissionCallbackData:i,pendingApproval:a,session:r}=e,o="approval"===a?.kind?[[{text:"Allow",callbackData:i(a.id,"allow")},{text:"Skip",callbackData:i(a.id,"skip")},{text:"Deny",callbackData:i(a.id,"deny")}]]:[];if("running"!==r.status)return o;const d=(r.queue?.items||[]).filter(e=>"queued"===e.status).slice(0,Ho);return[...d.map((e,t)=>[{text:Ko(e.message,t),callbackData:s(e.id)}]),d.length?[{text:"Delete Queue",callbackData:n()},{text:"Cancel",callbackData:t}]:[{text:"Cancel",callbackData:t}]]}function Vo(e,t){if("approval"===t.phase)return{id:`${e}:ai:${t.id}`,kind:"approval"}}function Ko(e,t){const s=`${t+1}. `,n=e.replace(/\s+/g," ").trim()||"Queued message",i=Math.max(8,Go-s.length),a=Array.from(n);return`${s}${a.length>i?`${a.slice(0,Math.max(1,i-3)).join("")}...`:n}`}function Jo(e,t,...s){return[e,t,...s].join(":")}function Qo(e){const t=sd(e);return t?.id?t.id:Ro(e.activeTurnId)}function Yo(e,t=""){const s=sd(e);return[t,s?.id||"",String(s?.revision??""),s?.status||e.status||"",s?.phase||e.phase||"",s?.lastMessage||"",s?.summary||"",s?.userPrompt||"",e.error||"",e.currentTool?.id||"",e.currentTool?.name||"",e.currentTool?.inputPreview||"",String(e.toolCallsSinceLastMessage??0),Xo(e)].join("\0")}function Xo(e){return(e.queue?.items||[]).map(e=>[e.id,e.status,e.message].join(":")).join("|")}function Zo(e,t){const s=function(e,t){let s="";t?"waiting"===t.status&&"approval"===t.phase&&t.summary?.trim()?s=t.summary.trim():t.lastMessage?.trim()?s=t.lastMessage.trim():t.summary?.trim()&&(s=t.summary.trim()):"waiting"===e.status&&"approval"===e.phase&&e.summary?.trim()?s=e.summary.trim():e.lastMessage?.trim()?s=e.lastMessage.trim():e.summary?.trim()?s=e.summary.trim():e.error?.trim()&&(s="failed"===e.status?`${e.agent} session failed:\n${e.error.trim()}`:e.error.trim());const n=function(e){if(e.currentTool?.name)return`Thinking... · ${e.currentTool.name}${e.currentTool.inputPreview?` · ${e.currentTool.inputPreview}`:""}`;const t=e.toolCallsSinceLastMessage??0;return t>0?`Thinking... · ${t} ${1===t?"tool":"tools"} completed`:""}(e);return[s,n&&!td(s,e)?n:""].filter(Boolean).join("\n\n")}(e,sd(e));return s?`${t}\n${s}`:""}function ed(e,t){const s=[t.status,t.phase&&"unknown"!==t.phase?t.phase:void 0].filter(Boolean).join("/");return`${e} · ${t.agent||"ai"} ${s||"unknown"}`}function td(e,t){if("waiting"!==t.status||"approval"!==t.phase)return!1;const s=t.currentTool?.inputPreview?.trim();return Boolean(s&&e.includes(s))}function sd(e){return(e.turns||[]).filter(e=>e.id||e.userPrompt?.trim()||e.lastMessage?.trim()||e.summary?.trim()).at(-1)}class nd{clients=new Set;listeners=new Set;seq=0;connect(e){e.topics=new Set(["*"]),this.clients.add(e),e.on("close",()=>{this.clients.delete(e)}),e.on("message",t=>{const s=function(e){try{const t=JSON.parse(String(e||"{}"));return t&&"object"==typeof t?t:void 0}catch{return}}(t);"subscribe"===s?.type&&(e.topics=new Set(s.topics?.length?s.topics:["*"]))})}publish(e,t,s={}){const n=this.createEvent(e,t,s),i=n.topic,a=JSON.stringify(n);for(const e of this.listeners)try{e(n)}catch{this.listeners.delete(e)}for(const t of this.clients)if(t.readyState===t.OPEN&&id(t.topics,i,e))try{t.send(a)}catch{this.clients.delete(t)}return n}on(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}send(e,t,s,n={}){const i=this.createEvent(t,s,n);return e.send(JSON.stringify(i)),i}createEvent(e,t,s={}){return{v:1,id:`evt_${Date.now().toString(36)}_${Math.random().toString(16).slice(2)}`,seq:++this.seq,type:e,topic:s.topic||an(e),createdAt:(new Date).toISOString(),payload:t,scope:s.scope}}}function id(e,t,s){return!e||e.has("*")||e.has(t)||e.has(s)}const ad={"image/bmp":".bmp","image/gif":".gif","image/jpeg":".jpg","image/png":".png","image/webp":".webp"},rd=r.z.object({instanceId:r.z.string().trim().min(1).max(120),sessionId:r.z.string().trim().min(1).max(120),kind:r.z.enum(["image","file"]),name:r.z.string().trim().min(1).max(240),mime:r.z.string().trim().min(1).max(120),data:r.z.string().min(1).max(31457280)}).strict();class od{items=new Map;ttlMs;constructor(e=864e5){this.ttlMs=e}upload(e){this.pruneExpired();const n=rd.parse(e),i=function(e,t){const s=/^data:([^;,]+);base64,(.+)$/i.exec(e.trim()),n=s?.[1]||t,i=s?.[2]||e;return{mime:n,buffer:Buffer.from(i,"base64")}}(n.data,n.mime),a=i.mime.toLowerCase(),r=ad[a]||"";if("image"===n.kind&&!r)throw new Error(`Unsupported image type: ${a}`);const d="image"===n.kind?i.buffer.length>w:i.buffer.length>=S;if(!i.buffer.length||d)throw new Error("image"===n.kind?"Image must be between 1 byte and 20971520 bytes.":"File must be smaller than 512000 bytes.");const c=`att_${o.randomUUID().replace(/-/g,"").slice(0,24)}`,l=new Date,p=new Date(l.getTime()+this.ttlMs),u=t.join(f.tmpdir(),"task-handoff-ai-session-attachments");s.mkdirSync(u,{recursive:!0});const m=function(e){return t.basename(e).replace(/[^\w.\- ()\u4e00-\u9fff]/g,"_").slice(0,160)||"attachment"}(n.name),g=t.join(u,`${c}-${m.endsWith(r)?m:`${m}${r}`}`);s.writeFileSync(g,i.buffer);const h={id:c,instanceId:n.instanceId,sessionId:n.sessionId,kind:n.kind,name:m,mime:a,size:i.buffer.length,source:{type:"inline",encoding:"base64",data:i.buffer.toString("base64")},path:g,createdAt:l.toISOString(),expiresAt:p.toISOString()};return this.items.set(c,h),this.publicAttachment(h)}resolveRefs(e,t,s){this.pruneExpired();const n=r.z.array(F).max(6).default([]).parse(e),i=n.map(e=>"runtime-path"===e.source.type?L.parse(e):this.requireAttachment(e,t,s));if(i.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>A)throw new Error("Inline attachments must be 41943040 bytes or less in total.");for(const e of n)"upload-ref"===e.source.type&&this.consumeAttachment(e.id);return i}requireAttachment(e,t,s){const n=this.items.get(e.id);if(!n)throw new Error(`AI session attachment not found or expired: ${e.id}`);if(e.kind&&e.kind!==n.kind)throw new Error(`AI session attachment kind mismatch: ${e.id}`);if(n.instanceId!==t||n.sessionId!==s)throw new Error(`AI session attachment scope mismatch: ${e.id}`);return L.parse({id:n.id,kind:n.kind,name:n.name,mime:n.mime,size:n.size,source:n.source})}consumeAttachment(e){const t=this.items.get(e);if(t){this.items.delete(e);try{s.unlinkSync(t.path)}catch{}}}publicAttachment(e){return{id:e.id,kind:e.kind,name:e.name,mime:e.mime,size:e.size,expiresAt:e.expiresAt}}pruneExpired(){const e=Date.now();for(const[t,n]of this.items)if(!(Date.parse(n.expiresAt)>e)){this.items.delete(t);try{s.unlinkSync(n.path)}catch{}}}}function dd(e){return e.readyState===(e.OPEN??1)}function cd(e,t,s){e.close(function(e){if("number"==typeof e&&Number.isInteger(e))return e>=3e3&&e<=4999||e>=1e3&&e<=1014&&1004!==e&&1005!==e&&1006!==e?e:void 0}(t),function(e){const t=Buffer.isBuffer(e)?e.toString("utf8"):"string"==typeof e?e:"";if(Buffer.byteLength(t,"utf8")<=123)return t;let s="";for(const e of t){const t=`${s}${e}`;if(Buffer.byteLength(t,"utf8")>123)break;s=t}return s}(s)||void 0)}function ld(e,t){e.send(t.data,{binary:t.isBinary})}function pd(e,t){if(dd(e))for(const s of t.splice(0))ld(e,s)}class ud{attempt=0;timer;get pending(){return Boolean(this.timer)}get attempts(){return this.attempt}schedule(e,t={}){if(this.timer)return;const s=function(e,t=Math.random){const s=Math.min(3e4,1e3*2**Math.max(0,e));return Math.min(3e4,Math.round(s*(.75+.5*t())))}(this.attempt,t.random);this.attempt+=1;const n=t.setTimeoutFn||setTimeout;return this.timer=n(()=>{this.timer=void 0,e()},s),{attempt:this.attempt,delay:s}}reset(e=clearTimeout){this.timer&&e(this.timer),this.timer=void 0,this.attempt=0}cancel(e=clearTimeout){this.timer&&e(this.timer),this.timer=void 0}}const md=r.z.object({nodeId:r.z.string().trim().min(1).max(120)}).strict();function gd(e,t){if(!t)throw Object.assign(new Error(`Node ${e.id} paired-HMAC auth is missing keyId.`),{statusCode:500,code:"NODE_AGENT_REMOTE_KEY_ID_MISSING"});return t}function hd(){return`req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`}const fd=new Map;function Id(e,t){if("paired-hmac"!==e.auth.mode||!e.auth.keyId||!e.auth.secret){const e=new Error("Reverse tunnel requires paired-HMAC node auth.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_TUNNEL_REQUIRES_PAIRED_HMAC"}),e}const s=function(e){const t=new Map;for(const[s,n]of Object.entries(e)){const e=Array.isArray(n)?n.join(","):"string"==typeof n?n:void 0===n?"":String(n);t.set(s.toLowerCase(),e)}return{nodeId:t.get("x-taskhandoff-node-id")||"",keyId:t.get("x-taskhandoff-key-id")||"",timestamp:t.get("x-taskhandoff-timestamp")||"",nonce:t.get("x-taskhandoff-nonce")||"",bodySha256:t.get("x-taskhandoff-body-sha256")||"",signature:t.get("x-taskhandoff-signature")||""}}(t.headers);if(s.nodeId!==e.id)throw Object.assign(new Error("Invalid reverse tunnel node id."),{statusCode:401,code:"NODE_AGENT_TUNNEL_NODE_MISMATCH"});if(s.keyId!==e.auth.keyId)throw Object.assign(new Error("Invalid reverse tunnel key id."),{statusCode:401,code:"NODE_AGENT_TUNNEL_KEY_INVALID"});const n=Date.parse(s.timestamp);if(!Number.isFinite(n)||Math.abs(Date.now()-n)>ir)throw Object.assign(new Error("Reverse tunnel timestamp is outside the allowed window."),{statusCode:401,code:"NODE_AGENT_TUNNEL_TIMESTAMP_INVALID"});const i=Date.now()-ir;for(const[e,t]of fd)t<=i&&fd.delete(e);const a=`${e.id}:${s.keyId}:${s.nonce}`;if(!s.nonce||fd.has(a))throw Object.assign(new Error("Reverse tunnel nonce is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_NONCE_INVALID"});const r=ar("");if(s.bodySha256!==r)throw Object.assign(new Error("Reverse tunnel body hash is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_BODY_HASH_INVALID"});const d=t.url||`/api/node-agent/tunnel?nodeId=${encodeURIComponent(e.id)}`,c=rr(e.auth.secret,{keyId:s.keyId,method:"GET",pathWithQuery:d,timestamp:s.timestamp,nonce:s.nonce,bodySha256:r});if(!function(e,t){const s=Buffer.from(e,"hex"),n=Buffer.from(t,"hex");return s.length===n.length&&o.timingSafeEqual(s,n)}(s.signature,c))throw Object.assign(new Error("Reverse tunnel signature is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_SIGNATURE_INVALID"});fd.set(a,Date.now()+ir)}function yd(e){return e?e instanceof Headers?Object.fromEntries(e.entries()):Array.isArray(e)?Object.fromEntries(e.map(([e,t])=>[e,t])):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,String(t)])):{}}class bd{sockets=new Map;events;onStreamsHello;validateInstanceScope;validatedInstanceScopes=new Map;validatingInstanceScopes=new Map;instanceEventQueues=new Map;constructor(e,t={}){this.events=e,this.onStreamsHello=t.onStreamsHello,this.validateInstanceScope=t.validateInstanceScope}attach(e,t){const s=this.sockets.get(e);if(s){for(const t of s.pending.values())clearTimeout(t.timer),t.reject(new Error(`Reverse tunnel for node ${e} was replaced.`));for(const[e,t]of s.streams)s.streams.delete(e),t.downstream.close(1011,"Reverse tunnel was replaced.");for(const t of s.httpStreams.values())clearTimeout(t.timer),t.body?.destroy(new Error(`Reverse tunnel for node ${e} was replaced.`)),t.settled||t.reject(new Error(`Reverse tunnel for node ${e} was replaced.`))}const n=new Map;this.sockets.set(e,{socket:t,pending:n,streams:new Map,httpStreams:new Map}),t.on?.("close",()=>this.detach(e,t)),t.on?.("error",()=>this.detach(e,t))}detach(e,t){const s=this.sockets.get(e);if(s&&(!t||s.socket===t)){this.sockets.delete(e);for(const t of s.pending.values())clearTimeout(t.timer),t.reject(new Error(`Reverse tunnel for node ${e} disconnected.`));for(const[e,t]of s.streams)s.streams.delete(e),t.downstream.close(1011,"Reverse tunnel disconnected.");for(const t of s.httpStreams.values())clearTimeout(t.timer),t.body?.destroy(new Error(`Reverse tunnel for node ${e} disconnected.`)),t.settled||t.reject(new Error(`Reverse tunnel for node ${e} disconnected.`))}}handleMessage(e,t){if(this.handleNodeAgentEvent(e,t))return!0;if(this.handleWebSocketMessage(e,t))return!0;if("node-agent.response"!==t.type)return!1;const s="string"==typeof t.requestId?t.requestId:"",n=this.sockets.get(e),i=n?.pending.get(s);if(!i||!n)return!0;n.pending.delete(s),clearTimeout(i.timer);const a=Number(t.status)||502,r=t.headers&&"object"==typeof t.headers?t.headers:{},o="string"==typeof t.body?t.body:"";if(t.error&&"object"==typeof t.error){const e=t.error,s=new Error("string"==typeof e.message?e.message:`Reverse tunnel request failed with HTTP ${a}`);return Object.assign(s,{statusCode:a,code:"string"==typeof e.code?e.code:"NODE_AGENT_REVERSE_REQUEST_FAILED"}),i.reject(s),!0}return i.resolve(new Response(o,{status:a,headers:r})),!0}handleNodeAgentEvent(e,t){const s="string"==typeof t.type?t.type:"";if("node-agent.streams.hello"===s){const e="string"==typeof t.instanceId?t.instanceId:"",s=dn.safeParse(t.payload);return e&&s.success&&this.onStreamsHello?.(e,s.data),!0}if(!s.startsWith("node-agent.event."))return!1;const n=t.event&&"object"==typeof t.event&&!Array.isArray(t.event)?t.event:void 0,i="string"==typeof n?.type?n.type:"";if(!i)return!0;const a="payload"in n?n.payload:{},r=n.scope&&"object"==typeof n.scope&&!Array.isArray(n.scope)?n.scope:{},o="string"==typeof t.instanceId?t.instanceId:void 0;if(i===Os){const t=xs.safeParse(a),s="string"==typeof r.instanceId?r.instanceId:"string"==typeof n.instanceId?n.instanceId:o;return!t.success||t.data.instanceId!==s||(this.enqueueValidatedInstanceEvent(e,t.data.instanceId,()=>{this.events?.publish(Os,t.data,{topic:"instances",scope:{...r,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(i===Vs){const t=Ks.safeParse(a),s="string"==typeof r.instanceId?r.instanceId:"string"==typeof n.instanceId?n.instanceId:o;return!t.success||t.data.instanceId!==s||(this.enqueueValidatedInstanceEvent(e,t.data.instanceId,()=>{this.events?.publish(Vs,t.data,{topic:"instances",scope:{...r,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(i===Ss||i===As){const t=i===Ss?zs.safeParse(a):Ts.safeParse(a),s="string"==typeof r.instanceId?r.instanceId:"string"==typeof n.instanceId?n.instanceId:o;return!t.success||t.data.instanceId!==s||(this.enqueueValidatedInstanceEvent(e,t.data.instanceId,()=>{this.events?.publish(i,t.data,{topic:"instances",scope:{...r,nodeId:e,instanceId:t.data.instanceId}})}),!0)}return this.events?.publish(i,a,{topic:"string"==typeof n.topic?n.topic:void 0,scope:{...r,nodeId:e,instanceId:"string"==typeof r.instanceId?r.instanceId:"string"==typeof n.instanceId?n.instanceId:o}}),!0}enqueueValidatedInstanceEvent(e,t,s){const n=`${e}:${t}`,i=(this.instanceEventQueues.get(n)||Promise.resolve()).then(async()=>{await this.isValidatedInstanceScope(e,t)&&s()});this.instanceEventQueues.set(n,i),i.finally(()=>{this.instanceEventQueues.get(n)===i&&this.instanceEventQueues.delete(n)})}async isValidatedInstanceScope(e,t){const s=`${e}:${t}`,n=Date.now();if((this.validatedInstanceScopes.get(s)||0)>n)return!0;const i=this.validatingInstanceScopes.get(s);if(i)return i;const a=(async()=>{let n=!1;try{n=Boolean(await(this.validateInstanceScope?.(e,t)))}catch{n=!1}return n&&this.validatedInstanceScopes.set(s,Date.now()+3e4),n})();return this.validatingInstanceScopes.set(s,a),a.finally(()=>{this.validatingInstanceScopes.get(s)===a&&this.validatingInstanceScopes.delete(s)})}async request(e,t,s={}){const n=this.sockets.get(e.id);if(!n){const t=new Error(`Reverse tunnel for node ${e.id} is not connected.`);throw Object.assign(t,{statusCode:503,code:"NODE_AGENT_REVERSE_TUNNEL_OFFLINE"}),t}const i=hd(),a="string"==typeof s.body?s.body:void 0===s.body||null===s.body?void 0:String(s.body),r={type:"control-plane.request",requestId:i,route:t,init:{method:s.method||"GET",headers:yd(s.headers),body:a}};return new Promise((s,a)=>{const o=setTimeout(()=>{n.pending.delete(i);const s=new Error(`Reverse tunnel request to ${e.id}${t} timed out.`);Object.assign(s,{statusCode:504,code:"NODE_AGENT_REVERSE_REQUEST_TIMEOUT"}),a(s)},3e4);n.pending.set(i,{resolve:s,reject:a,timer:o}),n.socket.send(JSON.stringify(r))})}async requestStream(e,t,s={}){const n=this.sockets.get(e.id);if(!n)throw Object.assign(new Error(`Reverse tunnel for node ${e.id} is not connected.`),{statusCode:503,code:"NODE_AGENT_REVERSE_TUNNEL_OFFLINE"});const i=hd(),a="string"==typeof s.body?s.body:void 0===s.body||null===s.body?void 0:String(s.body);return new Promise((r,o)=>{const d=setTimeout(()=>{n.httpStreams.delete(i),o(Object.assign(new Error(`Reverse tunnel stream to ${e.id}${t} timed out.`),{statusCode:504,code:"NODE_AGENT_REVERSE_STREAM_TIMEOUT"}))},3e4);n.httpStreams.set(i,{resolve:r,reject:o,timer:d,settled:!1}),n.socket.send(JSON.stringify({type:"control-plane.http.open",streamId:i,route:t,init:{method:s.method||"GET",headers:yd(s.headers),body:a}}))})}attachHttpStream(e,t,s){const n=this.sockets.get(e),i=n?.httpStreams.get(t);if(!n||!i)return s.close(1008,"Unknown reverse HTTP stream."),!1;i.socket=s;const a=e=>{clearTimeout(i.timer),n.httpStreams.delete(t),i.settled?i.body?.destroy(e):(i.settled=!0,i.reject(e))};return s.on("message",(e,r)=>{if(r)return i.body?void(i.body.write(Buffer.isBuffer(e)?e:Buffer.from(e))||(s.pause?.(),i.body.once("drain",()=>s.resume?.()))):a(new Error("Reverse HTTP stream received data before response headers."));let o;try{o=JSON.parse(String(e))}catch{return void a(new Error("Reverse HTTP stream sent invalid JSON control data."))}if("node-agent.http.head"===o.type){if(i.settled)return;clearTimeout(i.timer);const e=Number(o.status)||502,a=o.headers&&"object"==typeof o.headers?o.headers:{},r=new m.PassThrough({highWaterMark:65536});return i.body=r,i.settled=!0,r.once("close",()=>{n.httpStreams.delete(t)&&1===s.readyState&&s.close(1e3,"HTTP consumer closed.")}),void i.resolve(new Response(m.Readable.toWeb(r),{status:e,headers:a}))}if("node-agent.http.end"===o.type)return n.httpStreams.delete(t),i.body?.end(),void(i.settled||a(new Error("Reverse HTTP stream ended before response headers.")));"node-agent.http.error"===o.type&&a(new Error("string"==typeof o.message?o.message:"Reverse HTTP stream failed."))}),s.on("close",()=>{n.httpStreams.has(t)&&a(new Error("Reverse HTTP stream disconnected."))}),s.on("error",()=>a(new Error("Reverse HTTP stream failed."))),!0}proxyWebSocket(e,t,s,n,i={}){const a=e.id,r=this.sockets.get(a);if(!r)return void t.close(1011,"Reverse tunnel is not connected.");const o=hd(),d=e=>{try{r.socket.send(JSON.stringify(e))}catch{r.streams.delete(o),t.close(1011,"Reverse tunnel send failed.")}},c={downstream:t,pendingFrames:[]};r.streams.set(o,c),d({type:"control-plane.websocket.open",streamId:o,route:s,protocols:Array.isArray(n)?n:n?[n]:[],headers:i}),t.on("message",(e,s)=>{const n=r.streams.get(o);if(n&&!n.upstream)return n.pendingFrames.length>=256?(r.streams.delete(o),t.close(1011,"Reverse websocket stream pending frame limit exceeded."),void d({type:"control-plane.websocket.close",streamId:o,code:1011,reason:"Reverse websocket stream pending frame limit exceeded."})):void n.pendingFrames.push({data:e,isBinary:Boolean(s)})}),t.on("close",(e,t)=>{const s=r.streams.get(o);s&&(r.streams.delete(o),s.upstream?.close("number"==typeof e?e:1e3,Buffer.isBuffer(t)?t.toString("utf8"):"string"==typeof t?t:""),d({type:"control-plane.websocket.close",streamId:o,code:"number"==typeof e?e:1e3,reason:Buffer.isBuffer(t)?t.toString("utf8"):"string"==typeof t?t:""}))}),t.on("error",()=>{const e=r.streams.get(o);e&&(r.streams.delete(o),e.upstream?.close(1011,"Downstream websocket failed."),d({type:"control-plane.websocket.close",streamId:o,code:1011,reason:"Downstream websocket failed."}))})}attachWebSocketStream(e,t,s){const n=this.sockets.get(e),i=n?.streams.get(t);if(!n||!i)return s.close(1008,"Unknown reverse websocket stream."),!1;i.upstream=s;for(const e of i.pendingFrames.splice(0))s.send(e.data,{binary:e.isBinary});return function(e,t,s={}){const n=s.pendingFrameLimit??256;let i=!1,a=dd(t);const r=[],o=[];let d;const c=()=>{d&&(clearTimeout(d),d=void 0)},l=(s,i)=>{if(s.length>=n)return cd(e,1011,"WebSocket bridge pending frame limit exceeded."),void cd(t,1011,"WebSocket bridge pending frame limit exceeded.");s.push(i)};s.upstreamOpenTimeoutMs&&!a&&(d=setTimeout(()=>{a||i||(cd(e,1011,"Upstream websocket did not open."),cd(t))},s.upstreamOpenTimeoutMs)),e.on("open",()=>pd(e,o)),t.on("open",()=>{c(),a=!0,s.onUpstreamOpen?.(),pd(t,r)}),e.on("message",(e,s=!1)=>{const n={data:e,isBinary:Boolean(s)};dd(t)?ld(t,n):l(r,n)}),t.on("message",(t,s=!1)=>{const n={data:t,isBinary:Boolean(s)};dd(e)?ld(e,n):l(o,n)}),t.on("close",(t,n)=>{c(),s.onUpstreamClose?.(t,n),(a||i||!s.onUpstreamCloseBeforeOpen?.())&&cd(e,t,n)}),t.on("error",t=>{s.onUpstreamError?.(t),(a||i||!s.onUpstreamErrorBeforeOpen?.(t))&&cd(e)}),e.on("close",(e,n)=>{c(),i=!0,s.onClientClose?.(e,n),cd(t,e,n)}),e.on("error",e=>{c(),i=!0,s.onClientError?.(e),cd(t,1011,"WebSocket bridge client failed.")})}(i.downstream,s,{pendingFrameLimit:256,onClientClose:()=>{n.streams.delete(t)},onClientError:()=>{n.streams.delete(t)},onUpstreamClose:()=>{n.streams.delete(t)},onUpstreamError:()=>{n.streams.delete(t)}}),!0}handleWebSocketMessage(e,t){const s="string"==typeof t.type?t.type:"";if(!s.startsWith("node-agent.websocket."))return!1;const n=this.sockets.get(e),i="string"==typeof t.streamId?t.streamId:"",a=n?.streams.get(i);return!a||("node-agent.websocket.close"===s?(n?.streams.delete(i),a.downstream.close("number"==typeof t.code?t.code:1e3,"string"==typeof t.reason?t.reason:""),!0):"node-agent.websocket.error"!==s||(n?.streams.delete(i),a.downstream.close(1011,"string"==typeof t.message?t.message:"Reverse websocket proxy failed."),!0))}}class vd{sockets=new Map;socketUrls=new Map;retries=new Map;service;tunnel;safetyIntervalMs;logger;reconnectAttempts=0;safetyReconciliations=0;timer;constructor(e,t,s={}){var n;this.service=e,this.tunnel=t,this.safetyIntervalMs=(n=s.safetyIntervalMs,Math.min(6e4,Math.max(3e4,n??45e3))),this.logger=s.logger}start(){this.sync(),this.timer=setInterval(()=>{this.safetyReconciliations+=1,this.sync()},this.safetyIntervalMs)}stop(){this.timer&&(clearInterval(this.timer),this.timer=void 0);for(const e of this.sockets.values())e.close();this.sockets.clear(),this.socketUrls.clear();for(const e of this.retries.values())e.timer.cancel();this.retries.clear()}syncNow(){this.sync()}diagnostics(){return{reconnectAttempts:this.reconnectAttempts,safetyReconciliations:this.safetyReconciliations,activeConnections:this.sockets.size,pendingRetries:[...this.retries.values()].filter(e=>e.timer.pending).length,safetyIntervalMs:this.safetyIntervalMs}}sync(){const e=new Set;for(const t of this.service.listNodes()){const s=this.nodeEventsUrl(t);if(!s)continue;e.add(t.id);const n=this.retries.get(t.id);n?.timer.pending&&n.url!==s&&(n.timer.cancel(),this.retries.delete(t.id)),this.socketUrls.get(t.id)!==s&&this.sockets.has(t.id)&&(this.sockets.get(t.id)?.close(),this.sockets.delete(t.id)),this.sockets.has(t.id)||this.retries.get(t.id)?.timer.pending||this.connect(t,s)}for(const[t,s]of this.sockets)if(!e.has(t)){s.close(),this.sockets.delete(t),this.socketUrls.delete(t);const e=this.retries.get(t);e?.timer.cancel(),this.retries.delete(t)}for(const[t,s]of this.retries)e.has(t)||(s.timer.cancel(),this.retries.delete(t),this.socketUrls.delete(t))}connect(e,t){const s=dr(t),n=s?void 0:new URL(t),i=function(e,t){const s=function(e){return"paired-hmac"===e.auth.mode&&e.auth.secret?{keyId:e.auth.keyId,secret:e.auth.secret}:void 0}(e);if(s)return or({nodeId:e.id,keyId:gd(e,s.keyId),secret:s.secret,method:t.method,pathWithQuery:t.pathWithQuery});const n=function(e){if("local-ipc"===e.connectionMode||"local-loopback"===e.connectionMode)return"local-static-key"===e.auth.mode?e.auth.secret:void 0}(e);return n?{authorization:`Bearer ${n}`}:void 0}(e,{method:"GET",pathWithQuery:n?`${n.pathname}${n.search}`:"/api/node-agent/events"}),a=s?lr(s,"/events",void 0,i):new l.WebSocket(t,{headers:i});this.sockets.set(e.id,a),this.socketUrls.set(e.id,t),a.on("open",()=>{const s=this.retries.get(e.id);s?.timer.reset(),this.retries.set(e.id,{timer:s?.timer||new ud,url:t})}),a.on("message",t=>{let s;try{s=JSON.parse(String(t))}catch{return}if(s&&"object"==typeof s&&!Array.isArray(s)){const t=s,n="string"==typeof t.type&&t.type.startsWith("node-agent.")?t:{type:"node-agent.event.forwarded",event:t};this.tunnel.handleMessage(e.id,n)}}),a.on("close",()=>{this.sockets.get(e.id)===a&&(this.sockets.delete(e.id),this.socketUrls.delete(e.id),this.scheduleReconnect(e.id))}),a.on("error",()=>{a.close()})}scheduleReconnect(e){const t=this.service.listNodes().find(t=>t.id===e),s=t&&this.nodeEventsUrl(t);if(!t||!s)return;const n=this.retries.get(e)??{timer:new ud};if(n.timer.pending)return;n.url=s;const i=n.timer.schedule(()=>{this.sockets.has(e)||this.connect(t,s)});i&&(this.reconnectAttempts+=1,this.logger?.info?.({nodeId:e,url:s,attempt:i.attempt,delay:i.delay,reconnectAttempts:this.reconnectAttempts},"session-stream.node-connection.reconnect-scheduled")),this.retries.set(e,n)}nodeEventsUrl(e){const t=e.controlEndpoint||e.endpoint;if(t&&"reverse-wss"!==e.connectionMode){if(dr(t))return t;try{const e=new URL("/api/node-agent/events",t.replace(/\/$/,""));return e.protocol="https:"===e.protocol?"wss:":"ws:",e.toString()}catch{return}}}}function wd(e=function(){return process.env.TASK_HANDOFF_CONTROL_PLANE_DATA_DIR||t.join(f.homedir(),".config","task-handoff","control-plane")}()){const s=t.resolve(e);return{dataDir:s,settingsPath:t.join(s,"control-plane-settings.json"),projectsDir:t.join(s,"projects"),modelsDir:t.join(s,"models"),imagesDir:t.join(s,"images"),marketDir:t.join(s,"market"),marketCachePath:t.join(s,"market","catalog-cache.json"),marketStatePath:t.join(s,"market","catalog-state.json"),marketMigrationBackupPath:t.join(s,"market","legacy-image-migration-backup.json"),nodesDir:t.join(s,"nodes"),nodeRuntimesDir:t.join(s,"node-runtimes"),controlledInstancesDir:t.join(s,"controlled-instances"),chatSessionsDir:t.join(s,"chat-sessions"),chatBridgesDir:t.join(s,"chat-bridges"),triggersDir:t.join(s,"triggers"),nodeJoinInvitesDir:t.join(s,"node-join-invites"),authUsersDir:t.join(s,"auth-users"),authSessionsDir:t.join(s,"auth-sessions"),aiSessionUnreadDir:t.join(s,"ai-session-unread"),logsDir:t.join(s,"logs")}}class Sd extends Error{code;lockPath;owner;constructor(e,t,s={}){const n=t?.pid?` pid ${t.pid}`:"";super(`${s.label||"Process"} is already running${n}. Lock: ${e}`),this.name="ProcessSingletonError",this.code=s.code||"PROCESS_ALREADY_RUNNING",this.lockPath=e,this.owner=t}}function Ad(e){return t.join(e,"owner.json")}function kd(e){return t.join(e,"initializing.json")}function zd(e){return t.join(e,"recovering.json")}function Td(e){return Number.isInteger(e)&&Number(e)>0}function Cd(e,t=process.platform){if(Td(e)){if("linux"===t)try{const t=s.readFileSync(`/proc/${e}/stat`,"utf8"),n=t.lastIndexOf(")");if(n<0)return;const i=t.slice(n+2).trim().split(/\s+/)[19];return i?`linux:${i}`:void 0}catch{return}if(["darwin","freebsd","openbsd","aix","sunos"].includes(t)){const s=y.spawnSync("ps",["-o","lstart=","-p",String(e)],{encoding:"utf8",timeout:1e3}),n=0===s.status?s.stdout.trim().replace(/\s+/g," "):"";return n?`${t}:${n}`:void 0}if("win32"===t){const t=`(Get-Process -Id ${e} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks`,s=y.spawnSync("powershell.exe",["-NoProfile","-NonInteractive","-Command",t],{encoding:"utf8",timeout:2e3}),n=0===s.status?s.stdout.trim():"";return/^\d+$/.test(n)?`win32:${n}`:void 0}}}const Ed=function(e){try{const t=JSON.parse(s.readFileSync(Ad(e),"utf8"));if(Td(t.pid)&&"string"==typeof t.startTime){const e=t;return{pid:e.pid,hostname:"string"==typeof e.hostname&&e.hostname?e.hostname:f.hostname(),component:e.component||"server-update",command:"string"==typeof e.command?e.command:"",acquiredAt:"string"==typeof e.createdAt&&Number.isFinite(Date.parse(e.createdAt))?e.createdAt:new Date(0).toISOString(),token:"string"==typeof e.token&&e.token?e.token:`legacy:${e.pid}:${e.startTime}`,startIdentity:e.startTime.startsWith("linux:")?e.startTime:`linux:${e.startTime}`}}if(!(Td(t.pid)&&"string"==typeof t.hostname&&t.hostname&&"string"==typeof t.command&&"string"==typeof t.acquiredAt&&Number.isFinite(Date.parse(t.acquiredAt))&&"string"==typeof t.token&&t.token))return;return t}catch{return}};function Nd(e){if(!Td(e))return!1;try{return process.kill(Number(e),0),!0}catch(e){return"EPERM"===e.code}}function _d(e){if(!e||!Nd(e.pid))return!1;if(!e.startIdentity)return!0;const t=Cd(e.pid);return void 0===t||t===e.startIdentity}function xd(e,n){const i=t.join(e,`.owner-${n.token}.tmp`);s.writeFileSync(i,`${JSON.stringify(n,null,2)}\n`,{encoding:"utf8",mode:384,flag:"wx"});const a=Od(e);if(a?.token!==n.token)throw jd(i),new Error(`Process lock initialization was superseded: ${e}`);s.renameSync(i,Ad(e)),jd(kd(e))}function Od(e){try{return JSON.parse(s.readFileSync(kd(e),"utf8"))}catch{return}}function Rd(e){const t=Od(e);return Td(t?.pid)&&_d({pid:t.pid,startIdentity:t.startIdentity})}function jd(e){try{s.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}function Dd(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,20)}function Md(e){try{const t=Od(e),n=Date.parse(t?.startedAt||"");return Number.isFinite(n)?Math.max(0,Date.now()-n):Math.max(0,Date.now()-s.statSync(e).mtimeMs)}catch{return 0}}function Pd(e,n){try{return s.writeFileSync(zd(e),`${JSON.stringify({token:n,pid:process.pid,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),!0}catch(i){if("EEXIST"===i.code)return!!function(e){const n=zd(e),i=$d(n),a=Date.parse(i?.startedAt||"");if(Nd(i?.pid)||Number.isFinite(a)&&Date.now()-a<2e3)return!1;const r=t.join(e,`.recovering-${o.randomUUID()}.stale`);try{s.renameSync(n,r)}catch(e){if(["ENOENT","EEXIST"].includes(e.code||""))return!1;throw e}const d=$d(r);if(d?.token===i?.token)return jd(r),!0;try{s.linkSync(r,n)}catch(e){if("EEXIST"!==e.code)throw e}finally{jd(r)}return!1}(e)&&Pd(e,n);if("ENOENT"===i.code)return!1;throw i}}function $d(e){try{return JSON.parse(s.readFileSync(e,"utf8"))}catch{return}}function qd(e,t){return $d(zd(e))?.token===t}function Ld(e,t,s){if(function(e,t,s){return"system"===e.type||("chat-bridge"===e.type?function(e,t){return"ai-session"===t.type?["read","send-message","approve","interrupt"].includes(e):"instance"===t.type?["read","start","stop","restart"].includes(e):"read"===e}(t,s):"admin"===e.role||("viewer"===e.role?"read"===t:"operator"===e.role&&function(e,t){return"read"===e||("instance"===t.type?["create","update","delete","start","stop","restart","proxy"].includes(e):"ai-session"===t.type?["send-message","approve","interrupt"].includes(e):("trigger"===t.type||"runtime"===t.type)&&["create","update","delete"].includes(e))}(t,s)))}(e,t,s))return;const n=new Error(`Actor is not allowed to ${t} ${s.type}${s.id?` ${s.id}`:""}.`);throw Object.assign(n,{statusCode:403,code:"CONTROL_PLANE_FORBIDDEN"}),n}const Ud=r.z.string().trim().min(1),Fd=r.z.object({id:Ud}).strict(),Bd=r.z.object({id:Ud,keyId:Ud}).strict(),Hd=r.z.object({id:Ud,runtimeId:Ud}).strict(),Gd=r.z.object({id:Ud,folderId:Ud}).strict(),Wd=r.z.object({id:Ud,sessionId:Ud}).strict(),Vd=r.z.object({id:Ud,sessionId:Ud,queueId:Ud}).strict(),Kd=r.z.object({configHash:Ud}).strict(),Jd=r.z.object({id:Ud,configHash:Ud}).strict(),Qd=r.z.object({id:Ud,sessionId:Ud,configHash:Ud}).strict(),Yd=r.z.object({path:r.z.string().optional(),depth:r.z.coerce.number().int().min(0).max(32).optional()}).strict(),Xd=r.z.object({ids:r.z.array(r.z.string().trim().min(1).max(120)).default([])}).strict(),Zd=r.z.object({nodeId:r.z.string().trim().min(1).max(120),modelId:r.z.string().trim().min(1).max(120).optional()}).strict(),ec=r.z.object({source:r.z.object({channel:Xs,bridgeId:r.z.string().trim().min(1).max(120).optional(),chatSessionId:r.z.string().trim().min(1).max(240),userId:r.z.string().trim().max(240).optional()}).strict(),action:r.z.discriminatedUnion("type",[r.z.object({type:r.z.literal("ai-session"),index:r.z.number().int().nonnegative()}).strict(),r.z.object({type:r.z.literal("instance-app-menu"),instanceId:r.z.string().trim().min(1).max(120)}).strict(),r.z.object({type:r.z.literal("launch-app"),instanceId:r.z.string().trim().min(1).max(120),appId:r.z.string().trim().min(1).max(120)}).strict(),r.z.object({type:r.z.literal("pending-decision"),routeId:r.z.string().trim().min(1).max(120),decision:r.z.enum(["allow","deny","skip"])}).strict()])}).strict();function tc(e){const t=ec.parse(e);if(!t.action)throw new Error("Chat gateway action is required.");return{source:t.source,action:t.action}}const sc=r.z.object({path:r.z.string().trim().max(1e3).optional(),depth:r.z.coerce.number().int().min(0).max(2).optional()}).strict(),nc=r.z.object({id:r.z.string().trim().min(1),appId:r.z.string().trim().min(1)}).strict(),ic=r.z.object({id:r.z.string().trim().min(1),jobId:r.z.string().trim().min(1)}).strict();const ac=r.z.object({appId:r.z.string().trim().min(1).max(120).default("terminal-tty"),cwdFolderId:r.z.string().trim().min(1).max(120).optional(),options:r.z.record(r.z.string(),r.z.unknown()).default({})}).strict().default({appId:"terminal-tty",options:{}}),rc=r.z.object({title:r.z.string().trim().min(1).max(120)}).strict(),oc=r.z.object({}).strict();function dc(e){const{app:t,service:s,events:n,appSessionAggregator:i,aiSessionAggregator:a,aiSessionUnread:o,chatGateway:d,aiSessionAttachments:c,nodeAgentTunnel:l,nodeEventSubscriber:p,errorPayload:u}=e;!function({app:e,service:t,events:s}){e.get("/api/projects",async()=>({data:t.listProjects()})),e.post("/api/projects",async(e,n)=>{const i=t.createProject(e.body);return s.publish("project.created",{projectId:i.id}),n.code(201).send({data:i})}),e.get("/api/projects/:id",async e=>({data:t.requireProject(Fd.parse(e.params).id)})),e.patch("/api/projects/:id",async e=>{const n=t.updateProject(Fd.parse(e.params).id,e.body);return s.publish("project.updated",{projectId:n.id}),{data:n}}),e.delete("/api/projects/:id",async e=>{const n=Fd.parse(e.params).id,i=t.deleteProject(n);return s.publish("project.deleted",{projectId:n,deleted:i}),{data:{deleted:i}}}),e.get("/api/models",async()=>({data:await t.listFederatedModels()})),e.post("/api/models",async(e,n)=>{const i=await t.createModel(e.body);return s.publish("model.created",{modelId:i.id}),n.code(201).send({data:i})}),e.post("/api/models/reorder",async e=>{const n=Xd.parse(e.body),i=await t.reorderModels(n.ids);return s.publish("model.reordered",{ids:n.ids}),{data:i}}),e.get("/api/models/:id",async e=>({data:t.requireModel(Fd.parse(e.params).id)})),e.patch("/api/models/:id",async e=>{const n=await t.updateModel(Fd.parse(e.params).id,e.body);return s.publish("model.updated",{modelId:n.id}),{data:n}}),e.delete("/api/models/:id",async e=>{const n=Fd.parse(e.params).id,i=await t.deleteModel(n);return s.publish("model.deleted",{modelId:n,deleted:i}),{data:{deleted:i}}}),e.post("/api/nodes/:nodeId/models",async(e,n)=>{const{nodeId:i}=Zd.parse(e.params),a=await t.createNodeModel(i,e.body);return s.publish("model.created",{modelId:a.id,nodeId:i}),n.code(201).send({data:a})}),e.patch("/api/nodes/:nodeId/models/:modelId",async e=>{const{nodeId:n,modelId:i}=Zd.parse(e.params),a=await t.updateNodeModel(n,i,e.body);return s.publish("model.updated",{modelId:i,nodeId:n}),{data:a}}),e.delete("/api/nodes/:nodeId/models/:modelId",async e=>{const{nodeId:n,modelId:i}=Zd.parse(e.params),a=await t.deleteNodeModel(n,i);return s.publish("model.deleted",{modelId:i,nodeId:n,deleted:a.deleted}),{data:a}}),e.get("/api/market/catalog",async()=>({data:t.getMarketCatalog()})),e.post("/api/market/refresh",async()=>{const e=await t.refreshMarketCatalog();return s.publish("market.catalog.updated",{revision:e.catalog.revision,source:e.catalog.source}),{data:e}}),e.get("/api/image-options",async()=>({data:t.listImageOptions()})),e.get("/api/images",async()=>({data:t.listImages()})),e.post("/api/images",async(e,n)=>{const i=t.createImage(e.body);return s.publish("image.created",{imageId:i.id}),n.code(201).send({data:i})}),e.get("/api/images/:id",async e=>({data:t.requireImage(Fd.parse(e.params).id)})),e.patch("/api/images/:id",async e=>{const n=t.updateImage(Fd.parse(e.params).id,e.body);return s.publish("image.updated",{imageId:n.id}),{data:n}}),e.delete("/api/images/:id",async e=>{const n=Fd.parse(e.params).id,i=await t.deleteImage(n);return s.publish("image.deleted",{imageId:n,deleted:i}),{data:{deleted:i}}})}({app:t,service:s,events:n}),function({app:e,service:t,events:s,nodeAgentTunnel:n,nodeEventSubscriber:i,errorPayload:a}){const r=e=>{if(!e||"object"!=typeof e||Array.isArray(e)||!("target"in e))return;const t=e.target;if(!t||"object"!=typeof t||Array.isArray(t)||"controlled-instance"!==t.component)return;const s=new Error("Independent controlled-instance updates were retired; update the Node rollout instead.");throw Object.assign(s,{statusCode:404,code:"LEGACY_INSTANCE_UPDATE_RETIRED"}),s};e.get("/api/nodes",async()=>({data:t.listPublicNodes()})),e.post("/api/nodes/local/sync",async()=>{const e=await t.syncLocalNodeConnection();return i.syncNow(),s.publish("node.local-synced",{nodeId:e.id}),{data:t.requirePublicNode(e.id)}}),e.post("/api/nodes",async(e,n)=>{const a=await t.createNode(e.body);return i.syncNow(),s.publish("node.created",{nodeId:a.id}),n.code(201).send({data:t.requirePublicNode(a.id)})}),e.post("/api/nodes/:id/check",async e=>{const n=await t.checkNode(Fd.parse(e.params).id);return s.publish("node.checked",{nodeId:n.id}),{data:n}}),e.get("/api/nodes/:id/updates/jobs",async e=>({data:await t.listNodeUpdateJobs(Fd.parse(e.params).id)})),e.get("/api/nodes/:id/settings/external-listener",async e=>({data:await t.getLocalNodeExternalListener(Fd.parse(e.params).id)})),e.patch("/api/nodes/:id/settings/external-listener",async e=>{const n=Fd.parse(e.params).id,i=await t.updateLocalNodeExternalListener(n,e.body);return s.publish("node.external-listener.updated",{nodeId:n,bindScope:i.bindScope,port:i.port}),{data:i}}),e.post("/api/nodes/:id/updates/check",async e=>{r(e.body);const n=Fd.parse(e.params).id,i=await t.checkNodeUpdate(n,$t.parse(e.body));return s.publish("node.update.checked",{nodeId:n,availableVersion:i.availableVersion,impact:i.impact}),{data:i}}),e.post("/api/nodes/:id/updates/apply",async(e,n)=>{r(e.body);const i=Fd.parse(e.params).id,a=await t.applyNodeUpdate(i,Ut.parse(e.body));return s.publish("node.update.queued",{nodeId:i,updateJobId:a.id,desiredVersion:a.toVersion,impact:a.impact}),n.code(202).send({data:a})}),e.post("/api/nodes/:id/pairing/invites",async(e,n)=>{const i=Fd.parse(e.params),a=await t.createNodePairingInvite(i.id,e.body);return s.publish("node.pairing-invite.created",{nodeId:i.id}),n.code(201).send({data:a})}),e.post("/api/nodes/:id/remotes/connect",async e=>{const n=Fd.parse(e.params),i=await t.connectNodeToControlPlane(n.id,e.body);return s.publish("node.remote-connect.requested",{nodeId:n.id}),{data:i}}),e.get("/api/nodes/:id/remotes",async e=>{const s=Fd.parse(e.params);return{data:await t.listNodeRemoteControlPlanes(s.id)}}),e.delete("/api/nodes/:id/remotes/:keyId",async e=>{const n=Bd.parse(e.params),i=await t.deleteNodeRemoteControlPlane(n.id,n.keyId);return s.publish("node.remote-key.deleted",{nodeId:n.id,keyId:n.keyId}),{data:i}}),e.post("/api/node-join/invites",async(e,n)=>{const i=t.createNodeJoinInvite(e.body);return s.publish("node-join.invite.created",{inviteId:i.id}),n.code(201).send({data:i})}),e.post("/api/node-join/complete",async(e,n)=>{const a=t.completeNodeJoin(e.body);return i.syncNow(),s.publish("node.joined",{nodeId:a.id}),n.code(201).send({data:t.requirePublicNode(a.id)})}),e.get("/api/nodes/:id/runtimes",async e=>({data:await t.listNodeRuntimes(Fd.parse(e.params).id)})),e.post("/api/nodes/:id/runtimes",async(e,n)=>{const i=Fd.parse(e.params),a=await t.createNodeRuntime(i.id,e.body);return s.publish("node.runtime.created",{nodeId:i.id,runtimeId:a.id}),n.code(201).send({data:a})}),e.patch("/api/nodes/:id/runtimes/:runtimeId",async e=>{const n=Hd.parse(e.params),i=await t.updateNodeRuntime(n.id,n.runtimeId,e.body);return s.publish("node.runtime.updated",{nodeId:n.id,runtimeId:i.id}),{data:i}}),e.delete("/api/nodes/:id/runtimes/:runtimeId",async e=>{const n=Hd.parse(e.params),i=await t.deleteNodeRuntime(n.id,n.runtimeId);return s.publish("node.runtime.deleted",{nodeId:n.id,runtimeId:n.runtimeId}),{data:i}}),e.post("/api/nodes/:id/runtimes/:runtimeId/check",async e=>{const n=Hd.parse(e.params),i=await t.checkNodeRuntime(n.id,n.runtimeId);return s.publish("node.runtime.checked",{nodeId:n.id,runtimeId:i.id}),{data:i}}),e.get("/api/nodes/:id/docker/images",async e=>({data:await t.listNodeDockerImages(Fd.parse(e.params).id)})),e.get("/api/nodes/:id/image-options",async e=>({data:await t.listNodeImageAvailability(Fd.parse(e.params).id)})),e.get("/api/nodes/:id/local-folders",async e=>({data:await t.listNodeLocalFolders(Fd.parse(e.params).id)})),e.get("/api/nodes/:id/folders/tree",async e=>{const s=Fd.parse(e.params),n=Yd.parse(e.query);return{data:await t.listNodeFolderTree(s.id,{path:n.path,depth:n.depth})}}),e.post("/api/nodes/:id/local-folders",async(e,n)=>{const i=Fd.parse(e.params),a=await t.createNodeLocalFolder(i.id,e.body);return s.publish("node.local-folder.created",{nodeId:i.id,folderId:a.id}),n.code(201).send({data:a})}),e.delete("/api/nodes/:id/local-folders/:folderId",async e=>{const n=Gd.parse(e.params),i=await t.deleteNodeLocalFolder(n.id,n.folderId);return s.publish("node.local-folder.deleted",{nodeId:n.id,folderId:n.folderId}),{data:i}}),e.get("/api/nodes/:id",async e=>({data:t.requirePublicNode(Fd.parse(e.params).id)})),e.patch("/api/nodes/:id",async e=>{const n=t.updateNode(Fd.parse(e.params).id,e.body);return i.syncNow(),s.publish("node.updated",{nodeId:n.id}),{data:t.requirePublicNode(n.id)}}),e.delete("/api/nodes/:id",async e=>{const n=Fd.parse(e.params).id,a=t.deleteNode(n);return i.syncNow(),s.publish("node.deleted",{nodeId:n,deleted:a}),{data:{deleted:a}}}),e.get("/api/node-runtimes",async()=>{const e=await t.listNodeRuntimesWithDiagnostics();return{data:e.items,meta:{nodeErrors:e.nodeErrors}}}),function(e){const{app:t,service:s,nodeAgentTunnel:n,errorPayload:i}=e;t.get("/api/node-agent/tunnel",{websocket:!0},(e,t)=>{try{const i=md.parse(t.query),a=s.requireNode(i.nodeId);Id(a,t),n.attach(a.id,e),e.send(JSON.stringify({type:"control-plane.hello",protocolVersion:He,nodeId:a.id,serverTime:(new Date).toISOString(),capabilities:{reverseTunnel:"request-response",httpResponseStreaming:!0,lifecycleCommands:!0,instanceApiProxy:!0}})),e.on("message",t=>{let s;try{s=JSON.parse(String(t))}catch{return void e.send(JSON.stringify({type:"control-plane.error",code:"INVALID_JSON"}))}const i=s&&"object"==typeof s?s:{};n.handleMessage(a.id,i)||("node-agent.ping"!==i.type?"node-agent.identify"!==i.type?e.send(JSON.stringify({type:"control-plane.error",code:"UNSUPPORTED_MESSAGE"})):e.send(JSON.stringify({type:"control-plane.identified",nodeId:a.id,serverTime:(new Date).toISOString()})):e.send(JSON.stringify({type:"control-plane.pong",nodeId:a.id,serverTime:(new Date).toISOString()})))})}catch(t){const s=i(t);e.send(JSON.stringify({type:"control-plane.error",code:s.code,message:s.message})),e.close(1008,s.message)}}),t.get("/api/node-agent/tunnel/streams/:streamId",{websocket:!0},(e,t)=>{try{const i=md.parse(t.query),a=s.requireNode(i.nodeId);Id(a,t);const r=t.params.streamId;n.attachWebSocketStream(a.id,r,e)}catch(t){const s=i(t);e.close(1008,s.message)}}),t.get("/api/node-agent/tunnel/http-streams/:streamId",{websocket:!0},(e,t)=>{try{const i=md.parse(t.query),a=s.requireNode(i.nodeId);Id(a,t);const r=t.params.streamId;n.attachHttpStream(a.id,r,e)}catch(t){const s=i(t);e.close(1008,s.message)}})}({app:e,service:t,nodeAgentTunnel:n,errorPayload:a})}({app:t,service:s,events:n,nodeAgentTunnel:l,nodeEventSubscriber:p,errorPayload:u}),function({app:e,service:t,events:s}){e.get("/api/controlled-instances",async()=>({data:await t.listControlledInstances()})),e.post("/api/controlled-instances",async(e,n)=>{const i=await t.createControlledInstance(e.body);return s.publish("instance.created",{instanceId:i.id}),n.code(201).send({data:i})}),e.get("/api/controlled-instances/:id",async e=>({data:await t.requireControlledInstance(Fd.parse(e.params).id)})),e.get("/api/controlled-instances/:id/metrics",async e=>({data:await t.instanceResourceMetrics(Fd.parse(e.params).id)})),e.get("/api/controlled-instances/:id/apps/management",async e=>({data:await t.instanceAppManagement(Fd.parse(e.params).id)})),e.post("/api/controlled-instances/:id/apps/:appId/install",async e=>{const s=nc.parse(e.params),n=Nt.parse(e.body||{});return{data:await t.requestInstanceAppOperation(s.id,s.appId,"install",n)}}),e.post("/api/controlled-instances/:id/apps/:appId/uninstall",async e=>{const s=nc.parse(e.params),n=Nt.parse(e.body||{});return{data:await t.requestInstanceAppOperation(s.id,s.appId,"uninstall",n)}}),e.get("/api/controlled-instances/:id/apps/jobs/:jobId",async e=>{const s=ic.parse(e.params);return{data:await t.instanceAppManagementJob(s.id,s.jobId)}}),e.patch("/api/controlled-instances/:id",async e=>{const n=await t.updateControlledInstance(Fd.parse(e.params).id,e.body);return s.publish("instance.updated",{instanceId:n.id}),{data:n}}),e.delete("/api/controlled-instances/:id",async e=>{const n=Fd.parse(e.params).id,i=await t.deleteControlledInstance(n);return s.publish("instance.deleted",{instanceId:n,deleted:i}),{data:{deleted:i}}}),e.post("/api/controlled-instances/:id/start",async e=>{const n=await t.startControlledInstance(Fd.parse(e.params).id);return s.publish("instance.started",{instanceId:n.id}),{data:n}}),e.post("/api/controlled-instances/:id/stop",async e=>{const n=await t.stopControlledInstance(Fd.parse(e.params).id);return s.publish("instance.stopped",{instanceId:n.id}),{data:n}}),e.post("/api/controlled-instances/:id/restart",async e=>{const n=await t.restartControlledInstance(Fd.parse(e.params).id);return s.publish("instance.restarted",{instanceId:n.id}),{data:n}}),e.post("/api/controlled-instances/:id/image-provisioning/retry",async e=>{const n=await t.retryControlledInstanceImageProvisioning(Fd.parse(e.params).id);return s.publish("instance.image-provisioning-retried",{instanceId:n.id}),{data:n}}),e.get("/api/controlled-instances/:id/config-sync",async e=>({data:await t.instanceConfigSyncState(Fd.parse(e.params).id)})),e.get("/api/controlled-instances/:id/config-sync/folders",async e=>({data:await t.listInstanceConfigSyncFolders(Fd.parse(e.params).id,sc.parse(e.query))})),e.post("/api/controlled-instances/:id/config-sync",async e=>{const n=Fd.parse(e.params).id,i=await t.syncInstanceConfigs(n,e.body);return s.publish("instance.config-synced",{instanceId:n}),{data:i}}),e.get("/api/instance-board",async()=>{const e=await t.boardWithDiagnostics();return{data:e.items,meta:{nodeErrors:e.nodeErrors}}})}({app:t,service:s,events:n}),function({app:e,service:t,events:s,appSessionAggregator:n,aiSessionAggregator:i,aiSessionUnread:a,aiSessionAttachments:o}){e.get("/api/app-sessions",async e=>{const t=r.z.object({refresh:r.z.string().optional(),includeTombstones:r.z.string().optional(),instanceId:r.z.string().trim().min(1).optional(),streamId:r.z.string().trim().min(1).optional(),sinceRevision:r.z.string().optional()}).parse(e.query||{});if(void 0!==t.sinceRevision){const e=Number(t.sinceRevision);if(!Number.isInteger(e)||e<0){const e=new Error("sinceRevision must be a non-negative integer.");throw Object.assign(e,{statusCode:400,code:"APP_SESSION_DELTA_INVALID"}),e}if(!t.streamId){const e=new Error("streamId is required with sinceRevision.");throw Object.assign(e,{statusCode:400,code:"APP_SESSION_DELTA_INVALID"}),e}return{data:await n.delta({instanceId:t.instanceId,streamId:t.streamId,sinceRevision:e})}}return{data:await n.list({refresh:"true"===t.refresh||"1"===t.refresh,includeTombstones:"true"===t.includeTombstones||"1"===t.includeTombstones})}}),e.post("/api/controlled-instances/:id/apps/sessions",async e=>{const n=Fd.parse(e.params),i=ac.parse(e.body),a=await t.launchAppSession(n.id,i.appId,{...i.options,...i.cwdFolderId?{cwdFolderId:i.cwdFolderId}:{}});return s.publish("instance.app-session.launched",{instanceId:n.id,sessionId:"string"==typeof a.id?a.id:void 0,appId:i.appId}),{data:a}}),e.post("/api/controlled-instances/:id/apps/sessions/:sessionId/stop",async e=>{const n=Wd.parse(e.params),i=await t.stopAppSession(n.id,n.sessionId);return s.publish("instance.app-session.stopped",{instanceId:n.id,sessionId:n.sessionId}),{data:i}}),e.patch("/api/controlled-instances/:id/apps/sessions/:sessionId",async e=>{const n=Wd.parse(e.params),i=rc.parse(e.body),a=await t.renameAppSession(n.id,n.sessionId,i.title);return s.publish("instance.app-session.renamed",{instanceId:n.id,sessionId:n.sessionId,title:i.title}),{data:a}}),e.get("/api/controlled-instances/:id/app-sessions",async e=>{const t=Fd.parse(e.params),s=(await n.list()).instances.find(e=>e.instanceId===t.id);return{data:s?.appSessions||{runningCount:0,problemCount:0,sessions:[],updatedAt:(new Date).toISOString()}}}),e.get("/api/controlled-instances/:id/ai-sessions/history",async e=>{const s=Fd.parse(e.params);return{data:await t.listAiSessionHistory(s.id)}}),e.get("/api/controlled-instances/:id/ai-sessions/history/:sessionId",async e=>{const s=Wd.parse(e.params);return{data:await t.getAiSessionHistoryDetail(s.id,s.sessionId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/resume",async e=>{const n=Wd.parse(e.params);oc.parse(e.body||{});const i=await t.resumeAiSession(n.id,n.sessionId);return s.publish("instance.ai-session.resumed",{instanceId:n.id,sessionId:i.aiSessionId,providerSessionId:i.providerSessionId,appSessionId:i.appSessionId,disposition:i.disposition}),{data:i}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/messages",async e=>{const n=Wd.parse(e.params),i=ie.parse(e.body||{}),a=o.resolveRefs(i.attachments,n.id,n.sessionId),r=await t.sendAiSessionMessage(n.id,n.sessionId,i.message,i.mode,a,i.references,i.permissionMode);return s.publish("instance.ai-session.message-sent",{instanceId:n.id,sessionId:n.sessionId}),{data:r}}),e.get("/api/controlled-instances/:id/ai-sessions/:sessionId/mentions",async e=>{const s=Wd.parse(e.params);return{data:await t.aiSessionMentionCatalog(s.id,s.sessionId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/mentions/files",async e=>{const s=Wd.parse(e.params),n=te.parse(e.body||{});return{data:await t.searchAiSessionMentionFiles(s.id,s.sessionId,n.query)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/commands",async e=>{const n=Wd.parse(e.params),i=W.parse(e.body||{}),a=await t.executeAiSessionCommand(n.id,n.sessionId,i);return s.publish("instance.ai-session.command-executed",{instanceId:n.id,sessionId:n.sessionId,command:i.command}),{data:a}}),e.get("/api/controlled-instances/:id/ai-sessions/:sessionId/queue",async e=>{const s=Wd.parse(e.params);return{data:await t.aiSessionQueue(s.id,s.sessionId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/queue/:queueId/steer",async e=>{const n=Vd.parse(e.params),i=await t.steerAiSessionQueuedMessage(n.id,n.sessionId,n.queueId);return s.publish("instance.ai-session.message-sent",{instanceId:n.id,sessionId:n.sessionId}),{data:i}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/queue/:queueId/retry",async e=>{const s=Vd.parse(e.params);return{data:await t.retryAiSessionQueuedMessage(s.id,s.sessionId,s.queueId)}}),e.delete("/api/controlled-instances/:id/ai-sessions/:sessionId/queue/:queueId",async e=>{const s=Vd.parse(e.params);return{data:await t.removeAiSessionQueuedMessage(s.id,s.sessionId,s.queueId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/interrupt",async e=>{const n=Wd.parse(e.params),i=await t.interruptAiSession(n.id,n.sessionId);return s.publish("instance.ai-session.interrupted",{instanceId:n.id,sessionId:n.sessionId}),{data:i}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/approval",async e=>{const n=Wd.parse(e.params),i=ae.parse(e.body||{}),a=await t.resolveAiSessionApproval(n.id,n.sessionId,i.decision);return s.publish("instance.ai-session.approval-resolved",{instanceId:n.id,sessionId:n.sessionId,decision:i.decision}),{data:a}}),e.get("/api/ai-sessions",async(e,t)=>{const s=e.query;if(void 0!==s.sinceRevision){const e=Number(s.sinceRevision);if(!Number.isInteger(e)||e<0)return t.code(400).send({error:"invalid_since_revision"});try{return s.streamId?{data:await i.delta({instanceId:s.instanceId,streamId:s.streamId,sinceRevision:e})}:t.code(400).send({error:"stream_id_required"})}catch(e){if("AI_SESSION_DELTA_INSTANCE_ID_REQUIRED"===e.message)return t.code(400).send({error:"instance_id_required"});throw e}}const n=await i.list({refresh:"true"===s.refresh||"1"===s.refresh});for(const e of n.instances)a.reconcile(e.instanceId,e.aiSessions);return{data:{...n,instances:n.instances.map(e=>({...e,aiSessions:a.decorate(e.instanceId,e.aiSessions)}))}}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/read",async(e,t)=>{const n=Wd.parse(e.params),o=r.z.object({sessionUpdatedAt:r.z.string().datetime()}).strict().parse(e.body||{}),d=(await i.list()).instances.find(e=>e.instanceId===n.id),c=d?.aiSessions.sessions.find(e=>e.id===n.sessionId);if(!d||!c)return t.code(404).send({error:{code:"AI_SESSION_NOT_FOUND",message:"AI session was not found."}});a.reconcile(n.id,d.aiSessions);const l=a.markRead(n.id,n.sessionId,o.sessionUpdatedAt);return l?(s.publish(C,l,{topic:"ai.sessions",scope:{instanceId:n.id,sessionId:n.sessionId}}),{data:l}):t.code(404).send({error:{code:"AI_SESSION_NOT_FOUND",message:"AI session was not found."}})})}({app:t,service:s,events:n,appSessionAggregator:i,aiSessionAggregator:a,aiSessionUnread:o,aiSessionAttachments:c}),function({app:e,service:t,events:s}){e.get("/api/triggers",async()=>({data:await t.listTriggers()})),e.post("/api/triggers",async(e,n)=>{const i=t.createTrigger(e.body);return s.publish("trigger.created",{configHash:i.configHash}),n.code(201).send({data:i})}),e.delete("/api/triggers/:configHash",async e=>{const n=Kd.parse(e.params),i=await t.deleteTrigger(n.configHash);return s.publish("trigger.deleted",{configHash:n.configHash}),{data:i}}),e.post("/api/triggers/:configHash/apply",async e=>{const n=Kd.parse(e.params),i=await t.applyTrigger(n.configHash,e.body||{});return s.publish("trigger.applied",{configHash:n.configHash}),{data:i}}),e.get("/api/controlled-instances/:id/triggers",async e=>{const s=Fd.parse(e.params);return{data:await t.listInstanceTriggers(s.id)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/triggers",async e=>{const n=Wd.parse(e.params),i=await t.bindAiSessionTrigger(n.id,n.sessionId,e.body||{});return s.publish("instance.ai-session.trigger-bound",{instanceId:n.id,sessionId:n.sessionId}),{data:i}}),e.delete("/api/controlled-instances/:id/ai-sessions/:sessionId/triggers/:configHash",async e=>{const n=Qd.parse(e.params),i=await t.unbindAiSessionTrigger(n.id,n.sessionId,n.configHash);return s.publish("instance.ai-session.trigger-unbound",{instanceId:n.id,sessionId:n.sessionId,configHash:n.configHash}),{data:i}}),e.post("/api/controlled-instances/:id/triggers/:configHash/run",async e=>{const n=Jd.parse(e.params),i=await t.runInstanceTrigger(n.id,n.configHash,e.body||{});return s.publish("instance.trigger.run",{instanceId:n.id,configHash:n.configHash}),{data:i}})}({app:t,service:s,events:n}),function({app:e,service:t,chatGateway:s}){e.get("/api/chat/sessions",async()=>({data:t.listChatSessions()})),e.get("/api/chat/sessions/:id",async e=>({data:t.requireChatSession(Fd.parse(e.params).id)})),e.get("/api/chat-gateway/status",async()=>({data:s.status()})),e.post("/api/chat-gateway/poll-ai-sessions",async()=>({data:await s.pollAiSessionsNow()})),e.get("/api/chat-gateway/bridges",async()=>({data:t.listChatBridges()})),e.post("/api/chat-gateway/bridges",async e=>({data:t.createChatBridge(e.body)})),e.patch("/api/chat-gateway/bridges/:id",async n=>{const i=Fd.parse(n.params).id,a=t.requireChatBridge(i),r=ra.parse(n.body),o=t.updateChatBridge(i,n.body),d="boolean"==typeof r.enabled?r.enabled:void 0;return e.log.info({component:"control-plane-chat-gateway",action:"bridge-patch",bridgeId:i,hasEnabled:void 0!==d,requestedEnabled:d,previousEnabled:a.enabled,updatedEnabled:t.requireChatBridge(i).enabled},"chat gateway bridge state changed"),void 0!==d&&d!==a.enabled&&(d?s.startBridge(i):s.stopBridge(i)),{data:o}}),e.post("/api/chat-gateway/bridges/:id/start",async n=>{const i=Fd.parse(n.params).id,a=t.requireChatBridge(i);return t.updateChatBridge(i,{enabled:!0}),e.log.info({component:"control-plane-chat-gateway",action:"bridge-start-request",bridgeId:i,previousEnabled:a.enabled,updatedEnabled:t.requireChatBridge(i).enabled},"chat gateway bridge state changed"),{data:s.startBridge(i)}}),e.post("/api/chat-gateway/bridges/:id/stop",async n=>{const i=Fd.parse(n.params).id,a=t.requireChatBridge(i);return t.updateChatBridge(i,{enabled:!1}),e.log.info({component:"control-plane-chat-gateway",action:"bridge-stop-request",bridgeId:i,previousEnabled:a.enabled,updatedEnabled:t.requireChatBridge(i).enabled},"chat gateway bridge state changed"),{data:s.stopBridge(i)}}),e.delete("/api/chat-gateway/bridges/:id",async e=>({data:{deleted:t.deleteChatBridge(Fd.parse(e.params).id)}})),e.post("/api/chat-gateway/messages",{bodyLimit:67108864},async e=>({data:await t.handleChatGatewayMessage(tn.parse(e.body))})),e.post("/api/chat-gateway/actions",async e=>({data:await t.handleChatGatewayAction(tc(e.body))})),e.get("/api/pending-routes",async()=>({data:await t.listPendingRoutes()}))}({app:t,service:s,chatGateway:d})}const cc=new Set(["connection","content-length","keep-alive","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding","upgrade"]),lc=new Set(["content-encoding"]);function pc(e){return Object.fromEntries(Object.entries(e).filter(([e,t])=>!cc.has(e.toLowerCase())&&void 0!==t).map(([e,t])=>[e,Array.isArray(t)?t.join(", "):String(t)]))}function uc(e){return Object.fromEntries(Object.entries(pc(e)).filter(([e])=>{const t=e.toLowerCase();return"host"!==t&&"cookie"!==t&&"authorization"!==t&&(!t.startsWith("sec-websocket-")||"sec-websocket-origin"===t)}))}function mc(e){const t=e["sec-websocket-protocol"],s=(Array.isArray(t)?t.join(","):"string"==typeof t?t:"").split(",").map(e=>e.trim()).filter(Boolean);return s.length?s:void 0}function gc(e,t,s){for(const[s,n]of Object.entries(t.headers)){const t=s.toLowerCase();cc.has(t)||lc.has(t)||e.header(s,n)}if(!t.body)return e.code(t.status).send();const n=m.Readable.fromWeb(t.body);return e.raw.once("close",()=>n.destroy()),e.code(t.status).send(s?n.pipe(s):n)}function hc(e){return`/instances/${encodeURIComponent(e)}`}function fc(e,t,s,n){const i=function(e,t){const s=e.split("?",1)[0]||"/";return t.toLowerCase().includes("text/html")&&!s.startsWith("/api/")}(n,Object.entries(t.headers).find(([e])=>"content-type"===e.toLowerCase())?.[1]||"")?function(e){const t=[];let s=0,n=!1;const i=(i,a=!1)=>{if(n)return;const r=Buffer.concat(t,s).toString("utf8");!/<head(\s[^>]*)?>/i.exec(r)&&!a&&s<65536||(n=!0,i.push(function(e,t){if(e.includes("__TASK_HANDOFF_PUBLIC_BASE__"))return e;const s=`<script>window.__TASK_HANDOFF_PUBLIC_BASE__=${JSON.stringify(t)};<\/script>`;return/<head(\s[^>]*)?>/i.test(e)?e.replace(/<head(\s[^>]*)?>/i,e=>`${e}\n${s}`):`${s}\n${e}`}(r,e)),t.length=0,s=0)};return new m.Transform({transform(e,a,r){n?this.push(e):(t.push(Buffer.from(e)),s+=e.length,i(this)),r()},flush(e){i(this,!0),e()}})}(hc(s)):void 0;return gc(e,t,i)}function Ic(e){return Buffer.isBuffer(e)?e:e instanceof Uint8Array?Buffer.from(e):e instanceof URLSearchParams?e.toString():"string"==typeof e?e:null==e?void 0:JSON.stringify(e)}function yc(e,t){if("string"!=typeof t||!e.hasContentTypeParser(t))try{e.addContentTypeParser(t,{parseAs:"buffer"},(e,t,s)=>s(null,t))}catch(e){if(e instanceof Error&&/Content type parser .* already present/i.test(e.message))return;throw e}}function bc(e){return new URL(e,"http://control-plane.local").searchParams.get("token")||""}function vc(e,t,s){const n=encodeURIComponent(e),i=encodeURIComponent(t),a=s.vnc&&"object"==typeof s.vnc?s.vnc:{};return"kasmvnc"===("string"==typeof a.backend?a.backend:"")?`/apps/access/vnc/proxy/api/apps/sessions/${i}/web/?token=${n}`:`/apps/access/vnc/proxy/api/novnc/vnc.html?token=${n}&path=${encodeURIComponent(`apps/access/vnc/proxy/api/apps/sessions/${i}/vnc?token=${n}`)}&autoconnect=1&resize=scale`}class wc{snapshots=new Map;history=new Map;advertisedStreams=new Map;recoveries=new Map;listeners=new Set;bootstrap;logger;recoverDelta;recoverSnapshot;onRecoveredEvent;recoveryCount=0;counters={streamResets:0,gaps:0,deltaRecoveries:0,snapshotRecoveries:0,recoveryFailures:0};constructor(e){this.bootstrap=e.bootstrap,this.logger=e.logger,this.recoverDelta=e.recoverDelta,this.recoverSnapshot=e.recoverSnapshot,this.onRecoveredEvent=e.onRecoveredEvent}handleEvent(e){if("ai-session.message-delta"===e.type){const t=Ee.safeParse(e.payload);return t.success||this.logger?.warn?.({eventType:e.type,issues:t.error.issues,errorCode:"AI_SESSION_MESSAGE_DELTA_INVALID"},"ai-session.aggregator.message-delta.invalid"),!0}const t=e.type===k?ze:e.type===z?Te:e.type===T?Ce:void 0;if(!t)return!1;const s=t.safeParse(e.payload);return s.success?(e.type===k&&this.applySnapshot(s.data),e.type===z&&this.applyPatch(s.data),e.type===T&&this.applyRemoved(s.data),!0):(this.logger?.warn?.({eventType:e.type,issues:s.error.issues,errorCode:"AI_SESSION_EVENT_INVALID"},"ai-session.aggregator.event.invalid"),!0)}onSnapshot(e){return this.listeners.add(e),()=>this.listeners.delete(e)}applySnapshot(e){const t=this.advertisedStreams.get(e.meta.instanceId)?.streamId;return t&&t!==e.meta.streamId?(this.logger?.warn?.({instanceId:e.meta.instanceId,advertisedStreamId:t,streamId:e.meta.streamId,revision:e.meta.revision},"ai-session.aggregator.snapshot.obsolete-stream"),!1):this.apply({type:k,payload:e})}advertiseStream(e,t){return this.advertisedStreams.set(e,t),this.recoverStream(e,t.streamId,t.latestRevision).catch(s=>{this.counters.recoveryFailures+=1,this.logger?.warn?.({instanceId:e,streamId:t.streamId,error:s instanceof Error?s.message:String(s)},"ai-session.aggregator.recovery.failed")})}diagnostics(){return{...this.counters,recoveriesStarted:this.recoveryCount,activeRecoveries:this.recoveries.size}}removeInstance(e){this.snapshots.delete(e),this.history.delete(e),this.advertisedStreams.delete(e)}applyPatch(e){return this.apply({type:z,payload:e})}applyRemoved(e){return this.apply({type:T,payload:e})}async list(e={}){return(e.refresh||0===this.snapshots.size)&&await this.bootstrapFromInstances(),this.view()}async streamDescriptors(){return 0===this.snapshots.size&&await this.bootstrapFromInstances(),[...this.snapshots.entries()].map(([e,t])=>({topic:"ai.sessions",instanceId:e,streamId:t.streamId,latestRevision:t.revision,earliestRetainedRevision:this.history.get(e)?.[0]?.payload.meta.revision??t.revision+1}))}async delta(e){0===this.snapshots.size&&await this.bootstrapFromInstances();const t=this.resolveDeltaInstanceId(e.instanceId);this.pruneHistory(t);const s=this.snapshots.get(t)?.revision??0,n=this.snapshots.get(t)?.streamId||"unavailable",i=this.history.get(t)?.[0]?.payload.meta.revision??s+1;if(e.streamId!==n||e.sinceRevision>s)return Ne.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:!0,events:[]});if(e.sinceRevision===s)return Ne.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:!1,events:[]});const a=(this.history.get(t)??[]).filter(t=>t.payload.meta.revision>e.sinceRevision),r=!a.length||a[0].payload.meta.revision!==e.sinceRevision+1;return Ne.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:r,events:r?[]:a.map(({type:e,payload:t})=>({type:e,payload:t}))})}apply(e,t=!1){const{meta:s}=e.payload,n=this.advertisedStreams.get(s.instanceId)?.streamId;if(n&&n!==s.streamId)return this.logger?.warn?.({instanceId:s.instanceId,advertisedStreamId:n,streamId:s.streamId,revision:s.revision},"ai-session.aggregator.event.obsolete-stream"),!1;const i=this.recoveries.get(s.instanceId);if(i&&!t)return i.streamId===s.streamId&&(i.highWater=Math.max(i.highWater,s.revision)),!1;const a=this.snapshots.get(s.instanceId);e.type===k&&a&&a.streamId!==s.streamId&&(this.counters.streamResets+=1,this.logger?.info?.({instanceId:s.instanceId,previousStreamId:a.streamId,streamId:s.streamId,previousRevision:a.revision,revision:s.revision,traceId:s.traceId,resetCount:this.counters.streamResets},"ai-session.aggregator.stream-reset"));const r=function(e,t){const{meta:s}=t.payload;if(t.type===k)return e?.streamId===s.streamId&&s.revision===e.revision?{kind:"duplicate",projection:e}:e?.streamId===s.streamId&&s.revision<e.revision?{kind:"stale",projection:e}:{kind:"applied",projection:ve.parse({streamId:s.streamId,revision:s.revision,lastEventAt:s.generatedAt,snapshot:t.payload.snapshot})};if(!e||e.streamId!==s.streamId)return{kind:"snapshot-required",projection:e,streamId:s.streamId};if(s.revision===e.revision)return{kind:"duplicate",projection:e};if(s.revision<e.revision)return{kind:"stale",projection:e};if(s.revision!==e.revision+1||s.previousRevision!==e.revision)return{kind:"gap",projection:e,expectedRevision:e.revision+1,receivedRevision:s.revision};const n=new Map(e.snapshot.sessions.map(e=>[e.id,e]));if(t.type===z){for(const e of t.payload.upserted)n.set(e.id,e);for(const e of t.payload.removed)n.delete(e)}else for(const e of t.payload.sessionIds)n.delete(e);const i=[...n.values()],a=be.parse({runningCount:i.filter(e=>"running"===e.status).length,waitingCount:i.filter(e=>"waiting"===e.status).length,staleCount:e.snapshot.staleCount,sessions:i,updatedAt:s.generatedAt});return{kind:"applied",projection:ve.parse({streamId:s.streamId,revision:s.revision,lastEventAt:s.generatedAt,snapshot:a})}}(a,e);if("applied"!==r.kind)return this.logger?.warn?.({traceId:s.traceId,streamId:s.streamId,instanceId:s.instanceId,currentRevision:a?.revision,revision:s.revision,outcome:r.kind},`ai-session.aggregator.event.${r.kind}`),"gap"!==r.kind&&"snapshot-required"!==r.kind||("gap"===r.kind&&(this.counters.gaps+=1),this.recoverStream(s.instanceId,s.streamId,s.revision).catch(e=>{this.logger?.warn?.({instanceId:s.instanceId,streamId:s.streamId,revision:s.revision,error:e instanceof Error?e.message:String(e)},"ai-session.aggregator.recovery.failed")})),!1;const o=r.projection;this.snapshots.set(s.instanceId,o),this.rememberEvent(s.instanceId,e);const d={instanceId:s.instanceId,streamId:o.streamId,aiSessions:o.snapshot,revision:o.revision,lastEventAt:o.lastEventAt};for(const e of this.listeners)try{e(d)}catch{this.listeners.delete(e)}return this.logger?.info?.({traceId:s.traceId,streamId:s.streamId,instanceId:s.instanceId,revision:s.revision,reason:s.reason,sessionCount:o.snapshot.sessions.length},"ai-session.aggregator.event.accepted"),!0}async bootstrapFromInstances(){this.logger?.info?.({},"ai-session.aggregator.bootstrap");const e=new Map(this.snapshots),t=await this.bootstrap(),s=new Set(t.instances.map(e=>e.instanceId));for(const[t,n]of e)s.has(t)||this.snapshots.get(t)!==n||(this.snapshots.delete(t),this.history.delete(t),this.advertisedStreams.delete(t));for(const s of t.instances){const t=this.snapshots.get(s.instanceId);if(t!==e.get(s.instanceId))continue;const n=this.advertisedStreams.get(s.instanceId)?.streamId;n&&n!==s.streamId||t?.streamId===s.streamId&&t.revision>=s.revision||(t?.streamId!==s.streamId&&this.history.delete(s.instanceId),this.snapshots.set(s.instanceId,{streamId:s.streamId,snapshot:s.aiSessions,revision:s.revision,lastEventAt:s.lastEventAt}))}}recoverStream(e,t,s){const n=this.recoveries.get(e);if(n)return n.streamId===t?n.highWater=Math.max(n.highWater,s):(n.streamId=t,n.highWater=s),n.promise;if(!this.recoverDelta||!this.recoverSnapshot)return Promise.resolve();const i={promise:Promise.resolve(),streamId:t,highWater:s},a=++this.recoveryCount;return this.logger?.info?.({instanceId:e,highWater:s,recoveryNumber:a},"ai-session.aggregator.recovery.started"),i.promise=(async()=>{for(;;){const t=this.advertisedStreams.get(e);if(!t)return;i.streamId!==t.streamId&&(i.streamId=t.streamId,i.highWater=t.latestRevision);const s=this.snapshots.get(e);if(s&&s.streamId===t.streamId){if(s.revision<Math.max(t.latestRevision,i.highWater)){const t=await this.recoverDelta(e,s.streamId,s.revision);if(this.advertisedStreams.get(e)?.streamId!==t.streamId||i.streamId!==t.streamId)continue;if(t.syncRequired){const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const s={type:k,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`ais_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(s,!0)&&this.onRecoveredEvent?.(s),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:a,outcome:"snapshot-fallback"},"ai-session.aggregator.recovery.snapshot")}else{for(const e of t.events)this.apply(e,!0)&&this.onRecoveredEvent?.(e);this.counters.deltaRecoveries+=1,i.highWater=Math.max(i.highWater,t.latestRevision),this.logger?.info?.({instanceId:e,streamId:t.streamId,sinceRevision:t.sinceRevision,latestRevision:t.latestRevision,eventCount:t.events.length,recoveryNumber:a,outcome:"delta"},"ai-session.aggregator.recovery.delta")}}}else{const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const s={type:k,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`ais_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(s,!0)&&this.onRecoveredEvent?.(s),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:a,outcome:"snapshot"},"ai-session.aggregator.recovery.snapshot")}const n=this.snapshots.get(e),r=this.advertisedStreams.get(e);if(n&&r&&n.streamId===r.streamId&&n.revision>=Math.max(r.latestRevision,i.highWater))return void this.logger?.info?.({instanceId:e,streamId:n.streamId,revision:n.revision,recoveryNumber:a,outcome:"success"},"ai-session.aggregator.recovery.completed")}})().finally(()=>this.recoveries.delete(e)),this.recoveries.set(e,i),i.promise}rememberEvent(e,t){const s=this.history.get(e)??[],n=s.length&&s[0].payload.meta.streamId!==t.payload.meta.streamId?[]:s;n.push({...t,createdAtMs:Date.now()}),this.history.set(e,n),this.pruneHistory(e)}pruneHistory(e){const t=this.history.get(e);if(!t?.length)return;const s=t.filter(e=>e.createdAtMs>=Date.now()-36e5);s.length?this.history.set(e,s):this.history.delete(e)}resolveDeltaInstanceId(e){if(e)return e;const t=[...this.snapshots.keys()];if(1===t.length)return t[0];throw new Error("AI_SESSION_DELTA_INSTANCE_ID_REQUIRED")}view(){return{updatedAt:(new Date).toISOString(),instances:[...this.snapshots.entries()].map(([e,t])=>({instanceId:e,streamId:t.streamId,aiSessions:t.snapshot,revision:t.revision,lastEventAt:t.lastEventAt}))}}}const Sc=r.z.object({id:r.z.string().trim().min(1),instanceId:r.z.string().trim().min(1).max(160),sessionId:r.z.string().trim().min(1).max(120),unread:r.z.boolean(),lastStatus:r.z.enum(["running","waiting","idle","failed"]),sessionUpdatedAt:r.z.string().datetime(),createdAt:r.z.string().datetime(),updatedAt:r.z.string().datetime()}).strict();class Ac{records;bySession=new Map;listener;constructor(e,t={}){this.records=new fn(e.aiSessionUnreadDir,{schema:Sc}),this.listener=t.onChanged}init(){this.records.init(),this.bySession.clear();for(const e of this.records.list())this.bySession.set(zc(e.instanceId,e.sessionId),e)}reconcile(e,t){const s=new Set;for(const n of t.sessions)s.add(n.id),this.reconcileSession(e,n);for(const t of[...this.bySession.values()])t.instanceId!==e||s.has(t.sessionId)||this.deleteRecord(t)}decorate(e,t){return{...t,sessions:t.sessions.map(t=>({...t,unread:this.bySession.get(zc(e,t.id))?.unread||!1}))}}markRead(e,t,s){const n=this.bySession.get(zc(e,t));return n&&n.sessionUpdatedAt===s&&n.unread?kc(this.store({...n,unread:!1,updatedAt:(new Date).toISOString()},!1)):n?kc(n):void 0}removeInstance(e){for(const t of[...this.bySession.values()])t.instanceId===e&&this.deleteRecord(t)}reconcileSession(e,t){const s=zc(e,t.id),n=this.bySession.get(s),i=(new Date).toISOString();if(!n)return void this.store({id:Tc(e,t.id),instanceId:e,sessionId:t.id,unread:!1,lastStatus:t.status,sessionUpdatedAt:t.updatedAt,createdAt:i,updatedAt:i},!1);const a=function(e,t,s){return"running"!==s&&"waiting"!==s&&("running"===t||"waiting"===t||e)}(n.unread,n.lastStatus,t.status),r=a!==n.unread;(r||n.lastStatus!==t.status||n.sessionUpdatedAt!==t.updatedAt)&&this.store({...n,unread:a,lastStatus:t.status,sessionUpdatedAt:t.updatedAt,updatedAt:i},r)}store(e,t){const s=this.records.put(e);return this.bySession.set(zc(s.instanceId,s.sessionId),s),t&&this.listener?.(kc(s)),s}deleteRecord(e){this.records.delete(e.id),this.bySession.delete(zc(e.instanceId,e.sessionId))}}function kc(e){return _.parse({instanceId:e.instanceId,sessionId:e.sessionId,unread:e.unread,sessionUpdatedAt:e.sessionUpdatedAt,updatedAt:e.updatedAt})}function zc(e,t){return`${e}\0${t}`}function Tc(e,t){return`ais_unread_${o.createHash("sha256").update(zc(e,t)).digest("hex").slice(0,32)}`}class Cc{snapshots=new Map;history=new Map;advertisedStreams=new Map;recoveries=new Map;bootstrap;logger;recoverDelta;recoverSnapshot;onRecoveredEvent;recoveryCount=0;counters={streamResets:0,gaps:0,deltaRecoveries:0,snapshotRecoveries:0,recoveryFailures:0};constructor(e){this.bootstrap=e.bootstrap,this.logger=e.logger,this.recoverDelta=e.recoverDelta,this.recoverSnapshot=e.recoverSnapshot,this.onRecoveredEvent=e.onRecoveredEvent}handleEvent(e){const t=e.type===Un?Yn:e.type===Fn?Xn:e.type===Bn?Zn:void 0;if(!t)return!1;const s=t.safeParse(e.payload);return s.success?(e.type===Un&&this.applySnapshot(s.data),e.type===Fn&&this.applyPatch(s.data),e.type===Bn&&this.applyRemoved(s.data),!0):(this.logger?.warn?.({eventType:e.type,issues:s.error.issues,errorCode:"APP_SESSION_EVENT_INVALID"},"app-session.aggregator.event.invalid"),!0)}applySnapshot(e){const t=this.advertisedStreams.get(e.meta.instanceId)?.streamId;return t&&t!==e.meta.streamId?(this.logger?.warn?.({instanceId:e.meta.instanceId,advertisedStreamId:t,streamId:e.meta.streamId,revision:e.meta.revision},"app-session.aggregator.snapshot.obsolete-stream"),!1):this.apply({type:Un,payload:e})}advertiseStream(e,t){return this.advertisedStreams.set(e,t),this.recoverStream(e,t.streamId,t.latestRevision).catch(s=>{this.counters.recoveryFailures+=1,this.logger?.warn?.({instanceId:e,streamId:t.streamId,error:s instanceof Error?s.message:String(s)},"app-session.aggregator.recovery.failed")})}diagnostics(){return{...this.counters,recoveriesStarted:this.recoveryCount,activeRecoveries:this.recoveries.size}}removeInstance(e){this.snapshots.delete(e),this.history.delete(e),this.advertisedStreams.delete(e)}applyPatch(e){return this.apply({type:Fn,payload:e})}applyRemoved(e){return this.apply({type:Bn,payload:e})}async list(e={}){return(e.refresh||0===this.snapshots.size)&&await this.bootstrapFromInstances(),this.view({includeTombstones:e.includeTombstones})}async streamDescriptors(){return 0===this.snapshots.size&&await this.bootstrapFromInstances(),[...this.snapshots.entries()].map(([e,t])=>({topic:"app.sessions",instanceId:e,streamId:t.streamId,latestRevision:t.revision,earliestRetainedRevision:this.history.get(e)?.[0]?.payload.meta.revision??t.revision+1}))}async delta(e){0===this.snapshots.size&&await this.bootstrapFromInstances();const t=this.resolveDeltaInstanceId(e.instanceId);this.pruneHistory(t);const s=this.snapshots.get(t)?.revision??0,n=this.snapshots.get(t)?.streamId||"unavailable",i=this.history.get(t)?.[0]?.payload.meta.revision??s+1;if(e.streamId!==n||e.sinceRevision>s)return ei.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:!0,events:[]});if(e.sinceRevision===s)return ei.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:!1,events:[]});const a=(this.history.get(t)??[]).filter(t=>t.payload.meta.revision>e.sinceRevision),r=!a.length||a[0].payload.meta.revision!==e.sinceRevision+1;return ei.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:r,events:r?[]:a.map(({type:e,payload:t})=>({type:e,payload:t}))})}apply(e,t=!1){const{meta:s}=e.payload,n=this.advertisedStreams.get(s.instanceId)?.streamId;if(n&&n!==s.streamId)return this.logger?.warn?.({instanceId:s.instanceId,advertisedStreamId:n,streamId:s.streamId,revision:s.revision},"app-session.aggregator.event.obsolete-stream"),!1;const i=this.recoveries.get(s.instanceId);if(i&&!t)return i.streamId===s.streamId&&(i.highWater=Math.max(i.highWater,s.revision)),!1;const a=this.snapshots.get(s.instanceId);e.type===Un&&a&&a.streamId!==s.streamId&&(this.counters.streamResets+=1,this.logger?.info?.({instanceId:s.instanceId,previousStreamId:a.streamId,streamId:s.streamId,previousRevision:a.revision,revision:s.revision,traceId:s.traceId,resetCount:this.counters.streamResets},"app-session.aggregator.stream-reset"));const r=function(e,t){const{meta:s}=t.payload;if(t.type===Un)return e?.streamId===s.streamId&&s.revision===e.revision?{kind:"duplicate",projection:e}:e?.streamId===s.streamId&&s.revision<e.revision?{kind:"stale",projection:e}:{kind:"applied",projection:Kn.parse({streamId:s.streamId,revision:s.revision,lastEventAt:s.generatedAt,snapshot:t.payload.snapshot})};if(!e||e.streamId!==s.streamId)return{kind:"snapshot-required",projection:e,streamId:s.streamId};if(s.revision===e.revision)return{kind:"duplicate",projection:e};if(s.revision<e.revision)return{kind:"stale",projection:e};if(s.revision!==e.revision+1||s.previousRevision!==e.revision)return{kind:"gap",projection:e,expectedRevision:e.revision+1,receivedRevision:s.revision};const n=[...e.snapshot.sessions];if(t.type===Fn){const e=n.findIndex(e=>e.id===t.payload.session.id);e<0?n.push(t.payload.session):n[e]=t.payload.session}else{const e=n.findIndex(e=>e.id===t.payload.sessionId);e>=0&&n.splice(e,1),t.payload.tombstone&&n.push(t.payload.tombstone)}const i=ti(n,s.generatedAt);return{kind:"applied",projection:Kn.parse({streamId:s.streamId,revision:s.revision,lastEventAt:s.generatedAt,snapshot:i})}}(a,e);if("applied"!==r.kind)return this.logger?.warn?.({traceId:s.traceId,streamId:s.streamId,instanceId:s.instanceId,currentRevision:a?.revision,revision:s.revision,outcome:r.kind},`app-session.aggregator.event.${r.kind}`),"gap"!==r.kind&&"snapshot-required"!==r.kind||("gap"===r.kind&&(this.counters.gaps+=1),this.recoverStream(s.instanceId,s.streamId,s.revision).catch(e=>{this.logger?.warn?.({instanceId:s.instanceId,streamId:s.streamId,revision:s.revision,error:e instanceof Error?e.message:String(e)},"app-session.aggregator.recovery.failed")})),!1;const o=r.projection;return this.snapshots.set(s.instanceId,o),this.rememberEvent(s.instanceId,e),this.logger?.info?.({traceId:s.traceId,streamId:s.streamId,instanceId:s.instanceId,revision:s.revision,reason:s.reason,sessionCount:o.snapshot.sessions.length,runningCount:o.snapshot.runningCount,problemCount:o.snapshot.problemCount},"app-session.aggregator.event.accepted"),!0}async bootstrapFromInstances(){this.logger?.info?.({},"app-session.aggregator.bootstrap");const e=new Map(this.snapshots),t=await this.bootstrap(),s=new Set(t.instances.map(e=>e.instanceId));for(const[t,n]of e)s.has(t)||this.snapshots.get(t)!==n||(this.snapshots.delete(t),this.history.delete(t),this.advertisedStreams.delete(t));for(const s of t.instances){const t=this.snapshots.get(s.instanceId);if(t!==e.get(s.instanceId))continue;const n=this.advertisedStreams.get(s.instanceId)?.streamId;n&&n!==s.streamId||t?.streamId===s.streamId&&t.revision>=s.revision||(t?.streamId!==s.streamId&&this.history.delete(s.instanceId),this.snapshots.set(s.instanceId,{streamId:s.streamId,snapshot:s.appSessions,revision:s.revision,lastEventAt:s.lastEventAt}))}}recoverStream(e,t,s){const n=this.recoveries.get(e);if(n)return n.streamId===t?n.highWater=Math.max(n.highWater,s):(n.streamId=t,n.highWater=s),n.promise;if(!this.recoverDelta||!this.recoverSnapshot)return Promise.resolve();const i={promise:Promise.resolve(),streamId:t,highWater:s},a=++this.recoveryCount;return this.logger?.info?.({instanceId:e,highWater:s,recoveryNumber:a},"app-session.aggregator.recovery.started"),i.promise=(async()=>{for(;;){const t=this.advertisedStreams.get(e);if(!t)return;i.streamId!==t.streamId&&(i.streamId=t.streamId,i.highWater=t.latestRevision);const s=this.snapshots.get(e);if(s&&s.streamId===t.streamId){if(s.revision<Math.max(t.latestRevision,i.highWater)){const t=await this.recoverDelta(e,s.streamId,s.revision);if(this.advertisedStreams.get(e)?.streamId!==t.streamId||i.streamId!==t.streamId)continue;if(t.syncRequired){const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const s={type:Un,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`aps_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(s,!0)&&this.onRecoveredEvent?.(s),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:a,outcome:"snapshot-fallback"},"app-session.aggregator.recovery.snapshot")}else{for(const e of t.events)this.apply(e,!0)&&this.onRecoveredEvent?.(e);this.counters.deltaRecoveries+=1,i.highWater=Math.max(i.highWater,t.latestRevision),this.logger?.info?.({instanceId:e,streamId:t.streamId,sinceRevision:t.sinceRevision,latestRevision:t.latestRevision,eventCount:t.events.length,recoveryNumber:a,outcome:"delta"},"app-session.aggregator.recovery.delta")}}}else{const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const s={type:Un,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`aps_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(s,!0)&&this.onRecoveredEvent?.(s),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:a,outcome:"snapshot"},"app-session.aggregator.recovery.snapshot")}const n=this.snapshots.get(e),r=this.advertisedStreams.get(e);if(n&&r&&n.streamId===r.streamId&&n.revision>=Math.max(r.latestRevision,i.highWater))return void this.logger?.info?.({instanceId:e,streamId:n.streamId,revision:n.revision,recoveryNumber:a,outcome:"success"},"app-session.aggregator.recovery.completed")}})().finally(()=>this.recoveries.delete(e)),this.recoveries.set(e,i),i.promise}rememberEvent(e,t){const s=this.history.get(e)??[],n=s.length&&s[0].payload.meta.streamId!==t.payload.meta.streamId?[]:s;n.push({...t,createdAtMs:Date.now()}),this.history.set(e,n),this.pruneHistory(e)}pruneHistory(e){const t=this.history.get(e);if(!t?.length)return;const s=t.filter(e=>e.createdAtMs>=Date.now()-36e5);s.length?this.history.set(e,s):this.history.delete(e)}resolveDeltaInstanceId(e){if(e)return e;const t=[...this.snapshots.keys()];if(1===t.length)return t[0];throw new Error("APP_SESSION_DELTA_INSTANCE_ID_REQUIRED")}visibleSnapshot(e){return ti(e.sessions.filter(e=>Ia("string"==typeof e.status?e.status:void 0)),e.updatedAt)}view(e={}){return{updatedAt:(new Date).toISOString(),instances:[...this.snapshots.entries()].map(([t,s])=>({instanceId:t,streamId:s.streamId,appSessions:e.includeTombstones?s.snapshot:this.visibleSnapshot(s.snapshot),revision:s.revision,lastEventAt:s.lastEventAt}))}}}function Ec(e){return e.raw[0].replace(/\\\$\{/g,"${")}class Nc{states=new Map;events;constructor(e){this.events=e}handle(e){if(e.type===Vs){const t=Ks.safeParse(e.payload);return void(t.success&&this.reconcileLifecycle(t.data))}if("instance.deleted"===e.type){const t=e.payload&&"object"==typeof e.payload&&"instanceId"in e.payload?String(e.payload.instanceId||""):"";return void(t&&this.deleteState(t))}if(e.type===Ss){const t=zs.safeParse(e.payload);return void(t.success&&this.applyOutput(t.data))}if(e.type===As){const t=Ts.safeParse(e.payload);t.success&&this.applyFinished(t.data)}}snapshots(){return[...this.states.values()].filter(e=>e.progress).map(e=>Cs.parse({...e.progress,terminalTail:e.terminalTail,...e.terminalTruncated?{terminalTruncated:!0}:{}}))}close(){for(const e of this.states.values())e.timer&&clearTimeout(e.timer);this.states.clear()}deleteState(e){const t=this.states.get(e);t?.timer&&clearTimeout(t.timer),this.states.delete(e)}reconcileLifecycle(e){const t=this.states.get(e.instanceId),s=e.imageProvisioning;t&&(!s||s.generation>t.identity.generation||s.generation===t.identity.generation&&"ready"===s.phase)&&this.deleteState(e.instanceId)}applyOutput(e){const t=this.states.get(e.instanceId);if(t&&e.generation<t.identity.generation)return;if(t&&e.generation===t.identity.generation&&!e.replay&&e.sequence<=t.sequence)return;const s=this.requireState(e);e.replay&&(s.terminal=new Rc,s.terminalTail="",s.layers.clear(),s.terminalTruncated=!1),s.sequence=Math.max(s.sequence,e.sequence),s.observedAt=e.observedAt,s.terminal.write(e.data);const n=`${s.terminalTail}${e.data}`;s.terminalTruncated||=n.length>262144,s.terminalTail=n.slice(-262144),function(e,t){for(const s of t.split("\n")){const t=s.trim().match(/^([a-f0-9]{6,64}):\s+(.+)$/i);if(!t)continue;const[,n,i]=t,a=i.toLowerCase(),r=["pull complete","already exists","download complete","pulling fs layer","waiting","downloading","extracting"].find(e=>a.startsWith(e));if(!r)continue;const o=e.get(n)||{status:r},d=xc(i);e.set(n,{...o,status:r,...d||{}})}}(s.layers,s.terminal.text()),s.progress=_c(s),this.schedule(s)}applyFinished(e){const t=this.states.get(e.instanceId);if(t&&e.generation<t.identity.generation)return;if(t&&e.generation===t.identity.generation&&e.sequence<=t.sequence)return;const s=this.requireState(e);s.sequence=Math.max(s.sequence,e.sequence),s.observedAt=e.observedAt,s.outcome=e.outcome,s.progress=_c(s),this.publish(s,!0)}requireState(e){const t=this.states.get(e.instanceId);if(t&&t.identity.generation===e.generation)return t;t?.timer&&clearTimeout(t.timer);const s={identity:{instanceId:e.instanceId,generation:e.generation,requestedReference:e.requestedReference},sequence:e.sequence,observedAt:e.observedAt,terminal:new Rc,terminalTail:"",terminalTruncated:!1,layers:new Map};return this.states.set(e.instanceId,s),s}schedule(e){e.timer||(e.timer=setTimeout(()=>this.publish(e,!1),250),e.timer.unref?.())}publish(e,t){e.timer&&clearTimeout(e.timer),e.timer=void 0,e.progress&&(e.progress=_c(e),this.events.publish("image.pull.progress",e.progress,{topic:"instances",scope:{instanceId:e.identity.instanceId}}),t&&(e.progress=_c(e)))}}function _c(e){const t=[...e.layers.values()],s=t.filter(e=>["pull complete","already exists"].includes(e.status)).length,n=t.filter(e=>"download complete"===e.status).length,i=t.filter(e=>"downloading"===e.status).length,a=t.filter(e=>"extracting"===e.status).length;let r=0,o=0;for(const e of t)e.total&&(o+=e.total,r+=["pull complete","already exists","download complete"].includes(e.status)?e.total:Math.min(e.current||0,e.total));const d="failed"===e.outcome?"failed":"succeeded"===e.outcome?"complete":a>0?"extracting":t.length>0?"pulling":"connecting",c=t.filter(e=>"already exists"!==e.status),l=c.length>0&&c.every(e=>Boolean(e.total)),p=l&&o>0?Math.max(0,Math.min(100,r/o*100)):void 0,u="failed"===d?"Image pull failed":"complete"===d?"Image pull complete":t.length?`${s}/${t.length} layers ready${void 0===p?"":` · ${Math.round(p)}%`}`:"Connecting to image registry";return Cs.parse({...e.identity,sequence:e.sequence,observedAt:e.observedAt,status:d,layers:{total:t.length,completed:s,downloaded:n,downloading:i,extracting:a},...l&&o>0?{bytes:{current:r,total:o},percent:p}:{},message:u})}function xc(e){const t=e.match(/([\d.]+)\s*([kmgt]?i?b)\s*\/\s*([\d.]+)\s*([kmgt]?i?b)/i);if(t)return{current:Oc(t[1],t[2]),total:Oc(t[3],t[4])}}function Oc(e,t){return Number(e)*1024**({b:0,kb:1,kib:1,mb:2,mib:2,gb:3,gib:3,tb:4,tib:4}[t.toLowerCase()]||0)}class Rc{lines=[[]];row=0;col=0;pending="";write(e){const t=`${this.pending}${e}`;this.pending="";for(let e=0;e<t.length;){const s=t[e];if(""===s&&"["===t[e+1]){let s=e+2;for(;s<t.length&&!/[\x40-\x7e]/.test(t[s]);)s+=1;if(s>=t.length){this.pending=t.slice(e);break}this.csi(t.slice(e+2,s),t[s]),e=s+1;continue}"\r"===s?this.col=0:"\n"===s?(this.row+=1,this.col=0,this.ensureLine()):"\b"===s?this.col=Math.max(0,this.col-1):s>=" "&&(this.ensureLine(),this.lines[this.row][this.col]=s,this.col+=1),e+=1}this.trim()}text(){return this.lines.map(e=>e.join("").trimEnd()).join("\n")}csi(e,t){const s=e.replace(/^\?/,"").split(";").map(e=>Number(e||1)),n=s[0]||1;"A"===t?this.row=Math.max(0,this.row-n):"B"===t?(this.row+=n,this.ensureLine()):"C"===t?this.col+=n:"D"===t?this.col=Math.max(0,this.col-n):"G"===t?this.col=Math.max(0,n-1):"H"===t||"f"===t?(this.row=Math.max(0,n-1),this.col=Math.max(0,(s[1]||1)-1),this.ensureLine()):"K"===t?(this.ensureLine(),2===(s[0]||0)?this.lines[this.row]=[]:this.lines[this.row].splice(this.col)):"J"===t&&2===(s[0]||0)&&(this.lines=[[]],this.row=0,this.col=0)}ensureLine(){for(;this.lines.length<=this.row;)this.lines.push([])}trim(){if(this.lines.length<=500)return;const e=this.lines.length-500;this.lines.splice(0,e),this.row=Math.max(0,this.row-e)}}const jc=r.z.object({code:r.z.string().trim().min(1),message:r.z.string(),details:r.z.record(r.z.string(),r.z.unknown()).optional(),retryable:r.z.boolean().optional()}).strict(),Dc=r.z.object({error:jc}).strict();function Mc(e){const t=process.env[e]?.trim();return t||void 0}const Pc=function(e,t=process.env,s=e){let n;return()=>{const i=t.TASK_HANDOFF_VERSION?.trim();return i||(n||=v(e,t,s),n)}}("@task-handoff/control-plane");function $c(){return{component:"control-plane",packageName:"@task-handoff/control-plane",packageVersion:Pc(),protocolVersion:He,buildId:Mc("TASK_HANDOFF_BUILD_ID"),builtAt:Mc("TASK_HANDOFF_BUILT_AT"),gitCommit:Mc("TASK_HANDOFF_GIT_COMMIT"),imageRef:Mc("TASK_HANDOFF_IMAGE_REF"),imageDigest:Mc("TASK_HANDOFF_IMAGE_DIGEST")}}const qc={".css":"text/css; charset=utf-8",".html":"text/html; charset=utf-8",".ico":"image/x-icon",".js":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".map":"application/json; charset=utf-8",".png":"image/png",".svg":"image/svg+xml",".webp":"image/webp"};function Lc(e,n){const i=t.resolve(e),a=t.resolve(i,n);if(a===i||a.startsWith(`${i}${t.sep}`))return s.existsSync(a)&&s.statSync(a).isFile()?a:void 0}function Uc(e,n,i){const a=Lc(n,i);return a?(e.header("cache-control","no-store"),e.type(qc[t.extname(a).toLowerCase()]||"application/octet-stream"),e.send(s.createReadStream(a))):e.code(404).send({error:{code:"STATIC_FILE_NOT_FOUND",message:`${i} was not found in the Control Plane UI static directory.`}})}function Fc(e){if(e instanceof r.z.ZodError)return{statusCode:400,code:"VALIDATION_ERROR",message:e.issues.map(e=>`${e.path.join(".")||"body"}: ${e.message}`).join("; ")};const t=e&&"object"==typeof e?e:{};return{statusCode:"number"==typeof t.statusCode?t.statusCode:500,code:"string"==typeof t.code?t.code:"CONTROL_PLANE_ERROR",message:e instanceof Error?e.message:String(e),...t.details&&"object"==typeof t.details&&!Array.isArray(t.details)?{details:t.details}:{},..."boolean"==typeof t.retryable?{retryable:t.retryable}:{}}}async function Bc(e,t){if(!e.enabled())return{type:"system",reason:"auth-disabled"};const s=await e.userForSessionToken(t);return s?{type:"user",userId:s.id,role:s.role}:void 0}function Hc(e,t){const s=t.split("?")[0]||"/";if(!s.startsWith("/api/"))return;if(s.startsWith("/api/auth/")||"/api/health"===s||"/api/events"===s)return;const n=function(e){return"GET"===e||"HEAD"===e?"read":"POST"===e?"create":"PATCH"===e||"PUT"===e?"update":"DELETE"===e?"delete":"read"}(e);return s.startsWith("/api/control-plane/settings")?{action:"GET"===e?"read":"manage-settings",resource:{type:"control-plane-settings"}}:s.startsWith("/api/models")?{action:"GET"===e?"read":"manage-secrets",resource:{type:"model"}}:s.startsWith("/api/chat-gateway/bridges")?{action:"GET"===e?"read":"manage-secrets",resource:{type:"chat-bridge"}}:"/api/node-join/invites"===s?{action:"manage-node-auth",resource:{type:"node"}}:s.startsWith("/api/nodes")?/\/(runtimes|local-folders)(\/|$)/.test(s)?{action:n,resource:{type:"runtime"}}:{action:"GET"===e?"read":"manage-node-auth",resource:{type:"node"}}:s.startsWith("/api/controlled-instances")?s.includes("/ai-sessions/")?s.endsWith("/approval")?{action:"approve",resource:{type:"ai-session"}}:s.endsWith("/interrupt")?{action:"interrupt",resource:{type:"ai-session"}}:s.includes("/messages")||s.includes("/queue/")?{action:"send-message",resource:{type:"ai-session"}}:{action:"read",resource:{type:"ai-session"}}:s.includes("/start")?{action:"start",resource:{type:"instance"}}:s.includes("/stop")?{action:"stop",resource:{type:"instance"}}:s.includes("/restart")?{action:"restart",resource:{type:"instance"}}:/\/config-sync(?:\/|$)/.test(s)?{action:"update",resource:{type:"instance"}}:{action:n,resource:{type:"instance"}}:s.startsWith("/api/projects")?{action:n,resource:{type:"project"}}:s.startsWith("/api/images")||s.startsWith("/api/image-options")||s.startsWith("/api/market")?{action:n,resource:{type:"runtime"}}:s.startsWith("/api/triggers")?{action:n,resource:{type:"trigger"}}:{action:"read",resource:{type:"control-plane-settings"}}}async function Gc(e={}){const r=wd(e.dataDir),o=n({logger:e.logger??!0}),d=new yr(r,{...e.service,logger:o.log}),c=new On(r,e.auth),l=new nd,p=new Nc(l),u=new Ac(r,{onChanged:e=>queueMicrotask(()=>l.publish(C,e,{topic:"ai.sessions",scope:{instanceId:e.instanceId,sessionId:e.sessionId}}))});u.init();const m=new wc({bootstrap:()=>d.bootstrapAiSessionsFromInstances(),logger:o.log,recoverDelta:(e,t,s)=>d.recoverAiSessionDelta(e,t,s),recoverSnapshot:e=>d.recoverAiSessionSnapshot(e),onRecoveredEvent:e=>l.publish(e.type,e.payload)});m.onSnapshot(e=>u.reconcile(e.instanceId,e.aiSessions));const g=new Cc({bootstrap:()=>d.bootstrapAppSessionsFromInstances(),logger:o.log,recoverDelta:(e,t,s)=>d.recoverAppSessionDelta(e,t,s),recoverSnapshot:e=>d.recoverAppSessionSnapshot(e),onRecoveredEvent:e=>l.publish(e.type,e.payload)});d.setAppSessionSnapshotProvider(e=>g.list(e)),d.setAiSessionSnapshotProvider(e=>m.list(e)),l.on(e=>{if(p.handle(e),"instance.deleted"===e.type){const t=e.payload&&"object"==typeof e.payload&&"instanceId"in e.payload?String(e.payload.instanceId||""):"";t&&(g.removeInstance(t),m.removeInstance(t),u.removeInstance(t))}g.handleEvent(e),m.handleEvent(e)});const h=new od,f=new bd(l,{validateInstanceScope:(e,t)=>d.nodeOwnsInstance(e,t),onStreamsHello:(e,t)=>{for(const s of t.streams)"ai.sessions"===s.topic&&m.advertiseStream(e,s),"app.sessions"===s.topic&&g.advertiseStream(e,s)}});d.setNodeAgentTransport(f),d.init(),await d.syncLocalNodeConnection().catch(()=>{}),c.init();const I=new ko(d,e.service?.fetchImpl,{aiSessions:m,logger:o.log});I.startEnabled();const y=new vd(d,f,{safetyIntervalMs:Number(process.env.TASK_HANDOFF_EVENT_CONNECTION_SAFETY_INTERVAL_MS)||void 0,logger:o.log});y.start(),await o.register(i),await o.register(a),o.addHook("onClose",async()=>{p.close(),y.stop(),I.stopAll()});const b=(v=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(v||"").trim().toLowerCase()));var v;o.setErrorHandler((e,t,s)=>{const n=Fc(e);(b||n.statusCode>=500)&&(n.statusCode>=500?o.log.error.bind(o.log):o.log.warn.bind(o.log))({method:t.method,url:t.url,statusCode:n.statusCode,errorCode:n.code,error:n.message},"control plane request failed"),s.code(n.statusCode).send(Dc.parse({error:{code:n.code,message:n.message,...n.details?{details:n.details}:{},...void 0!==n.retryable?{retryable:n.retryable}:{}}}))}),o.addHook("preHandler",async(e,t)=>{if(!c.enabled()||function(e,t){const s=t.split("?")[0];return!("/api/health"!==s&&!s.startsWith("/api/auth/"))||"GET"===e&&"/install-node-agent.sh"===s||"POST"===e&&"/api/node-join/complete"===s||"/api/app-access/session"===s||!!s.startsWith("/apps/access/")||!("/favicon.ico"!==s&&!s.startsWith("/assets/"))||"GET"===e&&!s.startsWith("/api/")&&!s.startsWith("/instances/")}(e.method,e.url)){const t=await Bc(c,e.cookies[bn]),s=Hc(e.method,e.url);return void(s&&Ld(t||{type:"system",reason:"auth-disabled"},s.action,s.resource))}const s=await Bc(c,e.cookies[bn]);if(!s)return t.code(401).send({error:{code:"CONTROL_PLANE_AUTH_REQUIRED",message:"Sign in to access the Control Plane."}});const n=Hc(e.method,e.url);n&&Ld(s,n.action,n.resource)});const w=t.resolve(e.staticDir||process.env.TASK_HANDOFF_CONTROL_PLANE_STATIC_DIR||t.resolve(process.cwd(),"packages","control-plane-ui","dist"));return s.existsSync(w)&&(o.get("/assets/*",async(e,s)=>{const n=e.params["*"];return Uc(s,w,t.join("assets",n||""))}),o.get("/favicon.ico",async(e,t)=>Lc(w,"favicon.ico")?Uc(t,w,"favicon.ico"):t.code(204).send())),o.get("/api/health",async()=>({data:{ok:!0,role:"control-plane",protocolVersion:He,build:$c(),dataDir:r.dataDir,serverTime:(new Date).toISOString()}})),o.get("/api/session-streams/diagnostics",async()=>({data:{aiSessions:m.diagnostics(),appSessions:g.diagnostics(),nodeConnections:y.diagnostics()}})),o.get("/install-node-agent.sh",async(e,t)=>(t.header("cache-control","no-store"),t.type("text/x-shellscript; charset=utf-8"),Ec`#!/bin/sh
2
2
  set -eu
3
3
 
4
4
  CONTROL_PLANE_URL=""
@@ -220,4 +220,4 @@ fi
220
220
  echo "TaskHandoff node-agent is installed and running."
221
221
  echo "Service: task-handoff-node-agent.service"
222
222
  echo "Pairing token: sudo task-handoff-node-agent invite --ipc-path $IPC_PATH"
223
- `)),o.get("/api/events",{websocket:!0},async e=>{const t=[];let s=!1;const n={get readyState(){return e.readyState},OPEN:e.OPEN,send(n){s?e.send(n):t.push(n)},on(t,s){e.on(t,s)}};l.connect(n);const[i,a]=await Promise.all([m.streamDescriptors(),g.streamDescriptors()]);l.send(e,"streams.hello",{protocolVersion:1,streams:[...i,...a]}),s=!0;for(const s of t)e.send(s);for(const t of p.snapshots())l.send(e,"image.pull.snapshot",t,{topic:"instances",scope:{instanceId:t.instanceId}})}),o.get("/api/auth/session",async e=>({data:await c.currentSession(e.cookies[yn])})),o.post("/api/auth/bootstrap-admin",async(e,t)=>{const s=await c.bootstrapAdmin(e.body);return t.code(201).send({data:s})}),o.post("/api/auth/login",async(e,t)=>{const s=await c.login(e.body);return t.setCookie(yn,s.sessionToken,{path:"/",httpOnly:!0,sameSite:"lax",expires:new Date(s.expiresAt)}),{data:{user:s.user}}}),o.post("/api/auth/logout",async(e,t)=>{const s=c.logout(e.cookies[yn]);return t.clearCookie(yn,{path:"/"}),{data:s}}),o.get("/api/control-plane/status",async()=>({data:{protocolVersion:He,build:Mc(),storage:r}})),o.get("/api/control-plane/settings",async()=>({data:d.getSettings()})),o.patch("/api/control-plane/settings",async e=>({data:d.updateSettings(e.body||{})})),o.post("/api/ai-session-attachments",{bodyLimit:33554432},async(e,t)=>{try{return t.code(201).send({data:h.upload(e.body)})}catch(e){return t.code(400).send({error:{code:"AI_SESSION_ATTACHMENT_INVALID",message:e instanceof Error?e.message:String(e)}})}}),rc({app:o,service:d,events:l,appSessionAggregator:g,aiSessionAggregator:m,aiSessionUnread:u,chatGateway:I,aiSessionAttachments:h,nodeAgentTunnel:f,nodeEventSubscriber:y,errorPayload:Lc}),function({app:e,service:t}){!function(e){for(const t of["application/octet-stream","application/x-www-form-urlencoded"])fc(e,t);fc(e,/^multipart\/form-data(?:;.*)?$/i)}(e),e.get("/api/app-access/session",async e=>{const s=Ic(e.url),n="vnc"===e.query.mode?"vnc":"web"===e.query.mode?"web":"tty",i=await t.appAccessProxyTarget(s,n);return{data:{mode:n,instance:{id:i.instance.id,name:i.instance.name,projectId:i.instance.projectId,projectName:"string"==typeof i.instance.sourceSnapshot.name?i.instance.sourceSnapshot.name:void 0},session:i.session,expiresAt:i.access.expiresAt,ttySocketPath:"tty"===n?`/apps/access/tty/ws?token=${encodeURIComponent(s)}`:void 0,vncFramePath:"vnc"===n?yc(s,i.access.sessionId,i.session):void 0}}}),e.get("/apps/access/tty/ws",{websocket:!0},async(e,s)=>{try{const n=await t.appAccessProxyTarget(Ic(s.url),"tty");await t.proxyInstanceWebSocket(n.instance.id,e,n.path,pc(s.headers),lc(s.headers))}catch{e.close(1011,"TTY access link is invalid or expired.")}}),e.route({method:"GET",url:"/apps/access/vnc/proxy/*",wsHandler:async(e,s)=>{const n=s.params;try{const i=await t.appAccessProxyTarget(Ic(s.url),"vnc",n["*"]||"");await t.proxyInstanceWebSocket(i.instance.id,e,i.path,pc(s.headers),lc(s.headers))}catch{e.close(1011,"VNC access link is invalid or expired.")}},handler:async(e,s)=>{const n=e.params,i=await t.appAccessProxyTarget(Ic(e.url),"vnc",n["*"]||"");return gc(s,await t.proxyInstanceHttp(i.instance.id,i.path,{method:e.method,headers:cc(e.headers)}),i.instance.id,i.path)}}),e.all("/instances/:id",{bodyLimit:67108864},async(e,s)=>{const n=e.params;return"GET"===e.method||"HEAD"===e.method?s.redirect(`${mc(n.id)}/`):gc(s,await t.proxyInstanceHttp(n.id,"/",{method:e.method,headers:cc(e.headers),body:hc(e.body)}),n.id,"/")}),e.route({method:"GET",url:"/instances/:id/*",wsHandler:async(e,s)=>{const n=s.params,i=n["*"]||"",a=s.url.indexOf("?"),r=a>=0?s.url.slice(a):"";try{await t.proxyInstanceWebSocket(n.id,e,`/${i}${r}`,pc(s.headers),lc(s.headers))}catch{e.close(1011,"Instance websocket endpoint is not reachable.")}},handler:async(e,s)=>{const n=e.params,i=n["*"]||"",a=e.url.indexOf("?"),r=`/${i}${a>=0?e.url.slice(a):""}`;return gc(s,await t.proxyInstanceHttp(n.id,r,{method:e.method,headers:cc(e.headers)}),n.id,r)}}),e.route({method:["POST","PUT","PATCH","DELETE","OPTIONS"],url:"/instances/:id/*",bodyLimit:67108864,handler:async(e,s)=>{const n=e.params,i=n["*"]||"",a=e.url.indexOf("?"),r=a>=0?e.url.slice(a):"";return uc(s,await t.proxyInstanceHttp(n.id,`/${i}${r}`,{method:e.method,headers:cc(e.headers),body:hc(e.body)}))}})}({app:o,service:d}),o.get("*",async(e,t)=>s.existsSync(w)?qc(t,w,"index.html"):t.code(404).send({error:{code:"CONTROL_PLANE_UI_NOT_BUILT",message:"Control plane API is running, but the Web UI static directory was not found."}})),o}async function Hc(e){const n=bd(e.dataDir),i=function(e,n={}){return function(e,n={}){s.mkdirSync(t.dirname(e),{recursive:!0});const i={pid:process.pid,hostname:f.hostname(),component:n.component,command:process.argv.join(" "),acquiredAt:(new Date).toISOString(),token:o.randomUUID(),startIdentity:zd(process.pid),dataDir:n.dataDir,host:n.host,port:n.port,instanceId:n.instanceId};for(let t=0;t<5;t+=1)try{return s.mkdirSync(e),s.writeFileSync(Sd(e),`${JSON.stringify({token:i.token,pid:i.pid,startIdentity:i.startIdentity,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),Nd(e,i),{lockPath:e,owner:i,release(){const t=Td(e);t?.token===i.token&&s.rmSync(e,{recursive:!0,force:!0})}}}catch(i){if("EEXIST"!==i.code)throw i;const a=Td(e);if(Ed(a))throw new vd(e,a,n.error);if(!a&&(xd(e)||jd(e)<(n.incompleteLockStaleMs??2e3))){if(t+1<5){Rd();continue}throw new vd(e,void 0,n.error)}const r=o.randomUUID();if(!Dd(e,r)){if(t+1<5){Rd();continue}throw new vd(e,Td(e),n.error)}const d=Td(e);if(Ed(d))throw Od(Ad(e)),new vd(e,d,n.error);if(a?.token&&d?.token!==a.token){Od(Ad(e));continue}if(!Pd(e,r))continue;s.rmSync(e,{recursive:!0,force:!0})}const a=Td(e);throw new vd(e,a,n.error)}(e,{...n,component:"control-plane",error:{label:"Control plane",code:"CONTROL_PLANE_ALREADY_RUNNING"}})}(t.join(f.tmpdir(),`task-handoff-control-plane-${process.getuid?.()??"user"}.lock`),{dataDir:n.dataDir,host:e.host,port:e.port});try{const t=await Bc(e);t.addHook("onClose",async()=>{i.release()}),await t.listen({host:e.host,port:e.port})}catch(e){throw i.release(),e}}function Gc(e){const t=Number(e);if(!Number.isInteger(t)||t<=0||t>65535)throw new Error("Port must be a valid TCP port.");return t}function Wc(e){if("disabled"!==e&&"password"!==e)throw new Error("Auth mode must be disabled or password.");return e}(async function(){const s=new e.Command;s.name("task-handoff-control-plane").description("Run the TaskHandoff control plane.").version(v("@task-handoff/cli")).option("--host <host>","Control plane host",process.env.TASK_HANDOFF_CONTROL_PLANE_HOST||"127.0.0.1").option("-p, --port <port>","Control plane port",Gc,Number(process.env.TASK_HANDOFF_CONTROL_PLANE_PORT)||8081).option("--data-dir <path>","Control plane data directory").option("--static-dir <path>","Control plane Web UI static directory",process.env.TASK_HANDOFF_CONTROL_PLANE_STATIC_DIR||t.resolve(__dirname,"..","ui")).option("--auth-mode <mode>","Control plane auth mode: disabled or password",Wc,process.env.TASK_HANDOFF_CONTROL_PLANE_AUTH_MODE||"password").action(async e=>{await Hc({host:e.host,port:e.port,dataDir:e.dataDir,staticDir:e.staticDir,auth:{mode:e.authMode}})}),await s.parseAsync(process.argv)})().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});
223
+ `)),o.get("/api/events",{websocket:!0},async e=>{const t=[];let s=!1;const n={get readyState(){return e.readyState},OPEN:e.OPEN,send(n){s?e.send(n):t.push(n)},on(t,s){e.on(t,s)}};l.connect(n);const[i,a]=await Promise.all([m.streamDescriptors(),g.streamDescriptors()]);l.send(e,"streams.hello",{protocolVersion:1,streams:[...i,...a]}),s=!0;for(const s of t)e.send(s);for(const t of p.snapshots())l.send(e,"image.pull.snapshot",t,{topic:"instances",scope:{instanceId:t.instanceId}})}),o.get("/api/auth/session",async e=>({data:await c.currentSession(e.cookies[bn])})),o.post("/api/auth/bootstrap-admin",async(e,t)=>{const s=await c.bootstrapAdmin(e.body);return t.code(201).send({data:s})}),o.post("/api/auth/login",async(e,t)=>{const s=await c.login(e.body);return t.setCookie(bn,s.sessionToken,{path:"/",httpOnly:!0,sameSite:"lax",expires:new Date(s.expiresAt)}),{data:{user:s.user}}}),o.post("/api/auth/logout",async(e,t)=>{const s=c.logout(e.cookies[bn]);return t.clearCookie(bn,{path:"/"}),{data:s}}),o.get("/api/control-plane/status",async()=>({data:{protocolVersion:He,build:$c(),storage:r}})),o.get("/api/control-plane/settings",async()=>({data:d.getSettings()})),o.patch("/api/control-plane/settings",async e=>({data:d.updateSettings(e.body||{})})),o.post("/api/ai-session-attachments",{bodyLimit:33554432},async(e,t)=>{try{return t.code(201).send({data:h.upload(e.body)})}catch(e){return t.code(400).send({error:{code:"AI_SESSION_ATTACHMENT_INVALID",message:e instanceof Error?e.message:String(e)}})}}),dc({app:o,service:d,events:l,appSessionAggregator:g,aiSessionAggregator:m,aiSessionUnread:u,chatGateway:I,aiSessionAttachments:h,nodeAgentTunnel:f,nodeEventSubscriber:y,errorPayload:Fc}),function({app:e,service:t}){!function(e){for(const t of["application/octet-stream","application/x-www-form-urlencoded"])yc(e,t);yc(e,/^multipart\/form-data(?:;.*)?$/i)}(e),e.get("/api/app-access/session",async e=>{const s=bc(e.url),n="vnc"===e.query.mode?"vnc":"web"===e.query.mode?"web":"tty",i=await t.appAccessProxyTarget(s,n);return{data:{mode:n,instance:{id:i.instance.id,name:i.instance.name,projectId:i.instance.projectId,projectName:"string"==typeof i.instance.sourceSnapshot.name?i.instance.sourceSnapshot.name:void 0},session:i.session,expiresAt:i.access.expiresAt,ttySocketPath:"tty"===n?`/apps/access/tty/ws?token=${encodeURIComponent(s)}`:void 0,vncFramePath:"vnc"===n?vc(s,i.access.sessionId,i.session):void 0}}}),e.get("/apps/access/tty/ws",{websocket:!0},async(e,s)=>{try{const n=await t.appAccessProxyTarget(bc(s.url),"tty");await t.proxyInstanceWebSocket(n.instance.id,e,n.path,mc(s.headers),uc(s.headers))}catch{e.close(1011,"TTY access link is invalid or expired.")}}),e.route({method:"GET",url:"/apps/access/vnc/proxy/*",wsHandler:async(e,s)=>{const n=s.params;try{const i=await t.appAccessProxyTarget(bc(s.url),"vnc",n["*"]||"");await t.proxyInstanceWebSocket(i.instance.id,e,i.path,mc(s.headers),uc(s.headers))}catch{e.close(1011,"VNC access link is invalid or expired.")}},handler:async(e,s)=>{const n=e.params,i=await t.appAccessProxyTarget(bc(e.url),"vnc",n["*"]||"");return fc(s,await t.proxyInstanceHttp(i.instance.id,i.path,{method:e.method,headers:pc(e.headers)}),i.instance.id,i.path)}}),e.all("/instances/:id",{bodyLimit:67108864},async(e,s)=>{const n=e.params;return"GET"===e.method||"HEAD"===e.method?s.redirect(`${hc(n.id)}/`):fc(s,await t.proxyInstanceHttp(n.id,"/",{method:e.method,headers:pc(e.headers),body:Ic(e.body)}),n.id,"/")}),e.route({method:"GET",url:"/instances/:id/*",wsHandler:async(e,s)=>{const n=s.params,i=n["*"]||"",a=s.url.indexOf("?"),r=a>=0?s.url.slice(a):"";try{await t.proxyInstanceWebSocket(n.id,e,`/${i}${r}`,mc(s.headers),uc(s.headers))}catch{e.close(1011,"Instance websocket endpoint is not reachable.")}},handler:async(e,s)=>{const n=e.params,i=n["*"]||"",a=e.url.indexOf("?"),r=`/${i}${a>=0?e.url.slice(a):""}`;return fc(s,await t.proxyInstanceHttp(n.id,r,{method:e.method,headers:pc(e.headers)}),n.id,r)}}),e.route({method:["POST","PUT","PATCH","DELETE","OPTIONS"],url:"/instances/:id/*",bodyLimit:67108864,handler:async(e,s)=>{const n=e.params,i=n["*"]||"",a=e.url.indexOf("?"),r=a>=0?e.url.slice(a):"";return gc(s,await t.proxyInstanceHttp(n.id,`/${i}${r}`,{method:e.method,headers:pc(e.headers),body:Ic(e.body)}))}})}({app:o,service:d}),o.get("*",async(e,t)=>s.existsSync(w)?Uc(t,w,"index.html"):t.code(404).send({error:{code:"CONTROL_PLANE_UI_NOT_BUILT",message:"Control plane API is running, but the Web UI static directory was not found."}})),o}async function Wc(e){const n=wd(e.dataDir),i=function(e,n={}){return function(e,n={}){s.mkdirSync(t.dirname(e),{recursive:!0});const i={pid:process.pid,hostname:f.hostname(),component:n.component,command:process.argv.join(" "),acquiredAt:(new Date).toISOString(),token:o.randomUUID(),startIdentity:Cd(process.pid),dataDir:n.dataDir,host:n.host,port:n.port,instanceId:n.instanceId};for(let t=0;t<5;t+=1)try{return s.mkdirSync(e),s.writeFileSync(kd(e),`${JSON.stringify({token:i.token,pid:i.pid,startIdentity:i.startIdentity,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),xd(e,i),{lockPath:e,owner:i,release(){const t=Ed(e);t?.token===i.token&&s.rmSync(e,{recursive:!0,force:!0})}}}catch(i){if("EEXIST"!==i.code)throw i;const a=Ed(e);if(_d(a))throw new Sd(e,a,n.error);if(!a&&(Rd(e)||Md(e)<(n.incompleteLockStaleMs??2e3))){if(t+1<5){Dd();continue}throw new Sd(e,void 0,n.error)}const r=o.randomUUID();if(!Pd(e,r)){if(t+1<5){Dd();continue}throw new Sd(e,Ed(e),n.error)}const d=Ed(e);if(_d(d))throw jd(zd(e)),new Sd(e,d,n.error);if(a?.token&&d?.token!==a.token){jd(zd(e));continue}if(!qd(e,r))continue;s.rmSync(e,{recursive:!0,force:!0})}const a=Ed(e);throw new Sd(e,a,n.error)}(e,{...n,component:"control-plane",error:{label:"Control plane",code:"CONTROL_PLANE_ALREADY_RUNNING"}})}(t.join(f.tmpdir(),`task-handoff-control-plane-${process.getuid?.()??"user"}.lock`),{dataDir:n.dataDir,host:e.host,port:e.port});try{const t=await Gc(e);t.addHook("onClose",async()=>{i.release()}),await t.listen({host:e.host,port:e.port})}catch(e){throw i.release(),e}}function Vc(e){const t=Number(e);if(!Number.isInteger(t)||t<=0||t>65535)throw new Error("Port must be a valid TCP port.");return t}function Kc(e){if("disabled"!==e&&"password"!==e)throw new Error("Auth mode must be disabled or password.");return e}(async function(){const s=new e.Command;s.name("task-handoff-control-plane").description("Run the TaskHandoff control plane.").version(v("@task-handoff/cli")).option("--host <host>","Control plane host",process.env.TASK_HANDOFF_CONTROL_PLANE_HOST||"127.0.0.1").option("-p, --port <port>","Control plane port",Vc,Number(process.env.TASK_HANDOFF_CONTROL_PLANE_PORT)||8081).option("--data-dir <path>","Control plane data directory").option("--static-dir <path>","Control plane Web UI static directory",process.env.TASK_HANDOFF_CONTROL_PLANE_STATIC_DIR||t.resolve(__dirname,"..","ui")).option("--auth-mode <mode>","Control plane auth mode: disabled or password",Kc,process.env.TASK_HANDOFF_CONTROL_PLANE_AUTH_MODE||"password").action(async e=>{await Wc({host:e.host,port:e.port,dataDir:e.dataDir,staticDir:e.staticDir,auth:{mode:e.authMode}})}),await s.parseAsync(process.argv)})().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});