@task-handoff/control-plane 0.0.8 → 0.0.9-alpha.1

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 (76) hide show
  1. package/bin/task-handoff-control-plane +0 -0
  2. package/dist/cli.js +2 -2
  3. package/package.json +2 -1
  4. package/ui/assets/{CodeBlockNode-B4qCx4L9.js → CodeBlockNode-5VE-YAN-.js} +1 -1
  5. package/ui/assets/{Tooltip-BqGcOKbi.js → Tooltip-CxH9TDAz.js} +1 -1
  6. package/ui/assets/{abnfDiagram-VRR7QNED-B44ZFc5n.js → abnfDiagram-VRR7QNED-BpvZzUgF.js} +1 -1
  7. package/ui/assets/{arc-DqKhW_qq.js → arc-DzTolAl4.js} +1 -1
  8. package/ui/assets/{architectureDiagram-ZJ3FMSHR-azNUkGZN.js → architectureDiagram-ZJ3FMSHR-O_vrD-Kg.js} +1 -1
  9. package/ui/assets/{blockDiagram-677ZJIJ3-BVXgrZ_c.js → blockDiagram-677ZJIJ3-9b9AP9xx.js} +1 -1
  10. package/ui/assets/{c4Diagram-LMCZKHZV-B0_3sMo3.js → c4Diagram-LMCZKHZV-Bz1lYepv.js} +1 -1
  11. package/ui/assets/channel-C9EIswQg.js +1 -0
  12. package/ui/assets/{chunk-2Q5K7J3B-BKlXpLzr.js → chunk-2Q5K7J3B-HxKviy4N.js} +1 -1
  13. package/ui/assets/{chunk-32BRIVSS-BPbpFlvb.js → chunk-32BRIVSS-g8HNjkVJ.js} +1 -1
  14. package/ui/assets/{chunk-5VM5RSS4-D3ZF1J-P.js → chunk-5VM5RSS4-CbLo9cqj.js} +1 -1
  15. package/ui/assets/{chunk-EX3LRPZG-ClQ2wXXR.js → chunk-EX3LRPZG-CoVFcDFb.js} +1 -1
  16. package/ui/assets/{chunk-JWPE2WC7-B7Zc7iaA.js → chunk-JWPE2WC7-BQwf2rA9.js} +1 -1
  17. package/ui/assets/{chunk-MOJQB5TN-D5Osjwkq.js → chunk-MOJQB5TN-BVZiXylZ.js} +1 -1
  18. package/ui/assets/{chunk-RYQCIY6F-B4ujWQTl.js → chunk-RYQCIY6F-DP4U5HbS.js} +1 -1
  19. package/ui/assets/{chunk-V7JOEXUC-CoaugYrY.js → chunk-V7JOEXUC-DtiebA1E.js} +1 -1
  20. package/ui/assets/{chunk-VR4S4FIN-AEW2rOSv.js → chunk-VR4S4FIN-JXQtjgeY.js} +1 -1
  21. package/ui/assets/{chunk-XXDRQBXY-DkLI3HWo.js → chunk-XXDRQBXY-DYRxJvER.js} +1 -1
  22. package/ui/assets/classDiagram-OUVF2IWQ-Cw9xJKcE.js +1 -0
  23. package/ui/assets/classDiagram-v2-EOCWNBFH-Cw9xJKcE.js +1 -0
  24. package/ui/assets/{cose-bilkent-JH36ORCC-CAzAMQvD.js → cose-bilkent-JH36ORCC-DF8XGpjJ.js} +1 -1
  25. package/ui/assets/{cynefin-VYW2F7L2-DRAD9ZM8.js → cynefin-VYW2F7L2-C_ACNKil.js} +1 -1
  26. package/ui/assets/{cynefinDiagram-TSTJHNR4-DAzrbtnK.js → cynefinDiagram-TSTJHNR4-DvVj_iBQ.js} +1 -1
  27. package/ui/assets/{dagre-VKFMJZFB-CV3fupE4.js → dagre-VKFMJZFB-CG0riasY.js} +1 -1
  28. package/ui/assets/{diagram-FQU43EPY-pzg0v8zQ.js → diagram-FQU43EPY-CcbNcYZ9.js} +1 -1
  29. package/ui/assets/{diagram-G47NLZAW-DdssPI4E.js → diagram-G47NLZAW-BAZ-atX9.js} +1 -1
  30. package/ui/assets/{diagram-NH7WQ7WH-f_VLPoe7.js → diagram-NH7WQ7WH-B9_Ign5I.js} +1 -1
  31. package/ui/assets/{diagram-OA4YK3LP-zrLcwkZe.js → diagram-OA4YK3LP-CSnrB3yT.js} +1 -1
  32. package/ui/assets/{diagram-WEI45ONY-DqMyqGEf.js → diagram-WEI45ONY-D3pWH2dh.js} +1 -1
  33. package/ui/assets/{ebnfDiagram-CCIWWBDH-CwDC1GKZ.js → ebnfDiagram-CCIWWBDH-ryxrY1A7.js} +1 -1
  34. package/ui/assets/{erDiagram-Q63AITRT-D78Jc4OA.js → erDiagram-Q63AITRT-CBiOhTiv.js} +1 -1
  35. package/ui/assets/{flowDiagram-23GEKE2U-0aWbUJd9.js → flowDiagram-23GEKE2U-DPbY-__F.js} +1 -1
  36. package/ui/assets/{ganttDiagram-NO4QXBWP-DHdzAxXs.js → ganttDiagram-NO4QXBWP-B4dnpmhm.js} +1 -1
  37. package/ui/assets/{gitGraphDiagram-IHSO6WYX-BAN_QGEx.js → gitGraphDiagram-IHSO6WYX-BUNpOfGW.js} +1 -1
  38. package/ui/assets/index-Bt_Kfq_A.js +622 -0
  39. package/ui/assets/{index-C0YlMXrN.css → index-C3oTBVMx.css} +1 -1
  40. package/ui/assets/{index10-BD68yL_z.js → index10-D8sm2JRk.js} +1 -1
  41. package/ui/assets/{index11-FvrNwVwk.js → index11-DX34O4g9.js} +1 -1
  42. package/ui/assets/{index5-THF_Ihph.js → index5-B_SakbbX.js} +1 -1
  43. package/ui/assets/{index6-BEO9DdY1.js → index6-BtnMkdCW.js} +1 -1
  44. package/ui/assets/{index7-5hXcYw5b.js → index7-Ctmml1fN.js} +1 -1
  45. package/ui/assets/{index8-d-Q0s7Oi.js → index8-Crggf-rx.js} +1 -1
  46. package/ui/assets/{infoDiagram-FWYZ7A6U-DJi0o-cW.js → infoDiagram-FWYZ7A6U-CBpeXDRG.js} +1 -1
  47. package/ui/assets/{ishikawaDiagram-FXEZZL3T-DBM8yOgP.js → ishikawaDiagram-FXEZZL3T-Bahs_ewz.js} +1 -1
  48. package/ui/assets/{journeyDiagram-5HDEW3XC-DS4fT6dl.js → journeyDiagram-5HDEW3XC-QSV0tP1j.js} +1 -1
  49. package/ui/assets/{kanban-definition-HUTT4EX6-Ds3q_RV0.js → kanban-definition-HUTT4EX6-BkrMdnuj.js} +1 -1
  50. package/ui/assets/{linear-Dzd0a_sm.js → linear-CIFtoQJq.js} +1 -1
  51. package/ui/assets/{mermaid.core-BURwJSIP.js → mermaid.core-8Jeqeu-h.js} +4 -4
  52. package/ui/assets/{mhchem-Baj-lUPp.js → mhchem-DZsBVqvl.js} +1 -1
  53. package/ui/assets/{mindmap-definition-LN4V7U3C-DzWhQ2CO.js → mindmap-definition-LN4V7U3C-37vcWZt7.js} +1 -1
  54. package/ui/assets/{pegDiagram-2B236MQR-B6knxxes.js → pegDiagram-2B236MQR-B6B4rDgm.js} +1 -1
  55. package/ui/assets/{pieDiagram-ENE6RG2P-BoRtSCdp.js → pieDiagram-ENE6RG2P-B4ZsmPHG.js} +1 -1
  56. package/ui/assets/{quadrantDiagram-ABIIQ3AL-wJS1MoWW.js → quadrantDiagram-ABIIQ3AL-BuBF-msf.js} +1 -1
  57. package/ui/assets/{railroadDiagram-RFXS5EU6-BhM0t8Wk.js → railroadDiagram-RFXS5EU6-CVIR0es0.js} +1 -1
  58. package/ui/assets/{requirementDiagram-TGXJPOKE-C6e4RfSx.js → requirementDiagram-TGXJPOKE-9bpfcpgx.js} +1 -1
  59. package/ui/assets/{sankeyDiagram-HTMAVEWB-BvOP4z7B.js → sankeyDiagram-HTMAVEWB-CiOwYgR7.js} +1 -1
  60. package/ui/assets/{sequenceDiagram-DBY2YBRQ-DXJLsRiC.js → sequenceDiagram-DBY2YBRQ-3uszHO19.js} +1 -1
  61. package/ui/assets/{sizeCapture-X5ZJPWSS-BzrC649z.js → sizeCapture-X5ZJPWSS-B6zkInFQ.js} +1 -1
  62. package/ui/assets/{stateDiagram-2N3HPSRC-mbX1vyvl.js → stateDiagram-2N3HPSRC-ExT2BNP0.js} +1 -1
  63. package/ui/assets/stateDiagram-v2-6OUMAXLB-Bfe3Pcpu.js +1 -0
  64. package/ui/assets/{swimlanes-5IMT3BWC-6J2-l0Xz.js → swimlanes-5IMT3BWC-M1cIWUx6.js} +2 -2
  65. package/ui/assets/swimlanesDiagram-G3AALYLV-SWdR2stc.js +8 -0
  66. package/ui/assets/{timeline-definition-FHXFAJF6-8ZVjWia9.js → timeline-definition-FHXFAJF6-CTCWho0p.js} +1 -1
  67. package/ui/assets/{vennDiagram-L72KCM5P-D-HWXj-E.js → vennDiagram-L72KCM5P-CAo8iX8h.js} +1 -1
  68. package/ui/assets/{wardleyDiagram-EHGQE667-Bv6Hf9Om.js → wardleyDiagram-EHGQE667-DUwvX1C2.js} +1 -1
  69. package/ui/assets/{xychartDiagram-FW5EYKEG-DdJAv75W.js → xychartDiagram-FW5EYKEG-Dmm3Fv__.js} +1 -1
  70. package/ui/index.html +2 -2
  71. package/ui/assets/channel--rHMK7hI.js +0 -1
  72. package/ui/assets/classDiagram-OUVF2IWQ-CpZk0mua.js +0 -1
  73. package/ui/assets/classDiagram-v2-EOCWNBFH-CpZk0mua.js +0 -1
  74. package/ui/assets/index-B9JIKKhS.js +0 -614
  75. package/ui/assets/stateDiagram-v2-6OUMAXLB-DegvhIJT.js +0 -1
  76. package/ui/assets/swimlanesDiagram-G3AALYLV-BMMMc5js.js +0 -8
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var e=require("commander"),t=require("node:fs"),s=require("node:path"),n=require("node:url"),i=require("fastify"),a=require("@fastify/cookie"),r=require("@fastify/websocket"),o=require("zod"),d=require("node:crypto"),c=require("node:util"),l=require("write-file-atomic"),p=require("ws"),u=require("node:http"),m=require("node:net"),g=require("node:stream"),h=require("telegramify-markdown"),I=require("node:fs/promises"),f=require("node:os"),y=require("dingtalk-stream"),b="undefined"!=typeof document?document.currentScript:null;const v=20971520,w=512e3,S=41943040,A="ai-session.snapshot",k="ai-session.patch",T="ai-session.removed",z="ai-session.unread.updated",E=o.z.string().trim().min(1).max(80),C=o.z.enum(["running","waiting","idle","failed"]),N=o.z.object({instanceId:o.z.string().trim().min(1).max(160),sessionId:o.z.string().trim().min(1).max(120),unread:o.z.boolean(),sessionUpdatedAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict(),_=o.z.enum(["thinking","tool","editing","approval","responding","unknown"]),x=o.z.object({id:o.z.string().trim().min(1).max(240).optional(),kind:o.z.string().trim().min(1).max(80).optional(),name:o.z.string().trim().min(1).max(120),inputPreview:o.z.string().trim().max(500).optional(),startedAt:o.z.string().datetime().optional()}).strict(),O=o.z.enum(["pending-init","running","interrupted","completed","errored","shutdown","not-found"]),R=o.z.enum(["started","interacted","interrupted"]),j=o.z.object({threadId:o.z.string().trim().min(1).max(240),path:o.z.string().trim().max(4096).optional(),status:O,activity:R.optional(),message:o.z.string().trim().max(1e3).optional(),updatedAt:o.z.string().datetime()}).strict(),D=o.z.object({toolCalls:o.z.number().int().min(0).default(0),edits:o.z.number().int().min(0).default(0),approvals:o.z.number().int().min(0).default(0)}).strict().default({toolCalls:0,edits:0,approvals:0}),P=o.z.object({send:o.z.boolean().optional(),interrupt:o.z.boolean().optional(),approval:o.z.boolean().optional()}).strict(),M=o.z.enum(["image","file"]),$=o.z.object({id:o.z.string().trim().min(1).max(120),kind:M,name:o.z.string().trim().min(1).max(240),mime:o.z.string().trim().min(1).max(120),size:o.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER)}),q=o.z.union([$.extend({source:o.z.object({type:o.z.literal("inline"),encoding:o.z.literal("base64"),data:o.z.string().min(1).max(31457280)}).strict()}).strict().superRefine((e,t)=>{const s="image"===e.kind?v:w;(e.size<=0||("file"===e.kind?e.size>=s:e.size>s))&&t.addIssue({code:o.z.ZodIssueCode.custom,path:["size"],message:`Inline ${e.kind} must be between 1 byte and ${s} bytes.`})}),$.extend({source:o.z.object({type:o.z.literal("runtime-path"),path:o.z.string().trim().min(1).max(4096)}).strict()}).strict()]),L=$.extend({sourceType:o.z.enum(["inline","runtime-path"])}).strict(),U=o.z.union([o.z.object({id:o.z.string().trim().min(1).max(120),kind:M.optional(),source:o.z.object({type:o.z.literal("upload-ref")}).strict().default({type:"upload-ref"})}).strict(),o.z.object({id:o.z.string().trim().min(1).max(120),kind:M,name:o.z.string().trim().min(1).max(240),mime:o.z.string().trim().min(1).max(120),size:o.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),source:o.z.object({type:o.z.literal("runtime-path"),path:o.z.string().trim().min(1).max(4096)}).strict()}).strict()]),F=o.z.enum(["auto","queue","steer","immediate"]),B=o.z.enum(["ask","auto-review","full-access"]),H=o.z.enum(["review","rename","goal","compact"]),G=o.z.object({command:H,argument:o.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.`})}),W=o.z.object({command:H,value:o.z.string().max(4e3).optional(),turnId:o.z.string().trim().min(1).max(240).optional()}).strict();o.z.enum(["skill","app","plugin"]);const V=o.z.object({name:o.z.string().trim().min(1).max(240),path:o.z.string().trim().min(1).max(4096)}),J=o.z.discriminatedUnion("kind",[V.extend({kind:o.z.literal("skill"),path:o.z.string().trim().min(1).max(4096).refine(e=>e.startsWith("/"),"Skill paths must be absolute.")}).strict(),V.extend({kind:o.z.literal("app"),path:o.z.string().trim().min(7).max(4096).regex(/^app:\/\/[^\s/]+$/,"App paths must use app://<id>.")}).strict(),V.extend({kind:o.z.literal("plugin"),path:o.z.string().trim().min(10).max(4096).regex(/^plugin:\/\/[^\s/]+$/,"Plugin paths must use plugin://<id>.")}).strict()]),K=o.z.array(J).max(20).default([]),Q=o.z.enum(["plugin","skill","file","directory","app"]),X=o.z.object({kind:Q,name:o.z.string().trim().min(1).max(240),description:o.z.string().trim().max(1e3).optional(),path:o.z.string().trim().min(1).max(4096),icon:o.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."})}),Y=o.z.object({category:o.z.enum(["skills","plugins","apps","files"]),code:o.z.string().trim().min(1).max(160),message:o.z.string().trim().min(1).max(2e3)}).strict(),Z=o.z.object({sessionId:o.z.string().trim().min(1).max(120),providerSessionId:o.z.string().trim().min(1).max(240),cwd:o.z.string().trim().min(1).max(4096),candidates:o.z.array(X).max(1e3).default([]),diagnostics:o.z.array(Y).max(20).default([])}).strict(),ee=o.z.object({query:o.z.string().trim().max(240).default("")}).strict(),te=o.z.object({sessionId:o.z.string().trim().min(1).max(120),cwd:o.z.string().trim().min(1).max(4096),query:o.z.string().trim().max(240),requestId:o.z.string().trim().min(1).max(160),candidates:o.z.array(X.refine(e=>"file"===e.kind||"directory"===e.kind,"File search only returns files and directories.")).max(200).default([]),complete:o.z.boolean().default(!1)}).strict(),se=o.z.object({message:o.z.string().trim().min(1).max(2e4),mode:F.optional(),permissionMode:B.optional()});se.extend({attachments:o.z.array(q).max(6).default([]),references:K}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>S&&t.addIssue({code:o.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})});const ne=se.extend({attachments:o.z.array(U).max(6).default([]),references:K}).strict(),ie=o.z.object({decision:o.z.enum(["allow","deny","skip"])}).strict();o.z.object({queueIds:o.z.array(o.z.string().trim().min(1).max(120)).max(100)}).strict(),o.z.object({code:o.z.string().trim().min(1).max(160),message:o.z.string(),instanceId:o.z.string().trim().min(1).max(160).optional(),nodeId:o.z.string().trim().min(1).max(160).optional(),route:o.z.string().trim().min(1).max(4096).optional()}).strict();const ae=o.z.object({id:o.z.string().trim().min(1).max(120),message:o.z.string().trim().min(1).max(2e4),attachments:o.z.array(L).max(6).default([]),references:K,permissionMode:B.optional(),status:o.z.enum(["queued","sending","failed"]).default("queued"),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime(),error:o.z.string().trim().max(4e3).optional()}).strict(),re=o.z.object({pendingCount:o.z.number().int().min(0).default(0),items:o.z.array(ae).max(100).default([])}).strict().default({pendingCount:0,items:[]}),oe=o.z.enum(["control","realtime","adapter-snapshot","transcript-tail","transcript-scan","process-scan","app-session"]),de=o.z.object({id:o.z.string().trim().min(1).max(240),status:o.z.enum(["running","completed"]),startedAt:o.z.string().datetime().optional(),completedAt:o.z.string().datetime().optional()}).strict(),ce=o.z.object({id:o.z.string().trim().min(1).max(240),providerTurnId:o.z.string().trim().max(240).optional(),source:oe.optional(),userPrompt:o.z.string().trim().optional(),status:o.z.enum(["queued","running","waiting","completed","failed"]).default("running"),phase:_.optional(),summary:o.z.string().trim().max(1e3).optional(),lastMessage:o.z.string().trim().optional(),lastMessageItemId:o.z.string().trim().max(240).optional(),contextCompactions:o.z.array(de).max(20).optional(),revision:o.z.number().int().min(0).default(0),sourcePriority:o.z.number().int().min(0).max(100).optional(),snapshotVersion:o.z.number().int().min(0).optional(),observedAt:o.z.string().datetime().optional(),startedAt:o.z.string().datetime().optional(),updatedAt:o.z.string().datetime().optional(),completedAt:o.z.string().datetime().optional()}).strict(),le=ce.pick({id:!0,providerTurnId:!0,userPrompt:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,contextCompactions:!0,startedAt:!0,updatedAt:!0,completedAt:!0}).strict(),pe=o.z.object({id:o.z.string().trim().min(1).max(120),agent:E,appSessionId:o.z.string().trim().max(120).optional(),appId:o.z.string().trim().max(120).optional(),providerSessionId:o.z.string().trim().max(240).optional(),providerMeta:o.z.record(o.z.string(),o.z.unknown()).optional(),appBindingKeys:o.z.array(o.z.string().trim().min(1).max(240)).max(20).optional(),actions:P.optional(),activeTurnId:o.z.string().trim().max(240).optional(),title:o.z.string().trim().max(240).optional(),cwd:o.z.string().trim().max(4096).optional(),userPrompt:o.z.string().trim().optional(),turns:o.z.array(ce).max(50).optional(),status:C.default("running"),phase:_.default("unknown"),summary:o.z.string().trim().max(1e3).optional(),lastMessage:o.z.string().trim().optional(),lastMessageItemId:o.z.string().trim().max(240).optional(),currentTool:x.optional(),toolCallsSinceLastMessage:o.z.number().int().min(0).default(0),subAgents:o.z.array(j).max(50).default([]),transcriptPath:o.z.string().trim().max(4096).optional(),transcriptSize:o.z.number().int().min(0).optional(),startedAt:o.z.string().datetime(),updatedAt:o.z.string().datetime(),completedAt:o.z.string().datetime().optional(),error:o.z.string().trim().max(4e3).optional(),counters:D,queue:re}).strict(),ue=o.z.object({session:pe,provider:o.z.string().trim().min(1).max(80),action:o.z.enum(["send","queue","steer","interrupt","approval"]),decision:o.z.enum(["allow","deny","skip"]).optional(),turnId:o.z.string().trim().min(1).max(240).optional(),providerTurnId:o.z.string().trim().min(1).max(240).optional(),queueId:o.z.string().trim().min(1).max(120).optional()}).strict(),me=o.z.object({id:o.z.string().trim().min(1).max(120),agent:o.z.enum(["codex","claude"]),providerSessionId:o.z.string().trim().min(1).max(240),title:o.z.string().trim().max(240).optional(),userPrompt:o.z.string().trim().optional(),lastMessage:o.z.string().trim().optional(),cwd:o.z.string().trim().min(1).max(4096),lastActiveAt:o.z.string().datetime(),archivedAt:o.z.string().datetime()}).strict();o.z.object({schemaVersion:o.z.literal(1).default(1),items:o.z.array(me).max(50).default([])}).strict();const ge=o.z.object({items:o.z.array(me).max(50).default([])}).strict(),he=o.z.object({item:me,turns:o.z.array(le).max(50).default([])}).strict(),Ie=o.z.object({disposition:o.z.enum(["resumed","already-open"]),aiSessionId:o.z.string().trim().min(1).max(120),providerSessionId:o.z.string().trim().min(1).max(240),appSessionId:o.z.string().trim().min(1).max(120)}).strict(),fe=pe.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}),ye=o.z.object({runningCount:o.z.number().int().min(0).default(0),waitingCount:o.z.number().int().min(0).default(0),staleCount:o.z.number().int().min(0).default(0),sessions:o.z.array(fe).default([]),updatedAt:o.z.string().datetime()}).strict(),be=o.z.object({streamId:o.z.string().trim().min(1).max(240),revision:o.z.number().int().min(0),lastEventAt:o.z.string().datetime(),snapshot:ye}).strict(),ve=ye.pick({runningCount:!0,waitingCount:!0,staleCount:!0,updatedAt:!0}).extend({idleCount:o.z.number().int().min(0).default(0),problemCount:o.z.number().int().min(0).default(0),revision:o.z.number().int().min(0).optional()});function we(e,t={}){const s=e.sessions.filter(e=>"idle"===e.status).length,n=e.sessions.filter(e=>"failed"===e.status).length;return ve.parse({runningCount:e.runningCount,waitingCount:e.waitingCount,staleCount:e.staleCount,idleCount:s,problemCount:n,updatedAt:e.updatedAt,revision:t.revision})}const Se=o.z.enum(["provider-event","app-session-created","app-session-updated","app-session-deleted","discovery-scan","control-action","heartbeat-sync","startup"]),Ae=o.z.object({streamId:o.z.string().trim().min(1).max(240),instanceId:o.z.string().trim().min(1).max(160),nodeId:o.z.string().trim().min(1).max(160).optional(),revision:o.z.number().int().min(0),previousRevision:o.z.number().int().min(0).optional(),traceId:o.z.string().trim().min(1).max(160),generatedAt:o.z.string().datetime(),reason:Se}).strict(),ke=o.z.object({meta:Ae,snapshot:ye}).strict(),Te=o.z.object({meta:Ae,upserted:o.z.array(fe).default([]),removed:o.z.array(o.z.string().trim().min(1).max(120)).default([])}).strict(),ze=o.z.object({meta:Ae,sessionIds:o.z.array(o.z.string().trim().min(1).max(120)).default([]),expiresAt:o.z.string().datetime()}).strict(),Ee=o.z.object({instanceId:o.z.string().trim().min(1).max(160),nodeId:o.z.string().trim().min(1).max(160).optional(),sessionId:o.z.string().trim().min(1).max(120),providerSessionId:o.z.string().trim().min(1).max(240),turnId:o.z.string().trim().min(1).max(240),itemId:o.z.string().trim().min(1).max(240),delta:o.z.string().min(1),generatedAt:o.z.string().datetime()}).strict(),Ce=o.z.object({streamId:o.z.string().trim().min(1).max(240),instanceId:o.z.string().trim().min(1).max(160),sinceRevision:o.z.number().int().min(0),latestRevision:o.z.number().int().min(0),earliestRetainedRevision:o.z.number().int().min(0),syncRequired:o.z.boolean().default(!1),events:o.z.array(o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal(A),payload:ke}).strict(),o.z.object({type:o.z.literal(k),payload:Te}).strict(),o.z.object({type:o.z.literal(T),payload:ze}).strict()])).default([])}).strict(),Ne=o.z.object({source:oe,sourcePriority:o.z.number().int().min(0).max(100).optional(),observedAt:o.z.string().datetime().optional(),snapshotVersion:o.z.number().int().min(0).optional()}).strict(),_e=Ne.extend({type:o.z.literal("snapshot"),agent:o.z.string().trim().min(1).max(80),appSessionId:o.z.string().trim().max(120).optional(),appId:o.z.string().trim().max(120).optional(),providerSessionId:o.z.string().trim().max(240).optional(),providerMeta:o.z.record(o.z.string(),o.z.unknown()).optional(),appBindingKeys:o.z.array(o.z.string().trim().min(1).max(240)).max(20).optional(),actions:P.optional(),title:o.z.string().trim().max(240).optional(),cwd:o.z.string().trim().max(4096).optional(),activeTurnId:o.z.string().trim().max(240).optional(),userPrompt:o.z.string().trim().optional(),turns:o.z.array(ce).max(50).optional(),status:C.optional(),phase:_.optional(),summary:o.z.string().trim().max(1e3).optional(),lastMessage:o.z.string().trim().optional(),lastMessageItemId:o.z.string().trim().max(240).optional(),currentTool:x.optional(),toolCallsSinceLastMessage:o.z.number().int().min(0).optional(),subAgents:o.z.array(j).max(50).optional(),transcriptPath:o.z.string().trim().max(4096).optional(),transcriptSize:o.z.number().int().min(0).optional(),replaceActivity:o.z.boolean().optional()}).strict(),xe=Ne.extend({type:o.z.literal("event"),sessionId:o.z.string().trim().min(1).max(120),kind:o.z.enum(["lifecycle","send-ack","turn-started","user-message","assistant-message","approval-requested","turn-completed","tool-activity","sub-agent-activity","context-compaction"]),activeTurnId:o.z.string().trim().max(240).optional(),providerTurnId:o.z.string().trim().max(240).optional(),userPrompt:o.z.string().trim().optional(),text:o.z.string().optional(),itemId:o.z.string().trim().max(240).optional(),status:C.optional(),phase:_.optional(),summary:o.z.string().trim().max(1e3).optional(),error:o.z.string().trim().max(4e3).optional(),currentTool:x.nullable().optional(),toolCallsSinceLastMessage:o.z.number().int().min(0).optional(),subAgents:o.z.array(j).max(50).optional(),contextCompaction:de.optional(),counters:o.z.object({toolCalls:o.z.number().int().min(0).optional(),edits:o.z.number().int().min(0).optional(),approvals:o.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"})});o.z.discriminatedUnion("type",[_e,xe]);const Oe=o.z.union([o.z.object({type:o.z.literal("schedule"),scheduleKind:o.z.literal("interval").default("interval"),intervalMs:o.z.number().int().min(1e3)}).strict(),o.z.object({type:o.z.literal("schedule"),scheduleKind:o.z.literal("daily"),timeOfDay:o.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:o.z.string().trim().min(1).max(120)}).strict(),o.z.object({type:o.z.literal("schedule"),scheduleKind:o.z.literal("weekly"),weekdays:o.z.array(o.z.number().int().min(0).max(6)).min(1).max(7),timeOfDay:o.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:o.z.string().trim().min(1).max(120)}).strict()]),Re=o.z.union([Oe,o.z.object({type:o.z.literal("file-change"),roots:o.z.array(o.z.string().trim().min(1).max(4096)).min(1).max(50),globs:o.z.array(o.z.string().trim().min(1).max(500)).min(1).max(100),ignore:o.z.array(o.z.string().trim().min(1).max(500)).max(100).optional(),debounceMs:o.z.number().int().min(100).max(6e4).default(1500)}).strict(),o.z.object({type:o.z.literal("ai-session"),agent:o.z.string().trim().min(1).max(80).optional(),statuses:o.z.array(o.z.enum(["running","waiting","idle","failed"])).max(20).optional(),phases:o.z.array(o.z.enum(["thinking","tool","editing","approval","responding","unknown"])).max(20).optional()}).strict()]),je=o.z.object({promptTemplate:o.z.string().trim().min(1).max(2e4)}).strict(),De=o.z.object({cooldownMs:o.z.number().int().min(0).max(864e5).optional(),maxConcurrentRuns:o.z.number().int().min(1).max(20).default(1),whenBusy:o.z.enum(["skip","queue"]).default("skip")}).strict(),Pe=o.z.object({configHash:o.z.string().trim().min(8).max(80),name:o.z.string().trim().min(1).max(160),description:o.z.string().trim().max(1e3).optional(),source:Re,action:je,policy:De.default({maxConcurrentRuns:1,whenBusy:"skip"}),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict(),Me=o.z.object({type:o.z.literal("ai-session"),aiSessionId:o.z.string().trim().min(1).max(160)}).strict(),$e=o.z.object({configHash:o.z.string().trim().min(8).max(80),deploymentId:o.z.string().trim().min(1).max(240).optional(),instanceId:o.z.string().trim().min(1).max(160),origin:o.z.enum(["control-plane","controlled-instance"]).default("controlled-instance"),enabled:o.z.boolean().default(!0),target:Me,localName:o.z.string().trim().min(1).max(160).optional(),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict(),qe=o.z.object({configHash:o.z.string().trim().min(8).max(80),deploymentId:o.z.string().trim().min(1).max(240).optional(),instanceId:o.z.string().trim().min(1).max(160),status:o.z.enum(["idle","running","disabled","error"]).default("idle"),lastTriggeredAt:o.z.string().datetime().optional(),lastCompletedAt:o.z.string().datetime().optional(),lastSkippedAt:o.z.string().datetime().optional(),lastError:o.z.string().trim().max(4e3).optional(),runCount:o.z.number().int().min(0).default(0),skippedCount:o.z.number().int().min(0).default(0)}).strict(),Le=o.z.object({id:o.z.string().trim().min(1).max(160),configHash:o.z.string().trim().min(8).max(80),deploymentId:o.z.string().trim().min(1).max(240).optional(),instanceId:o.z.string().trim().min(1).max(160),eventType:o.z.enum(["manual","schedule","file-change","ai-session"]),status:o.z.enum(["started","completed","failed","skipped"]),target:Me,promptPreview:o.z.string().trim().max(500),eventSummary:o.z.string().trim().max(1e3).optional(),error:o.z.string().trim().max(4e3).optional(),startedAt:o.z.string().datetime(),completedAt:o.z.string().datetime().optional()}).strict(),Ue=o.z.object({schemaVersion:o.z.literal(1).default(1),configs:o.z.array(Pe).default([]),deployments:o.z.array($e).default([]),runtime:o.z.array(qe).default([]),recentRuns:o.z.array(Le).default([])}).strict();function Fe(e){return Array.isArray(e)?e.map(Fe):e&&"object"==typeof e?Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>[e,Fe(t)])):e}const Be="2026-07-26",He=o.z.string(),Ge=o.z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),We=o.z.string().datetime(),Ve=o.z.record(o.z.string(),o.z.string()).default({}),Je=o.z.record(o.z.string(),o.z.string()).default({}),Ke=/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/,Qe=/^sha256:([a-fA-F0-9]{64})$/,Xe=/^(?:localhost|[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?)(?::[1-9][0-9]{0,4})?$/,Ye=/^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*$/;function Ze(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?Qe.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&&!Ke.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&&!Xe.test(p[0].toLowerCase()))throw new Error("Docker image registry host or port is invalid.");if(!m.length||m.some(e=>!Ye.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}`:""}`}const et=o.z.string().trim().min(1).max(512).transform((e,t)=>{try{return Ze(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),o.z.NEVER}}),tt=o.z.string().trim().transform((e,t)=>{const s=Qe.exec(e);return s?`sha256:${s[1].toLowerCase()}`:(t.addIssue({code:"custom",message:"Docker image digest must use sha256 followed by 64 hexadecimal characters."}),o.z.NEVER)}),st=o.z.object({codexModelHash:Ge.nullable().optional(),claudeModelHash:Ge.nullable().optional()}).strict().default({}),nt=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(120),kind:o.z.enum(["tty","gui","web"]),source:o.z.enum(["builtin","custom"]),availability:o.z.enum(["available","missing-dependency"]),capabilities:o.z.object({automation:o.z.enum(["cdp"]).optional(),supportsCwdSelection:o.z.boolean().default(!1)}).strict().default({supportsCwdSelection:!1}),diagnosticCode:o.z.enum(["APP_EXECUTABLE_NOT_FOUND"]).optional()}).strict(),it=o.z.object({code:o.z.enum(["APP_CATALOG_INVALID"]),message:o.z.string().trim().min(1).max(500)}).strict(),at=o.z.object({items:o.z.array(nt).max(256),observedAt:We,issues:o.z.array(it).max(32).default([])}).strict(),rt=o.z.enum(["linux","darwin","win32","freebsd","openbsd","aix","sunos","unknown"]),ot=o.z.enum(["x64","arm64","arm","ia32","ppc64","s390x","riscv64","unknown"]),dt=o.z.enum(["apt","dnf","brew","npm"]),ct=o.z.enum(["user","passwordless-sudo","root"]),lt=o.z.object({platform:rt,arch:ot,installers:o.z.array(dt).max(8),privilege:ct,installerAccess:o.z.object({npmGlobalWritable:o.z.boolean().optional()}).strict().optional()}).strict(),pt=o.z.enum(["installed","not-installed","broken","unsupported"]),ut=o.z.enum(["recipe","bundled","external","none"]),mt=o.z.object({code:o.z.enum(["BUNDLED","EXTERNALLY_MANAGED","ALREADY_INSTALLED","NOT_INSTALLED","UNSUPPORTED_PLATFORM","INSTALLER_UNAVAILABLE","INSTALLER_NOT_WRITABLE","INSUFFICIENT_PRIVILEGE","OPERATION_IN_PROGRESS"]),message:o.z.string().trim().min(1).max(500)}).strict(),gt=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(120),kind:o.z.enum(["tty","gui","web"]),description:o.z.string().trim().max(500).optional(),state:pt,managementSource:ut,version:o.z.string().trim().max(120).optional(),canInstall:o.z.boolean(),canUninstall:o.z.boolean(),installReason:mt.optional(),uninstallReason:mt.optional(),activeJobId:Ge.optional()}).strict(),ht=o.z.enum(["install","uninstall"]),It=o.z.enum(["queued","running","succeeded","failed","cancelled","interrupted"]),ft=o.z.object({current:o.z.number().finite().nonnegative().optional(),total:o.z.number().finite().positive().optional(),unit:o.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"]}),yt=o.z.object({executable:o.z.string().trim().min(1).max(4096),args:o.z.array(o.z.string().max(4096)).max(128)}).strict(),bt=o.z.object({code:o.z.string().trim().min(1).max(120).regex(/^[a-z0-9][a-z0-9._-]*$/),message:o.z.string().trim().min(1).max(4096),retryable:o.z.boolean(),sessionIds:o.z.array(Ge).max(64).optional()}).strict(),vt=o.z.object({id:Ge,requestId:Ge.optional(),appId:Ge,operation:ht,state:It,phase:o.z.string().trim().min(1).max(120).optional(),progress:ft.optional(),command:yt.optional(),logTail:o.z.string().max(32768).optional(),logTruncated:o.z.boolean().optional(),error:bt.optional(),requestedAt:We,startedAt:We.optional(),finishedAt:We.optional(),updatedAt:We}).strict(),wt=o.z.object({streamId:Ge,sequence:o.z.number().int().nonnegative(),capabilities:lt,apps:o.z.array(gt).max(256),activeJobs:o.z.array(vt).max(256),recentJobs:o.z.array(vt).max(256),observedAt:We}).strict(),St=o.z.object({requestId:Ge.optional()}).strict(),At=o.z.object({job:vt}).strict();o.z.object({type:o.z.literal("app-management"),streamId:Ge,sequence:o.z.number().int().nonnegative(),observedAt:We,job:vt.optional(),snapshot:wt.optional()}).strict().refine(e=>void 0!==e.job||void 0!==e.snapshot,{message:"An app management event requires a job or snapshot."});const kt=o.z.object({component:o.z.enum(["control-plane","node-agent","controlled-instance"]),packageName:o.z.string().trim().max(160).optional(),packageVersion:o.z.string().trim().max(80).optional(),protocolVersion:He.optional(),buildId:o.z.string().trim().max(160).optional(),builtAt:o.z.string().trim().max(120).optional(),gitCommit:o.z.string().trim().max(120).optional(),imageRef:o.z.string().trim().max(512).optional(),imageDigest:o.z.string().trim().max(240).optional()}).strict(),Tt=o.z.enum(["stable","beta","alpha"]),zt=o.z.discriminatedUnion("component",[o.z.object({component:o.z.literal("node-agent")}).strict(),o.z.object({component:o.z.literal("controlled-instance"),instanceId:Ge}).strict()]),Et=o.z.object({target:zt,channel:Tt.default("stable")}).strict(),Ct=o.z.object({target:zt,source:o.z.enum(["npm","docker-registry"]),channel:Tt,currentVersion:o.z.string().trim().max(240).optional(),availableVersion:o.z.string().trim().min(1).max(240),artifactRef:o.z.string().trim().min(1).max(512).optional(),updateAvailable:o.z.boolean(),supported:o.z.boolean().default(!0),reason:o.z.string().trim().max(2048).optional(),checkedAt:We}).strict(),Nt=o.z.object({id:Ge,nodeId:Ge,target:zt,source:o.z.enum(["npm","docker-registry"]),channel:Tt,fromVersion:o.z.string().trim().max(240).optional(),toVersion:o.z.string().trim().min(1).max(240),artifactRef:o.z.string().trim().min(1).max(512).optional(),status:o.z.enum(["queued","updating","restarting","succeeded","failed"]),error:o.z.string().trim().max(4096).optional(),startedAt:We.optional(),completedAt:We.optional(),createdAt:We,updatedAt:We}).strict(),_t=o.z.object({type:o.z.enum(["branch","tag","commit"]),name:o.z.string().trim().min(1).max(240).optional(),commit:o.z.string().trim().max(80).optional()}).strict(),xt=o.z.object({type:o.z.enum(["none","ssh-key","https-token"]).default("none"),secretId:o.z.string().trim().min(1).max(120).optional()}).strict(),Ot=o.z.object({depth:o.z.number().int().positive().max(1e5).optional(),submodules:o.z.boolean().default(!1),lfs:o.z.boolean().default(!1),subdirectory:o.z.string().trim().max(240).default("")}).strict();o.z.object({id:Ge,name:o.z.string().trim().min(1).max(160),url:o.z.string().trim().min(1).max(2048),provider:o.z.string().trim().max(80).optional(),ref:_t.default({type:"branch",name:"main"}),auth:xt.default({type:"none"}),clone:Ot.default({submodules:!1,lfs:!1,subdirectory:""}),defaultImageId:Ge.optional(),labels:Ve,createdAt:We,updatedAt:We}).strict();const Rt=o.z.object({id:Ge,nodeId:Ge,name:o.z.string().trim().min(1).max(160),path:o.z.string().trim().min(1).max(4096),defaultImageId:Ge.optional(),labels:Ve,createdAt:We,updatedAt:We}).strict(),jt=o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal("git-repository"),repositoryId:Ge.optional(),url:o.z.string().trim().min(1).max(2048),provider:o.z.string().trim().max(80).optional(),ref:_t.default({type:"branch",name:"main"}),auth:xt.default({type:"none"}),clone:Ot.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),o.z.object({type:o.z.literal("git-template"),url:o.z.string().trim().min(1).max(2048),templateId:o.z.string().trim().min(1).max(120).optional(),ref:_t.default({type:"branch",name:"main"}),auth:xt.default({type:"none"}),clone:Ot.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),o.z.object({type:o.z.literal("local-folder"),localFolderId:Ge.optional(),path:o.z.string().trim().min(1).max(4096),ownerNodeId:Ge.optional()}).strict()]),Dt=o.z.object({mode:o.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]),path:o.z.string().trim().min(1).max(4096).default("/workspace"),volumeName:o.z.string().trim().max(240).optional(),readOnly:o.z.boolean().default(!1)}).strict(),Pt=o.z.enum(["codex","claude"]),Mt=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(160),source:jt,defaultImageId:Ge.optional(),defaultNodeId:Ge.optional(),defaultRuntimeId:Ge.optional(),workspacePolicy:Dt,labels:Ve,createdAt:We,updatedAt:We}).strict(),$t=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(160),endpoint:o.z.string().trim().min(1).max(2048),key:o.z.string().trim().min(1).max(4096),model:o.z.string().trim().min(1).max(240),app:Pt,enabled:o.z.boolean().default(!0),order:o.z.number().int().min(0).max(1e6).default(0),labels:Ve,createdAt:We,updatedAt:We}).strict(),qt=$t.omit({key:!0}).extend({keyPreview:o.z.string().trim().min(1).max(160),keySet:o.z.boolean()}).strict(),Lt=qt.extend({referenceCount:o.z.number().int().min(0)}).strict(),Ut=o.z.object({instanceId:Ge,codexModelHash:Ge.optional(),claudeModelHash:Ge.optional(),updatedAt:We}).strict(),Ft=o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal("control-plane"),name:$t.shape.name,enabled:$t.shape.enabled,order:$t.shape.order}).strict(),o.z.object({type:o.z.literal("node"),nodeId:Ge,name:$t.shape.name,enabled:$t.shape.enabled,order:$t.shape.order,referenceCount:o.z.number().int().min(0)}).strict()]),Bt=o.z.object({id:Ge,model:qt,locations:o.z.array(Ft).min(1),referenceCount:o.z.number().int().min(0).default(0)}).strict(),Ht=o.z.object({nodeId:Ge,code:o.z.string().trim().min(1).max(120),message:o.z.string().trim().min(1).max(2e3)}).strict(),Gt=o.z.object({models:o.z.array(Bt),nodeDiagnostics:o.z.array(Ht).default([]),updatedAt:We}).strict();function Wt(e){const t={app:Pt.parse(e.app),endpoint:$t.shape.endpoint.parse(e.endpoint),key:$t.shape.key.parse(e.key),model:$t.shape.model.parse(e.model)};return`mdl_${d.createHash("sha256").update(JSON.stringify(t)).digest("hex")}`}$t.omit({id:!0,createdAt:!0,updatedAt:!0}).strict().partial().strict(),o.z.object({modelSelection:st,codexModelHash:Ge.optional(),claudeModelHash:Ge.optional()}).strict();const Vt=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(160),reference:et,pullPolicy:o.z.literal("if-not-present").default("if-not-present"),capabilities:o.z.array(o.z.string().trim().min(1).max(80)).default([]),optionalApps:o.z.array(o.z.string().trim().min(1).max(120)).default([]),defaultEnv:Je,labels:Ve,createdAt:We,updatedAt:We}).strict(),Jt=Vt.omit({reference:!0}).extend({requestedReference:et,resolvedDigest:tt.optional(),resolvedReference:et.optional()}).strict(),Kt=o.z.object({phase:o.z.enum(["checking-image","pulling-image","resolving-image","ready","failed"]),requestedReference:et,generation:o.z.number().int().nonnegative().default(0),error:o.z.string().trim().max(4096).optional(),startedAt:We,updatedAt:We}).strict(),Qt="image.pull.terminal.output",Xt="image.pull.terminal.finished",Yt=o.z.object({instanceId:Ge,generation:o.z.number().int().nonnegative(),requestedReference:et,sequence:o.z.number().int().nonnegative(),observedAt:We}).strict(),Zt=Yt.extend({data:o.z.string().min(1).max(65536),replay:o.z.boolean().optional()}).strict(),es=Yt.extend({outcome:o.z.enum(["succeeded","failed"])}).strict(),ts=Yt.extend({status:o.z.enum(["connecting","pulling","extracting","complete","failed"]),layers:o.z.object({total:o.z.number().int().nonnegative(),completed:o.z.number().int().nonnegative(),downloaded:o.z.number().int().nonnegative(),downloading:o.z.number().int().nonnegative(),extracting:o.z.number().int().nonnegative()}).strict(),bytes:o.z.object({current:o.z.number().finite().nonnegative(),total:o.z.number().finite().positive()}).strict().optional(),percent:o.z.number().finite().min(0).max(100).optional(),message:o.z.string().trim().min(1).max(500),terminalTail:o.z.string().max(262144).optional(),terminalTruncated:o.z.boolean().optional()}).strict();function ss(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const s=e,n="string"==typeof s.id?s.id:void 0,i="string"==typeof s.image?s.image:void 0;void 0!==i&&t?.({imageId:n,field:"image"}),Object.prototype.hasOwnProperty.call(s,"registry")&&t?.({imageId:n,field:"registry"});let a="string"==typeof s.reference?s.reference:i;if(a)try{a=Ze(a)}catch{if(!a.includes("@")&&a.lastIndexOf(":")<=a.lastIndexOf("/"))try{a=Ze(`${a}:latest`),t?.({imageId:n,field:"reference:implicit-latest"})}catch{}}return{id:s.id,name:s.name,reference:a,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=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(160),connectionMode:o.z.enum(["local-ipc","local-loopback","direct-http","reverse-wss"]).default("direct-http"),auth:o.z.object({mode:o.z.enum(["local-static-key","paired-hmac"]).default("local-static-key"),keyId:Ge.optional(),secret:o.z.string().trim().max(4096).optional(),pairedAt:We.optional(),pairing:o.z.object({status:o.z.enum(["pending","paired","expired"]).default("pending"),joinToken:o.z.string().trim().max(4096).optional(),expiresAt:We.optional()}).strict().optional()}).strict().default({mode:"local-static-key"}),endpoint:o.z.string().trim().max(2048).optional(),controlEndpoint:o.z.string().trim().max(2048).optional(),containerEndpoint:o.z.string().trim().max(2048).optional(),publicWebBase:o.z.string().trim().max(2048).optional(),status:o.z.enum(["unknown","online","offline","degraded"]).default("unknown"),health:o.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({}),appInventory:at.optional(),labels:Ve,lastSeenAt:We.optional(),createdAt:We,updatedAt:We}).strict(),is=o.z.object({id:Ge,nodeId:Ge,name:o.z.string().trim().min(1).max(160),type:o.z.enum(["docker","kubernetes","local"]),status:o.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:o.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({}),labels:Ve,createdAt:We,updatedAt:We}).strict(),as=o.z.object({instanceId:Ge,runtimeKind:o.z.literal("docker"),state:o.z.enum(["pending","available","stopped","unavailable"]),sampledAt:We,cpu:o.z.object({usagePercent:o.z.number().nonnegative()}).strict().optional(),memory:o.z.object({usageBytes:o.z.number().nonnegative(),limitBytes:o.z.number().nonnegative().optional(),usagePercent:o.z.number().nonnegative().optional()}).strict().optional(),network:o.z.object({rxBytes:o.z.number().nonnegative(),txBytes:o.z.number().nonnegative()}).strict().optional(),blockIo:o.z.object({readBytes:o.z.number().nonnegative(),writeBytes:o.z.number().nonnegative()}).strict().optional(),pids:o.z.number().int().nonnegative().optional(),error:o.z.string().trim().max(2048).optional()}).strict(),rs="instance.metrics.snapshot",os=o.z.object({ok:o.z.boolean().optional(),role:o.z.string().optional(),nodeId:Ge.optional(),platform:rt.optional()}).passthrough(),ds=o.z.object({bindScope:o.z.enum(["loopback","all-ipv4"]),port:o.z.number().int().min(1).max(65535)}).strict().extend({host:o.z.enum(["127.0.0.1","0.0.0.0"]),status:o.z.enum(["listening","error"]),source:o.z.enum(["bootstrap","persisted"]),error:o.z.string().trim().max(2048).optional()}).strict(),cs=o.z.object({nodeId:Ge,joinToken:o.z.string().trim().min(1),expiresAt:We}).passthrough(),ls=o.z.object({id:Ge,keyId:Ge,url:o.z.string().trim().min(1).max(2048).optional(),name:o.z.string().trim().min(1).max(160).optional(),pairedAt:We,updatedAt:We.optional(),active:o.z.boolean().optional(),current:o.z.boolean().optional()}).passthrough(),ps=o.z.object({remote:o.z.object({id:Ge,url:o.z.string().trim().min(1).max(2048).optional(),keyId:Ge,pairedAt:We,active:o.z.boolean()}).passthrough(),tunnel:o.z.object({status:o.z.enum(["disabled","saved","connecting","connected","failed"]),error:o.z.string().trim().max(2048).optional()}).passthrough()}).passthrough(),us=o.z.object({deleted:o.z.boolean().optional()}).passthrough(),ms=o.z.object({repository:o.z.string().trim().optional(),tag:o.z.string().trim().optional(),id:o.z.string().trim(),createdSince:o.z.string().trim().optional(),size:o.z.string().trim().optional(),reference:o.z.string().trim().min(1),repoDigests:o.z.array(o.z.string().trim().min(1).max(512)).default([])}).passthrough(),gs=o.z.object({image:Vt,status:o.z.enum(["available","pull-required","unknown"]),localImage:ms.optional(),error:o.z.string().trim().max(2048).optional()}).strict(),hs=o.z.lazy(()=>o.z.object({name:o.z.string().trim().min(1),path:o.z.string().trim().min(1).max(4096),children:o.z.array(hs)}).passthrough()),Is=o.z.object({status:o.z.number().int().min(100).max(599).optional(),headers:o.z.record(o.z.string(),o.z.string()).optional(),bodyBase64:o.z.string().optional()}).passthrough(),fs=o.z.object({strategy:o.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),web:o.z.string().trim().max(2048).optional(),api:o.z.string().trim().max(2048).optional(),vnc:o.z.string().trim().max(2048).optional(),tty:o.z.string().trim().max(2048).optional(),logs:o.z.string().trim().max(2048).optional(),status:o.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict(),ys=o.z.object({strategy:o.z.enum(["control-plane-proxy","direct-port","node-proxy","kubernetes-ingress","kubernetes-port-forward"]).default("control-plane-proxy"),web:o.z.string().trim().max(2048).optional(),api:o.z.string().trim().max(2048).optional(),ws:o.z.string().trim().max(2048).optional(),status:o.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict().default({strategy:"control-plane-proxy",status:"unknown"}),bs=o.z.object({mode:o.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]).optional(),status:o.z.enum(["unknown","pending","ready","failed"]).default("unknown"),path:o.z.string().trim().max(4096).optional(),resolvedCommit:o.z.string().trim().max(120).optional(),error:o.z.string().trim().max(2048).optional()}).passthrough(),vs=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(160),projectId:Ge.optional(),source:jt,sourceSnapshot:o.z.record(o.z.string(),o.z.unknown()).default({}),modelSelection:st,nodeId:Ge,runtimeId:Ge,imageId:Ge.optional(),imageSnapshot:Jt.optional(),imageProvisioning:Kt.optional(),stateRevision:o.z.number().int().min(0).default(0),status:o.z.enum(["created","provisioning","starting","registering","registered","running","stopping","stopped","failed","unhealthy"]).default("created"),health:o.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),connectionStatus:o.z.enum(["unknown","online","offline","endpoint-unreachable"]).default("unknown"),agentStatus:o.z.enum(["unknown","online","offline"]).default("unknown"),targetStatus:o.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),uiAccessStatus:o.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),controlMode:o.z.enum(["standalone","controlled"]).default("controlled"),protocolVersion:He.optional(),instanceVersion:o.z.string().trim().max(80).optional(),build:kt.optional(),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({}),appInventory:at.optional(),config:o.z.object({autoImportAgentConfigs:o.z.boolean().default(!0),defaultCodexPermissionMode:B.default("ask")}).strict().default({autoImportAgentConfigs:!0,defaultCodexPermissionMode:"ask"}),workspace:bs.default({status:"unknown"}),target:fs.default({strategy:"direct-port",status:"unknown"}),access:ys,apps:o.z.object({runningCount:o.z.number().int().min(0).default(0),problemCount:o.z.number().int().min(0).default(0),updatedAt:We.optional(),revision:o.z.number().int().min(0).optional()}).strict().default({runningCount:0,problemCount:0}),aiSessions:ye.default(()=>({runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:(new Date).toISOString()})),triggers:o.z.object({enabledCount:o.z.number().int().min(0).default(0),runningCount:o.z.number().int().min(0).default(0),errorCount:o.z.number().int().min(0).default(0),configs:o.z.array(o.z.object({configHash:o.z.string().trim().min(1),config:Pe,deployments:o.z.array($e).default([]),runtime:o.z.array(qe).default([])}).strict()).default([]),recentRuns:o.z.array(Le).default([])}).strict().optional(),runtime:o.z.object({kind:o.z.string().trim().max(80).optional(),containerName:o.z.string().trim().max(240).optional(),containerId:o.z.string().trim().max(240).optional(),workspacePath:o.z.string().trim().max(4096).optional(),pid:o.z.number().int().positive().optional(),port:o.z.number().int().positive().max(65535).optional(),labels:o.z.record(o.z.string(),o.z.string()).default({})}).strict().default({labels:{}}),registrationToken:o.z.string().trim().min(1).max(240).optional(),lastHeartbeatAt:We.optional(),createdAt:We,updatedAt:We}).strict(),ws="instance.lifecycle.snapshot",Ss=o.z.object({instanceId:vs.shape.id,revision:o.z.number().int().min(0),updatedAt:We,status:vs.shape.status.unwrap(),health:vs.shape.health.unwrap(),connectionStatus:vs.shape.connectionStatus.unwrap(),accessStatus:o.z.enum(["reachable","endpoint-unreachable"]),imageProvisioning:Kt.optional(),workspace:bs,runtime:vs.shape.runtime.unwrap(),lastHeartbeatAt:We.optional()}).strict();function As(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]]))}o.z.object({instanceId:Ge.optional(),name:o.z.string().trim().min(1).max(160),projectId:Ge.optional(),source:jt.optional(),nodeId:Ge.optional(),runtimeId:Ge.optional(),imageId:Ge.optional(),instanceVersion:o.z.string().trim().max(80).optional(),protocolVersion:He,build:kt.optional(),controlMode:o.z.enum(["standalone","controlled"]).default("controlled"),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({}),appInventory:at.optional(),target:fs.default({strategy:"direct-port",status:"unknown"}),workspace:bs.default({status:"unknown"}),registrationToken:o.z.string().trim().max(240).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==Be||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol register payload requires appInventory."})}),o.z.object({status:vs.shape.status.optional(),health:vs.shape.health.optional(),protocolVersion:He,build:kt.optional(),capabilities:vs.shape.capabilities.optional(),appInventory:at.optional(),apps:vs.shape.apps.optional(),aiSessions:vs.shape.aiSessions.optional(),triggers:vs.shape.triggers.optional(),workspace:bs.optional(),target:fs.partial().optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==Be||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol heartbeat payload requires appInventory."})});const ks=o.z.enum(["web","telegram","wechat","dingding"]),Ts=o.z.object({id:Ge,channel:ks,name:o.z.string().trim().min(1).max(160),enabled:o.z.boolean().default(!1),token:o.z.string().trim().max(4096).optional(),defaultChatId:o.z.string().trim().max(240).optional(),allowedUserIds:o.z.array(o.z.string().trim().min(1).max(240)).default([]),pollIntervalMs:o.z.number().int().positive().max(6e4).default(3e3),settings:o.z.record(o.z.string(),o.z.unknown()).default({}),createdAt:We,updatedAt:We}).strict(),zs=o.z.object({id:Ge,channel:ks,bridgeId:Ge.optional(),chatSessionId:o.z.string().trim().min(1).max(240),userId:o.z.string().trim().max(240).optional(),activeProjectId:Ge.optional(),activeInstanceId:Ge.optional(),activeAiSessionId:o.z.string().trim().min(1).max(120).optional(),lastUsedAt:We,createdAt:We,updatedAt:We}).strict(),Es=o.z.object({source:o.z.object({type:o.z.literal("chat-gateway").default("chat-gateway"),channel:ks,bridgeId:Ge.optional(),chatSessionId:o.z.string().trim().min(1).max(240),userId:o.z.string().trim().max(240).optional()}).strict(),message:o.z.object({text:o.z.string().trim().max(2e4).default(""),attachments:o.z.array(q).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>S&&t.addIssue({code:o.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})}),target:o.z.object({instanceId:Ge,aiSessionId:o.z.string().trim().min(1).max(120)}).strict().optional()}).strict(),Cs=o.z.object({id:Ge,instanceId:Ge,projectId:Ge.optional(),aiSessionId:o.z.string().trim().min(1).max(120),providerSessionId:o.z.string().trim().max(240).optional(),result:o.z.string().default(""),timeoutMs:o.z.number().positive().optional(),source:o.z.string().trim().max(120).optional(),kind:o.z.enum(["task","approval"]).default("task"),status:o.z.enum(["pending","unreachable"]).default("pending"),error:o.z.string().trim().max(2048).optional(),lastSeenAt:We}).strict();function Ns(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":"system"}const _s=o.z.enum(["ai.sessions","app.sessions"]),xs=o.z.object({topic:_s,instanceId:o.z.string().trim().min(1).max(160),streamId:o.z.string().trim().min(1).max(240),latestRevision:o.z.number().int().min(0),earliestRetainedRevision:o.z.number().int().min(0)}).strict(),Os=o.z.object({protocolVersion:o.z.literal(1),streams:o.z.array(xs)}).strict();function Rs(e){t.mkdirSync(e,{recursive:!0})}function js(e,t){Rs(s.dirname(e)),l.sync(e,`${JSON.stringify(t,null,2)}\n`,{encoding:"utf8"})}function Ds(e,t){console.warn(JSON.stringify({message:e,...t}))}function Ps(e){return e&&"object"==typeof e&&"issues"in e?{issues:e.issues}:{error:e instanceof Error?e.message:String(e)}}function Ms(e,s){const n=s.logger||Ds;let i;try{i=JSON.parse(t.readFileSync(e,"utf8"))}catch(t){return void n("stored JSON could not be read",{filePath:e,...Ps(t)})}if(!s.schema)return i;const a=s.sanitize?s.sanitize(i):i,r=s.schema.safeParse(a);if(r.success)return r.data;const o="strip"in s.schema&&"function"==typeof s.schema.strip?s.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 $s(e){return`${e}_${d.randomUUID().replace(/-/g,"").slice(0,20)}`}function qs(){return d.randomBytes(24).toString("base64url")}class Ls{directory;options;constructor(e,t={}){this.directory=e,this.options=t}init(){Rs(this.directory)}filePath(e){return s.join(this.directory,`${e}.json`)}list(){return this.init(),t.readdirSync(this.directory).filter(e=>e.endsWith(".json")).sort().flatMap(e=>{const t=Ms(s.join(this.directory,e),this.options);return t?[t]:[]})}get(e){const s=this.filePath(e);return t.existsSync(s)?Ms(s,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 js(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 s=this.filePath(e);return!!t.existsSync(s)&&(t.unlinkSync(s),!0)}}class Us{filePathValue;defaults;options;constructor(e,t,s={}){this.filePathValue=e,this.defaults=t,this.options=s}init(){Rs(s.dirname(this.filePathValue)),t.existsSync(this.filePathValue)||this.put(this.defaults())}get(){return this.init(),t.existsSync(this.filePathValue)?Ms(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 js(this.filePathValue,s),s}}const Fs=c.promisify(d.scrypt),Bs="task_handoff_cp_session",Hs=o.z.enum(["disabled","password"]),Gs=o.z.string().trim().min(1).max(80).regex(/^[a-zA-Z0-9_.@-]+$/),Ws=o.z.string().min(8).max(4096),Vs=o.z.object({username:Gs,password:Ws}).strict(),Js=o.z.object({username:o.z.string().trim().min(1).max(80),password:o.z.string().min(1).max(4096)}).strict(),Ks=o.z.object({id:o.z.string().trim().min(1),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}),Qs=Ks.extend({username:Gs,passwordHash:o.z.string().trim().min(1),role:o.z.enum(["viewer","operator","admin"]),lastLoginAt:o.z.string().datetime().optional()}).strict(),Xs=Ks.extend({userId:o.z.string().trim().min(1),tokenHash:o.z.string().trim().min(1),expiresAt:o.z.string().datetime(),lastSeenAt:o.z.string().datetime().optional()}).strict();function Ys(){return(new Date).toISOString()}function Zs(e){return d.createHash("sha256").update(e).digest("base64url")}async function en(e){const t=d.randomBytes(16).toString("base64url");return`scrypt$${t}$${(await Fs(e,t,64)).toString("base64url")}`}function tn(e){return{id:e.id,username:e.username,role:e.role,createdAt:e.createdAt,updatedAt:e.updatedAt,lastLoginAt:e.lastLoginAt}}class sn{mode;users;sessions;constructor(e,t={}){this.mode=function(e={}){return Hs.parse(e.mode||process.env.TASK_HANDOFF_CONTROL_PLANE_AUTH_MODE||"disabled")}(t),this.users=new Ls(e.authUsersDir,{schema:Qs}),this.sessions=new Ls(e.authSessionsDir,{schema:Xs})}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=Vs.parse(e),s=Ys();return tn(this.users.put({id:$s("user"),username:t.username,passwordHash:await en(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=Js.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 Fs(e,n,64),r=Buffer.from(i,"base64url");return r.length===a.length&&d.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=Ys(),i=qs(),a=new Date(Date.now()+12096e5).toISOString(),r=this.sessions.put({id:$s("sess"),userId:s.id,tokenHash:Zs(i),expiresAt:a,createdAt:n,updatedAt:n,lastSeenAt:n});return this.users.put({...s,lastLoginAt:n,updatedAt:n}),{user:tn({...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<=Ys()||n.tokenHash!==Zs(s))return;const i=this.users.get(n.userId);return i?(this.sessions.put({...n,lastSeenAt:Ys(),updatedAt:Ys()}),tn(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 nn="app-session.snapshot",an="app-session.patch",rn="app-session.removed",on=o.z.enum(["created","starting","running","stopping","stopped","exited","failed","closed","terminated","unknown"]),dn=o.z.object({type:o.z.enum(["app-session","provider-session","adapter-key"]),id:o.z.string().trim().min(1).max(240),agent:o.z.string().trim().min(1).max(80).optional(),adapter:o.z.string().trim().min(1).max(80).optional(),key:o.z.string().trim().min(1).max(240).optional()}).strict(),cn=o.z.record(o.z.string(),o.z.unknown()).and(o.z.object({id:o.z.string().trim().min(1).max(160),appId:o.z.string().trim().min(1).max(160).optional(),title:o.z.string().trim().max(240).optional(),kind:o.z.string().trim().max(80).optional(),status:on.default("unknown"),bindings:o.z.array(dn).max(40).default([]),workspace:o.z.object({cwd:o.z.string().trim().max(4096).optional()}).strict().optional(),createdAt:o.z.string().datetime().optional(),updatedAt:o.z.string().datetime().optional()})),ln=o.z.object({runningCount:o.z.number().int().min(0).default(0),problemCount:o.z.number().int().min(0).default(0),sessions:o.z.array(cn).default([]),updatedAt:o.z.string().datetime()}).strict(),pn=o.z.object({streamId:o.z.string().trim().min(1).max(240),revision:o.z.number().int().min(0),lastEventAt:o.z.string().datetime(),snapshot:ln}).strict();ln.pick({runningCount:!0,problemCount:!0,updatedAt:!0}).extend({revision:o.z.number().int().min(0).optional()});const un=o.z.enum(["app-session-created","app-session-updated","app-session-deleted","app-session-recovered","control-action","heartbeat-sync","startup"]),mn=o.z.object({streamId:o.z.string().trim().min(1).max(240),instanceId:o.z.string().trim().min(1).max(160),nodeId:o.z.string().trim().min(1).max(160).optional(),revision:o.z.number().int().min(0),previousRevision:o.z.number().int().min(0).optional(),traceId:o.z.string().trim().min(1).max(160),generatedAt:o.z.string().datetime(),reason:un}).strict(),gn=o.z.object({meta:mn,snapshot:ln}).strict(),hn=o.z.object({meta:mn,session:cn}).strict(),In=o.z.object({meta:mn,sessionId:o.z.string().trim().min(1).max(160),tombstone:cn.optional(),expiresAt:o.z.string().datetime().optional()}).strict(),fn=o.z.object({streamId:o.z.string().trim().min(1).max(240),instanceId:o.z.string().trim().min(1).max(160),sinceRevision:o.z.number().int().min(0),latestRevision:o.z.number().int().min(0),earliestRetainedRevision:o.z.number().int().min(0),syncRequired:o.z.boolean().default(!1),events:o.z.array(o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal(nn),payload:gn}).strict(),o.z.object({type:o.z.literal(an),payload:hn}).strict(),o.z.object({type:o.z.literal(rn),payload:In}).strict()])).default([])}).strict();function yn(e,t=(new Date).toISOString()){const s=e.map(e=>bn(e));return ln.parse({runningCount:s.filter(e=>"running"===e.status).length,problemCount:s.filter(e=>"failed"===e.status).length,sessions:s,updatedAt:t})}function bn(e){const t=vn("string"==typeof e.status?e.status:void 0),s="string"==typeof e.id?e.id:"",n=function(e){const t=Tn(e.workspace),s=Tn(e.tty),n=Tn(e.launch),i=Tn(e.ai),a=Tn(i.claude),r=zn(t.cwd)||zn(s.cwd)||zn(a.cwd)||zn(n.cwd);return r?{cwd:r}:void 0}(e),{workspace:i,...a}=e;return cn.parse({...a,status:t,bindings:kn([...(r=e.bindings,Array.isArray(r)?r.flatMap(e=>{const t=dn.safeParse(e);return t.success?[t.data]:[]}):[]),...s?[{type:"app-session",id:s}]:[],...Sn(e)]),...n?{workspace:n}:{}});var r}function vn(e){const t=(e||"unknown").trim().toLowerCase();return"ended"===t?"closed":"terminating"===t?"stopping":on.options.includes(t)?t:"unknown"}function wn(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 Sn(e){const t=Tn(e.ai),s=Tn(t.claude),n=Tn(t.appServer),i=[],a=zn(s.short);a&&i.push({type:"adapter-key",adapter:"claude",agent:"claude",id:`short:${a}`,key:`short:${a}`});const r=zn(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=zn(t.activeThreadId);for(const e of[...new Set([d,...o].filter(Boolean))])i.push({type:"provider-session",agent:"codex",id:e});const c=zn(n.socketPath);return c&&i.push({type:"adapter-key",adapter:"codex-app-server",agent:"codex",id:`socket:${c}`,key:`socket:${c}`}),i}function An(e){const t="string"==typeof e.id?e.id:"",s=Tn(e.ai),n=zn(Tn(s.claude).short);return[t?`app:${t}`:"",n?`claude-short:${n}`:""]}function kn(e){const t=new Map;for(const s of e)t.set(wn(s),s);return[...t.values()]}function Tn(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function zn(e){return"string"==typeof e&&e.trim()?e.trim():""}function En(e=(new Date).toISOString()){return ln.parse({runningCount:0,problemCount:0,sessions:[],updatedAt:e})}function Cn(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 Nn{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 _n{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",os)}checkUpdate(e,t){return this.client.requestSchema(e,"/updates/check",Ct,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}applyUpdate(e,t){return this.client.requestSchema(e,"/updates/apply",Nt,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}listUpdateJobs(e){return this.client.requestSchema(e,"/updates/jobs",o.z.array(Nt))}getExternalListener(e){return this.client.requestSchema(e,"/settings/external-listener",ds)}updateExternalListener(e,t){return this.client.requestSchema(e,"/settings/external-listener",ds,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}createPairingInvite(e,t={}){return this.client.requestSchema(e,"/pairing/invites",cs,{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",o.z.array(ls))}deleteRemote(e,t){return this.client.requestSchema(e,`/remotes/${encodeURIComponent(t)}`,us,{method:"DELETE"})}listDockerImages(e){return this.client.requestSchema(e,"/docker/images",o.z.array(ms))}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}`:""),o.z.array(hs))}listLocalFolders(e){return this.client.requestSchema(e,"/local-folders",o.z.array(Rt))}createLocalFolder(e,t){return this.client.requestSchema(e,"/local-folders",Rt,{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",o.z.array(is))}createRuntime(e,t){return this.client.requestSchema(e,"/runtimes",is,{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,is,{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,is,{method:"POST"})}deleteRuntime(e,t){return this.client.requestSchema(e,`/runtimes/${encodeURIComponent(t)}`,us,{method:"DELETE"})}deleteLocalFolder(e,t){return this.client.requestSchema(e,`/local-folders/${encodeURIComponent(t)}`,us,{method:"DELETE"})}listModels(e){return this.client.requestSchema(e,"/models",o.z.array(Lt))}createModel(e,t){return this.client.requestSchema(e,"/models",Lt,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}deployModel(e,t,s){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}/deploy`,Lt,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}updateModel(e,t,s){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}`,Lt,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}deleteModel(e,t){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}`,us,{method:"DELETE"})}assignInstanceModels(e,t,s){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/model-assignment`,o.z.object({assignment:Ut,instance:vs}).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(Cn(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(Cn(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",o.z.array(o.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",vs,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}updateInstance(e,t,s){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}`,vs,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}deleteInstance(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/delete`,us,{method:"POST"})}startInstance(e,t,s={}){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/start`,vs,{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`,vs,{method:"POST"})}stopInstance(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/stop`,vs,{method:"POST"})}restartInstance(e,t,s={}){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/restart`,vs,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}instanceResourceMetrics(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/metrics`,as)}proxyRawInstance(e,t,s){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/proxy/raw`,Is,{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(Cn(e[n],"/instances","GET",s.reason)),t),{items:[],nodeErrors:[]})}normalizeInstance(e,t){const s=(n=t,vs.safeParse(function(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,s=new Set(Object.keys(vs.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}return n.appInventory=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,s={observedAt:t.observedAt,items:Array.isArray(t.items)?t.items.map(e=>{const t=As(e,["id","name","kind","source","availability","diagnosticCode"]);return!t||"object"!=typeof t||Array.isArray(t)?t:{...t,capabilities:As(e.capabilities,["automation","supportsCwdSelection"])}}):t.items,issues:Array.isArray(t.issues)?t.issues.map(e=>As(e,["code","message"])):t.issues},n=at.safeParse(s);return n.success?n.data:void 0}(t.appInventory),n.apps=As(t.apps,["runningCount","problemCount","updatedAt","revision"]),n.config=As(t.config,["autoImportAgentConfigs","defaultCodexPermissionMode"]),n.modelSelection=As(t.modelSelection,["codexModelHash","claudeModelHash"]),n.imageSnapshot=function(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const s=e,n=ss(s),{reference:i,...a}=n,r=s.requestedReference??i;return void 0!==s.image||s.reference,{...a,id:s.id??t,requestedReference:r,resolvedDigest:s.resolvedDigest,resolvedReference:s.resolvedReference}}(t.imageSnapshot,t.imageId,"string"==typeof t.id&&t.id),n.imageProvisioning=As(t.imageProvisioning,["phase","requestedReference","generation","error","startedAt","updatedAt"]),n.runtime=As(t.runtime,["kind","containerName","containerId","workspacePath","pid","port","labels"]),n.target=As(t.target??t.endpoints,["strategy","web","api","status"]),n.access=As(t.access,["strategy","web","api","ws","status"]),n}(n)));var n;if(s.success)return s.data.protocolVersion&&s.data.protocolVersion!==Be?(this.client.logger?.warn?.({nodeId:e.id,instanceId:s.data.id,expectedProtocolVersion:Be,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 xn(){return(new Date).toISOString()}function On(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 Rn(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 jn(e){return e instanceof Error?e.message:String(e)}function Dn(e,t){const s=new Error(t);throw Object.assign(s,{statusCode:404,code:e}),s}class Pn{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:On(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:On(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 Mn(e){if(!e||"object"!=typeof e)return;const t=e,s=kt.safeParse(t.build),n=rt.safeParse(t.platform);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}:{},...s.success?{build:s.data}:{},..."string"==typeof t.serverTime?{serverTime:t.serverTime}:{}}}function $n(e){const{agent:t,...s}=e.capabilities,{secret:n,...i}=e.auth,a=i.pairing?{...i.pairing,joinToken:void 0}:void 0,r=Mn(t);return{...e,auth:a?{...i,pairing:a}:i,capabilities:r?{...s,agent:r}:s}}function qn(e){return"local-folder"===e.type?{mode:"local-bind",path:"/workspace",readOnly:!1}:{mode:"git-clone",path:"/workspace",readOnly:!1}}function Ln(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 Un(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 Fn{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=Ln(e.instance);return{...n,aiSessions:we(n.aiSessions),connectionStatus:s,heartbeatAgeMs:t,project:e.project,image:e.image,node:e.node?$n(e.node):void 0,runtime:e.runtime,protocolCompatible:!e.instance.protocolVersion||e.instance.protocolVersion===Be}}({instance:e,currentTime:a,project:e.projectId?t.get(e.projectId):void 0,image:e.imageSnapshot||(e.imageId?s.get(e.imageId):void 0),node:n.get(e.nodeId),runtime:i.get(`${e.nodeId}:${e.runtimeId}`)})),nodeErrors:e.nodeErrors||[]}}}const Bn=Pe.extend({id:Pe.shape.configHash}),Hn=o.z.object({name:Pe.shape.name,description:Pe.shape.description,source:Re,action:je,policy:De.partial().optional()}).strict(),Gn=o.z.object({instanceIds:o.z.array(vs.shape.id).min(1),target:Me,enabled:o.z.boolean().optional()}).strict(),Wn=o.z.object({configHash:Pe.shape.configHash,enabled:o.z.boolean().optional()}).strict();class Vn{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=Hn.parse(e||{}),s=xn(),n={maxConcurrentRuns:t.policy?.maxConcurrentRuns??1,whenBusy:t.policy?.whenBusy??"skip",cooldownMs:t.policy?.cooldownMs},i=function(e){const t=Fe({source:e.source,action:e.action,policy:De.parse(e.policy||{})});return`trg_${d.createHash("sha256").update(JSON.stringify(t)).digest("hex").slice(0,24)}`}({source:t.source,action:t.action,policy:n}),a=Bn.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||Dn("TRIGGER_NOT_FOUND",`Trigger ${e} was not found.`),{configHash:e,deletedTemplate:t,results:n}}async applyTrigger(e,t){const s=this.requireTrigger(e),n=Gn.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=Wn.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:Jn(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=Jn(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||Dn("TRIGGER_NOT_FOUND",`Trigger ${e} was not found.`),t}}function Jn(e,t){return`ai_${Buffer.from(e).toString("base64url")}_${t}`}const Kn=o.z.object({id:Mt.shape.id.optional(),name:Mt.shape.name,source:jt,defaultImageId:Mt.shape.defaultImageId,defaultNodeId:Mt.shape.defaultNodeId,defaultRuntimeId:Mt.shape.defaultRuntimeId,workspacePolicy:Dt.optional(),labels:Mt.shape.labels.optional()}).strict(),Qn=Kn.omit({id:!0}).partial().strict();function Xn(e){return 1===Array.from(e).length&&!/[\p{L}\p{N}\s]/u.test(e)&&"/"!==e&&"\\"!==e}const Yn=o.z.string().refine(Xn,"Mention trigger must be one printable non-alphanumeric character other than / or \\.");function Zn(e){return 1===Array.from(e).length&&!/[\p{L}\p{N}\s]/u.test(e)&&"\\"!==e}const ei=o.z.string().refine(Zn,"Command trigger must be one printable non-alphanumeric character other than \\."),ti=o.z.object({publicBaseUrl:o.z.string().trim().max(2048).optional(),updateChannel:Tt.default("stable"),mentionTrigger:Yn.default("@"),commandTrigger:ei.default("/")}).strict().refine(e=>e.mentionTrigger!==e.commandTrigger,{path:["commandTrigger"],message:"Command and mention triggers must be different."}),si=o.z.object({publicBaseUrl:o.z.string().trim().max(2048).optional(),updateChannel:Tt.optional(),mentionTrigger:Yn.optional(),commandTrigger:ei.optional()}).strict();function ni(e){if(!e||"object"!=typeof e||Array.isArray(e))return{};const t={...e};return"string"==typeof t.mentionTrigger&&Xn(t.mentionTrigger)||(t.mentionTrigger="@"),"string"==typeof t.commandTrigger&&Zn(t.commandTrigger)&&t.commandTrigger!==t.mentionTrigger||(t.commandTrigger="/"),t}const ii=o.z.object({id:Vt.shape.id.optional(),name:Vt.shape.name,reference:Vt.shape.reference,pullPolicy:Vt.shape.pullPolicy.optional(),capabilities:o.z.array(o.z.string().trim().min(1).max(80)).optional(),optionalApps:o.z.array(o.z.string().trim().min(1).max(120)).optional(),defaultEnv:Vt.shape.defaultEnv.optional(),labels:Vt.shape.labels.optional()}).strict(),ai=ii.omit({id:!0}).partial().strict();class ri{options;constructor(e){this.options=e}getSettings(){return ti.parse(this.options.settings.get())}updateSettings(e){const t=si.parse(e),s=ti.parse({...this.getSettings(),...t,..."publicBaseUrl"in t?{publicBaseUrl:t.publicBaseUrl?.trim()||void 0}:{}});return this.options.settings.put(s)}seedDefaults(){if(this.options.images.get("img_default"))return;const e=xn();this.options.images.put(Vt.parse({id:"img_default",name:"TaskHandoff Browser + Terminal",reference:process.env.TASK_HANDOFF_CONTROLLED_IMAGE||"huadream/task-handoff-controlled-instance:latest",pullPolicy:"if-not-present",capabilities:["browser","terminal","gui-terminal","codex","claude"],optionalApps:["chromium","terminal-tty","gui-terminal","vscode-web"],defaultEnv:{},labels:{"task-handoff.image.kind":"controlled-instance"},createdAt:e,updatedAt:e}))}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=Kn.parse(e),s=xn(),n=jt.parse(t.source);return this.options.projects.put(Mt.parse({...t,id:t.id||$s("proj"),source:n,defaultImageId:t.defaultImageId||"img_default",defaultNodeId:t.defaultNodeId||this.options.defaultNodeId(),defaultRuntimeId:t.defaultRuntimeId||"runtime_local_docker",workspacePolicy:Dt.parse(t.workspacePolicy||qn(n)),labels:t.labels||{},createdAt:s,updatedAt:s}))}updateProject(e,t){const s=Qn.parse(t),n=this.requireProject(e),i=s.source?jt.parse(s.source):n.source;return this.options.projects.put(Mt.parse({...n,...s,id:e,source:i,workspacePolicy:s.workspacePolicy?Dt.parse(s.workspacePolicy):n.workspacePolicy,createdAt:n.createdAt,updatedAt:xn()}))}deleteProject(e){return this.options.projects.delete(e)}requireProject(e){const t=this.getProject(e);return t||Dn("PROJECT_NOT_FOUND",`Project ${e} was not found.`),t}listImages(){return this.options.images.list()}createImage(e){const t=ii.parse(e),s=xn();return this.options.images.put(Vt.parse({...t,id:t.id||$s("img"),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=ai.parse(t),n=this.requireImage(e);return this.options.images.put(Vt.parse({...n,...s,id:e,createdAt:n.createdAt,updatedAt:xn()}))}deleteImage(e){return this.options.images.delete(e)}requireImage(e){const t=this.options.images.get(e);return t||Dn("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={...e};return"defaultRuntimeId"in t||(t.defaultRuntimeId="runtime_local_docker"),Mt.parse(t)}return Mt.parse(e)}(e);return t!==e&&this.options.projects.put(t),t}}class oi{records=new Map;options;constructor(e){this.options=e}create(e){const t=d.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 di{requireInstance;listAppSessions;tokens=new oi({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=>pi(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:ci(t,n.sessionId,r,s)}}}function ci(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):pi(li(s.vnc).noVncPath)||`/api/apps/sessions/${encodeURIComponent(t)}/web/`:pi(li(s.web).webPath)||`/api/apps/sessions/${encodeURIComponent(t)}/web/`}function li(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function pi(e){return"string"==typeof e&&e.trim()?e.trim():void 0}const ui={allow:"a",deny:"d",skip:"s"};function mi(e){return d.createHash("sha256").update(e).digest("base64url").slice(0,Math.ceil(16))}class gi{tokens=new oi({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:${ui[t]}:${mi(e)}`}}function hi(e){const{token:t,...s}=e;return{...s,settings:fi(e.settings),tokenSet:Boolean(e.token)}}function Ii(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 fi(e){const t={...e};return"string"==typeof t.clientSecret&&t.clientSecret&&(delete t.clientSecret,t.clientSecretSet=!0),t}const yi=o.z.object({name:Ts.shape.name.optional(),channel:Ts.shape.channel.optional(),enabled:o.z.boolean().optional(),token:Ts.shape.token,defaultChatId:Ts.shape.defaultChatId,allowedUserIds:o.z.array(o.z.string().trim().min(1).max(240)).optional(),pollIntervalMs:Ts.shape.pollIntervalMs.optional(),settings:Ts.shape.settings.optional()}).strict(),bi=yi.extend({channel:Ts.shape.channel,name:Ts.shape.name.optional()});class vi{chatBridges;chatSessions;constructor(e){this.chatBridges=e.chatBridges,this.chatSessions=e.chatSessions}list(){return this.chatBridges.list().map(hi)}require(e){const t=this.chatBridges.get(e);return t||Dn("CHAT_BRIDGE_NOT_FOUND",`Chat bridge ${e} was not found.`),t}create(e){const t=bi.parse(e),s=xn(),n=Ts.parse({...t,id:$s(`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:Ii({},t.settings),createdAt:s,updatedAt:s});var i,a;return this.chatBridges.put(n),hi(n)}update(e,t){const s=yi.parse(t),n=this.require(e),i=Ii(n.settings,s.settings),a=Ts.parse({...n,...s,id:e,channel:n.channel,name:s.name||n.name,settings:i,updatedAt:xn()});return this.chatBridges.put(a),hi(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 wi{chatSessions;constructor(e){this.chatSessions=e.chatSessions}list(){return this.chatSessions.list()}require(e){const t=this.chatSessions.get(e);return t||Dn("CHAT_SESSION_NOT_FOUND",`Chat session ${e} was not found.`),t}upsert(e){const t=xn(),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=zs.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 Si(e){return{inline_keyboard:e.map(e=>e.map(e=>({text:e.text,...e.url?{url:e.url}:{callback_data:e.callbackData||""}})))}}function Ai(e,t=40){return Si(e.slice(0,t).map(e=>[e]))}function ki(e){return Buffer.from(e,"utf8").toString("base64url")}function Ti(e){const t=Ei(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=Ei(t),s=String(e.callback_data||""),i=String(e.text||"").trim();i&&s&&n.push({text:i,id:`th_cb_${ki(s)}`})}return n.slice(0,40)}function zi(e,t){const s=String(e||"").replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,Math.max(0,t-3))}...`}function Ei(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}const Ci=new Set(["stopped","failed","exited","closed","terminated"]);function Ni(e){return!function(e){return Ci.has(vn(e))}(e)}function _i(e){return(e.aiSessions?.sessions||[]).filter(t=>function(e,t){const s=(e.apps?.sessions||[]).filter(Pi);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[...bn(e).bindings.map(wn),...An(e)].filter(Boolean)}(e).some(e=>n.has(e))))}(e,t))}function xi(e){return"waiting"===e.status?4:"failed"===e.status?3:"running"===e.status?2:"idle"===e.status?1:0}function Oi(e){return[e.cwd||"",e.agent||"",e.providerSessionId||e.id].join("\0")}function Ri(e){const t=e.session.title||e.session.userPrompt||e.session.summary||e.session.lastMessage||"",s=t?` - ${zi(t,18)}`:"";return`${e.instance.name} - ${e.session.agent} ${e.session.status}${s}`}function ji(e,t=(e,t)=>`task_handoff:approval:${e.id}:${t}`){return Ai(e.flatMap(e=>"approval"!==e.kind?[]:[{text:zi(`Allow ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"allow")},{text:zi(`Skip ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"skip")},{text:zi(`Deny ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"deny")}]))}function Di(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 Pi(e){const t=e&&"object"==typeof e&&!Array.isArray(e)?e:{};return Ni("string"==typeof t.status?t.status:void 0)}class Mi{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?Ai(t.map(e=>({text:`${e.active?"✓ ":""}${Ri(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:Ln(t),aiSession:s,reply:`Interrupt sent to ${t.name} / ${e.activeAiSessionId}.`}}}function $i(e){return e.providerTurnId||e.turnId||""}function qi(e){return Ni(Hi(e.status))}function Li(e,t){const s=Hi(t.appId)||"App",n=Hi(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 Ui(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=Gi(e);t&&!s.has(t.id)&&s.set(t.id,t)}return[...s.values()]}function Fi(e,t,s,n){const i=Hi(t.id)?function(e,t){return`/apps/access/${Bi(e)}?token=${encodeURIComponent(t())}`}(t,n):`/instances/${encodeURIComponent(e.id)}/`,a=function(e,t){const s=Hi(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 Bi(e){const t=Hi(e.kind);return"gui"===t?"vnc":"web"===t?"web":"tty"}function Hi(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function Gi(e){if("string"==typeof e&&e.trim())return{id:e,label:Wi(e)};const t=function(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}(e),s=Hi(t.id).trim();return s?{id:s,label:Hi(t.name).trim()||Wi(s)}:void 0}function Wi(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 Vi{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?Ai(t.map(e=>{return{text:zi(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=Ui(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?Ai(n.map(e=>{return{text:zi(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=Ui(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(qi).filter(e=>Boolean(Hi(e.id))).map(e=>({instance:t,session:e,link:Fi(t,e,this.deps.controlPlanePublicBaseUrl(),()=>this.deps.createAppAccessToken({instanceId:t.id,sessionId:Hi(e.id)||"",mode:Bi(e)}).token)})):[]}),i=n.filter(e=>e.link.url).map(e=>({text:zi(`${e.instance.name} · ${Li(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?Ai(i):void 0};var a,r}}class Ji{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:Ln(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}: ${jn(t)}`}}}failedSendResult(e,t,s,n){return{accepted:!1,routed:!1,binding:e,project:t||void 0,instance:Ln(s),reply:n}}sentResult(e,t,s,n,i){return{accepted:!0,routed:!0,binding:e,project:t||void 0,instance:Ln(s),aiSession:i,instanceId:s.id,aiSessionId:n,turnId:(a=i,a.turnId||a.providerTurnId||a.session.activeTurnId||""),providerTurnId:$i(i),reply:`Sent to ${t?.name||s.sourceSnapshot.name||"Source"} / ${s.name} / ${n}.`};var a}}class Ki{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(Di).join("\n"):"No pending routes.",replyMarkup:s.length?ji(s,(e,t)=>this.deps.pendingDecisionCallbackData(e.id,t)):void 0}}async handlePendingDecisionCommand(e,t,s){const n=Rn(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=Rn(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 Qi{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 Xi{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?_i({...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=>_i(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=xi(t.session)-xi(e.session);return s||(String(e.instance.name||"").localeCompare(String(t.instance.name||""))||e.instance.id.localeCompare(t.instance.id)||Oi(e.session).localeCompare(Oi(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||Dn("PROJECT_NOT_FOUND",`Project ${e} was not found.`),t}async resolveInstance(e,t){const s=await this.findInstance(e,t);return s||Dn("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 Yi{deps;targets;sender;appCommands;pendingCommands;aiSessionCommands;targetCommands;constructor(e){this.deps=e,this.targets=new Xi(e),this.sender=new Ji(e,this.targets),this.appCommands=new Vi(e,this.targets),this.pendingCommands=new Ki(e),this.aiSessionCommands=new Mi(e,this.targets),this.targetCommands=new Qi(e,this.targets)}async handleChatGatewayMessage(e){const t=Es.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.`}}}}function Zi(e){return/^[a-z]:[\\/]/i.test(e)||/^[\\/]{2}[^\\/]+[\\/]+[^\\/]+/.test(e)}function ea(e){return Zi(e)?s.win32:s.posix}const ta=o.z.object({mode:o.z.enum(["local-static-key","paired-hmac"]).default("local-static-key"),keyId:ns.shape.id.optional(),secret:o.z.string().trim().max(4096).optional(),pairedAt:ns.shape.createdAt.optional(),pairing:o.z.object({status:o.z.enum(["pending","paired","expired"]).default("pending"),joinToken:o.z.string().trim().max(4096).optional(),expiresAt:ns.shape.createdAt.optional()}).strict().optional()}).strict(),sa=o.z.object({autoImportAgentConfigs:o.z.boolean().optional(),defaultCodexPermissionMode:B.optional()}).strict(),na=o.z.object({id:vs.shape.id.optional(),name:vs.shape.name.optional(),projectId:vs.shape.projectId,source:jt.optional(),sourceSnapshot:o.z.record(o.z.string(),o.z.unknown()).optional(),nodeId:vs.shape.nodeId.optional(),runtimeId:vs.shape.runtimeId.optional(),imageId:vs.shape.imageId,config:sa.optional(),modelSelection:vs.shape.modelSelection.optional(),start:o.z.boolean().default(!1)}).strict(),ia=o.z.object({name:vs.shape.name.optional(),config:sa.optional(),modelSelection:vs.shape.modelSelection.optional()}).strict(),aa=o.z.object({name:$t.shape.name,endpoint:$t.shape.endpoint,key:$t.shape.key,model:$t.shape.model,app:$t.shape.app,enabled:$t.shape.enabled.optional(),order:$t.shape.order.optional(),labels:$t.shape.labels.optional()}).strict(),ra=aa.partial().strict(),oa=o.z.object({id:ns.shape.id.optional(),name:ns.shape.name,connectionMode:ns.shape.connectionMode.optional(),auth:ta.optional(),joinToken:o.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:o.z.enum(["unknown","online","offline","degraded"]).optional(),health:o.z.enum(["unknown","ok","degraded","failed"]).optional(),capabilities:ns.shape.capabilities.optional(),labels:ns.shape.labels.optional(),lastSeenAt:ns.shape.lastSeenAt}).strict(),da=o.z.object({name:ns.shape.name.optional(),connectionMode:ns.shape.connectionMode.unwrap().optional(),auth:ta.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(),ca=o.z.object({controlPlaneUrl:o.z.string().trim().url().max(2048),joinToken:o.z.string().trim().min(1).max(4096),controlPlaneName:o.z.string().trim().min(1).max(160).optional(),activate:o.z.boolean().optional()}).strict(),la=o.z.object({nodeName:o.z.string().trim().min(1).max(160).optional(),expiresInMs:o.z.number().int().positive().max(36e5).optional()}).strict();function pa(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 ua="TASK_HANDOFF_NODE_AGENT_V1",ma=6e4;function ga(e=""){return d.createHash("sha256").update(e).digest("hex")}function ha(e,t){return d.createHmac("sha256",e).update(function(e){return[ua,e.keyId,e.method.toUpperCase(),e.pathWithQuery,e.timestamp,e.nonce,e.bodySha256].join("\n")}(t)).digest("hex")}function Ia(e){const t=e.timestamp||(new Date).toISOString(),s=e.nonce||d.randomBytes(18).toString("base64url"),n=ga(e.body||""),i=ha(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 fa(e){if(e.startsWith("ipc://"))return decodeURIComponent(e.slice(6))}function ya(e,s=process.platform){if(!function(e,s=process.platform){if("win32"===s)return e.startsWith("\\\\.\\pipe\\");const n=t.statSync(e),i=process.getuid?.();return void 0===i||n.uid===i}(e,s)){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 ba(e,t,s,n={}){const i=`/api/node-agent${t}`,a={createConnection:()=>m.connect(e),headers:{host:"task-handoff-node-agent.local",...n}};return s&&(Array.isArray(s)?s.length>0:s.trim().length>0)?new p.WebSocket(`ws://task-handoff-node-agent.local${i}`,s,a):new p.WebSocket(`ws://task-handoff-node-agent.local${i}`,a)}const va=o.z.object({id:o.z.string().trim().min(1),tokenHash:o.z.string().trim().min(1),expiresAt:o.z.string().datetime(),nodeName:o.z.string().trim().min(1).max(160).optional(),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict(),wa="task-handoff.control-plane.local",Sa="task-handoff.control-plane.builtin";function Aa(e){return"true"===e.labels[wa]}function ka(e){return"true"===e.labels[Sa]}function Ta(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 za(e,t){const s=function(e){return"paired-hmac"===e.auth.mode?e.auth.secret:void 0}(e);if(s)return Ia({nodeId:e.id,keyId:Ta(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 Ea{projects;models;images;nodes;chatSessions;chatBridges;triggers;nodeJoinInvites;settings;paths;fetchImpl;dockerCommandRunner;logger;appAccessService;chatActionTokenService=new gi;nodeAgentTransport;nodeAgentGateway;controlledInstanceGateway;instanceBoardReader=new Fn;controlPlaneTriggerService;catalogService;chatBridgeService;chatSessionService;chatSessionRuntime;aiSessionSnapshotProvider;appSessionSnapshotProvider;constructor(e,t={}){var s;this.paths=e,this.fetchImpl=t.fetchImpl||fetch,this.dockerCommandRunner=t.dockerCommandRunner,this.nodeAgentTransport=t.nodeAgentTransport,this.logger=(s=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(s||"").trim().toLowerCase())?t.logger:void 0);const n=new Nn({request:(e,t,s)=>this.nodeAgentFetch(e,t,s),logger:this.logger});this.nodeAgentGateway=new _n(n),this.controlledInstanceGateway=new Pn({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 i=e=>({schema:e,logger:(e,t)=>this.logWarn(t,e)});this.projects=new Ls(e.projectsDir,i(Mt)),this.models=new Ls(e.modelsDir,i($t)),this.images=new Ls(e.imagesDir,{...i(Vt),sanitize:e=>ss(e,e=>this.logWarn(e,"legacy image profile field was migrated"))}),this.nodes=new Ls(e.nodesDir,i(ns)),this.chatSessions=new Ls(e.chatSessionsDir,i(zs)),this.chatBridges=new Ls(e.chatBridgesDir,i(Ts)),this.triggers=new Ls(e.triggersDir,i(Bn)),this.nodeJoinInvites=new Ls(e.nodeJoinInvitesDir,i(va)),this.settings=new Us(e.settingsPath,()=>ti.parse({}),{...i(ti),sanitize:ni}),this.catalogService=new ri({projects:this.projects,images:this.images,settings:this.settings,defaultNodeId:()=>this.defaultNodeId()}),this.controlPlaneTriggerService=new Vn({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 di({requireInstance:async e=>this.requireControlledInstance(e,!0),listAppSessions:()=>this.listAppSessions()}),this.chatBridgeService=new vi({chatBridges:this.chatBridges,chatSessions:this.chatSessions}),this.chatSessionService=new wi({chatSessions:this.chatSessions}),this.chatSessionRuntime=new Yi({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.seedDefaults()}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(Aa)?.id||this.nodes.list()[0]?.id}async inspectNodeAgent(e,t){const s="/health",n=t?za(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!==Be&&this.logWarn({nodeId:r,nodeEndpoint:e,expectedProtocolVersion:Be,actualProtocolVersion:a.data?.protocolVersion,build:a.data?.build,errorCode:"PROTOCOL_VERSION_MISMATCH"},"node agent protocol version mismatch");const o=kt.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=fa(e);return n?(ya(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=u.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?g.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:xn()}}async syncLocalNodeConnection(){const e={[wa]:"true",[Sa]:"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 fa(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:xn(),updatedAt:xn()}),a=await this.inspectNodeAgent(s,i),r=xn(),o=this.nodes.get(a.nodeId),d=this.nodes.list().filter(e=>Aa(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:Mn(a.data)},labels:{...o?.labels||{},...e},createdAt:o?.createdAt||r,updatedAt:r});return this.nodes.put(c)}listModels(){return this.listAllModels().map(Un)}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:Un(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 Gt.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:xn()})}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=aa.parse(e),s=xn(),n=this.nextModelOrder(),i=Wt(t),a=this.models.get(i),r=$t.parse({...t,id:i,enabled:t.enabled??!0,order:t.order??n,labels:t.labels||{},createdAt:a?.createdAt||s,updatedAt:s});return Un(this.models.put(r))}async updateModel(e,t){const s=ra.parse(t),n=this.requireModelSecret(e),i=$t.parse({...n,...s,key:s.key?.trim()?s.key:n.key,createdAt:n.createdAt,updatedAt:xn()}),a=Wt(i);return Un(this.models.put($t.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)||Dn("MODEL_NOT_FOUND",`Model ${e} was not found.`);return t.forEach((e,t)=>{const n=s.get(e);this.models.put($t.parse({...n,order:100*(t+1),updatedAt:xn()}))}),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()}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=this.listImages();try{const e=await this.nodeAgentGateway.listDockerImages(t);return s.map(t=>{const s=e.find(e=>e.reference===t.reference||e.repoDigests.includes(t.reference));return gs.parse({image:t,status:s?"available":"pull-required",localImage:s})})}catch(e){const t=e instanceof Error?e.message:String(e);return s.map(e=>gs.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:Mn(e)}:t.capabilities,lastSeenAt:xn(),updatedAt:xn()});return n!==t.id&&this.nodes.delete(t.id),this.nodes.put(i),{id:i.id,status:"online",checkedAt:xn(),agent:e}}catch(e){const s=ns.parse({...t,status:"offline",health:"failed",updatedAt:xn()});return this.nodes.put(s),{id:t.id,status:"offline",checkedAt:xn(),error:e instanceof Error?e.message:String(e)}}}async checkNodeUpdate(e,t){return this.nodeAgentGateway.checkUpdate(this.requireNode(e),t)}async applyNodeUpdate(e,t){const s=this.requireNode(e);if("controlled-instance"===t.target.component){const e=await this.requireNodeInstance(t.target.instanceId);if(e.nodeId!==s.id){const t=new Error(`Instance ${e.id} does not belong to node ${s.id}.`);throw Object.assign(t,{statusCode:400,code:"UPDATE_TARGET_NODE_MISMATCH"}),t}return await this.ensureInstanceModelAssignment(e),this.nodeAgentGateway.applyUpdate(s,t)}return this.nodeAgentGateway.applyUpdate(s,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(!ka(t)||!Aa(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=la.parse(e&&"object"==typeof e?e:{}),s=xn(),n=qs(),i={id:$s("node_join"),tokenHash:d.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=o.z.object({joinToken:o.z.string().trim().min(1).max(4096),nodeId:ns.shape.id,nodeName:ns.shape.name.optional(),keyId:o.z.string().trim().min(1).max(160),secret:o.z.string().trim().min(1).max(4096),pairedAt:o.z.string().datetime().optional()}).parse(e),s=d.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=xn();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=ca.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.defaultImageId===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.defaultImageId===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.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($n)}async createNode(e){const t=oa.parse(e),s=xn(),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||$s("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:Mn(c.data)}:{}},labels:t.labels||{},createdAt:s,updatedAt:s});return this.nodes.put(m)}updateNode(e,t){const s=da.parse(t),n=this.requireNode(e),i=ns.parse({...n,...s,id:e,createdAt:n.createdAt,updatedAt:xn()});return this.nodes.put(i)}deleteNode(e){if(ka(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(Ln)}async listControlledInstancesWithDiagnostics(){const e=await this.listNodeInstancesWithDiagnostics();return{items:e.items.map(Ln),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=na.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.imageId||(o?s?.defaultImageId:void 0),c=d?this.requireImage(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,defaultImageId:c?.id});l={...l,localFolderId:e.id,ownerNodeId:e.nodeId,path:e.path},p=e}}const u=await this.prepareInstanceModels(a,t.modelSelection||{}),m=await this.nodeAgentGateway.createInstance(a,{id:t.id,name:t.name,runtimeId:n,...c?{imageId:c.id,image:c}:{},projectId:s?.id,source:l,sourceSnapshot:p,config:t.config,modelSelection:{}});let g=m;try{g=(await this.nodeAgentGateway.assignInstanceModels(a,m.id,u)).instance}catch(e){throw await this.nodeAgentGateway.deleteInstance(a,m.id).catch(()=>{}),e}let h={status:"not-requested"};if(t.start)try{g=await this.nodeAgentGateway.startInstance(a,g.id),h={status:"started"}}catch(e){h={status:"failed",error:Ca(e,"INSTANCE_START_FAILED")},g=await this.nodeAgentGateway.listInstances(a).then(e=>e.find(e=>e.id===g.id)||g).catch(()=>g)}return{...Ln(g),registrationToken:g.registrationToken,startOutcome:h}}async updateControlledInstance(e,t){const s=ia.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 Ln(o)}async deleteControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId),n=await this.nodeAgentGateway.deleteInstance(s,e);return Boolean(n.deleted)}async startControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId);return await this.ensureInstanceModelAssignment(t),Ln(await this.nodeAgentGateway.startInstance(s,e))}async stopControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId);return Ln(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),Ln(await this.nodeAgentGateway.restartInstance(s,e))}async retryControlledInstanceImageProvisioning(e){const t=await this.requireNodeInstance(e);return Ln(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.listImages(),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 be.parse(await this.instanceRequest(e,"/ai-sessions/state"))}catch(t){return this.logWarn({instanceId:e.id,error:jn(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=En();return{streamId:`bootstrap:${e.id}:app.sessions`,revision:0,lastEventAt:t.updatedAt,snapshot:t}}try{return pn.parse(await this.instanceRequest(e,"/apps/sessions/state"))}catch(t){this.logWarn({instanceId:e.id,error:jn(t),errorCode:"APP_SESSION_LIVE_REFRESH_FAILED"},"control plane failed to refresh live app sessions");const s=En();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 Ce.parse(await this.instanceRequest(n,`/ai-sessions?${i}`))}async recoverAiSessionSnapshot(e){const t=await this.requireControlledInstance(e,!0);return be.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 fn.parse(await this.instanceRequest(n,`/apps/sessions?${i}`))}async recoverAppSessionSnapshot(e){const t=await this.requireControlledInstance(e,!0);return pn.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=xn(),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=Cs.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:Ln(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 ue.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 ge.parse(await this.instanceRequest(t,"/ai-sessions/history"))}async getAiSessionHistoryDetail(e,t){const s=await this.requireControlledInstance(e,!0);return he.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 wt.parse(e)}catch(e){if(e instanceof o.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 o.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 At.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 At.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)<=S)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 ue.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 Z.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/mentions`))}async searchAiSessionMentionFiles(e,t,s){const n=await this.requireControlledInstance(e,!0);return te.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 W.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 re.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/queue`))}async steerAiSessionQueuedMessage(e,t,s){const n=await this.requireControlledInstance(e,!0);return ue.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 pe.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 pe.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 ue.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)}listConfigSyncPresets(){return[{id:"codex",label:"Codex",projectRoot:".task-handoff/configs/codex",items:[{id:"config",type:"file",projectPath:"config.toml",containerPath:"${CODEX_HOME}/config.toml"},{id:"auth",type:"file",projectPath:"auth.json",containerPath:"${CODEX_HOME}/auth.json"},{id:"agents",type:"file",projectPath:"AGENTS.md",containerPath:"${CODEX_HOME}/AGENTS.md"},{id:"skills",type:"dir",projectPath:"skills",containerPath:"${CODEX_HOME}/skills"}]},{id:"claude",label:"Claude",projectRoot:".task-handoff/configs/claude",items:[{id:"claude-json",type:"file",projectPath:".claude.json",containerPath:"${HOME}/.claude.json"},{id:"settings",type:"file",projectPath:"settings.json",containerPath:"${CLAUDE_HOME}/settings.json"},{id:"claude-md",type:"file",projectPath:"CLAUDE.md",containerPath:"${CLAUDE_HOME}/CLAUDE.md"},{id:"commands",type:"dir",projectPath:"commands",containerPath:"${CLAUDE_HOME}/commands"},{id:"agents",type:"dir",projectPath:"agents",containerPath:"${CLAUDE_HOME}/agents"},{id:"skills",type:"dir",projectPath:"skills",containerPath:"${CLAUDE_HOME}/skills"}]},{id:"browser",label:"Browser",projectRoot:".task-handoff/configs/browser",items:[{id:"chromium-profile",type:"dir",projectPath:"chromium",containerPath:"${HOME}/.config/chromium"}]}]}async syncInstanceConfig(e,t,s){const n=await this.requireControlledInstance(e,!0);if("export"===t&&"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=[{id:"codex",label:"Codex",projectRoot:".task-handoff/configs/codex",items:[{id:"config",type:"file",projectPath:"config.toml",containerPath:"${CODEX_HOME}/config.toml"},{id:"auth",type:"file",projectPath:"auth.json",containerPath:"${CODEX_HOME}/auth.json"},{id:"agents",type:"file",projectPath:"AGENTS.md",containerPath:"${CODEX_HOME}/AGENTS.md"},{id:"skills",type:"dir",projectPath:"skills",containerPath:"${CODEX_HOME}/skills"}]},{id:"claude",label:"Claude",projectRoot:".task-handoff/configs/claude",items:[{id:"claude-json",type:"file",projectPath:".claude.json",containerPath:"${HOME}/.claude.json"},{id:"settings",type:"file",projectPath:"settings.json",containerPath:"${CLAUDE_HOME}/settings.json"},{id:"claude-md",type:"file",projectPath:"CLAUDE.md",containerPath:"${CLAUDE_HOME}/CLAUDE.md"},{id:"commands",type:"dir",projectPath:"commands",containerPath:"${CLAUDE_HOME}/commands"},{id:"agents",type:"dir",projectPath:"agents",containerPath:"${CLAUDE_HOME}/agents"},{id:"skills",type:"dir",projectPath:"skills",containerPath:"${CLAUDE_HOME}/skills"}]},{id:"browser",label:"Browser",projectRoot:".task-handoff/configs/browser",items:[{id:"chromium-profile",type:"dir",projectPath:"chromium",containerPath:"${HOME}/.config/chromium"}]}].find(e=>e.id===s);if(!i){const e=new Error(`Config sync preset ${s} was not found.`);throw Object.assign(e,{statusCode:404,code:"CONFIG_SYNC_PRESET_NOT_FOUND"}),e}return this.instanceRequest(n,`/config-sync/${encodeURIComponent(t)}/${encodeURIComponent(s)}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({preset:i})})}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=za(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=fa(a),o=`/api/node-agent${s}`,d=za(e,{method:"GET",pathWithQuery:o});if(r){ya(r);const e=ba(r,s,n,{...i,...d});return e.on("open",()=>{t.on("message",(t,s)=>e.readyState===p.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===p.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 l=n?new p.WebSocket(c.toString(),n,{headers:{...i,...d}}):new p.WebSocket(c.toString(),{headers:{...i,...d}});l.on("open",()=>{t.on("message",(e,t)=>l.readyState===p.WebSocket.OPEN&&l.send(t?e:String(e))),t.on("close",()=>l.close()),t.on("error",()=>l.close())}),l.on("message",(e,s)=>{t.readyState===p.WebSocket.OPEN&&t.send(s?e:e.toString())}),l.on("close",()=>t.close()),l.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=Ue.parse(await this.instanceRequest(e,"/triggers"));return vs.parse({...e,triggers:pa(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{}Dn("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||Dn("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,t,n){if("local"===n.type)return ea(i=t.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(Zi(e)!==Zi(t))return;const s=ea(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,t.path);if(!a){const s=new Error(`Local folder ${t.path} is outside the instance workspace ${e.source.path}.`);throw Object.assign(s,{statusCode:400,code:"APP_CWD_OUTSIDE_WORKSPACE"}),s}const r=e.runtime.workspacePath||e.workspace.path||qn(e.source).path||"/workspace";return a.length?s.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||Dn("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||Dn("MODEL_NOT_FOUND",`Model ${e} was not found.`);const n=this.normalizeModelRecord(s);return t?n:Un(n)}requireModelSecret(e){const t=this.models.get(e);return t||Dn("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,$t.parse(t)}return $t.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||Dn("NODE_NOT_FOUND",`Node ${e} was not found.`),t}requirePublicNode(e){return $n(this.requireNode(e))}async requireControlledInstance(e,t=!1){const s=await this.requireNodeInstance(e);return t?s:Ln(s)}}function Ca(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 Na(e){return h(String(e),"escape").replace(/\n+$/,"")}function _a(e){const[t,...s]=String(e).split("\n");return[`*${Na(t)}*`,...s.map(Na)].join("\n")}function xa(e,t=4e3){const s=String(e),n=_a(s);if(n.length<=t)return n;const[i,...a]=s.split("\n"),r=a.join("\n"),o="...\n";let d=i;for(;_a(`${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=_a(`${d}\n${o}`);for(;c<=l;){const e=Math.floor((c+l)/2),s=_a(`${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=_a(`${d}\n${o}${m.slice(g+1)}`)),u}function Oa(e){return String(e).replace(/\\([_*\[\]()~`>#+\-=|{}.!\\])/g,"$1")}async function Ra(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:Na;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 Da(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 ja(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 Da(e,t,s,n,i){const a=i.parseMode||"MarkdownV2",r=i.rawMarkdownV2?String(s):Na(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&&Pa(d.description))return;if(i.rawMarkdownV2&&"MarkdownV2"===a){const i=await e({...n,text:Oa(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&&Pa(a.description))return}const c=await e({...n,text:i.rawMarkdownV2?Oa(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||!Pa(l.description))throw new Error(l.description||d.description||`Telegram ${t} failed with HTTP ${c.status}`)}function Pa(e){return"string"==typeof e&&e.toLowerCase().includes("message is not modified")}function Ma(e,t){if("string"==typeof e&&e.trim())return e.trim();qa(`Chat action token is missing ${t}.`)}function $a(e){if("allow"===e||"deny"===e||"skip"===e)return e;qa("Chat action token has an invalid decision.")}function qa(e){const t=new Error(e);throw Object.assign(t,{statusCode:400,code:"CHAT_ACTION_TOKEN_INVALID"}),t}function La(e){return Buffer.from(e,"base64url").toString("utf8")}function Ua(e){return String(e||"").split(/\r?\n/,1)[0].replace(/^#+\s*/,"").trim().slice(0,20)||"消息通知"}function Fa(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,"string"==typeof t?t:JSON.stringify(t)]))}function Ba(e){return`dtv1.card//IM_GROUP.${e}`}function Ha(e){return`dtv1.card//IM_ROBOT.${e}`}function Ga(e,t){return rr(t.biz_conversation_id).trim()||rr(e.spaceId).replace(/^dtv1\.card\/\/(?:IM_GROUP|IM_SINGLE|IM_ROBOT)\./,"")}function Wa(e,t={}){const s=rr(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 Va(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 Ja(e,t,s,n,i){const a=Ga(n,i);return{cardUpdateOptions:{updateCardDataByKey:!0,updatePrivateDataByKey:!1},cardData:{cardParamMap:Fa({type:"actions",title:"TaskHandoff",description:e,list:Ti(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 Ka(e,t,s){const n=[];return Qa(n,s),Qa(n,t),Qa(n,e),n.find(e=>Va(e))||""}function Qa(e,t){for(const s of["actionIdList","actionIds"]){const n=t[s];Array.isArray(n)&&e.push(...n.map(e=>rr(e)).filter(Boolean))}for(const s of["actionId","action_id","id","actionValue"]){const n=rr(t[s]);n&&e.push(n)}}async function Xa(e,t,s,n){n?.("info",{target:"sessionWebhook",hasSessionWebhook:Boolean(t),textPreview:nr(s)},"dingding webhook send requested");const i=await e(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({msgtype:"markdown",markdown:{title:Ua(s),text:s}})}),a=await i.json().catch(()=>{});if(n?.(i.ok?"info":"warn",{target:"sessionWebhook",status:i.status,response:sr(a)},"dingding webhook send completed"),!i.ok)throw new Error(`DingDing webhook failed with HTTP ${i.status}`)}async function Ya(e){const t=Ti(e.replyMarkup);if(!t.length&&!e.forceCard)return e.sessionWebhook&&await Xa(e.fetchImpl,e.sessionWebhook,e.text,e.runtime.onLog),e.sessionWebhook?{delivered:!0}:void 0;const s=e.bridge.settings||{},n=rr(s.robotCode),i=rr(s.cardTemplateId)||"13fc6717-12e4-43ed-8533-111a310d4995.schema",a=rr(s.cardCallbackRouteKey)||"bi_workflow_ticket",r=e.senderId||e.runtime.senderIds.get(e.chatId)||rr(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 Xa(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=Fa({type:"actions",title:e.title||Ua(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:Ha(s),imRobotOpenSpaceModel:{supportForward:!1},imRobotOpenDeliverModel:{extension:{},robotCode:n,spaceType:"IM_ROBOT"}}:{openSpaceId:Ba(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:nr(e.text)},"dingding card send requested"),await Za(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 Za(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=rr(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:er(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:sr(d)},"dingding api response"),!o.ok)throw new Error(`DingDing API failed with HTTP ${o.status}`);const c=ir(d),l=c.code??c.errcode,p=c.success;if(void 0!==l&&"0"!==String(l)||!1===p)throw new Error(rr(c.message||c.errmsg)||`DingDing API returned ${String(l||"failure")}`);const u=function(e){const t=ir(ir(e).result).deliverResults;if(!Array.isArray(t))return"";const s=t.find(e=>!1===ir(e).success);if(!s)return"";const n=ir(s),i=rr(n.spaceId),a=rr(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 er(e){const t=ir(e),s=ir(t.cardData),n=ir(s.cardParamMap);return{openSpaceId:rr(t.openSpaceId),outTrackId:rr(t.outTrackId),userId:rr(t.userId),userIdType:t.userIdType,callbackType:rr(t.callbackType),callbackRouteKey:rr(t.callbackRouteKey),hasImGroupTarget:Boolean(t.imGroupOpenSpaceModel||t.imGroupOpenDeliverModel),hasImSingleTarget:Boolean(t.imSingleOpenSpaceModel||t.imSingleOpenDeliverModel),hasImRobotTarget:Boolean(t.imRobotOpenSpaceModel||t.imRobotOpenDeliverModel),imGroupOpenDeliverModel:tr(t.imGroupOpenDeliverModel),imSingleOpenDeliverModel:tr(t.imSingleOpenDeliverModel),imRobotOpenDeliverModel:tr(t.imRobotOpenDeliverModel),bizConversationId:rr(n.biz_conversation_id),bizConversationType:rr(n.biz_conversation_type),bizSenderId:rr(n.biz_sender_id),hasBizSessionWebhook:Boolean(rr(n.biz_session_webhook))}}function tr(e){const t=ir(e);if(Object.keys(t).length)return{hasExtension:Boolean(t.extension),hasRobotCode:Boolean(rr(t.robotCode)),spaceType:rr(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 sr(e){const t=ir(e),s=ir(t.result);return{code:t.code??t.errcode,message:nr(t.message||t.errmsg,240),success:t.success,requestId:t.requestId,result:Object.keys(s).length?s:void 0}}function nr(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 ir(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function ar(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:"string"==typeof e&&e.trim()?or(e):{}}function rr(e){return"string"==typeof e?e:""}function or(e){try{return ir(JSON.parse(String(e||"{}")))}catch{return{}}}function dr(e){return JSON.stringify(Ti(e))}function cr(e){return e instanceof Error?e.message:String(e)}function lr(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 pr(e){return Array.isArray(e?.actionRows)?e.actionRows:(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e])}function ur(e,t){const s=pr(e),n=pr(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 mr{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||lr,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&&ur(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: ${cr(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&&ur(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&&ur(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: ${cr(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: ${cr(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 gr=20971520;function hr(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 Ir(e,t,n="telegram-image",i){if(i&&i>gr)throw new Error(`image is too large: ${i} bytes`);const a=await e.getFileLink(t),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>gr)throw new Error(`image is too large: ${d} bytes`);const c=s.extname(n)||".jpg";let l=c;try{l=s.extname(new URL(r).pathname)||c}catch{l=c}const p=s.join(f.tmpdir(),"task-handoff-images");await I.mkdir(p,{recursive:!0});const u=l.replace(/[^.\w-]/g,"")||".jpg",m=s.join(p,`telegram-${Date.now()}-${Math.random().toString(16).slice(2)}${u}`),g=Buffer.from(await o.arrayBuffer());if(g.length>gr)throw new Error(`image is too large: ${g.length} bytes`);return await I.writeFile(m,g),m}class fr{store;constructor(e){this.store=new mr({updateIntervalMs:e.updateIntervalMs,send:(t,s,n)=>e.send(e.requireBridge(s?.bridgeId||""),s?.chatId||"",xa(t),{rawMarkdownV2:!0,replyMarkup:Ar(n)}),edit:(t,s,n,i)=>e.edit(e.requireBridge(n?.bridgeId||""),n?.chatId||"",t,xa(s),{rawMarkdownV2:!0,replyMarkup:Ar(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 yr(e){return(await async function(e,t,s,n){const i=[];for(const a of hr(s))try{const s=await Ir({getFileLink:s=>ja(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=t.readFileSync(e);return{id:`tg_${d.randomUUID().replace(/-/g,"").slice(0,24)}`,kind:"image",name:s.basename(e)||`telegram-image-${n+1}`,mime:br(e),size:i.length,source:{type:"inline",encoding:"base64",data:i.toString("base64")}}})}function br(e){const t=s.extname(e).toLowerCase();return".png"===t?"image/png":".webp"===t?"image/webp":".gif"===t?"image/gif":".bmp"===t?"image/bmp":"image/jpeg"}function vr(e){const t=Number(e?.message_id);return Number.isInteger(t)?t:void 0}function wr(e){return e&&"object"==typeof e?e:{}}function Sr(e){return"string"==typeof e?e:""}function Ar(e){const t=Array.isArray(e?.actionRows)?e.actionRows:void 0;return Si(t||(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e]))}function kr(e){const t=Array.isArray(e.item_list)?e.item_list:[];for(const e of t){const t=Tr(e);if(1!==t.type)continue;const s=zr(Tr(t.text_item).text).trim();if(s)return s}return""}function Tr(e){return e&&"object"==typeof e?e:{}}function zr(e){return"string"==typeof e?e:""}function Er(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 Ra(e,t,s,n,{replyMarkup:i.replyMarkup,replyToMessageId:i.replyToMessageId,rawMarkdownV2:i.rawMarkdownV2,parseMode:i.parseMode});return{provider:"telegram",raw:a,messageId:Number(wr(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)||rr(t.settings.sessionWebhook);if(a.replyMarkup&&s){const o=await Ya({fetchImpl:e,bridge:t,runtime:s,chatId:n,text:i,replyMarkup:a.replyMarkup,sessionWebhook:r,senderId:a.senderId||s.senderIds.get(n)||rr(t.settings.senderId)});if(o?.delivered)return{provider:"dingding",interactionId:o.outTrackId}}if(r)return await Xa(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||zr(t.settings.contextToken);if(a)return await async function(e,t,s,n,i){const a=zr(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:d.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"}})}),o=await r.json().catch(()=>({}));if(!r.ok||o.errcode)throw new Error(zr(o.errmsg)||`WeChat send failed with HTTP ${r.status}`)}(e,t,s,a,n),{provider:"wechat"}}}}(t,s):{bridge:s,send:async()=>{}}}class Cr{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=dr(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,dr(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=Ya({fetchImpl:this.fetchImpl,bridge:e.bridge,runtime:t,chatId:e.chatId,text:e.text,replyMarkup:e.replyMarkup,sessionWebhook:t.chatWebhooks.get(e.chatId)||Nr(e.bridge.settings.sessionWebhook),senderId:t.senderIds.get(e.chatId)||Nr(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 Za(e.fetchImpl,e.bridge,e.runtime,"PUT","/v1.0/card/instances",{outTrackId:e.outTrackId,cardData:{cardParamMap:Fa({type:"actions",title:e.title,description:e.text,list:Ti(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 Nr(e){return"string"==typeof e?e:""}function _r(e){const t=new y.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 xr{runtimes=new Map;connected=new Set;reconnectTimers=new Map;progress;options;createClient;constructor(e){this.options=e,this.createClient=e.createClient||_r,this.progress=new Cr(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(y.TOPIC_ROBOT,t=>{this.options.onRobotMessage(e,n,t).catch(t=>{this.options.onError(e.id,t)})}),n.client.registerCallbackListener(y.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 Or{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 fr({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 xr({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,Fr(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:Fr(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=or(e),s=rr(t.conversationId),n=rr(ir(t.text).content).trim();if(!s||!n)return;const i=rr(t.senderStaffId||t.senderId);return{chatId:s,userId:i||void 0,senderId:i||void 0,sessionWebhook:rr(t.sessionWebhook)||void 0,conversationType:Wa(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)||Ur(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:Hr(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 Xa(this.fetchImpl,d,`Failed to handle message: ${Fr(e)}`,t.onLog);throw e}const l=Rr(c);if(l){const s=Dr(c);if(await this.sendDingdingRoutedAiSessionProgress(c,e,t,i,l,{replyMarkup:s,sessionWebhook:d,senderId:r,sourcePrompt:a}))return;const n=Er({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===Vr(c).routed,hasReplyMarkup:Boolean(s),replyPreview:Hr(l)},"dingding chat gateway reply sent");this.logWarn({bridgeId:e.id,chatId:i,senderId:r,hasReplyMarkup:Boolean(s),hasSessionWebhook:Boolean(d),hasRobotCode:Boolean(Ur(e.settings.robotCode)),replyPreview:Hr(l)},"dingding chat gateway reply skipped")}}async sendDingdingRoutedAiSessionProgress(e,t,s,n,i,a={}){if(!0!==e.routed)return!1;const r=Qr(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=no(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:Hr(a.sourcePrompt,80),textPreview:Hr(i)},"dingding ai session progress card started"),!0)}async handleDingdingCardCallback(e,t,s){const n=function(e){const t=or(e),s=ar(t.cardActionData||t.content),n=ar(s.cardPrivateData||t.cardPrivateData||t.content),i=ar(n.params||s.params||t.params),a=rr(t.userId),r=rr(i.biz_sender_id);return{userId:a||void 0,chatId:Ga(t,i),senderId:a||void 0,deliverySenderId:r||void 0,sessionWebhook:rr(i.biz_session_webhook)||void 0,conversationType:Wa(i.biz_conversation_type||t.conversationType||t.conversation_type),callbackData:Va(Ka(t,s,n)),body:t,params:i}}(s.data);if(!this.dingdingAllowed(e,n.userId))return Ja("not authorized",void 0,"unauthorized",n.body,n.params);const i=n.chatId||Ur(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(Br(e,"CHAT_PENDING_ACTION_STALE"))return Ja(Fr(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,...Gr(n)},"dingding card callback action unsupported"),Ja("Unsupported action",void 0,"unsupported",n.body,n.params);this.logInfo({bridgeId:e.id,chatId:i,userId:n.userId,senderId:a,actionType:o.type,...Gr(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&&jr(d)?Ja(`${o.decision} sent`,void 0,"approval_done",n.body,n.params):Ja(Rr(d)||Ur(d.message)||"updated",Dr(d),"updated",n.body,n.params)}async pollWechat(e){if(!e.token)return;const t=e.settings||{},s=this.wechatCursors.get(e.id)||Ur(t.updatesBuf),n=await async function(e){const t=zr((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(zr(n.errmsg||n.msg)||`chat bridge API returned ${i}`);const a=[];for(const e of Array.isArray(n.msgs)?n.msgs:[]){const t=Tr(e);if(1!==t.message_type)continue;const s=zr(t.from_user_id),n=kr(t);s&&n&&a.push({chatId:s,text:n,contextToken:zr(t.context_token)})}return{cursor:zr(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=Rr(await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"wechat",bridgeId:e.id,chatSessionId:n},message:{text:a,attachments:[]}}));if(r){const s=Er({fetchImpl:this.fetchImpl,bridge:e});await s.send(n,r,{contextToken:i||Ur(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=Xr(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 Er({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=wr(t.callback_query);if(Object.keys(a).length>0){i.push({updateId:n,callbackQuery:a});continue}const r=wr(t.message||t.edited_message);if(0===Object.keys(r).length)continue;const o=wr(r.chat),d=wr(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=Sr(r.text??r.caption),u=hr(r);(p||0!==u.length)&&i.push({updateId:n,chatId:c,userId:l,text:p,rawText:p,message:r,messageId:vr(r),replyToMessageId:vr(wr(r.reply_to_message)),quoteText:Sr(wr(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=Vr(s.message),i=Vr(n.chat),a=void 0!==i.id?String(i.id):e.defaultChatId;this.bridgeErrors.set(e.id,Fr(t)),a&&await this.sendTelegramMessage(e,a,`Action failed: ${Fr(t)}`).catch(t=>{this.bridgeErrors.set(e.id,Fr(t))})});continue}if(!this.telegramAllowed(e,t.userId))continue;const s=await yr({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:Fr(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:Hr(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=$r(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:Si([[{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:Si([[{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!==$r(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=d.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=Rr(d),l=Dr(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(Vr(s).message_id)||void 0,routed:!0===Vr(d).routed,replyPreview:Hr(c)},"telegram chat gateway reply sent"),await this.rememberTelegramProgressMessage(d,e,t,s,c)}}catch(s){this.bridgeErrors.set(e.id,Fr(s)),await this.sendTelegramMessage(e,t,`Failed to handle message: ${Fr(s)}`,{replyToMessageId:a.sourceMessageId}).catch(t=>{this.bridgeErrors.set(e.id,Fr(t))})}}async handleTelegramCallback(e,t){const s=Ur(t.data),n=Vr(t.from),i=Vr(t.message),a=Vr(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,Ur(t.id),s.instanceId,s.sessionId,o,Mr(i)):void await this.answerTelegramCallback(e,Ur(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,Ur(t.id),s.instanceId,s.sessionId,s.queueId,o,Mr(i)):void await this.answerTelegramCallback(e,Ur(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,Ur(t.id),s.instanceId,s.sessionId,o,Mr(i)):void await this.answerTelegramCallback(e,Ur(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,Ur(t.id),Mr(i),s.instanceId,s.sessionId,s.queueId,o):void await this.answerTelegramCallback(e,Ur(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,Ur(t.id),u[1],o);let m;try{m=await this.parseChatGatewayCallbackAction(s)}catch(s){if(Br(s,"CHAT_PENDING_ACTION_STALE")){await this.answerTelegramCallback(e,Ur(t.id),Fr(s));const n=Mr(i);return void(void 0!==n&&await this.deleteTelegramStandaloneApprovalMessage(e,r,n))}throw s}if(!m)return void await this.answerTelegramCallback(e,Ur(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&&jr(g)){await this.answerTelegramCallback(e,Ur(t.id),"");const s=Mr(i);return void(void 0!==s&&await this.deleteTelegramStandaloneApprovalMessage(e,r,s))}await this.answerTelegramCallback(e,Ur(t.id),Ur(g.message)||"updated");const h=Rr(g),I=Dr(g),f=i.message_id;h&&"number"==typeof f?await this.editTelegramMessage(e,r,f,h,{replyMarkup:I}):h&&await this.sendTelegramMessage(e,r,h,{replyMarkup:I})}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:Fr(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,Fr(t)),await this.answerTelegramCallback(e,s,`Interrupt failed: ${Hr(Fr(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,Fr(t)),await this.answerTelegramCallback(e,s,`Steer failed: ${Hr(Fr(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=Vr(await this.service.aiSessionQueue(n,i)),o=(Array.isArray(r.items)?r.items:[]).map(e=>Vr(e)).filter(e=>"queued"===Ur(e.status)&&Ur(e.id));if(!o.length)return void await this.answerTelegramCallback(e,s,"Queue is empty");const d=o.slice(0,Yr).map((e,t)=>[{text:so(Ur(e.message),t),callbackData:this.aiSessionQueueDeleteItemCallbackData(n,i,Ur(e.id))}]);await this.sendTelegramMessage(e,t,"Delete queued message",{replyMarkup:Si(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,Fr(t)),await this.answerTelegramCallback(e,s,`Queue menu failed: ${Hr(Fr(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:Fr(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,Fr(t)),await this.answerTelegramCallback(e,s,`Delete failed: ${Hr(Fr(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(ui).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&&mi(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:Ma(t(n[1],"instance-app-menu").instanceId,"instanceId")}:{type:"instance-app-menu",instanceId:La(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:Ma(e.instanceId,"instanceId"),appId:Ma(e.appId,"appId")}}return{type:"launch-app",instanceId:La(i[1]),appId:La(i[2])}}const a=e.match(/^task_handoff:cp_p:([^:]+)$/);if(a){const e=t(a[1],"pending-decision");return{type:"pending-decision",routeId:Ma(e.routeId,"routeId"),decision:$a(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 Da(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 Ra(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=Qr(e),{instanceId:r,sessionId:o,turnId:d}=a,c=Number(Vr(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=no(r,o,d,t.id,s),p=this.telegramProgress.remember(l,c,i,{bridgeId:t.id,chatId:s});this.telegramProgressMessageTargets.set(qr(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:Hr(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(qr(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:Fr(e)},"telegram queued progress message replacement cleanup failed")}))}telegramReplyAiSessionTarget(e,t,s){const n=this.telegramProgressMessageTargets.get(qr(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=Lr(n,e,t);if(i){const n={...i,messageId:s};return this.telegramProgressMessageTargets.set(qr(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(qr(n.route.bridgeId,n.route.chatId,n.messageId||0))||Lr(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(qr(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=co(s.get(e)||e,a),o=oo(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:Wr(po(a))});continue}const d=io(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:Wr(po(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=no(e,a.id,d,i.bridgeId||"",i.chatSessionId),l=ao(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:Wr(po(a)),textPreview:Hr(o)});continue}const p=eo({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)||to(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:Wr(po(a)),textPreview:Hr(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:Fr(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:Hr(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:Hr(t?.lastText),progressHasPending:Boolean(t?.pending),latestTurn:Wr(po(a)),textPreview:Hr(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:Hr(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=co(s.get(e)||e,r),c=oo(r,d),l=io(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:no(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=ao(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=eo({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)||to(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:Hr(c)}),g&&(this.telegramProgressMessageTargets.set(qr(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(qr(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(qr(n.route.bridgeId,n.route.chatId,n.messageId))||Lr(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||po(t)?.id===e.owner.turnId||po(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:Pr({actionRows:n})});return a&&this.logInfo({bridgeId:e.bridgeId,chatId:e.chatSessionId,key:s,textPreview:Hr(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:Hr(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:Hr(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=d.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=d.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 Rr(e){return"object"==typeof e&&e&&"reply"in e?String(e.reply||""):""}function jr(e){return"object"==typeof e&&null!==e&&!0===e.accepted}function Dr(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 Pr(e){const t=Array.isArray(e?.actionRows)?e.actionRows:void 0;return Si(t||(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e]))}function Mr(e){const t=Number(e?.message_id);return Number.isInteger(t)?t:void 0}function $r(e,t,s){return[e,t,s||""].join(":")}function qr(e,t,s){return[e,t,String(s)].join(":")}function Lr(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 Ur(e){return"string"==typeof e?e:""}function Fr(e){return e instanceof Error?e.message:String(e)}function Br(e,t){return Boolean(e&&"object"==typeof e&&"code"in e&&e.code===t)}function Hr(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 Gr(e){const t=Jr(e.body.cardActionData||e.body.content),s=Jr(t.cardPrivateData||e.body.cardPrivateData||e.body.content);return{outTrackId:Ur(e.body.outTrackId),spaceId:Ur(e.body.spaceId),conversationType:e.conversationType,hasCallbackData:Boolean(e.callbackData),callbackDataPreview:Hr(e.callbackData,120),actionIds:[...Kr(s.actionIdList),...Kr(t.actionIdList),Ur(s.actionId),Ur(t.actionId),Ur(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 Wr(e){if(e)return{id:e.id,providerTurnId:e.providerTurnId,source:e.source,status:e.status,phase:e.phase,revision:e.revision,userPrompt:Hr(e.userPrompt,80),hasResponse:Boolean(e.lastMessage?.trim()||e.summary?.trim()),responsePreview:Hr(e.lastMessage||e.summary,80),updatedAt:e.updatedAt,observedAt:e.observedAt}}function Vr(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function Jr(e){if(e&&"object"==typeof e&&!Array.isArray(e))return e;if("string"==typeof e&&e.trim())try{return Vr(JSON.parse(e))}catch{return{}}return{}}function Kr(e){return Array.isArray(e)?e.map(e=>Ur(e)).filter(Boolean):[]}function Qr(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?io(s):""),sources:{resultTurnId:e.turnId,resultProviderTurnId:e.providerTurnId,actionTurnId:t?.turnId,actionProviderTurnId:t?.providerTurnId,sessionActiveTurnId:s?.activeTurnId,latestTurnId:s?po(s)?.id:void 0}}}function Xr(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:Si([[{text:"Allow",callbackData:t(e.id,"allow")},{text:"Skip",callbackData:t(e.id,"skip")},{text:"Deny",callbackData:t(e.id,"deny")}]])}}const Yr=5,Zr=48;function eo(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,Yr);return[...d.map((e,t)=>[{text:so(e.message,t),callbackData:s(e.id)}]),d.length?[{text:"Delete Queue",callbackData:n()},{text:"Cancel",callbackData:t}]:[{text:"Cancel",callbackData:t}]]}function to(e,t){if("approval"===t.phase)return{id:`${e}:ai:${t.id}`,kind:"approval"}}function so(e,t){const s=`${t+1}. `,n=e.replace(/\s+/g," ").trim()||"Queued message",i=Math.max(8,Zr-s.length),a=Array.from(n);return`${s}${a.length>i?`${a.slice(0,Math.max(1,i-3)).join("")}...`:n}`}function no(e,t,...s){return[e,t,...s].join(":")}function io(e){const t=po(e);return t?.id?t.id:Ur(e.activeTurnId)}function ao(e,t=""){const s=po(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),ro(e)].join("\0")}function ro(e){return(e.queue?.items||[]).map(e=>[e.id,e.status,e.message].join(":")).join("|")}function oo(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&&!lo(s,e)?n:""].filter(Boolean).join("\n\n")}(e,po(e));return s?`${t}\n${s}`:""}function co(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 lo(e,t){if("waiting"!==t.status||"approval"!==t.phase)return!1;const s=t.currentTool?.inputPreview?.trim();return Boolean(s&&e.includes(s))}function po(e){return(e.turns||[]).filter(e=>e.id||e.userPrompt?.trim()||e.lastMessage?.trim()||e.summary?.trim()).at(-1)}class uo{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&&mo(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||Ns(e),createdAt:(new Date).toISOString(),payload:t,scope:s.scope}}}function mo(e,t,s){return!e||e.has("*")||e.has(t)||e.has(s)}const go={"image/bmp":".bmp","image/gif":".gif","image/jpeg":".jpg","image/png":".png","image/webp":".webp"},ho=o.z.object({instanceId:o.z.string().trim().min(1).max(120),sessionId:o.z.string().trim().min(1).max(120),kind:o.z.enum(["image","file"]),name:o.z.string().trim().min(1).max(240),mime:o.z.string().trim().min(1).max(120),data:o.z.string().min(1).max(31457280)}).strict();class Io{items=new Map;ttlMs;constructor(e=864e5){this.ttlMs=e}upload(e){this.pruneExpired();const n=ho.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=go[a]||"";if("image"===n.kind&&!r)throw new Error(`Unsupported image type: ${a}`);const o="image"===n.kind?i.buffer.length>v:i.buffer.length>=w;if(!i.buffer.length||o)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_${d.randomUUID().replace(/-/g,"").slice(0,24)}`,l=new Date,p=new Date(l.getTime()+this.ttlMs),u=s.join(f.tmpdir(),"task-handoff-ai-session-attachments");t.mkdirSync(u,{recursive:!0});const m=function(e){return s.basename(e).replace(/[^\w.\- ()\u4e00-\u9fff]/g,"_").slice(0,160)||"attachment"}(n.name),g=s.join(u,`${c}-${m.endsWith(r)?m:`${m}${r}`}`);t.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=o.z.array(U).max(6).default([]).parse(e),i=n.map(e=>"runtime-path"===e.source.type?q.parse(e):this.requireAttachment(e,t,s));if(i.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>S)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 q.parse({id:n.id,kind:n.kind,name:n.name,mime:n.mime,size:n.size,source:n.source})}consumeAttachment(e){const s=this.items.get(e);if(s){this.items.delete(e);try{t.unlinkSync(s.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[s,n]of this.items)if(!(Date.parse(n.expiresAt)>e)){this.items.delete(s);try{t.unlinkSync(n.path)}catch{}}}}function fo(e){return e.readyState===(e.OPEN??1)}function yo(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 bo(e,t){e.send(t.data,{binary:t.isBinary})}function vo(e,t){if(fo(e))for(const s of t.splice(0))bo(e,s)}class wo{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 So=o.z.object({nodeId:o.z.string().trim().min(1).max(120)}).strict();function Ao(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 ko(){return`req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`}const To=new Map;function zo(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)>ma)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()-ma;for(const[e,t]of To)t<=i&&To.delete(e);const a=`${e.id}:${s.keyId}:${s.nonce}`;if(!s.nonce||To.has(a))throw Object.assign(new Error("Reverse tunnel nonce is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_NONCE_INVALID"});const r=ga("");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 o=t.url||`/api/node-agent/tunnel?nodeId=${encodeURIComponent(e.id)}`,c=ha(e.auth.secret,{keyId:s.keyId,method:"GET",pathWithQuery:o,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&&d.timingSafeEqual(s,n)}(s.signature,c))throw Object.assign(new Error("Reverse tunnel signature is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_SIGNATURE_INVALID"});To.set(a,Date.now()+ma)}function Eo(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 Co{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=Os.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===rs){const t=as.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(rs,t.data,{topic:"instances",scope:{...r,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(i===ws){const t=Ss.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(ws,t.data,{topic:"instances",scope:{...r,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(i===Qt||i===Xt){const t=i===Qt?Zt.safeParse(a):es.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=ko(),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:Eo(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=ko(),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:Eo(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 g.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(g.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=ko(),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=fo(t);const r=[],o=[];let d;const c=()=>{d&&(clearTimeout(d),d=void 0)},l=(s,i)=>{if(s.length>=n)return yo(e,1011,"WebSocket bridge pending frame limit exceeded."),void yo(t,1011,"WebSocket bridge pending frame limit exceeded.");s.push(i)};s.upstreamOpenTimeoutMs&&!a&&(d=setTimeout(()=>{a||i||(yo(e,1011,"Upstream websocket did not open."),yo(t))},s.upstreamOpenTimeoutMs)),e.on("open",()=>vo(e,o)),t.on("open",()=>{c(),a=!0,s.onUpstreamOpen?.(),vo(t,r)}),e.on("message",(e,s=!1)=>{const n={data:e,isBinary:Boolean(s)};fo(t)?bo(t,n):l(r,n)}),t.on("message",(t,s=!1)=>{const n={data:t,isBinary:Boolean(s)};fo(e)?bo(e,n):l(o,n)}),t.on("close",(t,n)=>{c(),s.onUpstreamClose?.(t,n),(a||i||!s.onUpstreamCloseBeforeOpen?.())&&yo(e,t,n)}),t.on("error",t=>{s.onUpstreamError?.(t),(a||i||!s.onUpstreamErrorBeforeOpen?.(t))&&yo(e)}),e.on("close",(e,n)=>{c(),i=!0,s.onClientClose?.(e,n),yo(t,e,n)}),e.on("error",e=>{c(),i=!0,s.onClientError?.(e),yo(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 No{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=fa(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 Ia({nodeId:e.id,keyId:Ao(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?ba(s,"/events",void 0,i):new p.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 wo,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 wo};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(fa(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 _o(e=function(){return process.env.TASK_HANDOFF_CONTROL_PLANE_DATA_DIR||s.join(f.homedir(),".config","task-handoff","control-plane")}()){const t=s.resolve(e);return{dataDir:t,settingsPath:s.join(t,"control-plane-settings.json"),projectsDir:s.join(t,"projects"),modelsDir:s.join(t,"models"),imagesDir:s.join(t,"images"),nodesDir:s.join(t,"nodes"),nodeRuntimesDir:s.join(t,"node-runtimes"),controlledInstancesDir:s.join(t,"controlled-instances"),chatSessionsDir:s.join(t,"chat-sessions"),chatBridgesDir:s.join(t,"chat-bridges"),triggersDir:s.join(t,"triggers"),nodeJoinInvitesDir:s.join(t,"node-join-invites"),authUsersDir:s.join(t,"auth-users"),authSessionsDir:s.join(t,"auth-sessions"),aiSessionUnreadDir:s.join(t,"ai-session-unread"),logsDir:s.join(t,"logs")}}class xo 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 Oo(e){return s.join(e,"owner.json")}function Ro(e){return s.join(e,"initializing.json")}function jo(e){return s.join(e,"recovering.json")}function Do(e){try{const s=JSON.parse(t.readFileSync(Oo(e),"utf8"));if(!Number.isInteger(s.pid)||Number(s.pid)<=0||"string"!=typeof s.hostname||!s.hostname||"string"!=typeof s.command||"string"!=typeof s.acquiredAt||!Number.isFinite(Date.parse(s.acquiredAt))||"string"!=typeof s.token||!s.token)return;return s}catch{return}}function Po(e){if(!Number.isInteger(e)||Number(e)<=0)return!1;try{return process.kill(Number(e),0),!0}catch(e){return"EPERM"===e.code}}function Mo(e,n){const i=s.join(e,`.owner-${n.token}.tmp`);t.writeFileSync(i,`${JSON.stringify(n,null,2)}\n`,{encoding:"utf8",mode:384,flag:"wx"});const a=$o(e);if(a?.token!==n.token)throw qo(i),new Error(`Process lock initialization was superseded: ${e}`);t.renameSync(i,Oo(e)),qo(Ro(e))}function $o(e){try{return JSON.parse(t.readFileSync(Ro(e),"utf8"))}catch{return}}function qo(e){try{t.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}function Lo(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,20)}function Uo(e){try{const s=$o(e),n=Date.parse(s?.startedAt||"");return Number.isFinite(n)?Math.max(0,Date.now()-n):Math.max(0,Date.now()-t.statSync(e).mtimeMs)}catch{return 0}}function Fo(e,n){try{return t.writeFileSync(jo(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=jo(e),i=Bo(n),a=Date.parse(i?.startedAt||"");if(Po(i?.pid)||Number.isFinite(a)&&Date.now()-a<2e3)return!1;const r=s.join(e,`.recovering-${d.randomUUID()}.stale`);try{t.renameSync(n,r)}catch(e){if(["ENOENT","EEXIST"].includes(e.code||""))return!1;throw e}const o=Bo(r);if(o?.token===i?.token)return qo(r),!0;try{t.linkSync(r,n)}catch(e){if("EEXIST"!==e.code)throw e}finally{qo(r)}return!1}(e)&&Fo(e,n);if("ENOENT"===i.code)return!1;throw i}}function Bo(e){try{return JSON.parse(t.readFileSync(e,"utf8"))}catch{return}}function Ho(e,t){return Bo(jo(e))?.token===t}function Go(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 Wo=o.z.string().trim().min(1),Vo=o.z.object({id:Wo}).strict(),Jo=o.z.object({id:Wo,keyId:Wo}).strict(),Ko=o.z.object({id:Wo,runtimeId:Wo}).strict(),Qo=o.z.object({id:Wo,folderId:Wo}).strict(),Xo=o.z.object({id:Wo,sessionId:Wo}).strict(),Yo=o.z.object({id:Wo,sessionId:Wo,queueId:Wo}).strict(),Zo=o.z.object({configHash:Wo}).strict(),ed=o.z.object({id:Wo,configHash:Wo}).strict(),td=o.z.object({id:Wo,sessionId:Wo,configHash:Wo}).strict(),sd=o.z.object({path:o.z.string().optional(),depth:o.z.coerce.number().int().min(0).max(32).optional()}).strict(),nd=o.z.object({ids:o.z.array(o.z.string().trim().min(1).max(120)).default([])}).strict(),id=o.z.object({nodeId:o.z.string().trim().min(1).max(120),modelId:o.z.string().trim().min(1).max(120).optional()}).strict(),ad=o.z.object({source:o.z.object({channel:ks,bridgeId:o.z.string().trim().min(1).max(120).optional(),chatSessionId:o.z.string().trim().min(1).max(240),userId:o.z.string().trim().max(240).optional()}).strict(),action:o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal("ai-session"),index:o.z.number().int().nonnegative()}).strict(),o.z.object({type:o.z.literal("instance-app-menu"),instanceId:o.z.string().trim().min(1).max(120)}).strict(),o.z.object({type:o.z.literal("launch-app"),instanceId:o.z.string().trim().min(1).max(120),appId:o.z.string().trim().min(1).max(120)}).strict(),o.z.object({type:o.z.literal("pending-decision"),routeId:o.z.string().trim().min(1).max(120),decision:o.z.enum(["allow","deny","skip"])}).strict()])}).strict();function rd(e){const t=ad.parse(e);if(!t.action)throw new Error("Chat gateway action is required.");return{source:t.source,action:t.action}}const od=o.z.object({id:o.z.string().trim().min(1),direction:o.z.enum(["import","export"]),preset:o.z.string().trim().min(1).max(120)}),dd=o.z.object({id:o.z.string().trim().min(1),appId:o.z.string().trim().min(1)}).strict(),cd=o.z.object({id:o.z.string().trim().min(1),jobId:o.z.string().trim().min(1)}).strict();const ld=o.z.object({appId:o.z.string().trim().min(1).max(120).default("terminal-tty"),cwdFolderId:o.z.string().trim().min(1).max(120).optional(),options:o.z.record(o.z.string(),o.z.unknown()).default({})}).strict().default({appId:"terminal-tty",options:{}}),pd=o.z.object({title:o.z.string().trim().min(1).max(120)}).strict(),ud=o.z.object({}).strict();function md(e){const{app:t,service:s,events:n,appSessionAggregator:i,aiSessionAggregator:a,aiSessionUnread:r,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(Vo.parse(e.params).id)})),e.patch("/api/projects/:id",async e=>{const n=t.updateProject(Vo.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=Vo.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=nd.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(Vo.parse(e.params).id)})),e.patch("/api/models/:id",async e=>{const n=await t.updateModel(Vo.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=Vo.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}=id.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}=id.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}=id.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/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(Vo.parse(e.params).id)})),e.patch("/api/images/:id",async e=>{const n=t.updateImage(Vo.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=Vo.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}){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(Vo.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(Vo.parse(e.params).id)})),e.get("/api/nodes/:id/settings/external-listener",async e=>({data:await t.getLocalNodeExternalListener(Vo.parse(e.params).id)})),e.patch("/api/nodes/:id/settings/external-listener",async e=>{const n=Vo.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=>{const n=Vo.parse(e.params).id,i=await t.checkNodeUpdate(n,Et.parse(e.body));return s.publish("node.update.checked",{nodeId:n,target:i.target}),{data:i}}),e.post("/api/nodes/:id/updates/apply",async(e,n)=>{const i=Vo.parse(e.params).id,a=await t.applyNodeUpdate(i,Et.parse(e.body));return s.publish("node.update.queued",{nodeId:i,updateJobId:a.id,target:a.target}),n.code(202).send({data:a})}),e.post("/api/nodes/:id/pairing/invites",async(e,n)=>{const i=Vo.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=Vo.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=Vo.parse(e.params);return{data:await t.listNodeRemoteControlPlanes(s.id)}}),e.delete("/api/nodes/:id/remotes/:keyId",async e=>{const n=Jo.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(Vo.parse(e.params).id)})),e.post("/api/nodes/:id/runtimes",async(e,n)=>{const i=Vo.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=Ko.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=Ko.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=Ko.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(Vo.parse(e.params).id)})),e.get("/api/nodes/:id/images/catalog",async e=>({data:await t.listNodeImageAvailability(Vo.parse(e.params).id)})),e.get("/api/nodes/:id/local-folders",async e=>({data:await t.listNodeLocalFolders(Vo.parse(e.params).id)})),e.get("/api/nodes/:id/folders/tree",async e=>{const s=Vo.parse(e.params),n=sd.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=Vo.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=Qo.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(Vo.parse(e.params).id)})),e.patch("/api/nodes/:id",async e=>{const n=t.updateNode(Vo.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=Vo.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=So.parse(t.query),a=s.requireNode(i.nodeId);zo(a,t),n.attach(a.id,e),e.send(JSON.stringify({type:"control-plane.hello",protocolVersion:Be,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=So.parse(t.query),a=s.requireNode(i.nodeId);zo(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=So.parse(t.query),a=s.requireNode(i.nodeId);zo(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(Vo.parse(e.params).id)})),e.get("/api/controlled-instances/:id/metrics",async e=>({data:await t.instanceResourceMetrics(Vo.parse(e.params).id)})),e.get("/api/controlled-instances/:id/apps/management",async e=>({data:await t.instanceAppManagement(Vo.parse(e.params).id)})),e.post("/api/controlled-instances/:id/apps/:appId/install",async e=>{const s=dd.parse(e.params),n=St.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=dd.parse(e.params),n=St.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=cd.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(Vo.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=Vo.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(Vo.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(Vo.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(Vo.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(Vo.parse(e.params).id);return s.publish("instance.image-provisioning-retried",{instanceId:n.id}),{data:n}}),e.get("/api/config-sync/presets",async()=>({data:t.listConfigSyncPresets()})),e.post("/api/controlled-instances/:id/config-sync/:direction/:preset",async e=>{const n=od.parse(e.params),i=await t.syncInstanceConfig(n.id,n.direction,n.preset);return s.publish("instance.config-synced",{instanceId:n.id,direction:n.direction,preset:n.preset}),{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:r}){e.get("/api/app-sessions",async e=>{const t=o.z.object({refresh:o.z.string().optional(),includeTombstones:o.z.string().optional(),instanceId:o.z.string().trim().min(1).optional(),streamId:o.z.string().trim().min(1).optional(),sinceRevision:o.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=Vo.parse(e.params),i=ld.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=Xo.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=Xo.parse(e.params),i=pd.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=Vo.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=Vo.parse(e.params);return{data:await t.listAiSessionHistory(s.id)}}),e.get("/api/controlled-instances/:id/ai-sessions/history/:sessionId",async e=>{const s=Xo.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=Xo.parse(e.params);ud.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=Xo.parse(e.params),i=ne.parse(e.body||{}),a=r.resolveRefs(i.attachments,n.id,n.sessionId),o=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:o}}),e.get("/api/controlled-instances/:id/ai-sessions/:sessionId/mentions",async e=>{const s=Xo.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=Xo.parse(e.params),n=ee.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=Xo.parse(e.params),i=G.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=Xo.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=Yo.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=Yo.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=Yo.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=Xo.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=Xo.parse(e.params),i=ie.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=Xo.parse(e.params),r=o.z.object({sessionUpdatedAt:o.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,r.sessionUpdatedAt);return l?(s.publish(z,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:r,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=Zo.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=Zo.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=Vo.parse(e.params);return{data:await t.listInstanceTriggers(s.id)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/triggers",async e=>{const n=Xo.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=td.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=ed.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(Vo.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=Vo.parse(n.params).id,a=t.requireChatBridge(i),r=yi.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=Vo.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=Vo.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(Vo.parse(e.params).id)}})),e.post("/api/chat-gateway/messages",{bodyLimit:67108864},async e=>({data:await t.handleChatGatewayMessage(Es.parse(e.body))})),e.post("/api/chat-gateway/actions",async e=>({data:await t.handleChatGatewayAction(rd(e.body))})),e.get("/api/pending-routes",async()=>({data:await t.listPendingRoutes()}))}({app:t,service:s,chatGateway:d})}const gd=new Set(["connection","content-length","keep-alive","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding","upgrade"]),hd=new Set(["content-encoding"]);function Id(e){return Object.fromEntries(Object.entries(e).filter(([e,t])=>!gd.has(e.toLowerCase())&&void 0!==t).map(([e,t])=>[e,Array.isArray(t)?t.join(", "):String(t)]))}function fd(e){return Object.fromEntries(Object.entries(Id(e)).filter(([e])=>{const t=e.toLowerCase();return"host"!==t&&"cookie"!==t&&"authorization"!==t&&(!t.startsWith("sec-websocket-")||"sec-websocket-origin"===t)}))}function yd(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 bd(e,t,s){for(const[s,n]of Object.entries(t.headers)){const t=s.toLowerCase();gd.has(t)||hd.has(t)||e.header(s,n)}if(!t.body)return e.code(t.status).send();const n=g.Readable.fromWeb(t.body);return e.raw.once("close",()=>n.destroy()),e.code(t.status).send(s?n.pipe(s):n)}function vd(e){return`/instances/${encodeURIComponent(e)}`}function wd(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 g.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()}})}(vd(s)):void 0;return bd(e,t,i)}function Sd(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 Ad(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 kd(e){return new URL(e,"http://control-plane.local").searchParams.get("token")||""}function Td(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 zd{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===A?ke:e.type===k?Te:e.type===T?ze:void 0;if(!t)return!1;const s=t.safeParse(e.payload);return s.success?(e.type===A&&this.applySnapshot(s.data),e.type===k&&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:A,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:k,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 Ce.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:!0,events:[]});if(e.sinceRevision===s)return Ce.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 Ce.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===A&&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===A)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:be.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===k){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=ye.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:be.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:A,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:A,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 Ed=o.z.object({id:o.z.string().trim().min(1),instanceId:o.z.string().trim().min(1).max(160),sessionId:o.z.string().trim().min(1).max(120),unread:o.z.boolean(),lastStatus:o.z.enum(["running","waiting","idle","failed"]),sessionUpdatedAt:o.z.string().datetime(),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict();class Cd{records;bySession=new Map;listener;constructor(e,t={}){this.records=new Ls(e.aiSessionUnreadDir,{schema:Ed}),this.listener=t.onChanged}init(){this.records.init(),this.bySession.clear();for(const e of this.records.list())this.bySession.set(_d(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(_d(e,t.id))?.unread||!1}))}}markRead(e,t,s){const n=this.bySession.get(_d(e,t));return n&&n.sessionUpdatedAt===s&&n.unread?Nd(this.store({...n,unread:!1,updatedAt:(new Date).toISOString()},!1)):n?Nd(n):void 0}removeInstance(e){for(const t of[...this.bySession.values()])t.instanceId===e&&this.deleteRecord(t)}reconcileSession(e,t){const s=_d(e,t.id),n=this.bySession.get(s),i=(new Date).toISOString();if(!n)return void this.store({id:xd(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(_d(s.instanceId,s.sessionId),s),t&&this.listener?.(Nd(s)),s}deleteRecord(e){this.records.delete(e.id),this.bySession.delete(_d(e.instanceId,e.sessionId))}}function Nd(e){return N.parse({instanceId:e.instanceId,sessionId:e.sessionId,unread:e.unread,sessionUpdatedAt:e.sessionUpdatedAt,updatedAt:e.updatedAt})}function _d(e,t){return`${e}\0${t}`}function xd(e,t){return`ais_unread_${d.createHash("sha256").update(_d(e,t)).digest("hex").slice(0,32)}`}class Od{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===nn?gn:e.type===an?hn:e.type===rn?In:void 0;if(!t)return!1;const s=t.safeParse(e.payload);return s.success?(e.type===nn&&this.applySnapshot(s.data),e.type===an&&this.applyPatch(s.data),e.type===rn&&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:nn,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:an,payload:e})}applyRemoved(e){return this.apply({type:rn,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 fn.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:!0,events:[]});if(e.sinceRevision===s)return fn.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 fn.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===nn&&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===nn)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:pn.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===an){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=yn(n,s.generatedAt);return{kind:"applied",projection:pn.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:nn,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:nn,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 yn(e.sessions.filter(e=>Ni("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 Rd(e){return e.raw[0].replace(/\\\$\{/g,"${")}class jd{states=new Map;events;constructor(e){this.events=e}handle(e){if(e.type===ws){const t=Ss.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===Qt){const t=Zt.safeParse(e.payload);return void(t.success&&this.applyOutput(t.data))}if(e.type===Xt){const t=es.safeParse(e.payload);t.success&&this.applyFinished(t.data)}}snapshots(){return[...this.states.values()].filter(e=>e.progress).map(e=>ts.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 $d,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=Pd(i);e.set(n,{...o,status:r,...d||{}})}}(s.layers,s.terminal.text()),s.progress=Dd(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=Dd(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 $d,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=Dd(e),this.events.publish("image.pull.progress",e.progress,{topic:"instances",scope:{instanceId:e.identity.instanceId}}),t&&(e.progress=Dd(e)))}}function Dd(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 ts.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 Pd(e){const t=e.match(/([\d.]+)\s*([kmgt]?i?b)\s*\/\s*([\d.]+)\s*([kmgt]?i?b)/i);if(t)return{current:Md(t[1],t[2]),total:Md(t[3],t[4])}}function Md(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 $d{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 qd=o.z.object({code:o.z.string().trim().min(1),message:o.z.string(),details:o.z.record(o.z.string(),o.z.unknown()).optional(),retryable:o.z.boolean().optional()}).strict(),Ld=o.z.object({error:qd}).strict();function Ud(e){const t=process.env[e]?.trim();return t||void 0}function Fd(){try{const e=("undefined"==typeof document?require("url").pathToFileURL(__filename).href:b&&"SCRIPT"===b.tagName.toUpperCase()&&b.src||new URL("cli.js",document.baseURI).href)?s.dirname(n.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:b&&"SCRIPT"===b.tagName.toUpperCase()&&b.src||new URL("cli.js",document.baseURI).href)):__dirname,i=[s.resolve(e,"..","package.json"),s.resolve(e,"..","..","..","package.json")].find(e=>t.existsSync(e));return i&&JSON.parse(t.readFileSync(i,"utf8")).version||"unknown"}catch{return"unknown"}}function Bd(){return{component:"control-plane",packageName:"@task-handoff/control-plane",packageVersion:Fd(),protocolVersion:Be,buildId:Ud("TASK_HANDOFF_BUILD_ID"),builtAt:Ud("TASK_HANDOFF_BUILT_AT"),gitCommit:Ud("TASK_HANDOFF_GIT_COMMIT"),imageRef:Ud("TASK_HANDOFF_IMAGE_REF"),imageDigest:Ud("TASK_HANDOFF_IMAGE_DIGEST")}}const Hd={".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 Gd(e,n){const i=s.resolve(e),a=s.resolve(i,n);if(a===i||a.startsWith(`${i}${s.sep}`))return t.existsSync(a)&&t.statSync(a).isFile()?a:void 0}function Wd(e,n,i){const a=Gd(n,i);return a?(e.header("cache-control","no-store"),e.type(Hd[s.extname(a).toLowerCase()]||"application/octet-stream"),e.send(t.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 Vd(e){if(e instanceof o.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 Jd(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 Kd(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"}}:s.includes("/config-sync/")?{action:"update",resource:{type:"instance"}}:{action:n,resource:{type:"instance"}}:s.startsWith("/api/projects")?{action:n,resource:{type:"project"}}:s.startsWith("/api/images")?{action:n,resource:{type:"runtime"}}:s.startsWith("/api/triggers")?{action:n,resource:{type:"trigger"}}:{action:"read",resource:{type:"control-plane-settings"}}}async function Qd(e={}){const n=_o(e.dataDir),o=i({logger:e.logger??!0}),d=new Ea(n,{...e.service,logger:o.log}),c=new sn(n,e.auth),l=new uo,p=new jd(l),u=new Cd(n,{onChanged:e=>queueMicrotask(()=>l.publish(z,e,{topic:"ai.sessions",scope:{instanceId:e.instanceId,sessionId:e.sessionId}}))});u.init();const m=new zd({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 Od({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 Io,I=new Co(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(I),d.init(),await d.syncLocalNodeConnection().catch(()=>{}),c.init();const f=new Or(d,e.service?.fetchImpl,{aiSessions:m,logger:o.log});f.startEnabled();const y=new No(d,I,{safetyIntervalMs:Number(process.env.TASK_HANDOFF_EVENT_CONNECTION_SAFETY_INTERVAL_MS)||void 0,logger:o.log});y.start(),await o.register(a),await o.register(r),o.addHook("onClose",async()=>{p.close(),y.stop(),f.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=Vd(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(Ld.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 Jd(c,e.cookies[Bs]),s=Kd(e.method,e.url);return void(s&&Go(t||{type:"system",reason:"auth-disabled"},s.action,s.resource))}const s=await Jd(c,e.cookies[Bs]);if(!s)return t.code(401).send({error:{code:"CONTROL_PLANE_AUTH_REQUIRED",message:"Sign in to access the Control Plane."}});const n=Kd(e.method,e.url);n&&Go(s,n.action,n.resource)});const w=s.resolve(e.staticDir||process.env.TASK_HANDOFF_CONTROL_PLANE_STATIC_DIR||s.resolve(process.cwd(),"packages","control-plane-ui","dist"));return t.existsSync(w)&&(o.get("/assets/*",async(e,t)=>{const n=e.params["*"];return Wd(t,w,s.join("assets",n||""))}),o.get("/favicon.ico",async(e,t)=>Gd(w,"favicon.ico")?Wd(t,w,"favicon.ico"):t.code(204).send())),o.get("/api/health",async()=>({data:{ok:!0,role:"control-plane",protocolVersion:Be,build:Bd(),dataDir:n.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"),Rd`#!/bin/sh
1
+ "use strict";var e=require("commander"),t=require("node:fs"),s=require("node:path"),n=require("node:url"),i=require("fastify"),a=require("@fastify/cookie"),r=require("@fastify/websocket"),o=require("zod"),d=require("node:crypto"),c=require("node:util"),l=require("write-file-atomic"),p=require("ws"),u=require("node:http"),m=require("node:net"),g=require("node:stream"),h=require("telegramify-markdown"),f=require("node:fs/promises"),I=require("node:os"),y=require("dingtalk-stream"),b="undefined"!=typeof document?document.currentScript:null;const v=20971520,w=512e3,S=41943040,A="ai-session.snapshot",k="ai-session.patch",T="ai-session.removed",z="ai-session.unread.updated",E=o.z.string().trim().min(1).max(80),C=o.z.enum(["running","waiting","idle","failed"]),N=o.z.object({instanceId:o.z.string().trim().min(1).max(160),sessionId:o.z.string().trim().min(1).max(120),unread:o.z.boolean(),sessionUpdatedAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict(),_=o.z.enum(["thinking","tool","editing","approval","responding","unknown"]),x=o.z.object({id:o.z.string().trim().min(1).max(240).optional(),kind:o.z.string().trim().min(1).max(80).optional(),name:o.z.string().trim().min(1).max(120),inputPreview:o.z.string().trim().max(500).optional(),startedAt:o.z.string().datetime().optional()}).strict(),O=o.z.enum(["pending-init","running","interrupted","completed","errored","shutdown","not-found"]),R=o.z.enum(["started","interacted","interrupted"]),j=o.z.object({threadId:o.z.string().trim().min(1).max(240),path:o.z.string().trim().max(4096).optional(),status:O,activity:R.optional(),message:o.z.string().trim().max(1e3).optional(),updatedAt:o.z.string().datetime()}).strict(),D=o.z.object({toolCalls:o.z.number().int().min(0).default(0),edits:o.z.number().int().min(0).default(0),approvals:o.z.number().int().min(0).default(0)}).strict().default({toolCalls:0,edits:0,approvals:0}),P=o.z.object({send:o.z.boolean().optional(),interrupt:o.z.boolean().optional(),approval:o.z.boolean().optional()}).strict(),M=o.z.enum(["image","file"]),$=o.z.object({id:o.z.string().trim().min(1).max(120),kind:M,name:o.z.string().trim().min(1).max(240),mime:o.z.string().trim().min(1).max(120),size:o.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER)}),q=o.z.union([$.extend({source:o.z.object({type:o.z.literal("inline"),encoding:o.z.literal("base64"),data:o.z.string().min(1).max(31457280)}).strict()}).strict().superRefine((e,t)=>{const s="image"===e.kind?v:w;(e.size<=0||("file"===e.kind?e.size>=s:e.size>s))&&t.addIssue({code:o.z.ZodIssueCode.custom,path:["size"],message:`Inline ${e.kind} must be between 1 byte and ${s} bytes.`})}),$.extend({source:o.z.object({type:o.z.literal("runtime-path"),path:o.z.string().trim().min(1).max(4096)}).strict()}).strict()]),L=$.extend({sourceType:o.z.enum(["inline","runtime-path"])}).strict(),U=o.z.union([o.z.object({id:o.z.string().trim().min(1).max(120),kind:M.optional(),source:o.z.object({type:o.z.literal("upload-ref")}).strict().default({type:"upload-ref"})}).strict(),o.z.object({id:o.z.string().trim().min(1).max(120),kind:M,name:o.z.string().trim().min(1).max(240),mime:o.z.string().trim().min(1).max(120),size:o.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),source:o.z.object({type:o.z.literal("runtime-path"),path:o.z.string().trim().min(1).max(4096)}).strict()}).strict()]),F=o.z.enum(["auto","queue","steer","immediate"]),B=o.z.enum(["ask","auto-review","full-access"]),H=o.z.enum(["review","rename","goal","compact"]),G=o.z.object({command:H,argument:o.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.`})}),W=o.z.object({command:H,value:o.z.string().max(4e3).optional(),turnId:o.z.string().trim().min(1).max(240).optional()}).strict();o.z.enum(["skill","app","plugin"]);const V=o.z.object({name:o.z.string().trim().min(1).max(240),path:o.z.string().trim().min(1).max(4096)}),J=o.z.discriminatedUnion("kind",[V.extend({kind:o.z.literal("skill"),path:o.z.string().trim().min(1).max(4096).refine(e=>e.startsWith("/"),"Skill paths must be absolute.")}).strict(),V.extend({kind:o.z.literal("app"),path:o.z.string().trim().min(7).max(4096).regex(/^app:\/\/[^\s/]+$/,"App paths must use app://<id>.")}).strict(),V.extend({kind:o.z.literal("plugin"),path:o.z.string().trim().min(10).max(4096).regex(/^plugin:\/\/[^\s/]+$/,"Plugin paths must use plugin://<id>.")}).strict()]),K=o.z.array(J).max(20).default([]),Q=o.z.enum(["plugin","skill","file","directory","app"]),Y=o.z.object({kind:Q,name:o.z.string().trim().min(1).max(240),description:o.z.string().trim().max(1e3).optional(),path:o.z.string().trim().min(1).max(4096),icon:o.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."})}),X=o.z.object({category:o.z.enum(["skills","plugins","apps","files"]),code:o.z.string().trim().min(1).max(160),message:o.z.string().trim().min(1).max(2e3)}).strict(),Z=o.z.object({sessionId:o.z.string().trim().min(1).max(120),providerSessionId:o.z.string().trim().min(1).max(240),cwd:o.z.string().trim().min(1).max(4096),candidates:o.z.array(Y).max(1e3).default([]),diagnostics:o.z.array(X).max(20).default([])}).strict(),ee=o.z.object({query:o.z.string().trim().max(240).default("")}).strict(),te=o.z.object({sessionId:o.z.string().trim().min(1).max(120),cwd:o.z.string().trim().min(1).max(4096),query:o.z.string().trim().max(240),requestId:o.z.string().trim().min(1).max(160),candidates:o.z.array(Y.refine(e=>"file"===e.kind||"directory"===e.kind,"File search only returns files and directories.")).max(200).default([]),complete:o.z.boolean().default(!1)}).strict(),se=o.z.object({message:o.z.string().trim().min(1).max(2e4),mode:F.optional(),permissionMode:B.optional()});se.extend({attachments:o.z.array(q).max(6).default([]),references:K}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>S&&t.addIssue({code:o.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})});const ne=se.extend({attachments:o.z.array(U).max(6).default([]),references:K}).strict(),ie=o.z.object({decision:o.z.enum(["allow","deny","skip"])}).strict();o.z.object({queueIds:o.z.array(o.z.string().trim().min(1).max(120)).max(100)}).strict(),o.z.object({code:o.z.string().trim().min(1).max(160),message:o.z.string(),instanceId:o.z.string().trim().min(1).max(160).optional(),nodeId:o.z.string().trim().min(1).max(160).optional(),route:o.z.string().trim().min(1).max(4096).optional()}).strict();const ae=o.z.object({id:o.z.string().trim().min(1).max(120),message:o.z.string().trim().min(1).max(2e4),attachments:o.z.array(L).max(6).default([]),references:K,permissionMode:B.optional(),status:o.z.enum(["queued","sending","failed"]).default("queued"),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime(),error:o.z.string().trim().max(4e3).optional()}).strict(),re=o.z.object({pendingCount:o.z.number().int().min(0).default(0),items:o.z.array(ae).max(100).default([])}).strict().default({pendingCount:0,items:[]}),oe=o.z.enum(["control","realtime","adapter-snapshot","transcript-tail","transcript-scan","process-scan","app-session"]),de=o.z.object({id:o.z.string().trim().min(1).max(240),status:o.z.enum(["running","completed"]),startedAt:o.z.string().datetime().optional(),completedAt:o.z.string().datetime().optional()}).strict(),ce=o.z.object({id:o.z.string().trim().min(1).max(240),providerTurnId:o.z.string().trim().max(240).optional(),source:oe.optional(),userPrompt:o.z.string().trim().optional(),status:o.z.enum(["queued","running","waiting","completed","failed"]).default("running"),phase:_.optional(),summary:o.z.string().trim().max(1e3).optional(),lastMessage:o.z.string().trim().optional(),lastMessageItemId:o.z.string().trim().max(240).optional(),contextCompactions:o.z.array(de).max(20).optional(),revision:o.z.number().int().min(0).default(0),sourcePriority:o.z.number().int().min(0).max(100).optional(),snapshotVersion:o.z.number().int().min(0).optional(),observedAt:o.z.string().datetime().optional(),startedAt:o.z.string().datetime().optional(),updatedAt:o.z.string().datetime().optional(),completedAt:o.z.string().datetime().optional()}).strict(),le=ce.pick({id:!0,providerTurnId:!0,userPrompt:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,contextCompactions:!0,startedAt:!0,updatedAt:!0,completedAt:!0}).strict(),pe=o.z.object({id:o.z.string().trim().min(1).max(120),agent:E,appSessionId:o.z.string().trim().max(120).optional(),appId:o.z.string().trim().max(120).optional(),providerSessionId:o.z.string().trim().max(240).optional(),providerMeta:o.z.record(o.z.string(),o.z.unknown()).optional(),appBindingKeys:o.z.array(o.z.string().trim().min(1).max(240)).max(20).optional(),actions:P.optional(),activeTurnId:o.z.string().trim().max(240).optional(),title:o.z.string().trim().max(240).optional(),cwd:o.z.string().trim().max(4096).optional(),userPrompt:o.z.string().trim().optional(),turns:o.z.array(ce).max(50).optional(),status:C.default("running"),phase:_.default("unknown"),summary:o.z.string().trim().max(1e3).optional(),lastMessage:o.z.string().trim().optional(),lastMessageItemId:o.z.string().trim().max(240).optional(),currentTool:x.optional(),toolCallsSinceLastMessage:o.z.number().int().min(0).default(0),subAgents:o.z.array(j).max(50).default([]),transcriptPath:o.z.string().trim().max(4096).optional(),transcriptSize:o.z.number().int().min(0).optional(),startedAt:o.z.string().datetime(),updatedAt:o.z.string().datetime(),completedAt:o.z.string().datetime().optional(),error:o.z.string().trim().max(4e3).optional(),counters:D,queue:re}).strict(),ue=o.z.object({session:pe,provider:o.z.string().trim().min(1).max(80),action:o.z.enum(["send","queue","steer","interrupt","approval"]),decision:o.z.enum(["allow","deny","skip"]).optional(),turnId:o.z.string().trim().min(1).max(240).optional(),providerTurnId:o.z.string().trim().min(1).max(240).optional(),queueId:o.z.string().trim().min(1).max(120).optional()}).strict(),me=o.z.object({id:o.z.string().trim().min(1).max(120),agent:o.z.enum(["codex","claude"]),providerSessionId:o.z.string().trim().min(1).max(240),title:o.z.string().trim().max(240).optional(),userPrompt:o.z.string().trim().optional(),lastMessage:o.z.string().trim().optional(),cwd:o.z.string().trim().min(1).max(4096),lastActiveAt:o.z.string().datetime(),archivedAt:o.z.string().datetime()}).strict();o.z.object({schemaVersion:o.z.literal(1).default(1),items:o.z.array(me).max(50).default([])}).strict();const ge=o.z.object({items:o.z.array(me).max(50).default([])}).strict(),he=o.z.object({item:me,turns:o.z.array(le).max(50).default([])}).strict(),fe=o.z.object({disposition:o.z.enum(["resumed","already-open"]),aiSessionId:o.z.string().trim().min(1).max(120),providerSessionId:o.z.string().trim().min(1).max(240),appSessionId:o.z.string().trim().min(1).max(120)}).strict(),Ie=pe.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}),ye=o.z.object({runningCount:o.z.number().int().min(0).default(0),waitingCount:o.z.number().int().min(0).default(0),staleCount:o.z.number().int().min(0).default(0),sessions:o.z.array(Ie).default([]),updatedAt:o.z.string().datetime()}).strict(),be=o.z.object({streamId:o.z.string().trim().min(1).max(240),revision:o.z.number().int().min(0),lastEventAt:o.z.string().datetime(),snapshot:ye}).strict(),ve=ye.pick({runningCount:!0,waitingCount:!0,staleCount:!0,updatedAt:!0}).extend({idleCount:o.z.number().int().min(0).default(0),problemCount:o.z.number().int().min(0).default(0),revision:o.z.number().int().min(0).optional()});function we(e,t={}){const s=e.sessions.filter(e=>"idle"===e.status).length,n=e.sessions.filter(e=>"failed"===e.status).length;return ve.parse({runningCount:e.runningCount,waitingCount:e.waitingCount,staleCount:e.staleCount,idleCount:s,problemCount:n,updatedAt:e.updatedAt,revision:t.revision})}const Se=o.z.enum(["provider-event","app-session-created","app-session-updated","app-session-deleted","discovery-scan","control-action","heartbeat-sync","startup"]),Ae=o.z.object({streamId:o.z.string().trim().min(1).max(240),instanceId:o.z.string().trim().min(1).max(160),nodeId:o.z.string().trim().min(1).max(160).optional(),revision:o.z.number().int().min(0),previousRevision:o.z.number().int().min(0).optional(),traceId:o.z.string().trim().min(1).max(160),generatedAt:o.z.string().datetime(),reason:Se}).strict(),ke=o.z.object({meta:Ae,snapshot:ye}).strict(),Te=o.z.object({meta:Ae,upserted:o.z.array(Ie).default([]),removed:o.z.array(o.z.string().trim().min(1).max(120)).default([])}).strict(),ze=o.z.object({meta:Ae,sessionIds:o.z.array(o.z.string().trim().min(1).max(120)).default([]),expiresAt:o.z.string().datetime()}).strict(),Ee=o.z.object({instanceId:o.z.string().trim().min(1).max(160),nodeId:o.z.string().trim().min(1).max(160).optional(),sessionId:o.z.string().trim().min(1).max(120),providerSessionId:o.z.string().trim().min(1).max(240),turnId:o.z.string().trim().min(1).max(240),itemId:o.z.string().trim().min(1).max(240),delta:o.z.string().min(1),generatedAt:o.z.string().datetime()}).strict(),Ce=o.z.object({streamId:o.z.string().trim().min(1).max(240),instanceId:o.z.string().trim().min(1).max(160),sinceRevision:o.z.number().int().min(0),latestRevision:o.z.number().int().min(0),earliestRetainedRevision:o.z.number().int().min(0),syncRequired:o.z.boolean().default(!1),events:o.z.array(o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal(A),payload:ke}).strict(),o.z.object({type:o.z.literal(k),payload:Te}).strict(),o.z.object({type:o.z.literal(T),payload:ze}).strict()])).default([])}).strict(),Ne=o.z.object({source:oe,sourcePriority:o.z.number().int().min(0).max(100).optional(),observedAt:o.z.string().datetime().optional(),snapshotVersion:o.z.number().int().min(0).optional()}).strict(),_e=Ne.extend({type:o.z.literal("snapshot"),agent:o.z.string().trim().min(1).max(80),appSessionId:o.z.string().trim().max(120).optional(),appId:o.z.string().trim().max(120).optional(),providerSessionId:o.z.string().trim().max(240).optional(),providerMeta:o.z.record(o.z.string(),o.z.unknown()).optional(),appBindingKeys:o.z.array(o.z.string().trim().min(1).max(240)).max(20).optional(),actions:P.optional(),title:o.z.string().trim().max(240).optional(),cwd:o.z.string().trim().max(4096).optional(),activeTurnId:o.z.string().trim().max(240).optional(),userPrompt:o.z.string().trim().optional(),turns:o.z.array(ce).max(50).optional(),status:C.optional(),phase:_.optional(),summary:o.z.string().trim().max(1e3).optional(),lastMessage:o.z.string().trim().optional(),lastMessageItemId:o.z.string().trim().max(240).optional(),currentTool:x.optional(),toolCallsSinceLastMessage:o.z.number().int().min(0).optional(),subAgents:o.z.array(j).max(50).optional(),transcriptPath:o.z.string().trim().max(4096).optional(),transcriptSize:o.z.number().int().min(0).optional(),replaceActivity:o.z.boolean().optional()}).strict(),xe=Ne.extend({type:o.z.literal("event"),sessionId:o.z.string().trim().min(1).max(120),kind:o.z.enum(["lifecycle","send-ack","turn-started","user-message","assistant-message","approval-requested","turn-completed","tool-activity","sub-agent-activity","context-compaction"]),activeTurnId:o.z.string().trim().max(240).optional(),providerTurnId:o.z.string().trim().max(240).optional(),userPrompt:o.z.string().trim().optional(),text:o.z.string().optional(),itemId:o.z.string().trim().max(240).optional(),status:C.optional(),phase:_.optional(),summary:o.z.string().trim().max(1e3).optional(),error:o.z.string().trim().max(4e3).optional(),currentTool:x.nullable().optional(),toolCallsSinceLastMessage:o.z.number().int().min(0).optional(),subAgents:o.z.array(j).max(50).optional(),contextCompaction:de.optional(),counters:o.z.object({toolCalls:o.z.number().int().min(0).optional(),edits:o.z.number().int().min(0).optional(),approvals:o.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"})});o.z.discriminatedUnion("type",[_e,xe]);const Oe=o.z.union([o.z.object({type:o.z.literal("schedule"),scheduleKind:o.z.literal("interval").default("interval"),intervalMs:o.z.number().int().min(1e3)}).strict(),o.z.object({type:o.z.literal("schedule"),scheduleKind:o.z.literal("daily"),timeOfDay:o.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:o.z.string().trim().min(1).max(120)}).strict(),o.z.object({type:o.z.literal("schedule"),scheduleKind:o.z.literal("weekly"),weekdays:o.z.array(o.z.number().int().min(0).max(6)).min(1).max(7),timeOfDay:o.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:o.z.string().trim().min(1).max(120)}).strict()]),Re=o.z.union([Oe,o.z.object({type:o.z.literal("file-change"),roots:o.z.array(o.z.string().trim().min(1).max(4096)).min(1).max(50),globs:o.z.array(o.z.string().trim().min(1).max(500)).min(1).max(100),ignore:o.z.array(o.z.string().trim().min(1).max(500)).max(100).optional(),debounceMs:o.z.number().int().min(100).max(6e4).default(1500)}).strict(),o.z.object({type:o.z.literal("ai-session"),agent:o.z.string().trim().min(1).max(80).optional(),statuses:o.z.array(o.z.enum(["running","waiting","idle","failed"])).max(20).optional(),phases:o.z.array(o.z.enum(["thinking","tool","editing","approval","responding","unknown"])).max(20).optional()}).strict()]),je=o.z.object({promptTemplate:o.z.string().trim().min(1).max(2e4)}).strict(),De=o.z.object({cooldownMs:o.z.number().int().min(0).max(864e5).optional(),maxConcurrentRuns:o.z.number().int().min(1).max(20).default(1),whenBusy:o.z.enum(["skip","queue"]).default("skip")}).strict(),Pe=o.z.object({configHash:o.z.string().trim().min(8).max(80),name:o.z.string().trim().min(1).max(160),description:o.z.string().trim().max(1e3).optional(),source:Re,action:je,policy:De.default({maxConcurrentRuns:1,whenBusy:"skip"}),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict(),Me=o.z.object({type:o.z.literal("ai-session"),aiSessionId:o.z.string().trim().min(1).max(160)}).strict(),$e=o.z.object({configHash:o.z.string().trim().min(8).max(80),deploymentId:o.z.string().trim().min(1).max(240).optional(),instanceId:o.z.string().trim().min(1).max(160),origin:o.z.enum(["control-plane","controlled-instance"]).default("controlled-instance"),enabled:o.z.boolean().default(!0),target:Me,localName:o.z.string().trim().min(1).max(160).optional(),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict(),qe=o.z.object({configHash:o.z.string().trim().min(8).max(80),deploymentId:o.z.string().trim().min(1).max(240).optional(),instanceId:o.z.string().trim().min(1).max(160),status:o.z.enum(["idle","running","disabled","error"]).default("idle"),lastTriggeredAt:o.z.string().datetime().optional(),lastCompletedAt:o.z.string().datetime().optional(),lastSkippedAt:o.z.string().datetime().optional(),lastError:o.z.string().trim().max(4e3).optional(),runCount:o.z.number().int().min(0).default(0),skippedCount:o.z.number().int().min(0).default(0)}).strict(),Le=o.z.object({id:o.z.string().trim().min(1).max(160),configHash:o.z.string().trim().min(8).max(80),deploymentId:o.z.string().trim().min(1).max(240).optional(),instanceId:o.z.string().trim().min(1).max(160),eventType:o.z.enum(["manual","schedule","file-change","ai-session"]),status:o.z.enum(["started","completed","failed","skipped"]),target:Me,promptPreview:o.z.string().trim().max(500),eventSummary:o.z.string().trim().max(1e3).optional(),error:o.z.string().trim().max(4e3).optional(),startedAt:o.z.string().datetime(),completedAt:o.z.string().datetime().optional()}).strict(),Ue=o.z.object({schemaVersion:o.z.literal(1).default(1),configs:o.z.array(Pe).default([]),deployments:o.z.array($e).default([]),runtime:o.z.array(qe).default([]),recentRuns:o.z.array(Le).default([])}).strict();function Fe(e){return Array.isArray(e)?e.map(Fe):e&&"object"==typeof e?Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>[e,Fe(t)])):e}const Be="2026-07-27",He=o.z.string(),Ge=o.z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),We=o.z.string().datetime(),Ve=o.z.record(o.z.string(),o.z.string()).default({}),Je=o.z.record(o.z.string(),o.z.string()).default({}),Ke=/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/,Qe=/^sha256:([a-fA-F0-9]{64})$/,Ye=/^(?:localhost|[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?)(?::[1-9][0-9]{0,4})?$/,Xe=/^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*$/;function Ze(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?Qe.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&&!Ke.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&&!Ye.test(p[0].toLowerCase()))throw new Error("Docker image registry host or port is invalid.");if(!m.length||m.some(e=>!Xe.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}`:""}`}const et=o.z.string().trim().min(1).max(512).transform((e,t)=>{try{return Ze(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),o.z.NEVER}}),tt=o.z.string().trim().transform((e,t)=>{const s=Qe.exec(e);return s?`sha256:${s[1].toLowerCase()}`:(t.addIssue({code:"custom",message:"Docker image digest must use sha256 followed by 64 hexadecimal characters."}),o.z.NEVER)}),st=o.z.object({codexModelHash:Ge.nullable().optional(),claudeModelHash:Ge.nullable().optional()}).strict().default({}),nt=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(120),kind:o.z.enum(["tty","gui","web"]),source:o.z.enum(["builtin","custom"]),availability:o.z.enum(["available","missing-dependency"]),capabilities:o.z.object({automation:o.z.enum(["cdp"]).optional(),supportsCwdSelection:o.z.boolean().default(!1)}).strict().default({supportsCwdSelection:!1}),diagnosticCode:o.z.enum(["APP_EXECUTABLE_NOT_FOUND"]).optional()}).strict(),it=o.z.object({code:o.z.enum(["APP_CATALOG_INVALID"]),message:o.z.string().trim().min(1).max(500)}).strict(),at=o.z.object({items:o.z.array(nt).max(256),observedAt:We,issues:o.z.array(it).max(32).default([])}).strict(),rt=o.z.enum(["linux","darwin","win32","freebsd","openbsd","aix","sunos","unknown"]),ot=o.z.enum(["x64","arm64","arm","ia32","ppc64","s390x","riscv64","unknown"]),dt=o.z.enum(["apt","dnf","brew","npm"]),ct=o.z.enum(["user","passwordless-sudo","root"]),lt=o.z.object({platform:rt,arch:ot,installers:o.z.array(dt).max(8),privilege:ct,installerAccess:o.z.object({npmGlobalWritable:o.z.boolean().optional()}).strict().optional()}).strict(),pt=o.z.enum(["installed","not-installed","broken","unsupported"]),ut=o.z.enum(["recipe","bundled","external","none"]),mt=o.z.object({code:o.z.enum(["BUNDLED","EXTERNALLY_MANAGED","ALREADY_INSTALLED","NOT_INSTALLED","UNSUPPORTED_PLATFORM","INSTALLER_UNAVAILABLE","INSTALLER_NOT_WRITABLE","INSUFFICIENT_PRIVILEGE","OPERATION_IN_PROGRESS"]),message:o.z.string().trim().min(1).max(500)}).strict(),gt=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(120),kind:o.z.enum(["tty","gui","web"]),description:o.z.string().trim().max(500).optional(),state:pt,managementSource:ut,version:o.z.string().trim().max(120).optional(),canInstall:o.z.boolean(),canUninstall:o.z.boolean(),installReason:mt.optional(),uninstallReason:mt.optional(),activeJobId:Ge.optional()}).strict(),ht=o.z.enum(["install","uninstall"]),ft=o.z.enum(["queued","running","succeeded","failed","cancelled","interrupted"]),It=o.z.object({current:o.z.number().finite().nonnegative().optional(),total:o.z.number().finite().positive().optional(),unit:o.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"]}),yt=o.z.object({executable:o.z.string().trim().min(1).max(4096),args:o.z.array(o.z.string().max(4096)).max(128)}).strict(),bt=o.z.object({code:o.z.string().trim().min(1).max(120).regex(/^[a-z0-9][a-z0-9._-]*$/),message:o.z.string().trim().min(1).max(4096),retryable:o.z.boolean(),sessionIds:o.z.array(Ge).max(64).optional()}).strict(),vt=o.z.object({id:Ge,requestId:Ge.optional(),appId:Ge,operation:ht,state:ft,phase:o.z.string().trim().min(1).max(120).optional(),progress:It.optional(),command:yt.optional(),logTail:o.z.string().max(32768).optional(),logTruncated:o.z.boolean().optional(),error:bt.optional(),requestedAt:We,startedAt:We.optional(),finishedAt:We.optional(),updatedAt:We}).strict(),wt=o.z.object({streamId:Ge,sequence:o.z.number().int().nonnegative(),capabilities:lt,apps:o.z.array(gt).max(256),activeJobs:o.z.array(vt).max(256),recentJobs:o.z.array(vt).max(256),observedAt:We}).strict(),St=o.z.object({requestId:Ge.optional()}).strict(),At=o.z.object({job:vt}).strict();o.z.object({type:o.z.literal("app-management"),streamId:Ge,sequence:o.z.number().int().nonnegative(),observedAt:We,job:vt.optional(),snapshot:wt.optional()}).strict().refine(e=>void 0!==e.job||void 0!==e.snapshot,{message:"An app management event requires a job or snapshot."});const kt=o.z.object({component:o.z.enum(["control-plane","node-agent","controlled-instance"]),packageName:o.z.string().trim().max(160).optional(),packageVersion:o.z.string().trim().max(80).optional(),protocolVersion:He.optional(),buildId:o.z.string().trim().max(160).optional(),builtAt:o.z.string().trim().max(120).optional(),gitCommit:o.z.string().trim().max(120).optional(),imageRef:o.z.string().trim().max(512).optional(),imageDigest:o.z.string().trim().max(240).optional()}).strict(),Tt=o.z.enum(["stable","beta","alpha"]),zt=o.z.object({packageName:o.z.literal("@task-handoff/controlled-instance"),version:o.z.string().trim().min(1).max(80),platform:o.z.string().trim().min(1).max(40),arch:o.z.string().trim().min(1).max(40),formatVersion:o.z.number().int().positive(),launcherAbi:o.z.number().int().positive(),entrypoint:o.z.string().trim().min(1).max(512),sha256:o.z.string().regex(/^[a-f0-9]{64}$/)}).strict(),Et=o.z.object({code:o.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:o.z.string().trim().min(1).max(4096),expectedVersion:o.z.string().trim().min(1).max(80).optional(),actualVersion:o.z.string().trim().min(1).max(80).optional(),retryable:o.z.boolean().default(!1)}).strict(),Ct=o.z.object({desiredVersion:o.z.string().trim().min(1).max(80),actualVersion:o.z.string().trim().min(1).max(80).optional(),phase:o.z.enum(["pending","draining","installing","restarting","verifying","matched","failed"]),attempt:o.z.number().int().nonnegative().default(0),lastAttemptAt:We.optional(),matchedAt:We.optional(),error:Et.optional()}).strict(),Nt=o.z.object({runningInstanceCount:o.z.number().int().nonnegative(),stoppedInstanceCount:o.z.number().int().nonnegative(),activeInstanceCount:o.z.number().int().nonnegative(),restartInstanceCount:o.z.number().int().nonnegative(),runningInstanceIds:o.z.array(Ge).max(1024).default([]),stoppedInstanceIds:o.z.array(Ge).max(1024).default([]),activeInstanceIds:o.z.array(Ge).max(1024).default([])}).strict(),_t=o.z.object({phase:o.z.enum(["queued","updating-node","restarting-node","converging-instances","succeeded","degraded","failed"]),desiredVersion:o.z.string().trim().min(1).max(80),nodeVersion:o.z.string().trim().min(1).max(80).optional(),expectedInstanceIds:o.z.array(Ge).max(1024).default([]),expectedInstanceCount:o.z.number().int().nonnegative(),matchedInstanceCount:o.z.number().int().nonnegative(),pendingInstanceCount:o.z.number().int().nonnegative(),failedInstanceCount:o.z.number().int().nonnegative(),deferredInstanceCount:o.z.number().int().nonnegative().default(0)}).strict(),xt=o.z.object({channel:Tt.default("stable")}).strict(),Ot=o.z.object({source:o.z.literal("npm"),channel:Tt,currentVersion:o.z.string().trim().max(240).optional(),availableVersion:o.z.string().trim().min(1).max(240),artifactRef:o.z.string().trim().min(1).max(512).optional(),runtimeArtifacts:o.z.array(zt).default([]),impact:Nt,updateAvailable:o.z.boolean(),supported:o.z.boolean().default(!0),reason:o.z.string().trim().max(2048).optional(),checkedAt:We,preflightToken:o.z.string().trim().min(16).max(240).optional()}).strict(),Rt=o.z.object({id:Ge,nodeId:Ge,source:o.z.literal("npm"),channel:Tt,fromVersion:o.z.string().trim().max(240).optional(),toVersion:o.z.string().trim().min(1).max(240),artifactRef:o.z.string().trim().min(1).max(512).optional(),runtimeArtifacts:o.z.array(zt).default([]),impact:Nt,rollout:_t,status:o.z.enum(["queued","updating-node","restarting-node","converging-instances","succeeded","degraded","failed"]),error:Et.optional(),startedAt:We.optional(),completedAt:We.optional(),createdAt:We,updatedAt:We}).strict(),jt=o.z.object({channel:Tt,targetVersion:o.z.string().trim().min(1).max(240),preflightToken:o.z.string().trim().min(16).max(240)}).strict(),Dt=o.z.object({type:o.z.enum(["branch","tag","commit"]),name:o.z.string().trim().min(1).max(240).optional(),commit:o.z.string().trim().max(80).optional()}).strict(),Pt=o.z.object({type:o.z.enum(["none","ssh-key","https-token"]).default("none"),secretId:o.z.string().trim().min(1).max(120).optional()}).strict(),Mt=o.z.object({depth:o.z.number().int().positive().max(1e5).optional(),submodules:o.z.boolean().default(!1),lfs:o.z.boolean().default(!1),subdirectory:o.z.string().trim().max(240).default("")}).strict();o.z.object({id:Ge,name:o.z.string().trim().min(1).max(160),url:o.z.string().trim().min(1).max(2048),provider:o.z.string().trim().max(80).optional(),ref:Dt.default({type:"branch",name:"main"}),auth:Pt.default({type:"none"}),clone:Mt.default({submodules:!1,lfs:!1,subdirectory:""}),defaultImageId:Ge.optional(),labels:Ve,createdAt:We,updatedAt:We}).strict();const $t=o.z.object({id:Ge,nodeId:Ge,name:o.z.string().trim().min(1).max(160),path:o.z.string().trim().min(1).max(4096),defaultImageId:Ge.optional(),labels:Ve,createdAt:We,updatedAt:We}).strict(),qt=o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal("git-repository"),repositoryId:Ge.optional(),url:o.z.string().trim().min(1).max(2048),provider:o.z.string().trim().max(80).optional(),ref:Dt.default({type:"branch",name:"main"}),auth:Pt.default({type:"none"}),clone:Mt.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),o.z.object({type:o.z.literal("git-template"),url:o.z.string().trim().min(1).max(2048),templateId:o.z.string().trim().min(1).max(120).optional(),ref:Dt.default({type:"branch",name:"main"}),auth:Pt.default({type:"none"}),clone:Mt.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),o.z.object({type:o.z.literal("local-folder"),localFolderId:Ge.optional(),path:o.z.string().trim().min(1).max(4096),ownerNodeId:Ge.optional()}).strict()]),Lt=o.z.object({mode:o.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]),path:o.z.string().trim().min(1).max(4096).default("/workspace"),volumeName:o.z.string().trim().max(240).optional(),readOnly:o.z.boolean().default(!1)}).strict(),Ut=o.z.enum(["codex","claude"]),Ft=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(160),source:qt,defaultImageId:Ge.optional(),defaultNodeId:Ge.optional(),defaultRuntimeId:Ge.optional(),workspacePolicy:Lt,labels:Ve,createdAt:We,updatedAt:We}).strict(),Bt=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(160),endpoint:o.z.string().trim().min(1).max(2048),key:o.z.string().trim().min(1).max(4096),model:o.z.string().trim().min(1).max(240),app:Ut,enabled:o.z.boolean().default(!0),order:o.z.number().int().min(0).max(1e6).default(0),labels:Ve,createdAt:We,updatedAt:We}).strict(),Ht=Bt.omit({key:!0}).extend({keyPreview:o.z.string().trim().min(1).max(160),keySet:o.z.boolean()}).strict(),Gt=Ht.extend({referenceCount:o.z.number().int().min(0)}).strict(),Wt=o.z.object({instanceId:Ge,codexModelHash:Ge.optional(),claudeModelHash:Ge.optional(),updatedAt:We}).strict(),Vt=o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal("control-plane"),name:Bt.shape.name,enabled:Bt.shape.enabled,order:Bt.shape.order}).strict(),o.z.object({type:o.z.literal("node"),nodeId:Ge,name:Bt.shape.name,enabled:Bt.shape.enabled,order:Bt.shape.order,referenceCount:o.z.number().int().min(0)}).strict()]),Jt=o.z.object({id:Ge,model:Ht,locations:o.z.array(Vt).min(1),referenceCount:o.z.number().int().min(0).default(0)}).strict(),Kt=o.z.object({nodeId:Ge,code:o.z.string().trim().min(1).max(120),message:o.z.string().trim().min(1).max(2e3)}).strict(),Qt=o.z.object({models:o.z.array(Jt),nodeDiagnostics:o.z.array(Kt).default([]),updatedAt:We}).strict();function Yt(e){const t={app:Ut.parse(e.app),endpoint:Bt.shape.endpoint.parse(e.endpoint),key:Bt.shape.key.parse(e.key),model:Bt.shape.model.parse(e.model)};return`mdl_${d.createHash("sha256").update(JSON.stringify(t)).digest("hex")}`}Bt.omit({id:!0,createdAt:!0,updatedAt:!0}).strict().partial().strict(),o.z.object({modelSelection:st,codexModelHash:Ge.optional(),claudeModelHash:Ge.optional()}).strict();const Xt=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(160),reference:et,pullPolicy:o.z.literal("if-not-present").default("if-not-present"),capabilities:o.z.array(o.z.string().trim().min(1).max(80)).default([]),optionalApps:o.z.array(o.z.string().trim().min(1).max(120)).default([]),defaultEnv:Je,labels:Ve,createdAt:We,updatedAt:We}).strict(),Zt=Xt.omit({reference:!0}).extend({requestedReference:et,resolvedDigest:tt.optional(),resolvedReference:et.optional()}).strict(),es=o.z.object({phase:o.z.enum(["checking-image","pulling-image","resolving-image","ready","failed"]),requestedReference:et,generation:o.z.number().int().nonnegative().default(0),error:o.z.string().trim().max(4096).optional(),startedAt:We,updatedAt:We}).strict(),ts="image.pull.terminal.output",ss="image.pull.terminal.finished",ns=o.z.object({instanceId:Ge,generation:o.z.number().int().nonnegative(),requestedReference:et,sequence:o.z.number().int().nonnegative(),observedAt:We}).strict(),is=ns.extend({data:o.z.string().min(1).max(65536),replay:o.z.boolean().optional()}).strict(),as=ns.extend({outcome:o.z.enum(["succeeded","failed"])}).strict(),rs=ns.extend({status:o.z.enum(["connecting","pulling","extracting","complete","failed"]),layers:o.z.object({total:o.z.number().int().nonnegative(),completed:o.z.number().int().nonnegative(),downloaded:o.z.number().int().nonnegative(),downloading:o.z.number().int().nonnegative(),extracting:o.z.number().int().nonnegative()}).strict(),bytes:o.z.object({current:o.z.number().finite().nonnegative(),total:o.z.number().finite().positive()}).strict().optional(),percent:o.z.number().finite().min(0).max(100).optional(),message:o.z.string().trim().min(1).max(500),terminalTail:o.z.string().max(262144).optional(),terminalTruncated:o.z.boolean().optional()}).strict();function os(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const s=e,n="string"==typeof s.id?s.id:void 0,i="string"==typeof s.image?s.image:void 0;void 0!==i&&t?.({imageId:n,field:"image"}),Object.prototype.hasOwnProperty.call(s,"registry")&&t?.({imageId:n,field:"registry"});let a="string"==typeof s.reference?s.reference:i;if(a)try{a=Ze(a)}catch{if(!a.includes("@")&&a.lastIndexOf(":")<=a.lastIndexOf("/"))try{a=Ze(`${a}:latest`),t?.({imageId:n,field:"reference:implicit-latest"})}catch{}}return{id:s.id,name:s.name,reference:a,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 ds=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(160),connectionMode:o.z.enum(["local-ipc","local-loopback","direct-http","reverse-wss"]).default("direct-http"),auth:o.z.object({mode:o.z.enum(["local-static-key","paired-hmac"]).default("local-static-key"),keyId:Ge.optional(),secret:o.z.string().trim().max(4096).optional(),pairedAt:We.optional(),pairing:o.z.object({status:o.z.enum(["pending","paired","expired"]).default("pending"),joinToken:o.z.string().trim().max(4096).optional(),expiresAt:We.optional()}).strict().optional()}).strict().default({mode:"local-static-key"}),endpoint:o.z.string().trim().max(2048).optional(),controlEndpoint:o.z.string().trim().max(2048).optional(),containerEndpoint:o.z.string().trim().max(2048).optional(),publicWebBase:o.z.string().trim().max(2048).optional(),status:o.z.enum(["unknown","online","offline","degraded"]).default("unknown"),health:o.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({}),appInventory:at.optional(),labels:Ve,lastSeenAt:We.optional(),createdAt:We,updatedAt:We}).strict(),cs=o.z.object({id:Ge,nodeId:Ge,name:o.z.string().trim().min(1).max(160),type:o.z.enum(["docker","kubernetes","local"]),status:o.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:o.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({}),labels:Ve,createdAt:We,updatedAt:We}).strict(),ls=o.z.object({instanceId:Ge,runtimeKind:o.z.literal("docker"),state:o.z.enum(["pending","available","stopped","unavailable"]),sampledAt:We,cpu:o.z.object({usagePercent:o.z.number().nonnegative()}).strict().optional(),memory:o.z.object({usageBytes:o.z.number().nonnegative(),limitBytes:o.z.number().nonnegative().optional(),usagePercent:o.z.number().nonnegative().optional()}).strict().optional(),network:o.z.object({rxBytes:o.z.number().nonnegative(),txBytes:o.z.number().nonnegative()}).strict().optional(),blockIo:o.z.object({readBytes:o.z.number().nonnegative(),writeBytes:o.z.number().nonnegative()}).strict().optional(),pids:o.z.number().int().nonnegative().optional(),error:o.z.string().trim().max(2048).optional()}).strict(),ps="instance.metrics.snapshot",us=o.z.object({ok:o.z.boolean().optional(),role:o.z.string().optional(),nodeId:Ge.optional(),platform:rt.optional()}).passthrough(),ms=o.z.object({bindScope:o.z.enum(["loopback","all-ipv4"]),port:o.z.number().int().min(1).max(65535)}).strict().extend({host:o.z.enum(["127.0.0.1","0.0.0.0"]),status:o.z.enum(["listening","error"]),source:o.z.enum(["bootstrap","persisted"]),error:o.z.string().trim().max(2048).optional()}).strict(),gs=o.z.object({nodeId:Ge,joinToken:o.z.string().trim().min(1),expiresAt:We}).passthrough(),hs=o.z.object({id:Ge,keyId:Ge,url:o.z.string().trim().min(1).max(2048).optional(),name:o.z.string().trim().min(1).max(160).optional(),pairedAt:We,updatedAt:We.optional(),active:o.z.boolean().optional(),current:o.z.boolean().optional()}).passthrough(),fs=o.z.object({remote:o.z.object({id:Ge,url:o.z.string().trim().min(1).max(2048).optional(),keyId:Ge,pairedAt:We,active:o.z.boolean()}).passthrough(),tunnel:o.z.object({status:o.z.enum(["disabled","saved","connecting","connected","failed"]),error:o.z.string().trim().max(2048).optional()}).passthrough()}).passthrough(),Is=o.z.object({deleted:o.z.boolean().optional()}).passthrough(),ys=o.z.object({repository:o.z.string().trim().optional(),tag:o.z.string().trim().optional(),id:o.z.string().trim(),createdSince:o.z.string().trim().optional(),size:o.z.string().trim().optional(),reference:o.z.string().trim().min(1),repoDigests:o.z.array(o.z.string().trim().min(1).max(512)).default([])}).passthrough(),bs=o.z.object({image:Xt,status:o.z.enum(["available","pull-required","unknown"]),localImage:ys.optional(),error:o.z.string().trim().max(2048).optional()}).strict(),vs=o.z.lazy(()=>o.z.object({name:o.z.string().trim().min(1),path:o.z.string().trim().min(1).max(4096),children:o.z.array(vs)}).passthrough()),ws=o.z.object({status:o.z.number().int().min(100).max(599).optional(),headers:o.z.record(o.z.string(),o.z.string()).optional(),bodyBase64:o.z.string().optional()}).passthrough(),Ss=o.z.object({strategy:o.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),web:o.z.string().trim().max(2048).optional(),api:o.z.string().trim().max(2048).optional(),vnc:o.z.string().trim().max(2048).optional(),tty:o.z.string().trim().max(2048).optional(),logs:o.z.string().trim().max(2048).optional(),status:o.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict(),As=o.z.object({strategy:o.z.enum(["control-plane-proxy","direct-port","node-proxy","kubernetes-ingress","kubernetes-port-forward"]).default("control-plane-proxy"),web:o.z.string().trim().max(2048).optional(),api:o.z.string().trim().max(2048).optional(),ws:o.z.string().trim().max(2048).optional(),status:o.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict().default({strategy:"control-plane-proxy",status:"unknown"}),ks=o.z.object({mode:o.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]).optional(),status:o.z.enum(["unknown","pending","ready","failed"]).default("unknown"),path:o.z.string().trim().max(4096).optional(),resolvedCommit:o.z.string().trim().max(120).optional(),error:o.z.string().trim().max(2048).optional()}).passthrough(),Ts=o.z.object({id:Ge,name:o.z.string().trim().min(1).max(160),projectId:Ge.optional(),source:qt,sourceSnapshot:o.z.record(o.z.string(),o.z.unknown()).default({}),modelSelection:st,nodeId:Ge,runtimeId:Ge,imageId:Ge.optional(),imageSnapshot:Zt.optional(),imageProvisioning:es.optional(),stateRevision:o.z.number().int().min(0).default(0),status:o.z.enum(["created","provisioning","starting","registering","registered","running","stopping","stopped","failed","unhealthy"]).default("created"),health:o.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),connectionStatus:o.z.enum(["unknown","online","offline","endpoint-unreachable"]).default("unknown"),agentStatus:o.z.enum(["unknown","online","offline"]).default("unknown"),targetStatus:o.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),uiAccessStatus:o.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),controlMode:o.z.enum(["standalone","controlled"]).default("controlled"),protocolVersion:He.optional(),instanceVersion:o.z.string().trim().max(80).optional(),build:kt.optional(),runtimeVersion:Ct.optional(),ready:o.z.boolean().default(!1),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({}),appInventory:at.optional(),config:o.z.object({autoImportAgentConfigs:o.z.boolean().default(!0),defaultCodexPermissionMode:B.default("ask")}).strict().default({autoImportAgentConfigs:!0,defaultCodexPermissionMode:"ask"}),workspace:ks.default({status:"unknown"}),target:Ss.default({strategy:"direct-port",status:"unknown"}),access:As,apps:o.z.object({runningCount:o.z.number().int().min(0).default(0),problemCount:o.z.number().int().min(0).default(0),updatedAt:We.optional(),revision:o.z.number().int().min(0).optional()}).strict().default({runningCount:0,problemCount:0}),aiSessions:ye.default(()=>({runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:(new Date).toISOString()})),triggers:o.z.object({enabledCount:o.z.number().int().min(0).default(0),runningCount:o.z.number().int().min(0).default(0),errorCount:o.z.number().int().min(0).default(0),configs:o.z.array(o.z.object({configHash:o.z.string().trim().min(1),config:Pe,deployments:o.z.array($e).default([]),runtime:o.z.array(qe).default([])}).strict()).default([]),recentRuns:o.z.array(Le).default([])}).strict().optional(),runtime:o.z.object({kind:o.z.string().trim().max(80).optional(),containerName:o.z.string().trim().max(240).optional(),containerId:o.z.string().trim().max(240).optional(),workspacePath:o.z.string().trim().max(4096).optional(),pid:o.z.number().int().positive().optional(),port:o.z.number().int().positive().max(65535).optional(),labels:o.z.record(o.z.string(),o.z.string()).default({})}).strict().default({labels:{}}),registrationToken:o.z.string().trim().min(1).max(240).optional(),lastHeartbeatAt:We.optional(),createdAt:We,updatedAt:We}).strict(),zs="instance.lifecycle.snapshot",Es=o.z.object({instanceId:Ts.shape.id,revision:o.z.number().int().min(0),updatedAt:We,status:Ts.shape.status.unwrap(),health:Ts.shape.health.unwrap(),connectionStatus:Ts.shape.connectionStatus.unwrap(),accessStatus:o.z.enum(["reachable","endpoint-unreachable"]),imageProvisioning:es.optional(),workspace:ks,runtime:Ts.shape.runtime.unwrap(),runtimeVersion:Ct.optional(),ready:o.z.boolean(),lastHeartbeatAt:We.optional()}).strict();function Cs(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=Ns(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=Ns(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 Ns(e,t){let s=e;for(const e of t){if(!s||"object"!=typeof s)return;s=s[e]}return s}function _s(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]]))}o.z.object({instanceId:Ge.optional(),name:o.z.string().trim().min(1).max(160),projectId:Ge.optional(),source:qt.optional(),nodeId:Ge.optional(),runtimeId:Ge.optional(),imageId:Ge.optional(),instanceVersion:o.z.string().trim().max(80).optional(),protocolVersion:He,build:kt.optional(),controlMode:o.z.enum(["standalone","controlled"]).default("controlled"),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({}),appInventory:at.optional(),target:Ss.default({strategy:"direct-port",status:"unknown"}),workspace:ks.default({status:"unknown"}),registrationToken:o.z.string().trim().max(240).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==Be||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol register payload requires appInventory."})}),o.z.object({status:Ts.shape.status.optional(),health:Ts.shape.health.optional(),protocolVersion:He,build:kt.optional(),capabilities:Ts.shape.capabilities.optional(),appInventory:at.optional(),apps:Ts.shape.apps.optional(),aiSessions:Ts.shape.aiSessions.optional(),triggers:Ts.shape.triggers.optional(),workspace:ks.optional(),target:Ss.partial().optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==Be||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol heartbeat payload requires appInventory."})});const xs=o.z.enum(["web","telegram","wechat","dingding"]),Os=o.z.object({id:Ge,channel:xs,name:o.z.string().trim().min(1).max(160),enabled:o.z.boolean().default(!1),token:o.z.string().trim().max(4096).optional(),defaultChatId:o.z.string().trim().max(240).optional(),allowedUserIds:o.z.array(o.z.string().trim().min(1).max(240)).default([]),pollIntervalMs:o.z.number().int().positive().max(6e4).default(3e3),settings:o.z.record(o.z.string(),o.z.unknown()).default({}),createdAt:We,updatedAt:We}).strict(),Rs=o.z.object({id:Ge,channel:xs,bridgeId:Ge.optional(),chatSessionId:o.z.string().trim().min(1).max(240),userId:o.z.string().trim().max(240).optional(),activeProjectId:Ge.optional(),activeInstanceId:Ge.optional(),activeAiSessionId:o.z.string().trim().min(1).max(120).optional(),lastUsedAt:We,createdAt:We,updatedAt:We}).strict(),js=o.z.object({source:o.z.object({type:o.z.literal("chat-gateway").default("chat-gateway"),channel:xs,bridgeId:Ge.optional(),chatSessionId:o.z.string().trim().min(1).max(240),userId:o.z.string().trim().max(240).optional()}).strict(),message:o.z.object({text:o.z.string().trim().max(2e4).default(""),attachments:o.z.array(q).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>S&&t.addIssue({code:o.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})}),target:o.z.object({instanceId:Ge,aiSessionId:o.z.string().trim().min(1).max(120)}).strict().optional()}).strict(),Ds=o.z.object({id:Ge,instanceId:Ge,projectId:Ge.optional(),aiSessionId:o.z.string().trim().min(1).max(120),providerSessionId:o.z.string().trim().max(240).optional(),result:o.z.string().default(""),timeoutMs:o.z.number().positive().optional(),source:o.z.string().trim().max(120).optional(),kind:o.z.enum(["task","approval"]).default("task"),status:o.z.enum(["pending","unreachable"]).default("pending"),error:o.z.string().trim().max(2048).optional(),lastSeenAt:We}).strict();function Ps(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":"system"}const Ms=o.z.enum(["ai.sessions","app.sessions"]),$s=o.z.object({topic:Ms,instanceId:o.z.string().trim().min(1).max(160),streamId:o.z.string().trim().min(1).max(240),latestRevision:o.z.number().int().min(0),earliestRetainedRevision:o.z.number().int().min(0)}).strict(),qs=o.z.object({protocolVersion:o.z.literal(1),streams:o.z.array($s)}).strict();function Ls(e){t.mkdirSync(e,{recursive:!0})}function Us(e,t){Ls(s.dirname(e)),l.sync(e,`${JSON.stringify(t,null,2)}\n`,{encoding:"utf8"})}function Fs(e,t){console.warn(JSON.stringify({message:e,...t}))}function Bs(e){return e&&"object"==typeof e&&"issues"in e?{issues:e.issues}:{error:e instanceof Error?e.message:String(e)}}function Hs(e,s){const n=s.logger||Fs;let i;try{i=JSON.parse(t.readFileSync(e,"utf8"))}catch(t){return void n("stored JSON could not be read",{filePath:e,...Bs(t)})}if(!s.schema)return i;const a=s.sanitize?s.sanitize(i):i,r=s.schema.safeParse(a);if(r.success)return r.data;const o="strip"in s.schema&&"function"==typeof s.schema.strip?s.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 Gs(e){return`${e}_${d.randomUUID().replace(/-/g,"").slice(0,20)}`}function Ws(){return d.randomBytes(24).toString("base64url")}class Vs{directory;options;constructor(e,t={}){this.directory=e,this.options=t}init(){Ls(this.directory)}filePath(e){return s.join(this.directory,`${e}.json`)}list(){return this.init(),t.readdirSync(this.directory).filter(e=>e.endsWith(".json")).sort().flatMap(e=>{const t=Hs(s.join(this.directory,e),this.options);return t?[t]:[]})}get(e){const s=this.filePath(e);return t.existsSync(s)?Hs(s,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 Us(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 s=this.filePath(e);return!!t.existsSync(s)&&(t.unlinkSync(s),!0)}}class Js{filePathValue;defaults;options;constructor(e,t,s={}){this.filePathValue=e,this.defaults=t,this.options=s}init(){Ls(s.dirname(this.filePathValue)),t.existsSync(this.filePathValue)||this.put(this.defaults())}get(){return this.init(),t.existsSync(this.filePathValue)?Hs(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 Us(this.filePathValue,s),s}}const Ks=c.promisify(d.scrypt),Qs="task_handoff_cp_session",Ys=o.z.enum(["disabled","password"]),Xs=o.z.string().trim().min(1).max(80).regex(/^[a-zA-Z0-9_.@-]+$/),Zs=o.z.string().min(8).max(4096),en=o.z.object({username:Xs,password:Zs}).strict(),tn=o.z.object({username:o.z.string().trim().min(1).max(80),password:o.z.string().min(1).max(4096)}).strict(),sn=o.z.object({id:o.z.string().trim().min(1),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}),nn=sn.extend({username:Xs,passwordHash:o.z.string().trim().min(1),role:o.z.enum(["viewer","operator","admin"]),lastLoginAt:o.z.string().datetime().optional()}).strict(),an=sn.extend({userId:o.z.string().trim().min(1),tokenHash:o.z.string().trim().min(1),expiresAt:o.z.string().datetime(),lastSeenAt:o.z.string().datetime().optional()}).strict();function rn(){return(new Date).toISOString()}function on(e){return d.createHash("sha256").update(e).digest("base64url")}async function dn(e){const t=d.randomBytes(16).toString("base64url");return`scrypt$${t}$${(await Ks(e,t,64)).toString("base64url")}`}function cn(e){return{id:e.id,username:e.username,role:e.role,createdAt:e.createdAt,updatedAt:e.updatedAt,lastLoginAt:e.lastLoginAt}}class ln{mode;users;sessions;constructor(e,t={}){this.mode=function(e={}){return Ys.parse(e.mode||process.env.TASK_HANDOFF_CONTROL_PLANE_AUTH_MODE||"disabled")}(t),this.users=new Vs(e.authUsersDir,{schema:nn}),this.sessions=new Vs(e.authSessionsDir,{schema:an})}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=en.parse(e),s=rn();return cn(this.users.put({id:Gs("user"),username:t.username,passwordHash:await dn(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=tn.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 Ks(e,n,64),r=Buffer.from(i,"base64url");return r.length===a.length&&d.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=rn(),i=Ws(),a=new Date(Date.now()+12096e5).toISOString(),r=this.sessions.put({id:Gs("sess"),userId:s.id,tokenHash:on(i),expiresAt:a,createdAt:n,updatedAt:n,lastSeenAt:n});return this.users.put({...s,lastLoginAt:n,updatedAt:n}),{user:cn({...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<=rn()||n.tokenHash!==on(s))return;const i=this.users.get(n.userId);return i?(this.sessions.put({...n,lastSeenAt:rn(),updatedAt:rn()}),cn(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 pn="app-session.snapshot",un="app-session.patch",mn="app-session.removed",gn=o.z.enum(["created","starting","running","stopping","stopped","exited","failed","closed","terminated","unknown"]),hn=o.z.object({type:o.z.enum(["app-session","provider-session","adapter-key"]),id:o.z.string().trim().min(1).max(240),agent:o.z.string().trim().min(1).max(80).optional(),adapter:o.z.string().trim().min(1).max(80).optional(),key:o.z.string().trim().min(1).max(240).optional()}).strict(),fn=o.z.record(o.z.string(),o.z.unknown()).and(o.z.object({id:o.z.string().trim().min(1).max(160),appId:o.z.string().trim().min(1).max(160).optional(),title:o.z.string().trim().max(240).optional(),kind:o.z.string().trim().max(80).optional(),status:gn.default("unknown"),bindings:o.z.array(hn).max(40).default([]),workspace:o.z.object({cwd:o.z.string().trim().max(4096).optional()}).strict().optional(),createdAt:o.z.string().datetime().optional(),updatedAt:o.z.string().datetime().optional()})),In=o.z.object({runningCount:o.z.number().int().min(0).default(0),problemCount:o.z.number().int().min(0).default(0),sessions:o.z.array(fn).default([]),updatedAt:o.z.string().datetime()}).strict(),yn=o.z.object({streamId:o.z.string().trim().min(1).max(240),revision:o.z.number().int().min(0),lastEventAt:o.z.string().datetime(),snapshot:In}).strict();In.pick({runningCount:!0,problemCount:!0,updatedAt:!0}).extend({revision:o.z.number().int().min(0).optional()});const bn=o.z.enum(["app-session-created","app-session-updated","app-session-deleted","app-session-recovered","control-action","heartbeat-sync","startup"]),vn=o.z.object({streamId:o.z.string().trim().min(1).max(240),instanceId:o.z.string().trim().min(1).max(160),nodeId:o.z.string().trim().min(1).max(160).optional(),revision:o.z.number().int().min(0),previousRevision:o.z.number().int().min(0).optional(),traceId:o.z.string().trim().min(1).max(160),generatedAt:o.z.string().datetime(),reason:bn}).strict(),wn=o.z.object({meta:vn,snapshot:In}).strict(),Sn=o.z.object({meta:vn,session:fn}).strict(),An=o.z.object({meta:vn,sessionId:o.z.string().trim().min(1).max(160),tombstone:fn.optional(),expiresAt:o.z.string().datetime().optional()}).strict(),kn=o.z.object({streamId:o.z.string().trim().min(1).max(240),instanceId:o.z.string().trim().min(1).max(160),sinceRevision:o.z.number().int().min(0),latestRevision:o.z.number().int().min(0),earliestRetainedRevision:o.z.number().int().min(0),syncRequired:o.z.boolean().default(!1),events:o.z.array(o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal(pn),payload:wn}).strict(),o.z.object({type:o.z.literal(un),payload:Sn}).strict(),o.z.object({type:o.z.literal(mn),payload:An}).strict()])).default([])}).strict();function Tn(e,t=(new Date).toISOString()){const s=e.map(e=>zn(e));return In.parse({runningCount:s.filter(e=>"running"===e.status).length,problemCount:s.filter(e=>"failed"===e.status).length,sessions:s,updatedAt:t})}function zn(e){const t=En("string"==typeof e.status?e.status:void 0),s="string"==typeof e.id?e.id:"",n=function(e){const t=On(e.workspace),s=On(e.tty),n=On(e.launch),i=On(e.ai),a=On(i.claude),r=Rn(t.cwd)||Rn(s.cwd)||Rn(a.cwd)||Rn(n.cwd);return r?{cwd:r}:void 0}(e),{workspace:i,...a}=e;return fn.parse({...a,status:t,bindings:xn([...(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}]:[],...Nn(e)]),...n?{workspace:n}:{}});var r}function En(e){const t=(e||"unknown").trim().toLowerCase();return"ended"===t?"closed":"terminating"===t?"stopping":gn.options.includes(t)?t:"unknown"}function Cn(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 Nn(e){const t=On(e.ai),s=On(t.claude),n=On(t.appServer),i=[],a=Rn(s.short);a&&i.push({type:"adapter-key",adapter:"claude",agent:"claude",id:`short:${a}`,key:`short:${a}`});const r=Rn(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=Rn(t.activeThreadId);for(const e of[...new Set([d,...o].filter(Boolean))])i.push({type:"provider-session",agent:"codex",id:e});const c=Rn(n.socketPath);return c&&i.push({type:"adapter-key",adapter:"codex-app-server",agent:"codex",id:`socket:${c}`,key:`socket:${c}`}),i}function _n(e){const t="string"==typeof e.id?e.id:"",s=On(e.ai),n=Rn(On(s.claude).short);return[t?`app:${t}`:"",n?`claude-short:${n}`:""]}function xn(e){const t=new Map;for(const s of e)t.set(Cn(s),s);return[...t.values()]}function On(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function Rn(e){return"string"==typeof e&&e.trim()?e.trim():""}function jn(e=(new Date).toISOString()){return In.parse({runningCount:0,problemCount:0,sessions:[],updatedAt:e})}function Dn(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 Pn{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 Mn{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",us)}checkUpdate(e,t){return this.client.requestSchema(e,"/updates/check",Ot,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}applyUpdate(e,t){return this.client.requestSchema(e,"/updates/apply",Rt,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}listUpdateJobs(e){return this.client.requestSchema(e,"/updates/jobs",o.z.array(Rt))}getExternalListener(e){return this.client.requestSchema(e,"/settings/external-listener",ms)}updateExternalListener(e,t){return this.client.requestSchema(e,"/settings/external-listener",ms,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}createPairingInvite(e,t={}){return this.client.requestSchema(e,"/pairing/invites",gs,{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",fs,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}listRemotes(e){return this.client.requestSchema(e,"/remotes",o.z.array(hs))}deleteRemote(e,t){return this.client.requestSchema(e,`/remotes/${encodeURIComponent(t)}`,Is,{method:"DELETE"})}listDockerImages(e){return this.client.requestSchema(e,"/docker/images",o.z.array(ys))}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}`:""),o.z.array(vs))}listLocalFolders(e){return this.client.requestSchema(e,"/local-folders",o.z.array($t))}createLocalFolder(e,t){return this.client.requestSchema(e,"/local-folders",$t,{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",o.z.array(cs))}createRuntime(e,t){return this.client.requestSchema(e,"/runtimes",cs,{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,cs,{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,cs,{method:"POST"})}deleteRuntime(e,t){return this.client.requestSchema(e,`/runtimes/${encodeURIComponent(t)}`,Is,{method:"DELETE"})}deleteLocalFolder(e,t){return this.client.requestSchema(e,`/local-folders/${encodeURIComponent(t)}`,Is,{method:"DELETE"})}listModels(e){return this.client.requestSchema(e,"/models",o.z.array(Gt))}createModel(e,t){return this.client.requestSchema(e,"/models",Gt,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}deployModel(e,t,s){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}/deploy`,Gt,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}updateModel(e,t,s){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}`,Gt,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}deleteModel(e,t){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}`,Is,{method:"DELETE"})}assignInstanceModels(e,t,s){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/model-assignment`,o.z.object({assignment:Wt,instance:Ts}).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(Dn(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(Dn(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",o.z.array(o.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",Ts,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}updateInstance(e,t,s){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}`,Ts,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}deleteInstance(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/delete`,Is,{method:"POST"})}startInstance(e,t,s={}){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/start`,Ts,{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`,Ts,{method:"POST"})}stopInstance(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/stop`,Ts,{method:"POST"})}restartInstance(e,t,s={}){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/restart`,Ts,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)})}instanceResourceMetrics(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/metrics`,ls)}proxyRawInstance(e,t,s){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/proxy/raw`,ws,{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(Dn(e[n],"/instances","GET",s.reason)),t),{items:[],nodeErrors:[]})}normalizeInstance(e,t){const s=(n=t,Ts.safeParse(function(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,s=new Set(Object.keys(Ts.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=_s(e,["id","name","kind","source","availability","diagnosticCode"]);return!t||"object"!=typeof t||Array.isArray(t)?t:{...t,capabilities:_s(e.capabilities,["automation","supportsCwdSelection"])}}):t.items,issues:Array.isArray(t.issues)?t.issues.map(e=>_s(e,["code","message"])):t.issues},n=at.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=qt.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=_s(t,i);if("local-folder"!==s){const e=Cs(Dt,_s(t.ref,["type","name","commit"])),s=Cs(Pt,_s(t.auth,["type","secretId"])),n=Cs(Mt,_s(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=Cs(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=Cs(kt,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=Cs(Ie,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:We.safeParse(t.updatedAt).success?t.updatedAt:(new Date).toISOString()};return Object.keys(t).some(e=>!["runningCount","waitingCount","staleCount","sessions","updatedAt"].includes(e)),ye.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=Cs(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=_s(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=_s(e,n)}return s.action=_s(t.action,["promptTemplate"]),s.policy=_s(t.policy,["cooldownMs","maxConcurrentRuns","whenBusy"]),Cs(Pe,s)}(n.config);return"string"==typeof n.configHash&&i?[{configHash:n.configHash,config:i,deployments:s(n.deployments,$e),runtime:s(n.runtime,qe)}]:[]}):[];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,Le)}}(t.triggers,"string"==typeof t.id&&t.id),n.apps=Cs(Ts.shape.apps.unwrap(),_s(t.apps,["runningCount","problemCount","updatedAt","revision"]),0,0,"string"==typeof t.id&&t.id)||{runningCount:0,problemCount:0},n.config=Cs(Ts.shape.config.unwrap(),_s(t.config,["autoImportAgentConfigs","defaultCodexPermissionMode"]),0,0,"string"==typeof t.id&&t.id)||{autoImportAgentConfigs:!0,defaultCodexPermissionMode:"ask"},n.modelSelection=Cs(st.unwrap(),_s(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=os(s),{reference:i,...a}=n,r=s.requestedReference??i;return void 0!==s.image||s.reference,{...a,id:s.id??t,requestedReference:r,resolvedDigest:s.resolvedDigest,resolvedReference:s.resolvedReference}}(t.imageSnapshot,t.imageId,"string"==typeof t.id&&t.id),n.imageProvisioning=Cs(es,_s(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(Ct.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(Et.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=We.safeParse(t.lastAttemptAt),l=We.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=Cs(ks,t.workspace,0,0,"string"==typeof t.id&&t.id)||{status:"unknown"},n.runtime=Cs(Ts.shape.runtime.unwrap(),_s(t.runtime,["kind","containerName","containerId","workspacePath","pid","port","labels"]),0,0,"string"==typeof t.id&&t.id)||{labels:{}},n.target=Cs(Ss,_s(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=Cs(As,_s(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(Ts.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!==Be?(this.client.logger?.warn?.({nodeId:e.id,instanceId:s.data.id,expectedProtocolVersion:Be,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 $n(){return(new Date).toISOString()}function qn(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 Ln(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 Un(e){return e instanceof Error?e.message:String(e)}function Fn(e,t){const s=new Error(t);throw Object.assign(s,{statusCode:404,code:e}),s}class Bn{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:qn(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:qn(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 Hn(e){if(!e||"object"!=typeof e)return;const t=e,s=kt.safeParse(t.build),n=rt.safeParse(t.platform);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}:{},...s.success?{build:s.data}:{},..."string"==typeof t.serverTime?{serverTime:t.serverTime}:{}}}function Gn(e){const{agent:t,...s}=e.capabilities,{secret:n,...i}=e.auth,a=i.pairing?{...i.pairing,joinToken:void 0}:void 0,r=Hn(t);return{...e,auth:a?{...i,pairing:a}:i,capabilities:r?{...s,agent:r}:s}}function Wn(e){return"local-folder"===e.type?{mode:"local-bind",path:"/workspace",readOnly:!1}:{mode:"git-clone",path:"/workspace",readOnly:!1}}function Vn(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 Jn(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 Kn{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=Vn(e.instance);return{...n,aiSessions:we(n.aiSessions),connectionStatus:s,heartbeatAgeMs:t,project:e.project,image:e.image,node:e.node?Gn(e.node):void 0,runtime:e.runtime,protocolCompatible:!e.instance.protocolVersion||e.instance.protocolVersion===Be}}({instance:e,currentTime:a,project:e.projectId?t.get(e.projectId):void 0,image:e.imageSnapshot||(e.imageId?s.get(e.imageId):void 0),node:n.get(e.nodeId),runtime:i.get(`${e.nodeId}:${e.runtimeId}`)})),nodeErrors:e.nodeErrors||[]}}}const Qn=Pe.extend({id:Pe.shape.configHash}),Yn=o.z.object({name:Pe.shape.name,description:Pe.shape.description,source:Re,action:je,policy:De.partial().optional()}).strict(),Xn=o.z.object({instanceIds:o.z.array(Ts.shape.id).min(1),target:Me,enabled:o.z.boolean().optional()}).strict(),Zn=o.z.object({configHash:Pe.shape.configHash,enabled:o.z.boolean().optional()}).strict();class ei{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=Yn.parse(e||{}),s=$n(),n={maxConcurrentRuns:t.policy?.maxConcurrentRuns??1,whenBusy:t.policy?.whenBusy??"skip",cooldownMs:t.policy?.cooldownMs},i=function(e){const t=Fe({source:e.source,action:e.action,policy:De.parse(e.policy||{})});return`trg_${d.createHash("sha256").update(JSON.stringify(t)).digest("hex").slice(0,24)}`}({source:t.source,action:t.action,policy:n}),a=Qn.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||Fn("TRIGGER_NOT_FOUND",`Trigger ${e} was not found.`),{configHash:e,deletedTemplate:t,results:n}}async applyTrigger(e,t){const s=this.requireTrigger(e),n=Xn.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=Zn.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:ti(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=ti(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||Fn("TRIGGER_NOT_FOUND",`Trigger ${e} was not found.`),t}}function ti(e,t){return`ai_${Buffer.from(e).toString("base64url")}_${t}`}const si=o.z.object({id:Ft.shape.id.optional(),name:Ft.shape.name,source:qt,defaultImageId:Ft.shape.defaultImageId,defaultNodeId:Ft.shape.defaultNodeId,defaultRuntimeId:Ft.shape.defaultRuntimeId,workspacePolicy:Lt.optional(),labels:Ft.shape.labels.optional()}).strict(),ni=si.omit({id:!0}).partial().strict();function ii(e){return 1===Array.from(e).length&&!/[\p{L}\p{N}\s]/u.test(e)&&"/"!==e&&"\\"!==e}const ai=o.z.string().refine(ii,"Mention trigger must be one printable non-alphanumeric character other than / or \\.");function ri(e){return 1===Array.from(e).length&&!/[\p{L}\p{N}\s]/u.test(e)&&"\\"!==e}const oi=o.z.string().refine(ri,"Command trigger must be one printable non-alphanumeric character other than \\."),di=o.z.object({publicBaseUrl:o.z.string().trim().max(2048).optional(),updateChannel:Tt.default("stable"),mentionTrigger:ai.default("@"),commandTrigger:oi.default("/")}).strict().refine(e=>e.mentionTrigger!==e.commandTrigger,{path:["commandTrigger"],message:"Command and mention triggers must be different."}),ci=o.z.object({publicBaseUrl:o.z.string().trim().max(2048).optional(),updateChannel:Tt.optional(),mentionTrigger:ai.optional(),commandTrigger:oi.optional()}).strict();function li(e){if(!e||"object"!=typeof e||Array.isArray(e))return{};const t={...e};return"string"==typeof t.mentionTrigger&&ii(t.mentionTrigger)||(t.mentionTrigger="@"),"string"==typeof t.commandTrigger&&ri(t.commandTrigger)&&t.commandTrigger!==t.mentionTrigger||(t.commandTrigger="/"),t}const pi=o.z.object({id:Xt.shape.id.optional(),name:Xt.shape.name,reference:Xt.shape.reference,pullPolicy:Xt.shape.pullPolicy.optional(),capabilities:o.z.array(o.z.string().trim().min(1).max(80)).optional(),optionalApps:o.z.array(o.z.string().trim().min(1).max(120)).optional(),defaultEnv:Xt.shape.defaultEnv.optional(),labels:Xt.shape.labels.optional()}).strict(),ui=pi.omit({id:!0}).partial().strict();class mi{options;constructor(e){this.options=e}getSettings(){return di.parse(this.options.settings.get())}updateSettings(e){const t=ci.parse(e),s=di.parse({...this.getSettings(),...t,..."publicBaseUrl"in t?{publicBaseUrl:t.publicBaseUrl?.trim()||void 0}:{}});return this.options.settings.put(s)}seedDefaults(){if(this.options.images.get("img_default"))return;const e=$n();this.options.images.put(Xt.parse({id:"img_default",name:"TaskHandoff Browser + Terminal",reference:process.env.TASK_HANDOFF_CONTROLLED_IMAGE||"huadream/task-handoff-controlled-instance:latest",pullPolicy:"if-not-present",capabilities:["browser","terminal","gui-terminal","codex","claude"],optionalApps:["chromium","terminal-tty","gui-terminal","vscode-web"],defaultEnv:{},labels:{"task-handoff.image.kind":"controlled-instance"},createdAt:e,updatedAt:e}))}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=si.parse(e),s=$n(),n=qt.parse(t.source);return this.options.projects.put(Ft.parse({...t,id:t.id||Gs("proj"),source:n,defaultImageId:t.defaultImageId||"img_default",defaultNodeId:t.defaultNodeId||this.options.defaultNodeId(),defaultRuntimeId:t.defaultRuntimeId||"runtime_local_docker",workspacePolicy:Lt.parse(t.workspacePolicy||Wn(n)),labels:t.labels||{},createdAt:s,updatedAt:s}))}updateProject(e,t){const s=ni.parse(t),n=this.requireProject(e),i=s.source?qt.parse(s.source):n.source;return this.options.projects.put(Ft.parse({...n,...s,id:e,source:i,workspacePolicy:s.workspacePolicy?Lt.parse(s.workspacePolicy):n.workspacePolicy,createdAt:n.createdAt,updatedAt:$n()}))}deleteProject(e){return this.options.projects.delete(e)}requireProject(e){const t=this.getProject(e);return t||Fn("PROJECT_NOT_FOUND",`Project ${e} was not found.`),t}listImages(){return this.options.images.list()}createImage(e){const t=pi.parse(e),s=$n();return this.options.images.put(Xt.parse({...t,id:t.id||Gs("img"),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);return this.options.images.put(Xt.parse({...n,...s,id:e,createdAt:n.createdAt,updatedAt:$n()}))}deleteImage(e){return this.options.images.delete(e)}requireImage(e){const t=this.options.images.get(e);return t||Fn("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={...e};return"defaultRuntimeId"in t||(t.defaultRuntimeId="runtime_local_docker"),Ft.parse(t)}return Ft.parse(e)}(e);return t!==e&&this.options.projects.put(t),t}}class gi{records=new Map;options;constructor(e){this.options=e}create(e){const t=d.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 hi{requireInstance;listAppSessions;tokens=new gi({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:fi(t,n.sessionId,r,s)}}}function fi(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(Ii(s.vnc).noVncPath)||`/api/apps/sessions/${encodeURIComponent(t)}/web/`:yi(Ii(s.web).webPath)||`/api/apps/sessions/${encodeURIComponent(t)}/web/`}function Ii(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function yi(e){return"string"==typeof e&&e.trim()?e.trim():void 0}const bi={allow:"a",deny:"d",skip:"s"};function vi(e){return d.createHash("sha256").update(e).digest("base64url").slice(0,Math.ceil(16))}class wi{tokens=new gi({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:${bi[t]}:${vi(e)}`}}function Si(e){const{token:t,...s}=e;return{...s,settings:ki(e.settings),tokenSet:Boolean(e.token)}}function Ai(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 ki(e){const t={...e};return"string"==typeof t.clientSecret&&t.clientSecret&&(delete t.clientSecret,t.clientSecretSet=!0),t}const Ti=o.z.object({name:Os.shape.name.optional(),channel:Os.shape.channel.optional(),enabled:o.z.boolean().optional(),token:Os.shape.token,defaultChatId:Os.shape.defaultChatId,allowedUserIds:o.z.array(o.z.string().trim().min(1).max(240)).optional(),pollIntervalMs:Os.shape.pollIntervalMs.optional(),settings:Os.shape.settings.optional()}).strict(),zi=Ti.extend({channel:Os.shape.channel,name:Os.shape.name.optional()});class Ei{chatBridges;chatSessions;constructor(e){this.chatBridges=e.chatBridges,this.chatSessions=e.chatSessions}list(){return this.chatBridges.list().map(Si)}require(e){const t=this.chatBridges.get(e);return t||Fn("CHAT_BRIDGE_NOT_FOUND",`Chat bridge ${e} was not found.`),t}create(e){const t=zi.parse(e),s=$n(),n=Os.parse({...t,id:Gs(`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:Ai({},t.settings),createdAt:s,updatedAt:s});var i,a;return this.chatBridges.put(n),Si(n)}update(e,t){const s=Ti.parse(t),n=this.require(e),i=Ai(n.settings,s.settings),a=Os.parse({...n,...s,id:e,channel:n.channel,name:s.name||n.name,settings:i,updatedAt:$n()});return this.chatBridges.put(a),Si(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 Ci{chatSessions;constructor(e){this.chatSessions=e.chatSessions}list(){return this.chatSessions.list()}require(e){const t=this.chatSessions.get(e);return t||Fn("CHAT_SESSION_NOT_FOUND",`Chat session ${e} was not found.`),t}upsert(e){const t=$n(),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=Rs.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 Ni(e){return{inline_keyboard:e.map(e=>e.map(e=>({text:e.text,...e.url?{url:e.url}:{callback_data:e.callbackData||""}})))}}function _i(e,t=40){return Ni(e.slice(0,t).map(e=>[e]))}function xi(e){return Buffer.from(e,"utf8").toString("base64url")}function Oi(e){const t=ji(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=ji(t),s=String(e.callback_data||""),i=String(e.text||"").trim();i&&s&&n.push({text:i,id:`th_cb_${xi(s)}`})}return n.slice(0,40)}function Ri(e,t){const s=String(e||"").replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,Math.max(0,t-3))}...`}function ji(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}const Di=new Set(["stopped","failed","exited","closed","terminated"]);function Pi(e){return!function(e){return Di.has(En(e))}(e)}function Mi(e){return(e.aiSessions?.sessions||[]).filter(t=>function(e,t){const s=(e.apps?.sessions||[]).filter(Bi);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[...zn(e).bindings.map(Cn),..._n(e)].filter(Boolean)}(e).some(e=>n.has(e))))}(e,t))}function $i(e){return"waiting"===e.status?4:"failed"===e.status?3:"running"===e.status?2:"idle"===e.status?1:0}function qi(e){return[e.cwd||"",e.agent||"",e.providerSessionId||e.id].join("\0")}function Li(e){const t=e.session.title||e.session.userPrompt||e.session.summary||e.session.lastMessage||"",s=t?` - ${Ri(t,18)}`:"";return`${e.instance.name} - ${e.session.agent} ${e.session.status}${s}`}function Ui(e,t=(e,t)=>`task_handoff:approval:${e.id}:${t}`){return _i(e.flatMap(e=>"approval"!==e.kind?[]:[{text:Ri(`Allow ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"allow")},{text:Ri(`Skip ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"skip")},{text:Ri(`Deny ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"deny")}]))}function Fi(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 Bi(e){const t=e&&"object"==typeof e&&!Array.isArray(e)?e:{};return Pi("string"==typeof t.status?t.status:void 0)}class Hi{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?_i(t.map(e=>({text:`${e.active?"✓ ":""}${Li(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:Vn(t),aiSession:s,reply:`Interrupt sent to ${t.name} / ${e.activeAiSessionId}.`}}}function Gi(e){return e.providerTurnId||e.turnId||""}function Wi(e){return Pi(Yi(e.status))}function Vi(e,t){const s=Yi(t.appId)||"App",n=Yi(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 Ji(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=Xi(e);t&&!s.has(t.id)&&s.set(t.id,t)}return[...s.values()]}function Ki(e,t,s,n){const i=Yi(t.id)?function(e,t){return`/apps/access/${Qi(e)}?token=${encodeURIComponent(t())}`}(t,n):`/instances/${encodeURIComponent(e.id)}/`,a=function(e,t){const s=Yi(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 Qi(e){const t=Yi(e.kind);return"gui"===t?"vnc":"web"===t?"web":"tty"}function Yi(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function Xi(e){if("string"==typeof e&&e.trim())return{id:e,label:Zi(e)};const t=function(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}(e),s=Yi(t.id).trim();return s?{id:s,label:Yi(t.name).trim()||Zi(s)}:void 0}function Zi(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 ea{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?_i(t.map(e=>{return{text:Ri(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=Ji(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?_i(n.map(e=>{return{text:Ri(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=Ji(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(Wi).filter(e=>Boolean(Yi(e.id))).map(e=>({instance:t,session:e,link:Ki(t,e,this.deps.controlPlanePublicBaseUrl(),()=>this.deps.createAppAccessToken({instanceId:t.id,sessionId:Yi(e.id)||"",mode:Qi(e)}).token)})):[]}),i=n.filter(e=>e.link.url).map(e=>({text:Ri(`${e.instance.name} · ${Vi(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?_i(i):void 0};var a,r}}class ta{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:Vn(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}: ${Un(t)}`}}}failedSendResult(e,t,s,n){return{accepted:!1,routed:!1,binding:e,project:t||void 0,instance:Vn(s),reply:n}}sentResult(e,t,s,n,i){return{accepted:!0,routed:!0,binding:e,project:t||void 0,instance:Vn(s),aiSession:i,instanceId:s.id,aiSessionId:n,turnId:(a=i,a.turnId||a.providerTurnId||a.session.activeTurnId||""),providerTurnId:Gi(i),reply:`Sent to ${t?.name||s.sourceSnapshot.name||"Source"} / ${s.name} / ${n}.`};var a}}class sa{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(Fi).join("\n"):"No pending routes.",replyMarkup:s.length?Ui(s,(e,t)=>this.deps.pendingDecisionCallbackData(e.id,t)):void 0}}async handlePendingDecisionCommand(e,t,s){const n=Ln(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=Ln(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 na{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 ia{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?Mi({...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=>Mi(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=$i(t.session)-$i(e.session);return s||(String(e.instance.name||"").localeCompare(String(t.instance.name||""))||e.instance.id.localeCompare(t.instance.id)||qi(e.session).localeCompare(qi(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||Fn("PROJECT_NOT_FOUND",`Project ${e} was not found.`),t}async resolveInstance(e,t){const s=await this.findInstance(e,t);return s||Fn("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 aa{deps;targets;sender;appCommands;pendingCommands;aiSessionCommands;targetCommands;constructor(e){this.deps=e,this.targets=new ia(e),this.sender=new ta(e,this.targets),this.appCommands=new ea(e,this.targets),this.pendingCommands=new sa(e),this.aiSessionCommands=new Hi(e,this.targets),this.targetCommands=new na(e,this.targets)}async handleChatGatewayMessage(e){const t=js.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.`}}}}function ra(e){return/^[a-z]:[\\/]/i.test(e)||/^[\\/]{2}[^\\/]+[\\/]+[^\\/]+/.test(e)}function oa(e){return ra(e)?s.win32:s.posix}const da=o.z.object({mode:o.z.enum(["local-static-key","paired-hmac"]).default("local-static-key"),keyId:ds.shape.id.optional(),secret:o.z.string().trim().max(4096).optional(),pairedAt:ds.shape.createdAt.optional(),pairing:o.z.object({status:o.z.enum(["pending","paired","expired"]).default("pending"),joinToken:o.z.string().trim().max(4096).optional(),expiresAt:ds.shape.createdAt.optional()}).strict().optional()}).strict(),ca=o.z.object({autoImportAgentConfigs:o.z.boolean().optional(),defaultCodexPermissionMode:B.optional()}).strict(),la=o.z.object({id:Ts.shape.id.optional(),name:Ts.shape.name.optional(),projectId:Ts.shape.projectId,source:qt.optional(),sourceSnapshot:o.z.record(o.z.string(),o.z.unknown()).optional(),nodeId:Ts.shape.nodeId.optional(),runtimeId:Ts.shape.runtimeId.optional(),imageId:Ts.shape.imageId,config:ca.optional(),modelSelection:Ts.shape.modelSelection.optional(),start:o.z.boolean().default(!1)}).strict(),pa=o.z.object({name:Ts.shape.name.optional(),config:ca.optional(),modelSelection:Ts.shape.modelSelection.optional()}).strict(),ua=o.z.object({name:Bt.shape.name,endpoint:Bt.shape.endpoint,key:Bt.shape.key,model:Bt.shape.model,app:Bt.shape.app,enabled:Bt.shape.enabled.optional(),order:Bt.shape.order.optional(),labels:Bt.shape.labels.optional()}).strict(),ma=ua.partial().strict(),ga=o.z.object({id:ds.shape.id.optional(),name:ds.shape.name,connectionMode:ds.shape.connectionMode.optional(),auth:da.optional(),joinToken:o.z.string().trim().min(1).max(4096).optional(),endpoint:ds.shape.endpoint,controlEndpoint:ds.shape.controlEndpoint,containerEndpoint:ds.shape.containerEndpoint,publicWebBase:ds.shape.publicWebBase,status:o.z.enum(["unknown","online","offline","degraded"]).optional(),health:o.z.enum(["unknown","ok","degraded","failed"]).optional(),capabilities:ds.shape.capabilities.optional(),labels:ds.shape.labels.optional(),lastSeenAt:ds.shape.lastSeenAt}).strict(),ha=o.z.object({name:ds.shape.name.optional(),connectionMode:ds.shape.connectionMode.unwrap().optional(),auth:da.optional(),endpoint:ds.shape.endpoint,controlEndpoint:ds.shape.controlEndpoint,containerEndpoint:ds.shape.containerEndpoint,publicWebBase:ds.shape.publicWebBase,status:ds.shape.status.unwrap().optional(),health:ds.shape.health.unwrap().optional(),capabilities:ds.shape.capabilities.unwrap().optional(),labels:ds.shape.labels.unwrap().optional(),lastSeenAt:ds.shape.lastSeenAt}).strict(),fa=o.z.object({controlPlaneUrl:o.z.string().trim().url().max(2048),joinToken:o.z.string().trim().min(1).max(4096),controlPlaneName:o.z.string().trim().min(1).max(160).optional(),activate:o.z.boolean().optional()}).strict(),Ia=o.z.object({nodeName:o.z.string().trim().min(1).max(160).optional(),expiresInMs:o.z.number().int().positive().max(36e5).optional()}).strict();function ya(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 ba="TASK_HANDOFF_NODE_AGENT_V1",va=6e4;function wa(e=""){return d.createHash("sha256").update(e).digest("hex")}function Sa(e,t){return d.createHmac("sha256",e).update(function(e){return[ba,e.keyId,e.method.toUpperCase(),e.pathWithQuery,e.timestamp,e.nonce,e.bodySha256].join("\n")}(t)).digest("hex")}function Aa(e){const t=e.timestamp||(new Date).toISOString(),s=e.nonce||d.randomBytes(18).toString("base64url"),n=wa(e.body||""),i=Sa(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 ka(e){if(e.startsWith("ipc://"))return decodeURIComponent(e.slice(6))}function Ta(e,s=process.platform){if(!function(e,s=process.platform){if("win32"===s)return e.startsWith("\\\\.\\pipe\\");const n=t.statSync(e),i=process.getuid?.();return void 0===i||n.uid===i}(e,s)){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 za(e,t,s,n={}){const i=`/api/node-agent${t}`,a={createConnection:()=>m.connect(e),headers:{host:"task-handoff-node-agent.local",...n}};return s&&(Array.isArray(s)?s.length>0:s.trim().length>0)?new p.WebSocket(`ws://task-handoff-node-agent.local${i}`,s,a):new p.WebSocket(`ws://task-handoff-node-agent.local${i}`,a)}const Ea=o.z.object({id:o.z.string().trim().min(1),tokenHash:o.z.string().trim().min(1),expiresAt:o.z.string().datetime(),nodeName:o.z.string().trim().min(1).max(160).optional(),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict(),Ca="task-handoff.control-plane.local",Na="task-handoff.control-plane.builtin";function _a(e){return"true"===e.labels[Ca]}function xa(e){return"true"===e.labels[Na]}function Oa(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 Ra(e,t){const s=function(e){return"paired-hmac"===e.auth.mode?e.auth.secret:void 0}(e);if(s)return Aa({nodeId:e.id,keyId:Oa(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 ja{projects;models;images;nodes;chatSessions;chatBridges;triggers;nodeJoinInvites;settings;paths;fetchImpl;dockerCommandRunner;logger;appAccessService;chatActionTokenService=new wi;nodeAgentTransport;nodeAgentGateway;controlledInstanceGateway;instanceBoardReader=new Kn;controlPlaneTriggerService;catalogService;chatBridgeService;chatSessionService;chatSessionRuntime;aiSessionSnapshotProvider;appSessionSnapshotProvider;constructor(e,t={}){var s;this.paths=e,this.fetchImpl=t.fetchImpl||fetch,this.dockerCommandRunner=t.dockerCommandRunner,this.nodeAgentTransport=t.nodeAgentTransport,this.logger=(s=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(s||"").trim().toLowerCase())?t.logger:void 0);const n=new Pn({request:(e,t,s)=>this.nodeAgentFetch(e,t,s),logger:this.logger});this.nodeAgentGateway=new Mn(n),this.controlledInstanceGateway=new Bn({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 i=e=>({schema:e,logger:(e,t)=>this.logWarn(t,e)});this.projects=new Vs(e.projectsDir,i(Ft)),this.models=new Vs(e.modelsDir,i(Bt)),this.images=new Vs(e.imagesDir,{...i(Xt),sanitize:e=>os(e,e=>this.logWarn(e,"legacy image profile field was migrated"))}),this.nodes=new Vs(e.nodesDir,i(ds)),this.chatSessions=new Vs(e.chatSessionsDir,i(Rs)),this.chatBridges=new Vs(e.chatBridgesDir,i(Os)),this.triggers=new Vs(e.triggersDir,i(Qn)),this.nodeJoinInvites=new Vs(e.nodeJoinInvitesDir,i(Ea)),this.settings=new Js(e.settingsPath,()=>di.parse({}),{...i(di),sanitize:li}),this.catalogService=new mi({projects:this.projects,images:this.images,settings:this.settings,defaultNodeId:()=>this.defaultNodeId()}),this.controlPlaneTriggerService=new ei({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 hi({requireInstance:async e=>this.requireControlledInstance(e,!0),listAppSessions:()=>this.listAppSessions()}),this.chatBridgeService=new Ei({chatBridges:this.chatBridges,chatSessions:this.chatSessions}),this.chatSessionService=new Ci({chatSessions:this.chatSessions}),this.chatSessionRuntime=new aa({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.seedDefaults()}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(_a)?.id||this.nodes.list()[0]?.id}async inspectNodeAgent(e,t){const s="/health",n=t?Ra(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!==Be&&this.logWarn({nodeId:r,nodeEndpoint:e,expectedProtocolVersion:Be,actualProtocolVersion:a.data?.protocolVersion,build:a.data?.build,errorCode:"PROTOCOL_VERSION_MISMATCH"},"node agent protocol version mismatch");const o=kt.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=ka(e);return n?(Ta(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=u.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?g.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:$n()}}async syncLocalNodeConnection(){const e={[Ca]:"true",[Na]:"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 ka(e)?"local-ipc":"local-loopback"}(s),i=ds.parse({id:"node_local_probe",name:"Local Node Probe",connectionMode:n,auth:{mode:"local-static-key",secret:t},labels:e,createdAt:$n(),updatedAt:$n()}),a=await this.inspectNodeAgent(s,i),r=$n(),o=this.nodes.get(a.nodeId),d=this.nodes.list().filter(e=>_a(e)&&e.id!==a.nodeId);for(const e of d)this.nodes.delete(e.id);const c=ds.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:Hn(a.data)},labels:{...o?.labels||{},...e},createdAt:o?.createdAt||r,updatedAt:r});return this.nodes.put(c)}listModels(){return this.listAllModels().map(Jn)}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:Jn(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 Qt.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:$n()})}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=ua.parse(e),s=$n(),n=this.nextModelOrder(),i=Yt(t),a=this.models.get(i),r=Bt.parse({...t,id:i,enabled:t.enabled??!0,order:t.order??n,labels:t.labels||{},createdAt:a?.createdAt||s,updatedAt:s});return Jn(this.models.put(r))}async updateModel(e,t){const s=ma.parse(t),n=this.requireModelSecret(e),i=Bt.parse({...n,...s,key:s.key?.trim()?s.key:n.key,createdAt:n.createdAt,updatedAt:$n()}),a=Yt(i);return Jn(this.models.put(Bt.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)||Fn("MODEL_NOT_FOUND",`Model ${e} was not found.`);return t.forEach((e,t)=>{const n=s.get(e);this.models.put(Bt.parse({...n,order:100*(t+1),updatedAt:$n()}))}),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()}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=this.listImages();try{const e=await this.nodeAgentGateway.listDockerImages(t);return s.map(t=>{const s=e.find(e=>e.reference===t.reference||e.repoDigests.includes(t.reference));return bs.parse({image:t,status:s?"available":"pull-required",localImage:s})})}catch(e){const t=e instanceof Error?e.message:String(e);return s.map(e=>bs.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=ds.parse({...t,id:n,status:"online",health:"ok",capabilities:"object"==typeof e&&e?{...t.capabilities,agent:Hn(e)}:t.capabilities,lastSeenAt:$n(),updatedAt:$n()});return n!==t.id&&this.nodes.delete(t.id),this.nodes.put(i),{id:i.id,status:"online",checkedAt:$n(),agent:e}}catch(e){const s=ds.parse({...t,status:"offline",health:"failed",updatedAt:$n()});return this.nodes.put(s),{id:t.id,status:"offline",checkedAt:$n(),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(!xa(t)||!_a(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=Ia.parse(e&&"object"==typeof e?e:{}),s=$n(),n=Ws(),i={id:Gs("node_join"),tokenHash:d.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=o.z.object({joinToken:o.z.string().trim().min(1).max(4096),nodeId:ds.shape.id,nodeName:ds.shape.name.optional(),keyId:o.z.string().trim().min(1).max(160),secret:o.z.string().trim().min(1).max(4096),pairedAt:o.z.string().datetime().optional()}).parse(e),s=d.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=$n();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=ds.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=fa.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.defaultImageId===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.defaultImageId===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.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(Gn)}async createNode(e){const t=ga.parse(e),s=$n(),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||Gs("node"),d=ds.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=ds.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:Hn(c.data)}:{}},labels:t.labels||{},createdAt:s,updatedAt:s});return this.nodes.put(m)}updateNode(e,t){const s=ha.parse(t),n=this.requireNode(e),i=ds.parse({...n,...s,id:e,createdAt:n.createdAt,updatedAt:$n()});return this.nodes.put(i)}deleteNode(e){if(xa(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(Vn)}async listControlledInstancesWithDiagnostics(){const e=await this.listNodeInstancesWithDiagnostics();return{items:e.items.map(Vn),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=la.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.imageId||(o?s?.defaultImageId:void 0),c=d?this.requireImage(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,defaultImageId:c?.id});l={...l,localFolderId:e.id,ownerNodeId:e.nodeId,path:e.path},p=e}}const u=await this.prepareInstanceModels(a,t.modelSelection||{}),m=await this.nodeAgentGateway.createInstance(a,{id:t.id,name:t.name,runtimeId:n,...c?{imageId:c.id,image:c}:{},projectId:s?.id,source:l,sourceSnapshot:p,config:t.config,modelSelection:{}});let g=m;try{g=(await this.nodeAgentGateway.assignInstanceModels(a,m.id,u)).instance}catch(e){throw await this.nodeAgentGateway.deleteInstance(a,m.id).catch(()=>{}),e}let h={status:"not-requested"};if(t.start)try{g=await this.nodeAgentGateway.startInstance(a,g.id),h={status:"started"}}catch(e){h={status:"failed",error:Da(e,"INSTANCE_START_FAILED")},g=await this.nodeAgentGateway.listInstances(a).then(e=>e.find(e=>e.id===g.id)||g).catch(()=>g)}return{...Vn(g),registrationToken:g.registrationToken,startOutcome:h}}async updateControlledInstance(e,t){const s=pa.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 Vn(o)}async deleteControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId),n=await this.nodeAgentGateway.deleteInstance(s,e);return Boolean(n.deleted)}async startControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId);return await this.ensureInstanceModelAssignment(t),Vn(await this.nodeAgentGateway.startInstance(s,e))}async stopControlledInstance(e){const t=await this.requireNodeInstance(e),s=this.requireNode(t.nodeId);return Vn(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),Vn(await this.nodeAgentGateway.restartInstance(s,e))}async retryControlledInstanceImageProvisioning(e){const t=await this.requireNodeInstance(e);return Vn(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.listImages(),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 be.parse(await this.instanceRequest(e,"/ai-sessions/state"))}catch(t){return this.logWarn({instanceId:e.id,error:Un(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=jn();return{streamId:`bootstrap:${e.id}:app.sessions`,revision:0,lastEventAt:t.updatedAt,snapshot:t}}try{return yn.parse(await this.instanceRequest(e,"/apps/sessions/state"))}catch(t){this.logWarn({instanceId:e.id,error:Un(t),errorCode:"APP_SESSION_LIVE_REFRESH_FAILED"},"control plane failed to refresh live app sessions");const s=jn();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 Ce.parse(await this.instanceRequest(n,`/ai-sessions?${i}`))}async recoverAiSessionSnapshot(e){const t=await this.requireControlledInstance(e,!0);return be.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 kn.parse(await this.instanceRequest(n,`/apps/sessions?${i}`))}async recoverAppSessionSnapshot(e){const t=await this.requireControlledInstance(e,!0);return yn.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=$n(),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=Ds.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:Vn(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 ue.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 ge.parse(await this.instanceRequest(t,"/ai-sessions/history"))}async getAiSessionHistoryDetail(e,t){const s=await this.requireControlledInstance(e,!0);return he.parse(await this.instanceRequest(s,`/ai-sessions/history/${encodeURIComponent(t)}`))}async resumeAiSession(e,t){const s=await this.requireControlledInstance(e,!0),n=fe.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 wt.parse(e)}catch(e){if(e instanceof o.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 o.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 At.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 At.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)<=S)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 ue.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 Z.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/mentions`))}async searchAiSessionMentionFiles(e,t,s){const n=await this.requireControlledInstance(e,!0);return te.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 W.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 re.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/queue`))}async steerAiSessionQueuedMessage(e,t,s){const n=await this.requireControlledInstance(e,!0);return ue.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 pe.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 pe.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 ue.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)}listConfigSyncPresets(){return[{id:"codex",label:"Codex",projectRoot:".task-handoff/configs/codex",items:[{id:"config",type:"file",projectPath:"config.toml",containerPath:"${CODEX_HOME}/config.toml"},{id:"auth",type:"file",projectPath:"auth.json",containerPath:"${CODEX_HOME}/auth.json"},{id:"agents",type:"file",projectPath:"AGENTS.md",containerPath:"${CODEX_HOME}/AGENTS.md"},{id:"skills",type:"dir",projectPath:"skills",containerPath:"${CODEX_HOME}/skills"}]},{id:"claude",label:"Claude",projectRoot:".task-handoff/configs/claude",items:[{id:"claude-json",type:"file",projectPath:".claude.json",containerPath:"${HOME}/.claude.json"},{id:"settings",type:"file",projectPath:"settings.json",containerPath:"${CLAUDE_HOME}/settings.json"},{id:"claude-md",type:"file",projectPath:"CLAUDE.md",containerPath:"${CLAUDE_HOME}/CLAUDE.md"},{id:"commands",type:"dir",projectPath:"commands",containerPath:"${CLAUDE_HOME}/commands"},{id:"agents",type:"dir",projectPath:"agents",containerPath:"${CLAUDE_HOME}/agents"},{id:"skills",type:"dir",projectPath:"skills",containerPath:"${CLAUDE_HOME}/skills"}]},{id:"browser",label:"Browser",projectRoot:".task-handoff/configs/browser",items:[{id:"chromium-profile",type:"dir",projectPath:"chromium",containerPath:"${HOME}/.config/chromium"}]}]}async syncInstanceConfig(e,t,s){const n=await this.requireControlledInstance(e,!0);if("export"===t&&"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=[{id:"codex",label:"Codex",projectRoot:".task-handoff/configs/codex",items:[{id:"config",type:"file",projectPath:"config.toml",containerPath:"${CODEX_HOME}/config.toml"},{id:"auth",type:"file",projectPath:"auth.json",containerPath:"${CODEX_HOME}/auth.json"},{id:"agents",type:"file",projectPath:"AGENTS.md",containerPath:"${CODEX_HOME}/AGENTS.md"},{id:"skills",type:"dir",projectPath:"skills",containerPath:"${CODEX_HOME}/skills"}]},{id:"claude",label:"Claude",projectRoot:".task-handoff/configs/claude",items:[{id:"claude-json",type:"file",projectPath:".claude.json",containerPath:"${HOME}/.claude.json"},{id:"settings",type:"file",projectPath:"settings.json",containerPath:"${CLAUDE_HOME}/settings.json"},{id:"claude-md",type:"file",projectPath:"CLAUDE.md",containerPath:"${CLAUDE_HOME}/CLAUDE.md"},{id:"commands",type:"dir",projectPath:"commands",containerPath:"${CLAUDE_HOME}/commands"},{id:"agents",type:"dir",projectPath:"agents",containerPath:"${CLAUDE_HOME}/agents"},{id:"skills",type:"dir",projectPath:"skills",containerPath:"${CLAUDE_HOME}/skills"}]},{id:"browser",label:"Browser",projectRoot:".task-handoff/configs/browser",items:[{id:"chromium-profile",type:"dir",projectPath:"chromium",containerPath:"${HOME}/.config/chromium"}]}].find(e=>e.id===s);if(!i){const e=new Error(`Config sync preset ${s} was not found.`);throw Object.assign(e,{statusCode:404,code:"CONFIG_SYNC_PRESET_NOT_FOUND"}),e}return this.instanceRequest(n,`/config-sync/${encodeURIComponent(t)}/${encodeURIComponent(s)}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({preset:i})})}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=Ra(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=ka(a),o=`/api/node-agent${s}`,d=Ra(e,{method:"GET",pathWithQuery:o});if(r){Ta(r);const e=za(r,s,n,{...i,...d});return e.on("open",()=>{t.on("message",(t,s)=>e.readyState===p.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===p.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 l=n?new p.WebSocket(c.toString(),n,{headers:{...i,...d}}):new p.WebSocket(c.toString(),{headers:{...i,...d}});l.on("open",()=>{t.on("message",(e,t)=>l.readyState===p.WebSocket.OPEN&&l.send(t?e:String(e))),t.on("close",()=>l.close()),t.on("error",()=>l.close())}),l.on("message",(e,s)=>{t.readyState===p.WebSocket.OPEN&&t.send(s?e:e.toString())}),l.on("close",()=>t.close()),l.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=Ue.parse(await this.instanceRequest(e,"/triggers"));return Ts.parse({...e,triggers:ya(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{}Fn("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||Fn("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,t,n){if("local"===n.type)return oa(i=t.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(ra(e)!==ra(t))return;const s=oa(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,t.path);if(!a){const s=new Error(`Local folder ${t.path} is outside the instance workspace ${e.source.path}.`);throw Object.assign(s,{statusCode:400,code:"APP_CWD_OUTSIDE_WORKSPACE"}),s}const r=e.runtime.workspacePath||e.workspace.path||Wn(e.source).path||"/workspace";return a.length?s.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||Fn("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||Fn("MODEL_NOT_FOUND",`Model ${e} was not found.`);const n=this.normalizeModelRecord(s);return t?n:Jn(n)}requireModelSecret(e){const t=this.models.get(e);return t||Fn("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,Bt.parse(t)}return Bt.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||Fn("NODE_NOT_FOUND",`Node ${e} was not found.`),t}requirePublicNode(e){return Gn(this.requireNode(e))}async requireControlledInstance(e,t=!1){const s=await this.requireNodeInstance(e);return t?s:Vn(s)}}function Da(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 Pa(e){return h(String(e),"escape").replace(/\n+$/,"")}function Ma(e){const[t,...s]=String(e).split("\n");return[`*${Pa(t)}*`,...s.map(Pa)].join("\n")}function $a(e,t=4e3){const s=String(e),n=Ma(s);if(n.length<=t)return n;const[i,...a]=s.split("\n"),r=a.join("\n"),o="...\n";let d=i;for(;Ma(`${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=Ma(`${d}\n${o}`);for(;c<=l;){const e=Math.floor((c+l)/2),s=Ma(`${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=Ma(`${d}\n${o}${m.slice(g+1)}`)),u}function qa(e){return String(e).replace(/\\([_*\[\]()~`>#+\-=|{}.!\\])/g,"$1")}async function La(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:Pa;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 Fa(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 Ua(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 Fa(e,t,s,n,i){const a=i.parseMode||"MarkdownV2",r=i.rawMarkdownV2?String(s):Pa(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&&Ba(d.description))return;if(i.rawMarkdownV2&&"MarkdownV2"===a){const i=await e({...n,text:qa(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&&Ba(a.description))return}const c=await e({...n,text:i.rawMarkdownV2?qa(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||!Ba(l.description))throw new Error(l.description||d.description||`Telegram ${t} failed with HTTP ${c.status}`)}function Ba(e){return"string"==typeof e&&e.toLowerCase().includes("message is not modified")}function Ha(e,t){if("string"==typeof e&&e.trim())return e.trim();Wa(`Chat action token is missing ${t}.`)}function Ga(e){if("allow"===e||"deny"===e||"skip"===e)return e;Wa("Chat action token has an invalid decision.")}function Wa(e){const t=new Error(e);throw Object.assign(t,{statusCode:400,code:"CHAT_ACTION_TOKEN_INVALID"}),t}function Va(e){return Buffer.from(e,"base64url").toString("utf8")}function Ja(e){return String(e||"").split(/\r?\n/,1)[0].replace(/^#+\s*/,"").trim().slice(0,20)||"消息通知"}function Ka(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,"string"==typeof t?t:JSON.stringify(t)]))}function Qa(e){return`dtv1.card//IM_GROUP.${e}`}function Ya(e){return`dtv1.card//IM_ROBOT.${e}`}function Xa(e,t){return mr(t.biz_conversation_id).trim()||mr(e.spaceId).replace(/^dtv1\.card\/\/(?:IM_GROUP|IM_SINGLE|IM_ROBOT)\./,"")}function Za(e,t={}){const s=mr(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 er(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 tr(e,t,s,n,i){const a=Xa(n,i);return{cardUpdateOptions:{updateCardDataByKey:!0,updatePrivateDataByKey:!1},cardData:{cardParamMap:Ka({type:"actions",title:"TaskHandoff",description:e,list:Oi(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 sr(e,t,s){const n=[];return nr(n,s),nr(n,t),nr(n,e),n.find(e=>er(e))||""}function nr(e,t){for(const s of["actionIdList","actionIds"]){const n=t[s];Array.isArray(n)&&e.push(...n.map(e=>mr(e)).filter(Boolean))}for(const s of["actionId","action_id","id","actionValue"]){const n=mr(t[s]);n&&e.push(n)}}async function ir(e,t,s,n){n?.("info",{target:"sessionWebhook",hasSessionWebhook:Boolean(t),textPreview:lr(s)},"dingding webhook send requested");const i=await e(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({msgtype:"markdown",markdown:{title:Ja(s),text:s}})}),a=await i.json().catch(()=>{});if(n?.(i.ok?"info":"warn",{target:"sessionWebhook",status:i.status,response:cr(a)},"dingding webhook send completed"),!i.ok)throw new Error(`DingDing webhook failed with HTTP ${i.status}`)}async function ar(e){const t=Oi(e.replyMarkup);if(!t.length&&!e.forceCard)return e.sessionWebhook&&await ir(e.fetchImpl,e.sessionWebhook,e.text,e.runtime.onLog),e.sessionWebhook?{delivered:!0}:void 0;const s=e.bridge.settings||{},n=mr(s.robotCode),i=mr(s.cardTemplateId)||"13fc6717-12e4-43ed-8533-111a310d4995.schema",a=mr(s.cardCallbackRouteKey)||"bi_workflow_ticket",r=e.senderId||e.runtime.senderIds.get(e.chatId)||mr(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 ir(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=Ka({type:"actions",title:e.title||Ja(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:Ya(s),imRobotOpenSpaceModel:{supportForward:!1},imRobotOpenDeliverModel:{extension:{},robotCode:n,spaceType:"IM_ROBOT"}}:{openSpaceId:Qa(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:lr(e.text)},"dingding card send requested"),await rr(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 rr(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=mr(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:or(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:cr(d)},"dingding api response"),!o.ok)throw new Error(`DingDing API failed with HTTP ${o.status}`);const c=pr(d),l=c.code??c.errcode,p=c.success;if(void 0!==l&&"0"!==String(l)||!1===p)throw new Error(mr(c.message||c.errmsg)||`DingDing API returned ${String(l||"failure")}`);const u=function(e){const t=pr(pr(e).result).deliverResults;if(!Array.isArray(t))return"";const s=t.find(e=>!1===pr(e).success);if(!s)return"";const n=pr(s),i=mr(n.spaceId),a=mr(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 or(e){const t=pr(e),s=pr(t.cardData),n=pr(s.cardParamMap);return{openSpaceId:mr(t.openSpaceId),outTrackId:mr(t.outTrackId),userId:mr(t.userId),userIdType:t.userIdType,callbackType:mr(t.callbackType),callbackRouteKey:mr(t.callbackRouteKey),hasImGroupTarget:Boolean(t.imGroupOpenSpaceModel||t.imGroupOpenDeliverModel),hasImSingleTarget:Boolean(t.imSingleOpenSpaceModel||t.imSingleOpenDeliverModel),hasImRobotTarget:Boolean(t.imRobotOpenSpaceModel||t.imRobotOpenDeliverModel),imGroupOpenDeliverModel:dr(t.imGroupOpenDeliverModel),imSingleOpenDeliverModel:dr(t.imSingleOpenDeliverModel),imRobotOpenDeliverModel:dr(t.imRobotOpenDeliverModel),bizConversationId:mr(n.biz_conversation_id),bizConversationType:mr(n.biz_conversation_type),bizSenderId:mr(n.biz_sender_id),hasBizSessionWebhook:Boolean(mr(n.biz_session_webhook))}}function dr(e){const t=pr(e);if(Object.keys(t).length)return{hasExtension:Boolean(t.extension),hasRobotCode:Boolean(mr(t.robotCode)),spaceType:mr(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 cr(e){const t=pr(e),s=pr(t.result);return{code:t.code??t.errcode,message:lr(t.message||t.errmsg,240),success:t.success,requestId:t.requestId,result:Object.keys(s).length?s:void 0}}function lr(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 pr(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function ur(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:"string"==typeof e&&e.trim()?gr(e):{}}function mr(e){return"string"==typeof e?e:""}function gr(e){try{return pr(JSON.parse(String(e||"{}")))}catch{return{}}}function hr(e){return JSON.stringify(Oi(e))}function fr(e){return e instanceof Error?e.message:String(e)}function Ir(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 yr(e){return Array.isArray(e?.actionRows)?e.actionRows:(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e])}function br(e,t){const s=yr(e),n=yr(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 vr{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||Ir,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&&br(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: ${fr(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&&br(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&&br(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: ${fr(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: ${fr(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 wr=20971520;function Sr(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 Ar(e,t,n="telegram-image",i){if(i&&i>wr)throw new Error(`image is too large: ${i} bytes`);const a=await e.getFileLink(t),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>wr)throw new Error(`image is too large: ${d} bytes`);const c=s.extname(n)||".jpg";let l=c;try{l=s.extname(new URL(r).pathname)||c}catch{l=c}const p=s.join(I.tmpdir(),"task-handoff-images");await f.mkdir(p,{recursive:!0});const u=l.replace(/[^.\w-]/g,"")||".jpg",m=s.join(p,`telegram-${Date.now()}-${Math.random().toString(16).slice(2)}${u}`),g=Buffer.from(await o.arrayBuffer());if(g.length>wr)throw new Error(`image is too large: ${g.length} bytes`);return await f.writeFile(m,g),m}class kr{store;constructor(e){this.store=new vr({updateIntervalMs:e.updateIntervalMs,send:(t,s,n)=>e.send(e.requireBridge(s?.bridgeId||""),s?.chatId||"",$a(t),{rawMarkdownV2:!0,replyMarkup:_r(n)}),edit:(t,s,n,i)=>e.edit(e.requireBridge(n?.bridgeId||""),n?.chatId||"",t,$a(s),{rawMarkdownV2:!0,replyMarkup:_r(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 Tr(e){return(await async function(e,t,s,n){const i=[];for(const a of Sr(s))try{const s=await Ar({getFileLink:s=>Ua(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=t.readFileSync(e);return{id:`tg_${d.randomUUID().replace(/-/g,"").slice(0,24)}`,kind:"image",name:s.basename(e)||`telegram-image-${n+1}`,mime:zr(e),size:i.length,source:{type:"inline",encoding:"base64",data:i.toString("base64")}}})}function zr(e){const t=s.extname(e).toLowerCase();return".png"===t?"image/png":".webp"===t?"image/webp":".gif"===t?"image/gif":".bmp"===t?"image/bmp":"image/jpeg"}function Er(e){const t=Number(e?.message_id);return Number.isInteger(t)?t:void 0}function Cr(e){return e&&"object"==typeof e?e:{}}function Nr(e){return"string"==typeof e?e:""}function _r(e){const t=Array.isArray(e?.actionRows)?e.actionRows:void 0;return Ni(t||(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e]))}function xr(e){const t=Array.isArray(e.item_list)?e.item_list:[];for(const e of t){const t=Or(e);if(1!==t.type)continue;const s=Rr(Or(t.text_item).text).trim();if(s)return s}return""}function Or(e){return e&&"object"==typeof e?e:{}}function Rr(e){return"string"==typeof e?e:""}function jr(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 La(e,t,s,n,{replyMarkup:i.replyMarkup,replyToMessageId:i.replyToMessageId,rawMarkdownV2:i.rawMarkdownV2,parseMode:i.parseMode});return{provider:"telegram",raw:a,messageId:Number(Cr(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)||mr(t.settings.sessionWebhook);if(a.replyMarkup&&s){const o=await ar({fetchImpl:e,bridge:t,runtime:s,chatId:n,text:i,replyMarkup:a.replyMarkup,sessionWebhook:r,senderId:a.senderId||s.senderIds.get(n)||mr(t.settings.senderId)});if(o?.delivered)return{provider:"dingding",interactionId:o.outTrackId}}if(r)return await ir(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||Rr(t.settings.contextToken);if(a)return await async function(e,t,s,n,i){const a=Rr(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:d.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"}})}),o=await r.json().catch(()=>({}));if(!r.ok||o.errcode)throw new Error(Rr(o.errmsg)||`WeChat send failed with HTTP ${r.status}`)}(e,t,s,a,n),{provider:"wechat"}}}}(t,s):{bridge:s,send:async()=>{}}}class Dr{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=hr(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,hr(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=ar({fetchImpl:this.fetchImpl,bridge:e.bridge,runtime:t,chatId:e.chatId,text:e.text,replyMarkup:e.replyMarkup,sessionWebhook:t.chatWebhooks.get(e.chatId)||Pr(e.bridge.settings.sessionWebhook),senderId:t.senderIds.get(e.chatId)||Pr(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 rr(e.fetchImpl,e.bridge,e.runtime,"PUT","/v1.0/card/instances",{outTrackId:e.outTrackId,cardData:{cardParamMap:Ka({type:"actions",title:e.title,description:e.text,list:Oi(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 Pr(e){return"string"==typeof e?e:""}function Mr(e){const t=new y.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 $r{runtimes=new Map;connected=new Set;reconnectTimers=new Map;progress;options;createClient;constructor(e){this.options=e,this.createClient=e.createClient||Mr,this.progress=new Dr(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(y.TOPIC_ROBOT,t=>{this.options.onRobotMessage(e,n,t).catch(t=>{this.options.onError(e.id,t)})}),n.client.registerCallbackListener(y.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 qr{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 kr({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 $r({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,Kr(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:Kr(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=gr(e),s=mr(t.conversationId),n=mr(pr(t.text).content).trim();if(!s||!n)return;const i=mr(t.senderStaffId||t.senderId);return{chatId:s,userId:i||void 0,senderId:i||void 0,sessionWebhook:mr(t.sessionWebhook)||void 0,conversationType:Za(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)||Jr(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:Yr(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 ir(this.fetchImpl,d,`Failed to handle message: ${Kr(e)}`,t.onLog);throw e}const l=Lr(c);if(l){const s=Fr(c);if(await this.sendDingdingRoutedAiSessionProgress(c,e,t,i,l,{replyMarkup:s,sessionWebhook:d,senderId:r,sourcePrompt:a}))return;const n=jr({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===eo(c).routed,hasReplyMarkup:Boolean(s),replyPreview:Yr(l)},"dingding chat gateway reply sent");this.logWarn({bridgeId:e.id,chatId:i,senderId:r,hasReplyMarkup:Boolean(s),hasSessionWebhook:Boolean(d),hasRobotCode:Boolean(Jr(e.settings.robotCode)),replyPreview:Yr(l)},"dingding chat gateway reply skipped")}}async sendDingdingRoutedAiSessionProgress(e,t,s,n,i,a={}){if(!0!==e.routed)return!1;const r=no(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=po(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:Yr(a.sourcePrompt,80),textPreview:Yr(i)},"dingding ai session progress card started"),!0)}async handleDingdingCardCallback(e,t,s){const n=function(e){const t=gr(e),s=ur(t.cardActionData||t.content),n=ur(s.cardPrivateData||t.cardPrivateData||t.content),i=ur(n.params||s.params||t.params),a=mr(t.userId),r=mr(i.biz_sender_id);return{userId:a||void 0,chatId:Xa(t,i),senderId:a||void 0,deliverySenderId:r||void 0,sessionWebhook:mr(i.biz_session_webhook)||void 0,conversationType:Za(i.biz_conversation_type||t.conversationType||t.conversation_type),callbackData:er(sr(t,s,n)),body:t,params:i}}(s.data);if(!this.dingdingAllowed(e,n.userId))return tr("not authorized",void 0,"unauthorized",n.body,n.params);const i=n.chatId||Jr(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(Qr(e,"CHAT_PENDING_ACTION_STALE"))return tr(Kr(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,...Xr(n)},"dingding card callback action unsupported"),tr("Unsupported action",void 0,"unsupported",n.body,n.params);this.logInfo({bridgeId:e.id,chatId:i,userId:n.userId,senderId:a,actionType:o.type,...Xr(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&&Ur(d)?tr(`${o.decision} sent`,void 0,"approval_done",n.body,n.params):tr(Lr(d)||Jr(d.message)||"updated",Fr(d),"updated",n.body,n.params)}async pollWechat(e){if(!e.token)return;const t=e.settings||{},s=this.wechatCursors.get(e.id)||Jr(t.updatesBuf),n=await async function(e){const t=Rr((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(Rr(n.errmsg||n.msg)||`chat bridge API returned ${i}`);const a=[];for(const e of Array.isArray(n.msgs)?n.msgs:[]){const t=Or(e);if(1!==t.message_type)continue;const s=Rr(t.from_user_id),n=xr(t);s&&n&&a.push({chatId:s,text:n,contextToken:Rr(t.context_token)})}return{cursor:Rr(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=Lr(await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"wechat",bridgeId:e.id,chatSessionId:n},message:{text:a,attachments:[]}}));if(r){const s=jr({fetchImpl:this.fetchImpl,bridge:e});await s.send(n,r,{contextToken:i||Jr(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=io(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 jr({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=Cr(t.callback_query);if(Object.keys(a).length>0){i.push({updateId:n,callbackQuery:a});continue}const r=Cr(t.message||t.edited_message);if(0===Object.keys(r).length)continue;const o=Cr(r.chat),d=Cr(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=Nr(r.text??r.caption),u=Sr(r);(p||0!==u.length)&&i.push({updateId:n,chatId:c,userId:l,text:p,rawText:p,message:r,messageId:Er(r),replyToMessageId:Er(Cr(r.reply_to_message)),quoteText:Nr(Cr(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=eo(s.message),i=eo(n.chat),a=void 0!==i.id?String(i.id):e.defaultChatId;this.bridgeErrors.set(e.id,Kr(t)),a&&await this.sendTelegramMessage(e,a,`Action failed: ${Kr(t)}`).catch(t=>{this.bridgeErrors.set(e.id,Kr(t))})});continue}if(!this.telegramAllowed(e,t.userId))continue;const s=await Tr({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:Kr(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:Yr(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=Gr(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:Ni([[{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:Ni([[{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!==Gr(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=d.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=Lr(d),l=Fr(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(eo(s).message_id)||void 0,routed:!0===eo(d).routed,replyPreview:Yr(c)},"telegram chat gateway reply sent"),await this.rememberTelegramProgressMessage(d,e,t,s,c)}}catch(s){this.bridgeErrors.set(e.id,Kr(s)),await this.sendTelegramMessage(e,t,`Failed to handle message: ${Kr(s)}`,{replyToMessageId:a.sourceMessageId}).catch(t=>{this.bridgeErrors.set(e.id,Kr(t))})}}async handleTelegramCallback(e,t){const s=Jr(t.data),n=eo(t.from),i=eo(t.message),a=eo(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,Jr(t.id),s.instanceId,s.sessionId,o,Hr(i)):void await this.answerTelegramCallback(e,Jr(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,Jr(t.id),s.instanceId,s.sessionId,s.queueId,o,Hr(i)):void await this.answerTelegramCallback(e,Jr(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,Jr(t.id),s.instanceId,s.sessionId,o,Hr(i)):void await this.answerTelegramCallback(e,Jr(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,Jr(t.id),Hr(i),s.instanceId,s.sessionId,s.queueId,o):void await this.answerTelegramCallback(e,Jr(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,Jr(t.id),u[1],o);let m;try{m=await this.parseChatGatewayCallbackAction(s)}catch(s){if(Qr(s,"CHAT_PENDING_ACTION_STALE")){await this.answerTelegramCallback(e,Jr(t.id),Kr(s));const n=Hr(i);return void(void 0!==n&&await this.deleteTelegramStandaloneApprovalMessage(e,r,n))}throw s}if(!m)return void await this.answerTelegramCallback(e,Jr(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&&Ur(g)){await this.answerTelegramCallback(e,Jr(t.id),"");const s=Hr(i);return void(void 0!==s&&await this.deleteTelegramStandaloneApprovalMessage(e,r,s))}await this.answerTelegramCallback(e,Jr(t.id),Jr(g.message)||"updated");const h=Lr(g),f=Fr(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:Kr(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,Kr(t)),await this.answerTelegramCallback(e,s,`Interrupt failed: ${Yr(Kr(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,Kr(t)),await this.answerTelegramCallback(e,s,`Steer failed: ${Yr(Kr(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=eo(await this.service.aiSessionQueue(n,i)),o=(Array.isArray(r.items)?r.items:[]).map(e=>eo(e)).filter(e=>"queued"===Jr(e.status)&&Jr(e.id));if(!o.length)return void await this.answerTelegramCallback(e,s,"Queue is empty");const d=o.slice(0,ao).map((e,t)=>[{text:lo(Jr(e.message),t),callbackData:this.aiSessionQueueDeleteItemCallbackData(n,i,Jr(e.id))}]);await this.sendTelegramMessage(e,t,"Delete queued message",{replyMarkup:Ni(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,Kr(t)),await this.answerTelegramCallback(e,s,`Queue menu failed: ${Yr(Kr(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:Kr(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,Kr(t)),await this.answerTelegramCallback(e,s,`Delete failed: ${Yr(Kr(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(bi).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&&vi(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:Ha(t(n[1],"instance-app-menu").instanceId,"instanceId")}:{type:"instance-app-menu",instanceId:Va(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:Ha(e.instanceId,"instanceId"),appId:Ha(e.appId,"appId")}}return{type:"launch-app",instanceId:Va(i[1]),appId:Va(i[2])}}const a=e.match(/^task_handoff:cp_p:([^:]+)$/);if(a){const e=t(a[1],"pending-decision");return{type:"pending-decision",routeId:Ha(e.routeId,"routeId"),decision:Ga(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 Fa(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 La(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=no(e),{instanceId:r,sessionId:o,turnId:d}=a,c=Number(eo(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=po(r,o,d,t.id,s),p=this.telegramProgress.remember(l,c,i,{bridgeId:t.id,chatId:s});this.telegramProgressMessageTargets.set(Wr(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:Yr(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(Wr(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:Kr(e)},"telegram queued progress message replacement cleanup failed")}))}telegramReplyAiSessionTarget(e,t,s){const n=this.telegramProgressMessageTargets.get(Wr(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=Vr(n,e,t);if(i){const n={...i,messageId:s};return this.telegramProgressMessageTargets.set(Wr(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(Wr(n.route.bridgeId,n.route.chatId,n.messageId||0))||Vr(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(Wr(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=fo(s.get(e)||e,a),o=ho(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:Zr(yo(a))});continue}const d=uo(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:Zr(yo(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=po(e,a.id,d,i.bridgeId||"",i.chatSessionId),l=mo(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:Zr(yo(a)),textPreview:Yr(o)});continue}const p=oo({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)||co(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:Zr(yo(a)),textPreview:Yr(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:Kr(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:Yr(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:Yr(t?.lastText),progressHasPending:Boolean(t?.pending),latestTurn:Zr(yo(a)),textPreview:Yr(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:Yr(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=fo(s.get(e)||e,r),c=ho(r,d),l=uo(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:po(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=mo(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=oo({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)||co(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:Yr(c)}),g&&(this.telegramProgressMessageTargets.set(Wr(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(Wr(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(Wr(n.route.bridgeId,n.route.chatId,n.messageId))||Vr(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||yo(t)?.id===e.owner.turnId||yo(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:Br({actionRows:n})});return a&&this.logInfo({bridgeId:e.bridgeId,chatId:e.chatSessionId,key:s,textPreview:Yr(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:Yr(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:Yr(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=d.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=d.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 Lr(e){return"object"==typeof e&&e&&"reply"in e?String(e.reply||""):""}function Ur(e){return"object"==typeof e&&null!==e&&!0===e.accepted}function Fr(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 Br(e){const t=Array.isArray(e?.actionRows)?e.actionRows:void 0;return Ni(t||(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e]))}function Hr(e){const t=Number(e?.message_id);return Number.isInteger(t)?t:void 0}function Gr(e,t,s){return[e,t,s||""].join(":")}function Wr(e,t,s){return[e,t,String(s)].join(":")}function Vr(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 Jr(e){return"string"==typeof e?e:""}function Kr(e){return e instanceof Error?e.message:String(e)}function Qr(e,t){return Boolean(e&&"object"==typeof e&&"code"in e&&e.code===t)}function Yr(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 Xr(e){const t=to(e.body.cardActionData||e.body.content),s=to(t.cardPrivateData||e.body.cardPrivateData||e.body.content);return{outTrackId:Jr(e.body.outTrackId),spaceId:Jr(e.body.spaceId),conversationType:e.conversationType,hasCallbackData:Boolean(e.callbackData),callbackDataPreview:Yr(e.callbackData,120),actionIds:[...so(s.actionIdList),...so(t.actionIdList),Jr(s.actionId),Jr(t.actionId),Jr(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 Zr(e){if(e)return{id:e.id,providerTurnId:e.providerTurnId,source:e.source,status:e.status,phase:e.phase,revision:e.revision,userPrompt:Yr(e.userPrompt,80),hasResponse:Boolean(e.lastMessage?.trim()||e.summary?.trim()),responsePreview:Yr(e.lastMessage||e.summary,80),updatedAt:e.updatedAt,observedAt:e.observedAt}}function eo(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function to(e){if(e&&"object"==typeof e&&!Array.isArray(e))return e;if("string"==typeof e&&e.trim())try{return eo(JSON.parse(e))}catch{return{}}return{}}function so(e){return Array.isArray(e)?e.map(e=>Jr(e)).filter(Boolean):[]}function no(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?uo(s):""),sources:{resultTurnId:e.turnId,resultProviderTurnId:e.providerTurnId,actionTurnId:t?.turnId,actionProviderTurnId:t?.providerTurnId,sessionActiveTurnId:s?.activeTurnId,latestTurnId:s?yo(s)?.id:void 0}}}function io(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:Ni([[{text:"Allow",callbackData:t(e.id,"allow")},{text:"Skip",callbackData:t(e.id,"skip")},{text:"Deny",callbackData:t(e.id,"deny")}]])}}const ao=5,ro=48;function oo(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,ao);return[...d.map((e,t)=>[{text:lo(e.message,t),callbackData:s(e.id)}]),d.length?[{text:"Delete Queue",callbackData:n()},{text:"Cancel",callbackData:t}]:[{text:"Cancel",callbackData:t}]]}function co(e,t){if("approval"===t.phase)return{id:`${e}:ai:${t.id}`,kind:"approval"}}function lo(e,t){const s=`${t+1}. `,n=e.replace(/\s+/g," ").trim()||"Queued message",i=Math.max(8,ro-s.length),a=Array.from(n);return`${s}${a.length>i?`${a.slice(0,Math.max(1,i-3)).join("")}...`:n}`}function po(e,t,...s){return[e,t,...s].join(":")}function uo(e){const t=yo(e);return t?.id?t.id:Jr(e.activeTurnId)}function mo(e,t=""){const s=yo(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),go(e)].join("\0")}function go(e){return(e.queue?.items||[]).map(e=>[e.id,e.status,e.message].join(":")).join("|")}function ho(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&&!Io(s,e)?n:""].filter(Boolean).join("\n\n")}(e,yo(e));return s?`${t}\n${s}`:""}function fo(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 Io(e,t){if("waiting"!==t.status||"approval"!==t.phase)return!1;const s=t.currentTool?.inputPreview?.trim();return Boolean(s&&e.includes(s))}function yo(e){return(e.turns||[]).filter(e=>e.id||e.userPrompt?.trim()||e.lastMessage?.trim()||e.summary?.trim()).at(-1)}class bo{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&&vo(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||Ps(e),createdAt:(new Date).toISOString(),payload:t,scope:s.scope}}}function vo(e,t,s){return!e||e.has("*")||e.has(t)||e.has(s)}const wo={"image/bmp":".bmp","image/gif":".gif","image/jpeg":".jpg","image/png":".png","image/webp":".webp"},So=o.z.object({instanceId:o.z.string().trim().min(1).max(120),sessionId:o.z.string().trim().min(1).max(120),kind:o.z.enum(["image","file"]),name:o.z.string().trim().min(1).max(240),mime:o.z.string().trim().min(1).max(120),data:o.z.string().min(1).max(31457280)}).strict();class Ao{items=new Map;ttlMs;constructor(e=864e5){this.ttlMs=e}upload(e){this.pruneExpired();const n=So.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=wo[a]||"";if("image"===n.kind&&!r)throw new Error(`Unsupported image type: ${a}`);const o="image"===n.kind?i.buffer.length>v:i.buffer.length>=w;if(!i.buffer.length||o)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_${d.randomUUID().replace(/-/g,"").slice(0,24)}`,l=new Date,p=new Date(l.getTime()+this.ttlMs),u=s.join(I.tmpdir(),"task-handoff-ai-session-attachments");t.mkdirSync(u,{recursive:!0});const m=function(e){return s.basename(e).replace(/[^\w.\- ()\u4e00-\u9fff]/g,"_").slice(0,160)||"attachment"}(n.name),g=s.join(u,`${c}-${m.endsWith(r)?m:`${m}${r}`}`);t.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=o.z.array(U).max(6).default([]).parse(e),i=n.map(e=>"runtime-path"===e.source.type?q.parse(e):this.requireAttachment(e,t,s));if(i.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>S)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 q.parse({id:n.id,kind:n.kind,name:n.name,mime:n.mime,size:n.size,source:n.source})}consumeAttachment(e){const s=this.items.get(e);if(s){this.items.delete(e);try{t.unlinkSync(s.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[s,n]of this.items)if(!(Date.parse(n.expiresAt)>e)){this.items.delete(s);try{t.unlinkSync(n.path)}catch{}}}}function ko(e){return e.readyState===(e.OPEN??1)}function To(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 zo(e,t){e.send(t.data,{binary:t.isBinary})}function Eo(e,t){if(ko(e))for(const s of t.splice(0))zo(e,s)}class Co{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 No=o.z.object({nodeId:o.z.string().trim().min(1).max(120)}).strict();function _o(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 xo(){return`req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`}const Oo=new Map;function Ro(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)>va)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()-va;for(const[e,t]of Oo)t<=i&&Oo.delete(e);const a=`${e.id}:${s.keyId}:${s.nonce}`;if(!s.nonce||Oo.has(a))throw Object.assign(new Error("Reverse tunnel nonce is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_NONCE_INVALID"});const r=wa("");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 o=t.url||`/api/node-agent/tunnel?nodeId=${encodeURIComponent(e.id)}`,c=Sa(e.auth.secret,{keyId:s.keyId,method:"GET",pathWithQuery:o,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&&d.timingSafeEqual(s,n)}(s.signature,c))throw Object.assign(new Error("Reverse tunnel signature is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_SIGNATURE_INVALID"});Oo.set(a,Date.now()+va)}function jo(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 Do{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=qs.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===ps){const t=ls.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(ps,t.data,{topic:"instances",scope:{...r,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(i===zs){const t=Es.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(zs,t.data,{topic:"instances",scope:{...r,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(i===ts||i===ss){const t=i===ts?is.safeParse(a):as.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=xo(),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:jo(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=xo(),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:jo(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 g.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(g.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=xo(),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=ko(t);const r=[],o=[];let d;const c=()=>{d&&(clearTimeout(d),d=void 0)},l=(s,i)=>{if(s.length>=n)return To(e,1011,"WebSocket bridge pending frame limit exceeded."),void To(t,1011,"WebSocket bridge pending frame limit exceeded.");s.push(i)};s.upstreamOpenTimeoutMs&&!a&&(d=setTimeout(()=>{a||i||(To(e,1011,"Upstream websocket did not open."),To(t))},s.upstreamOpenTimeoutMs)),e.on("open",()=>Eo(e,o)),t.on("open",()=>{c(),a=!0,s.onUpstreamOpen?.(),Eo(t,r)}),e.on("message",(e,s=!1)=>{const n={data:e,isBinary:Boolean(s)};ko(t)?zo(t,n):l(r,n)}),t.on("message",(t,s=!1)=>{const n={data:t,isBinary:Boolean(s)};ko(e)?zo(e,n):l(o,n)}),t.on("close",(t,n)=>{c(),s.onUpstreamClose?.(t,n),(a||i||!s.onUpstreamCloseBeforeOpen?.())&&To(e,t,n)}),t.on("error",t=>{s.onUpstreamError?.(t),(a||i||!s.onUpstreamErrorBeforeOpen?.(t))&&To(e)}),e.on("close",(e,n)=>{c(),i=!0,s.onClientClose?.(e,n),To(t,e,n)}),e.on("error",e=>{c(),i=!0,s.onClientError?.(e),To(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 Po{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=ka(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 Aa({nodeId:e.id,keyId:_o(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?za(s,"/events",void 0,i):new p.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 Co,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 Co};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(ka(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 Mo(e=function(){return process.env.TASK_HANDOFF_CONTROL_PLANE_DATA_DIR||s.join(I.homedir(),".config","task-handoff","control-plane")}()){const t=s.resolve(e);return{dataDir:t,settingsPath:s.join(t,"control-plane-settings.json"),projectsDir:s.join(t,"projects"),modelsDir:s.join(t,"models"),imagesDir:s.join(t,"images"),nodesDir:s.join(t,"nodes"),nodeRuntimesDir:s.join(t,"node-runtimes"),controlledInstancesDir:s.join(t,"controlled-instances"),chatSessionsDir:s.join(t,"chat-sessions"),chatBridgesDir:s.join(t,"chat-bridges"),triggersDir:s.join(t,"triggers"),nodeJoinInvitesDir:s.join(t,"node-join-invites"),authUsersDir:s.join(t,"auth-users"),authSessionsDir:s.join(t,"auth-sessions"),aiSessionUnreadDir:s.join(t,"ai-session-unread"),logsDir:s.join(t,"logs")}}class $o 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 qo(e){return s.join(e,"owner.json")}function Lo(e){return s.join(e,"initializing.json")}function Uo(e){return s.join(e,"recovering.json")}function Fo(e){try{const s=JSON.parse(t.readFileSync(qo(e),"utf8"));if(!Number.isInteger(s.pid)||Number(s.pid)<=0||"string"!=typeof s.hostname||!s.hostname||"string"!=typeof s.command||"string"!=typeof s.acquiredAt||!Number.isFinite(Date.parse(s.acquiredAt))||"string"!=typeof s.token||!s.token)return;return s}catch{return}}function Bo(e){if(!Number.isInteger(e)||Number(e)<=0)return!1;try{return process.kill(Number(e),0),!0}catch(e){return"EPERM"===e.code}}function Ho(e,n){const i=s.join(e,`.owner-${n.token}.tmp`);t.writeFileSync(i,`${JSON.stringify(n,null,2)}\n`,{encoding:"utf8",mode:384,flag:"wx"});const a=Go(e);if(a?.token!==n.token)throw Wo(i),new Error(`Process lock initialization was superseded: ${e}`);t.renameSync(i,qo(e)),Wo(Lo(e))}function Go(e){try{return JSON.parse(t.readFileSync(Lo(e),"utf8"))}catch{return}}function Wo(e){try{t.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}function Vo(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,20)}function Jo(e){try{const s=Go(e),n=Date.parse(s?.startedAt||"");return Number.isFinite(n)?Math.max(0,Date.now()-n):Math.max(0,Date.now()-t.statSync(e).mtimeMs)}catch{return 0}}function Ko(e,n){try{return t.writeFileSync(Uo(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=Uo(e),i=Qo(n),a=Date.parse(i?.startedAt||"");if(Bo(i?.pid)||Number.isFinite(a)&&Date.now()-a<2e3)return!1;const r=s.join(e,`.recovering-${d.randomUUID()}.stale`);try{t.renameSync(n,r)}catch(e){if(["ENOENT","EEXIST"].includes(e.code||""))return!1;throw e}const o=Qo(r);if(o?.token===i?.token)return Wo(r),!0;try{t.linkSync(r,n)}catch(e){if("EEXIST"!==e.code)throw e}finally{Wo(r)}return!1}(e)&&Ko(e,n);if("ENOENT"===i.code)return!1;throw i}}function Qo(e){try{return JSON.parse(t.readFileSync(e,"utf8"))}catch{return}}function Yo(e,t){return Qo(Uo(e))?.token===t}function Xo(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 Zo=o.z.string().trim().min(1),ed=o.z.object({id:Zo}).strict(),td=o.z.object({id:Zo,keyId:Zo}).strict(),sd=o.z.object({id:Zo,runtimeId:Zo}).strict(),nd=o.z.object({id:Zo,folderId:Zo}).strict(),id=o.z.object({id:Zo,sessionId:Zo}).strict(),ad=o.z.object({id:Zo,sessionId:Zo,queueId:Zo}).strict(),rd=o.z.object({configHash:Zo}).strict(),od=o.z.object({id:Zo,configHash:Zo}).strict(),dd=o.z.object({id:Zo,sessionId:Zo,configHash:Zo}).strict(),cd=o.z.object({path:o.z.string().optional(),depth:o.z.coerce.number().int().min(0).max(32).optional()}).strict(),ld=o.z.object({ids:o.z.array(o.z.string().trim().min(1).max(120)).default([])}).strict(),pd=o.z.object({nodeId:o.z.string().trim().min(1).max(120),modelId:o.z.string().trim().min(1).max(120).optional()}).strict(),ud=o.z.object({source:o.z.object({channel:xs,bridgeId:o.z.string().trim().min(1).max(120).optional(),chatSessionId:o.z.string().trim().min(1).max(240),userId:o.z.string().trim().max(240).optional()}).strict(),action:o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal("ai-session"),index:o.z.number().int().nonnegative()}).strict(),o.z.object({type:o.z.literal("instance-app-menu"),instanceId:o.z.string().trim().min(1).max(120)}).strict(),o.z.object({type:o.z.literal("launch-app"),instanceId:o.z.string().trim().min(1).max(120),appId:o.z.string().trim().min(1).max(120)}).strict(),o.z.object({type:o.z.literal("pending-decision"),routeId:o.z.string().trim().min(1).max(120),decision:o.z.enum(["allow","deny","skip"])}).strict()])}).strict();function md(e){const t=ud.parse(e);if(!t.action)throw new Error("Chat gateway action is required.");return{source:t.source,action:t.action}}const gd=o.z.object({id:o.z.string().trim().min(1),direction:o.z.enum(["import","export"]),preset:o.z.string().trim().min(1).max(120)}),hd=o.z.object({id:o.z.string().trim().min(1),appId:o.z.string().trim().min(1)}).strict(),fd=o.z.object({id:o.z.string().trim().min(1),jobId:o.z.string().trim().min(1)}).strict();const Id=o.z.object({appId:o.z.string().trim().min(1).max(120).default("terminal-tty"),cwdFolderId:o.z.string().trim().min(1).max(120).optional(),options:o.z.record(o.z.string(),o.z.unknown()).default({})}).strict().default({appId:"terminal-tty",options:{}}),yd=o.z.object({title:o.z.string().trim().min(1).max(120)}).strict(),bd=o.z.object({}).strict();function vd(e){const{app:t,service:s,events:n,appSessionAggregator:i,aiSessionAggregator:a,aiSessionUnread:r,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(ed.parse(e.params).id)})),e.patch("/api/projects/:id",async e=>{const n=t.updateProject(ed.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=ed.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=ld.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(ed.parse(e.params).id)})),e.patch("/api/models/:id",async e=>{const n=await t.updateModel(ed.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=ed.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}=pd.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}=pd.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}=pd.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/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(ed.parse(e.params).id)})),e.patch("/api/images/:id",async e=>{const n=t.updateImage(ed.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=ed.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(ed.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(ed.parse(e.params).id)})),e.get("/api/nodes/:id/settings/external-listener",async e=>({data:await t.getLocalNodeExternalListener(ed.parse(e.params).id)})),e.patch("/api/nodes/:id/settings/external-listener",async e=>{const n=ed.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=ed.parse(e.params).id,i=await t.checkNodeUpdate(n,xt.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=ed.parse(e.params).id,a=await t.applyNodeUpdate(i,jt.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=ed.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=ed.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=ed.parse(e.params);return{data:await t.listNodeRemoteControlPlanes(s.id)}}),e.delete("/api/nodes/:id/remotes/:keyId",async e=>{const n=td.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(ed.parse(e.params).id)})),e.post("/api/nodes/:id/runtimes",async(e,n)=>{const i=ed.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=sd.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=sd.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=sd.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(ed.parse(e.params).id)})),e.get("/api/nodes/:id/images/catalog",async e=>({data:await t.listNodeImageAvailability(ed.parse(e.params).id)})),e.get("/api/nodes/:id/local-folders",async e=>({data:await t.listNodeLocalFolders(ed.parse(e.params).id)})),e.get("/api/nodes/:id/folders/tree",async e=>{const s=ed.parse(e.params),n=cd.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=ed.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=nd.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(ed.parse(e.params).id)})),e.patch("/api/nodes/:id",async e=>{const n=t.updateNode(ed.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=ed.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=No.parse(t.query),a=s.requireNode(i.nodeId);Ro(a,t),n.attach(a.id,e),e.send(JSON.stringify({type:"control-plane.hello",protocolVersion:Be,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=No.parse(t.query),a=s.requireNode(i.nodeId);Ro(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=No.parse(t.query),a=s.requireNode(i.nodeId);Ro(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(ed.parse(e.params).id)})),e.get("/api/controlled-instances/:id/metrics",async e=>({data:await t.instanceResourceMetrics(ed.parse(e.params).id)})),e.get("/api/controlled-instances/:id/apps/management",async e=>({data:await t.instanceAppManagement(ed.parse(e.params).id)})),e.post("/api/controlled-instances/:id/apps/:appId/install",async e=>{const s=hd.parse(e.params),n=St.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=hd.parse(e.params),n=St.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=fd.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(ed.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=ed.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(ed.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(ed.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(ed.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(ed.parse(e.params).id);return s.publish("instance.image-provisioning-retried",{instanceId:n.id}),{data:n}}),e.get("/api/config-sync/presets",async()=>({data:t.listConfigSyncPresets()})),e.post("/api/controlled-instances/:id/config-sync/:direction/:preset",async e=>{const n=gd.parse(e.params),i=await t.syncInstanceConfig(n.id,n.direction,n.preset);return s.publish("instance.config-synced",{instanceId:n.id,direction:n.direction,preset:n.preset}),{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:r}){e.get("/api/app-sessions",async e=>{const t=o.z.object({refresh:o.z.string().optional(),includeTombstones:o.z.string().optional(),instanceId:o.z.string().trim().min(1).optional(),streamId:o.z.string().trim().min(1).optional(),sinceRevision:o.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=ed.parse(e.params),i=Id.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=id.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=id.parse(e.params),i=yd.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=ed.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=ed.parse(e.params);return{data:await t.listAiSessionHistory(s.id)}}),e.get("/api/controlled-instances/:id/ai-sessions/history/:sessionId",async e=>{const s=id.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=id.parse(e.params);bd.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=id.parse(e.params),i=ne.parse(e.body||{}),a=r.resolveRefs(i.attachments,n.id,n.sessionId),o=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:o}}),e.get("/api/controlled-instances/:id/ai-sessions/:sessionId/mentions",async e=>{const s=id.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=id.parse(e.params),n=ee.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=id.parse(e.params),i=G.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=id.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=ad.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=ad.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=ad.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=id.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=id.parse(e.params),i=ie.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=id.parse(e.params),r=o.z.object({sessionUpdatedAt:o.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,r.sessionUpdatedAt);return l?(s.publish(z,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:r,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=rd.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=rd.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=ed.parse(e.params);return{data:await t.listInstanceTriggers(s.id)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/triggers",async e=>{const n=id.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=dd.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=od.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(ed.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=ed.parse(n.params).id,a=t.requireChatBridge(i),r=Ti.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=ed.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=ed.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(ed.parse(e.params).id)}})),e.post("/api/chat-gateway/messages",{bodyLimit:67108864},async e=>({data:await t.handleChatGatewayMessage(js.parse(e.body))})),e.post("/api/chat-gateway/actions",async e=>({data:await t.handleChatGatewayAction(md(e.body))})),e.get("/api/pending-routes",async()=>({data:await t.listPendingRoutes()}))}({app:t,service:s,chatGateway:d})}const wd=new Set(["connection","content-length","keep-alive","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding","upgrade"]),Sd=new Set(["content-encoding"]);function Ad(e){return Object.fromEntries(Object.entries(e).filter(([e,t])=>!wd.has(e.toLowerCase())&&void 0!==t).map(([e,t])=>[e,Array.isArray(t)?t.join(", "):String(t)]))}function kd(e){return Object.fromEntries(Object.entries(Ad(e)).filter(([e])=>{const t=e.toLowerCase();return"host"!==t&&"cookie"!==t&&"authorization"!==t&&(!t.startsWith("sec-websocket-")||"sec-websocket-origin"===t)}))}function Td(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 zd(e,t,s){for(const[s,n]of Object.entries(t.headers)){const t=s.toLowerCase();wd.has(t)||Sd.has(t)||e.header(s,n)}if(!t.body)return e.code(t.status).send();const n=g.Readable.fromWeb(t.body);return e.raw.once("close",()=>n.destroy()),e.code(t.status).send(s?n.pipe(s):n)}function Ed(e){return`/instances/${encodeURIComponent(e)}`}function Cd(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 g.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()}})}(Ed(s)):void 0;return zd(e,t,i)}function Nd(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 _d(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 xd(e){return new URL(e,"http://control-plane.local").searchParams.get("token")||""}function Od(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 Rd{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===A?ke:e.type===k?Te:e.type===T?ze:void 0;if(!t)return!1;const s=t.safeParse(e.payload);return s.success?(e.type===A&&this.applySnapshot(s.data),e.type===k&&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:A,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:k,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 Ce.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:!0,events:[]});if(e.sinceRevision===s)return Ce.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 Ce.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===A&&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===A)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:be.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===k){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=ye.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:be.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:A,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:A,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 jd=o.z.object({id:o.z.string().trim().min(1),instanceId:o.z.string().trim().min(1).max(160),sessionId:o.z.string().trim().min(1).max(120),unread:o.z.boolean(),lastStatus:o.z.enum(["running","waiting","idle","failed"]),sessionUpdatedAt:o.z.string().datetime(),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict();class Dd{records;bySession=new Map;listener;constructor(e,t={}){this.records=new Vs(e.aiSessionUnreadDir,{schema:jd}),this.listener=t.onChanged}init(){this.records.init(),this.bySession.clear();for(const e of this.records.list())this.bySession.set(Md(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(Md(e,t.id))?.unread||!1}))}}markRead(e,t,s){const n=this.bySession.get(Md(e,t));return n&&n.sessionUpdatedAt===s&&n.unread?Pd(this.store({...n,unread:!1,updatedAt:(new Date).toISOString()},!1)):n?Pd(n):void 0}removeInstance(e){for(const t of[...this.bySession.values()])t.instanceId===e&&this.deleteRecord(t)}reconcileSession(e,t){const s=Md(e,t.id),n=this.bySession.get(s),i=(new Date).toISOString();if(!n)return void this.store({id:$d(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(Md(s.instanceId,s.sessionId),s),t&&this.listener?.(Pd(s)),s}deleteRecord(e){this.records.delete(e.id),this.bySession.delete(Md(e.instanceId,e.sessionId))}}function Pd(e){return N.parse({instanceId:e.instanceId,sessionId:e.sessionId,unread:e.unread,sessionUpdatedAt:e.sessionUpdatedAt,updatedAt:e.updatedAt})}function Md(e,t){return`${e}\0${t}`}function $d(e,t){return`ais_unread_${d.createHash("sha256").update(Md(e,t)).digest("hex").slice(0,32)}`}class qd{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===pn?wn:e.type===un?Sn:e.type===mn?An:void 0;if(!t)return!1;const s=t.safeParse(e.payload);return s.success?(e.type===pn&&this.applySnapshot(s.data),e.type===un&&this.applyPatch(s.data),e.type===mn&&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:pn,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:mn,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 kn.parse({streamId:n,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:s,earliestRetainedRevision:i,syncRequired:!0,events:[]});if(e.sinceRevision===s)return kn.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 kn.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===pn&&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===pn)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:yn.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=Tn(n,s.generatedAt);return{kind:"applied",projection:yn.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:pn,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:pn,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 Tn(e.sessions.filter(e=>Pi("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 Ld(e){return e.raw[0].replace(/\\\$\{/g,"${")}class Ud{states=new Map;events;constructor(e){this.events=e}handle(e){if(e.type===zs){const t=Es.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===ts){const t=is.safeParse(e.payload);return void(t.success&&this.applyOutput(t.data))}if(e.type===ss){const t=as.safeParse(e.payload);t.success&&this.applyFinished(t.data)}}snapshots(){return[...this.states.values()].filter(e=>e.progress).map(e=>rs.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 Gd,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=Bd(i);e.set(n,{...o,status:r,...d||{}})}}(s.layers,s.terminal.text()),s.progress=Fd(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=Fd(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 Gd,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=Fd(e),this.events.publish("image.pull.progress",e.progress,{topic:"instances",scope:{instanceId:e.identity.instanceId}}),t&&(e.progress=Fd(e)))}}function Fd(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 rs.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 Bd(e){const t=e.match(/([\d.]+)\s*([kmgt]?i?b)\s*\/\s*([\d.]+)\s*([kmgt]?i?b)/i);if(t)return{current:Hd(t[1],t[2]),total:Hd(t[3],t[4])}}function Hd(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 Gd{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 Wd=o.z.object({code:o.z.string().trim().min(1),message:o.z.string(),details:o.z.record(o.z.string(),o.z.unknown()).optional(),retryable:o.z.boolean().optional()}).strict(),Vd=o.z.object({error:Wd}).strict();function Jd(e){const t=process.env[e]?.trim();return t||void 0}function Kd(){try{const e=("undefined"==typeof document?require("url").pathToFileURL(__filename).href:b&&"SCRIPT"===b.tagName.toUpperCase()&&b.src||new URL("cli.js",document.baseURI).href)?s.dirname(n.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:b&&"SCRIPT"===b.tagName.toUpperCase()&&b.src||new URL("cli.js",document.baseURI).href)):__dirname,i=[s.resolve(e,"..","package.json"),s.resolve(e,"..","..","..","package.json")].find(e=>t.existsSync(e));return i&&JSON.parse(t.readFileSync(i,"utf8")).version||"unknown"}catch{return"unknown"}}function Qd(){return{component:"control-plane",packageName:"@task-handoff/control-plane",packageVersion:Kd(),protocolVersion:Be,buildId:Jd("TASK_HANDOFF_BUILD_ID"),builtAt:Jd("TASK_HANDOFF_BUILT_AT"),gitCommit:Jd("TASK_HANDOFF_GIT_COMMIT"),imageRef:Jd("TASK_HANDOFF_IMAGE_REF"),imageDigest:Jd("TASK_HANDOFF_IMAGE_DIGEST")}}const Yd={".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 Xd(e,n){const i=s.resolve(e),a=s.resolve(i,n);if(a===i||a.startsWith(`${i}${s.sep}`))return t.existsSync(a)&&t.statSync(a).isFile()?a:void 0}function Zd(e,n,i){const a=Xd(n,i);return a?(e.header("cache-control","no-store"),e.type(Yd[s.extname(a).toLowerCase()]||"application/octet-stream"),e.send(t.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 ec(e){if(e instanceof o.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 tc(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 sc(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"}}:s.includes("/config-sync/")?{action:"update",resource:{type:"instance"}}:{action:n,resource:{type:"instance"}}:s.startsWith("/api/projects")?{action:n,resource:{type:"project"}}:s.startsWith("/api/images")?{action:n,resource:{type:"runtime"}}:s.startsWith("/api/triggers")?{action:n,resource:{type:"trigger"}}:{action:"read",resource:{type:"control-plane-settings"}}}async function nc(e={}){const n=Mo(e.dataDir),o=i({logger:e.logger??!0}),d=new ja(n,{...e.service,logger:o.log}),c=new ln(n,e.auth),l=new bo,p=new Ud(l),u=new Dd(n,{onChanged:e=>queueMicrotask(()=>l.publish(z,e,{topic:"ai.sessions",scope:{instanceId:e.instanceId,sessionId:e.sessionId}}))});u.init();const m=new Rd({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 qd({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 Ao,f=new Do(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 qr(d,e.service?.fetchImpl,{aiSessions:m,logger:o.log});I.startEnabled();const y=new Po(d,f,{safetyIntervalMs:Number(process.env.TASK_HANDOFF_EVENT_CONNECTION_SAFETY_INTERVAL_MS)||void 0,logger:o.log});y.start(),await o.register(a),await o.register(r),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=ec(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(Vd.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 tc(c,e.cookies[Qs]),s=sc(e.method,e.url);return void(s&&Xo(t||{type:"system",reason:"auth-disabled"},s.action,s.resource))}const s=await tc(c,e.cookies[Qs]);if(!s)return t.code(401).send({error:{code:"CONTROL_PLANE_AUTH_REQUIRED",message:"Sign in to access the Control Plane."}});const n=sc(e.method,e.url);n&&Xo(s,n.action,n.resource)});const w=s.resolve(e.staticDir||process.env.TASK_HANDOFF_CONTROL_PLANE_STATIC_DIR||s.resolve(process.cwd(),"packages","control-plane-ui","dist"));return t.existsSync(w)&&(o.get("/assets/*",async(e,t)=>{const n=e.params["*"];return Zd(t,w,s.join("assets",n||""))}),o.get("/favicon.ico",async(e,t)=>Xd(w,"favicon.ico")?Zd(t,w,"favicon.ico"):t.code(204).send())),o.get("/api/health",async()=>({data:{ok:!0,role:"control-plane",protocolVersion:Be,build:Qd(),dataDir:n.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"),Ld`#!/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[Bs])})),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(Bs,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[Bs]);return t.clearCookie(Bs,{path:"/"}),{data:s}}),o.get("/api/control-plane/status",async()=>({data:{protocolVersion:Be,build:Bd(),storage:n}})),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)}})}}),md({app:o,service:d,events:l,appSessionAggregator:g,aiSessionAggregator:m,aiSessionUnread:u,chatGateway:f,aiSessionAttachments:h,nodeAgentTunnel:I,nodeEventSubscriber:y,errorPayload:Vd}),function({app:e,service:t}){!function(e){for(const t of["application/octet-stream","application/x-www-form-urlencoded"])Ad(e,t);Ad(e,/^multipart\/form-data(?:;.*)?$/i)}(e),e.get("/api/app-access/session",async e=>{const s=kd(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?Td(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(kd(s.url),"tty");await t.proxyInstanceWebSocket(n.instance.id,e,n.path,yd(s.headers),fd(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(kd(s.url),"vnc",n["*"]||"");await t.proxyInstanceWebSocket(i.instance.id,e,i.path,yd(s.headers),fd(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(kd(e.url),"vnc",n["*"]||"");return wd(s,await t.proxyInstanceHttp(i.instance.id,i.path,{method:e.method,headers:Id(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(`${vd(n.id)}/`):wd(s,await t.proxyInstanceHttp(n.id,"/",{method:e.method,headers:Id(e.headers),body:Sd(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}`,yd(s.headers),fd(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 wd(s,await t.proxyInstanceHttp(n.id,r,{method:e.method,headers:Id(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 bd(s,await t.proxyInstanceHttp(n.id,`/${i}${r}`,{method:e.method,headers:Id(e.headers),body:Sd(e.body)}))}})}({app:o,service:d}),o.get("*",async(e,s)=>t.existsSync(w)?Wd(s,w,"index.html"):s.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 Xd(e){const n=_o(e.dataDir),i=function(e,n={}){return function(e,n={}){t.mkdirSync(s.dirname(e),{recursive:!0});const i={pid:process.pid,hostname:f.hostname(),component:n.component,command:process.argv.join(" "),acquiredAt:(new Date).toISOString(),token:d.randomUUID(),dataDir:n.dataDir,host:n.host,port:n.port};for(let s=0;s<5;s+=1)try{return t.mkdirSync(e),t.writeFileSync(Ro(e),`${JSON.stringify({token:i.token,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),Mo(e,i),{lockPath:e,owner:i,release(){const s=Do(e);s?.token===i.token&&t.rmSync(e,{recursive:!0,force:!0})}}}catch(i){if("EEXIST"!==i.code)throw i;const a=Do(e);if(Po(a?.pid))throw new xo(e,a,n.error);if(!a&&Uo(e)<2e3){if(s+1<5){Lo();continue}throw new xo(e,void 0,n.error)}const r=d.randomUUID();if(!Fo(e,r)){if(s+1<5){Lo();continue}throw new xo(e,Do(e),n.error)}const o=Do(e);if(Po(o?.pid))throw qo(jo(e)),new xo(e,o,n.error);if(a?.token&&o?.token!==a.token){qo(jo(e));continue}if(!Ho(e,r))continue;t.rmSync(e,{recursive:!0,force:!0})}const a=Do(e);throw new xo(e,a,n.error)}(e,{...n,component:"control-plane",error:{label:"Control plane",code:"CONTROL_PLANE_ALREADY_RUNNING"}})}(s.join(f.tmpdir(),`task-handoff-control-plane-${process.getuid?.()??"user"}.lock`),{dataDir:n.dataDir,host:e.host,port:e.port});try{const t=await Qd(e);t.addHook("onClose",async()=>{i.release()}),await t.listen({host:e.host,port:e.port})}catch(e){throw i.release(),e}}function Yd(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 Zd(e){if("disabled"!==e&&"password"!==e)throw new Error("Auth mode must be disabled or password.");return e}(async function(){const n=new e.Command;n.name("task-handoff-control-plane").description("Run the TaskHandoff control plane.").version(function(){try{return JSON.parse(t.readFileSync(s.resolve(__dirname,"..","package.json"),"utf8")).version||"unknown"}catch{return"unknown"}}()).option("--host <host>","Control plane host",process.env.TASK_HANDOFF_CONTROL_PLANE_HOST||"127.0.0.1").option("-p, --port <port>","Control plane port",Yd,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||s.resolve(__dirname,"..","ui")).option("--auth-mode <mode>","Control plane auth mode: disabled or password",Zd,process.env.TASK_HANDOFF_CONTROL_PLANE_AUTH_MODE||"password").action(async e=>{await Xd({host:e.host,port:e.port,dataDir:e.dataDir,staticDir:e.staticDir,auth:{mode:e.authMode}})}),await n.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[Qs])})),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(Qs,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[Qs]);return t.clearCookie(Qs,{path:"/"}),{data:s}}),o.get("/api/control-plane/status",async()=>({data:{protocolVersion:Be,build:Qd(),storage:n}})),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)}})}}),vd({app:o,service:d,events:l,appSessionAggregator:g,aiSessionAggregator:m,aiSessionUnread:u,chatGateway:I,aiSessionAttachments:h,nodeAgentTunnel:f,nodeEventSubscriber:y,errorPayload:ec}),function({app:e,service:t}){!function(e){for(const t of["application/octet-stream","application/x-www-form-urlencoded"])_d(e,t);_d(e,/^multipart\/form-data(?:;.*)?$/i)}(e),e.get("/api/app-access/session",async e=>{const s=xd(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?Od(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(xd(s.url),"tty");await t.proxyInstanceWebSocket(n.instance.id,e,n.path,Td(s.headers),kd(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(xd(s.url),"vnc",n["*"]||"");await t.proxyInstanceWebSocket(i.instance.id,e,i.path,Td(s.headers),kd(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(xd(e.url),"vnc",n["*"]||"");return Cd(s,await t.proxyInstanceHttp(i.instance.id,i.path,{method:e.method,headers:Ad(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(`${Ed(n.id)}/`):Cd(s,await t.proxyInstanceHttp(n.id,"/",{method:e.method,headers:Ad(e.headers),body:Nd(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}`,Td(s.headers),kd(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 Cd(s,await t.proxyInstanceHttp(n.id,r,{method:e.method,headers:Ad(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 zd(s,await t.proxyInstanceHttp(n.id,`/${i}${r}`,{method:e.method,headers:Ad(e.headers),body:Nd(e.body)}))}})}({app:o,service:d}),o.get("*",async(e,s)=>t.existsSync(w)?Zd(s,w,"index.html"):s.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 ic(e){const n=Mo(e.dataDir),i=function(e,n={}){return function(e,n={}){t.mkdirSync(s.dirname(e),{recursive:!0});const i={pid:process.pid,hostname:I.hostname(),component:n.component,command:process.argv.join(" "),acquiredAt:(new Date).toISOString(),token:d.randomUUID(),dataDir:n.dataDir,host:n.host,port:n.port};for(let s=0;s<5;s+=1)try{return t.mkdirSync(e),t.writeFileSync(Lo(e),`${JSON.stringify({token:i.token,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),Ho(e,i),{lockPath:e,owner:i,release(){const s=Fo(e);s?.token===i.token&&t.rmSync(e,{recursive:!0,force:!0})}}}catch(i){if("EEXIST"!==i.code)throw i;const a=Fo(e);if(Bo(a?.pid))throw new $o(e,a,n.error);if(!a&&Jo(e)<2e3){if(s+1<5){Vo();continue}throw new $o(e,void 0,n.error)}const r=d.randomUUID();if(!Ko(e,r)){if(s+1<5){Vo();continue}throw new $o(e,Fo(e),n.error)}const o=Fo(e);if(Bo(o?.pid))throw Wo(Uo(e)),new $o(e,o,n.error);if(a?.token&&o?.token!==a.token){Wo(Uo(e));continue}if(!Yo(e,r))continue;t.rmSync(e,{recursive:!0,force:!0})}const a=Fo(e);throw new $o(e,a,n.error)}(e,{...n,component:"control-plane",error:{label:"Control plane",code:"CONTROL_PLANE_ALREADY_RUNNING"}})}(s.join(I.tmpdir(),`task-handoff-control-plane-${process.getuid?.()??"user"}.lock`),{dataDir:n.dataDir,host:e.host,port:e.port});try{const t=await nc(e);t.addHook("onClose",async()=>{i.release()}),await t.listen({host:e.host,port:e.port})}catch(e){throw i.release(),e}}function ac(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 rc(e){if("disabled"!==e&&"password"!==e)throw new Error("Auth mode must be disabled or password.");return e}(async function(){const n=new e.Command;n.name("task-handoff-control-plane").description("Run the TaskHandoff control plane.").version(function(){try{return JSON.parse(t.readFileSync(s.resolve(__dirname,"..","package.json"),"utf8")).version||"unknown"}catch{return"unknown"}}()).option("--host <host>","Control plane host",process.env.TASK_HANDOFF_CONTROL_PLANE_HOST||"127.0.0.1").option("-p, --port <port>","Control plane port",ac,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||s.resolve(__dirname,"..","ui")).option("--auth-mode <mode>","Control plane auth mode: disabled or password",rc,process.env.TASK_HANDOFF_CONTROL_PLANE_AUTH_MODE||"password").action(async e=>{await ic({host:e.host,port:e.port,dataDir:e.dataDir,staticDir:e.staticDir,auth:{mode:e.authMode}})}),await n.parseAsync(process.argv)})().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});