@tangle-network/agent-app 0.43.56 → 0.43.57
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.
- package/dist/DesignCanvas-UNL34LS2.js +12 -0
- package/dist/DesignCanvasEditor-FJIVYOA2.js +13 -0
- package/dist/IntakeInterview-3ZIQMZS6.js +8 -0
- package/dist/InvitationsPanel-CJ7DR4F7.js +8 -0
- package/dist/MembersPanel-BB4OD2KB.js +8 -0
- package/dist/{TimelineEditor-I35SJJAJ.js → TimelineEditor-2ECKSU6P.js} +3 -3
- package/dist/{VaultPane-DrQ_tH6c.d.ts → VaultPane-BDP6SB_K.d.ts} +1 -0
- package/dist/{access-BZ81Btt5.d.ts → access-490O6aOU.d.ts} +16 -1
- package/dist/app-auth/index.d.ts +6 -1
- package/dist/app-auth/index.js +3 -3
- package/dist/app-auth/index.js.map +1 -1
- package/dist/{apply-Gk4raT2j.d.ts → apply-6wlMOLf8.d.ts} +30 -1
- package/dist/assets/index.d.ts +41 -0
- package/dist/assets/index.js +1 -1
- package/dist/assistant/index.d.ts +26 -6
- package/dist/assistant/index.js +9 -9
- package/dist/assistant/index.js.map +1 -1
- package/dist/{attachment-validation-D0csjCvz.d.ts → attachment-validation-tPRp-PP1.d.ts} +7 -1
- package/dist/{auth-DuptSkWh.d.ts → auth-_FU8w01b.d.ts} +4 -1
- package/dist/billing/index.d.ts +8 -0
- package/dist/billing/index.js +1 -1
- package/dist/brand/index.d.ts +2 -0
- package/dist/brand/index.js.map +1 -1
- package/dist/brand-extraction/index.d.ts +3 -0
- package/dist/brand-extraction/index.js.map +1 -1
- package/dist/catalog/index.d.ts +2 -0
- package/dist/catalog/index.js +1 -1
- package/dist/chat-routes/index.d.ts +41 -10
- package/dist/chat-routes/index.js +15 -15
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/chat-store/index.d.ts +18 -4
- package/dist/chat-store/index.js +4 -4
- package/dist/chat-store/index.js.map +1 -1
- package/dist/{chunk-H5FM75MR.js → chunk-22GLESKE.js} +3 -3
- package/dist/chunk-22GLESKE.js.map +1 -0
- package/dist/{chunk-WEBBJBDH.js → chunk-2DRYTJHI.js} +1 -1
- package/dist/chunk-2DRYTJHI.js.map +1 -0
- package/dist/{chunk-7EVZUIHW.js → chunk-3EJ6SFJI.js} +1 -1
- package/dist/chunk-3EJ6SFJI.js.map +1 -0
- package/dist/{chunk-HRJJH3RX.js → chunk-3OFQFW5K.js} +4 -4
- package/dist/chunk-3OFQFW5K.js.map +1 -0
- package/dist/{chunk-7SUPA2YT.js → chunk-4KKXZQPT.js} +2 -2
- package/dist/chunk-4KKXZQPT.js.map +1 -0
- package/dist/{chunk-5PTGEJZL.js → chunk-5SD6HT2V.js} +1 -1
- package/dist/chunk-5SD6HT2V.js.map +1 -0
- package/dist/{chunk-7C64WKAH.js → chunk-5SXS3YAB.js} +2 -2
- package/dist/{chunk-L2TG5DBW.js → chunk-5TESA4SN.js} +1 -1
- package/dist/chunk-5TESA4SN.js.map +1 -0
- package/dist/{chunk-63CE7FEZ.js → chunk-6XIAPIW6.js} +1 -1
- package/dist/chunk-6XIAPIW6.js.map +1 -0
- package/dist/{chunk-MFRCM32T.js → chunk-7CTIUCQ4.js} +1 -1
- package/dist/chunk-7CTIUCQ4.js.map +1 -0
- package/dist/{chunk-6KVH5SP5.js → chunk-7YW6EBDX.js} +2 -2
- package/dist/chunk-7YW6EBDX.js.map +1 -0
- package/dist/{chunk-ZXNXAQAH.js → chunk-AP2UXKUK.js} +1 -1
- package/dist/chunk-AP2UXKUK.js.map +1 -0
- package/dist/{chunk-K63OWUDJ.js → chunk-BPSV5AA2.js} +2 -2
- package/dist/chunk-BPSV5AA2.js.map +1 -0
- package/dist/{chunk-MFI4ZM7W.js → chunk-BT4QFCTB.js} +1 -1
- package/dist/chunk-BT4QFCTB.js.map +1 -0
- package/dist/{chunk-ZYBWGSAZ.js → chunk-BVKQKGRK.js} +1 -1
- package/dist/chunk-BVKQKGRK.js.map +1 -0
- package/dist/{chunk-RVUISYUE.js → chunk-C3NSB66C.js} +7 -7
- package/dist/chunk-C3NSB66C.js.map +1 -0
- package/dist/{chunk-BQV42AFK.js → chunk-CCVWR33L.js} +1 -1
- package/dist/chunk-CCVWR33L.js.map +1 -0
- package/dist/{chunk-MCJSS6SM.js → chunk-CQZSAR77.js} +1 -1
- package/dist/chunk-CQZSAR77.js.map +1 -0
- package/dist/{chunk-3SVAA3MA.js → chunk-DJ4VJIH5.js} +1 -1
- package/dist/chunk-DJ4VJIH5.js.map +1 -0
- package/dist/{chunk-XKBIYSSM.js → chunk-DTSPS2HV.js} +3 -3
- package/dist/chunk-DTSPS2HV.js.map +1 -0
- package/dist/{chunk-4QN75UHX.js → chunk-E7N67FYI.js} +8 -8
- package/dist/chunk-E7N67FYI.js.map +1 -0
- package/dist/{chunk-2QI7XV2T.js → chunk-FBVLEGEG.js} +1 -1
- package/dist/{chunk-2QI7XV2T.js.map → chunk-FBVLEGEG.js.map} +1 -1
- package/dist/{chunk-5RMIUJDI.js → chunk-FKCB5FTD.js} +1 -1
- package/dist/chunk-FKCB5FTD.js.map +1 -0
- package/dist/{chunk-EEPJGZJW.js → chunk-GPBRL2XR.js} +1 -1
- package/dist/chunk-GPBRL2XR.js.map +1 -0
- package/dist/{chunk-G3HCU7TA.js → chunk-GXE6KHTS.js} +1 -1
- package/dist/chunk-GXE6KHTS.js.map +1 -0
- package/dist/{chunk-65P3HJY3.js → chunk-HCOROIRT.js} +1 -1
- package/dist/chunk-HCOROIRT.js.map +1 -0
- package/dist/{chunk-54GSK2KK.js → chunk-HYTIHYNV.js} +2 -2
- package/dist/chunk-HYTIHYNV.js.map +1 -0
- package/dist/{chunk-5MG74GVQ.js → chunk-IG46XCZM.js} +3 -3
- package/dist/chunk-IG46XCZM.js.map +1 -0
- package/dist/{chunk-ND3XEGBE.js → chunk-JDEGS53O.js} +1 -1
- package/dist/chunk-JDEGS53O.js.map +1 -0
- package/dist/{chunk-7W5XSTUF.js → chunk-JML7WKWU.js} +1 -1
- package/dist/chunk-JML7WKWU.js.map +1 -0
- package/dist/{chunk-Q4TKVF3L.js → chunk-JTLYXUEV.js} +1 -1
- package/dist/chunk-JTLYXUEV.js.map +1 -0
- package/dist/{chunk-CCRVDLV3.js → chunk-JWBZ74TW.js} +2 -2
- package/dist/chunk-JWBZ74TW.js.map +1 -0
- package/dist/{chunk-ZU5GNSOJ.js → chunk-K3LIPKFV.js} +3 -3
- package/dist/chunk-K3LIPKFV.js.map +1 -0
- package/dist/{chunk-GQRL6VNU.js → chunk-LGBO24FL.js} +1 -1
- package/dist/chunk-LGBO24FL.js.map +1 -0
- package/dist/{chunk-SWUVTGMR.js → chunk-MEUNTJL5.js} +3 -3
- package/dist/{chunk-SWUVTGMR.js.map → chunk-MEUNTJL5.js.map} +1 -1
- package/dist/{chunk-YNALYIQ3.js → chunk-NLSSFLLS.js} +1 -1
- package/dist/chunk-NLSSFLLS.js.map +1 -0
- package/dist/{chunk-HFC4BTWJ.js → chunk-PTUMBMVH.js} +1 -1
- package/dist/chunk-PTUMBMVH.js.map +1 -0
- package/dist/{chunk-R4NOKZMD.js → chunk-ROLFDOMJ.js} +3 -3
- package/dist/{chunk-NYATNLRK.js → chunk-RT5RKAFX.js} +1 -1
- package/dist/chunk-RT5RKAFX.js.map +1 -0
- package/dist/{chunk-GNL3MG5J.js → chunk-S564OFTL.js} +2 -2
- package/dist/{chunk-GUSCYE3S.js → chunk-TQOFR6XY.js} +1 -1
- package/dist/chunk-TQOFR6XY.js.map +1 -0
- package/dist/{chunk-47C6PJCW.js → chunk-TZO3SOUL.js} +1 -1
- package/dist/chunk-TZO3SOUL.js.map +1 -0
- package/dist/{chunk-YGUNTIT5.js → chunk-UKTYKG57.js} +1 -1
- package/dist/chunk-UKTYKG57.js.map +1 -0
- package/dist/{chunk-23LUHHHN.js → chunk-VCOD5AOR.js} +1 -1
- package/dist/chunk-VCOD5AOR.js.map +1 -0
- package/dist/{chunk-5I72FRZY.js → chunk-VOJB7MVG.js} +2 -2
- package/dist/{chunk-XWJXZ6WE.js → chunk-VTWKH4YU.js} +2 -2
- package/dist/chunk-VTWKH4YU.js.map +1 -0
- package/dist/{chunk-4K7BZYO7.js → chunk-VWAEWOWK.js} +3 -3
- package/dist/chunk-VWAEWOWK.js.map +1 -0
- package/dist/{chunk-M2EBUDZ7.js → chunk-VWOPOLVO.js} +2 -2
- package/dist/chunk-VWOPOLVO.js.map +1 -0
- package/dist/{chunk-BN2USWIR.js → chunk-VYECQAGM.js} +2 -2
- package/dist/chunk-VYECQAGM.js.map +1 -0
- package/dist/{chunk-SIXYZ2FB.js → chunk-YJMCRXQQ.js} +1 -1
- package/dist/chunk-YJMCRXQQ.js.map +1 -0
- package/dist/{chunk-PJC4NXPA.js → chunk-YKBDH2UY.js} +3 -3
- package/dist/chunk-YKBDH2UY.js.map +1 -0
- package/dist/{chunk-XAWFPMAR.js → chunk-YKOM4ZLQ.js} +1 -1
- package/dist/chunk-YKOM4ZLQ.js.map +1 -0
- package/dist/{chunk-TKVJE63N.js → chunk-YRMLYFVR.js} +3 -3
- package/dist/chunk-YRMLYFVR.js.map +1 -0
- package/dist/{chunk-USYXHDKE.js → chunk-YZ6KQULN.js} +2 -2
- package/dist/{command-stack-B_-WNfZU.d.ts → command-stack-TNPWJCzW.d.ts} +14 -2
- package/dist/{completion-Kaqp_tWk.d.ts → completion-CvcNUz07.d.ts} +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/{contract-KfqJh_au.d.ts → contract-TjB68uqA.d.ts} +17 -0
- package/dist/design-canvas/drizzle.d.ts +10 -2
- package/dist/design-canvas/drizzle.js.map +1 -1
- package/dist/design-canvas/index.d.ts +29 -11
- package/dist/design-canvas/index.js +1070 -17
- package/dist/design-canvas/index.js.map +1 -1
- package/dist/design-canvas-react/engine.d.ts +42 -6
- package/dist/design-canvas-react/engine.js +4 -4
- package/dist/design-canvas-react/index.d.ts +12 -7
- package/dist/design-canvas-react/index.js +11 -11
- package/dist/design-canvas-react/index.js.map +1 -1
- package/dist/design-canvas-react/lazy.d.ts +4 -4
- package/dist/design-canvas-react/lazy.js +1 -1
- package/dist/durable-chat/index.d.ts +46 -1
- package/dist/durable-chat/index.js +3 -3
- package/dist/eval/index.d.ts +1 -1
- package/dist/{export-presets-DAsU5Wal.d.ts → export-presets-mgVulRaV.d.ts} +10 -1
- package/dist/{flow-types-Cb_AblZs.d.ts → flow-types-CqomVAUN.d.ts} +1 -0
- package/dist/harness/index.d.ts +5 -0
- package/dist/harness/index.js +1 -1
- package/dist/index.d.ts +10 -18
- package/dist/index.js +30 -231
- package/dist/intakes/api.d.ts +3 -2
- package/dist/intakes/api.js +3 -3
- package/dist/intakes/api.js.map +1 -1
- package/dist/intakes/drizzle.d.ts +10 -2
- package/dist/intakes/drizzle.js +3 -3
- package/dist/intakes/drizzle.js.map +1 -1
- package/dist/intakes/index.d.ts +2 -2
- package/dist/intakes/index.js +2 -2
- package/dist/intakes-react/index.d.ts +2 -1
- package/dist/intakes-react/index.js +2 -2
- package/dist/intakes-react/lazy.d.ts +2 -1
- package/dist/intakes-react/lazy.js +1 -1
- package/dist/intakes-react/lazy.js.map +1 -1
- package/dist/integrations/index.d.ts +4 -0
- package/dist/integrations/index.js +1 -1
- package/dist/interactions/index.d.ts +13 -2
- package/dist/interactions/index.js +2 -2
- package/dist/{invitations-CZ5G2tIn.d.ts → invitations-DoYMvXea.d.ts} +11 -1
- package/dist/{invitations-schema-DaMO4hZE.d.ts → invitations-schema-_fI7HkJP.d.ts} +5 -1
- package/dist/knowledge/index.d.ts +2 -0
- package/dist/knowledge/index.js +1 -1
- package/dist/knowledge-loop/index.d.ts +4 -1
- package/dist/knowledge-loop/index.js +1 -1
- package/dist/{lazy-C7LiVeFt.d.ts → lazy-DrkVyDpe.d.ts} +2 -2
- package/dist/{mcp-BHfIoGLW.d.ts → mcp-BjPqQ-3C.d.ts} +4 -2
- package/dist/{mcp-rpc-DLw_r9PQ.d.ts → mcp-rpc-BfUOsalw.d.ts} +3 -0
- package/dist/missions/index.d.ts +25 -0
- package/dist/missions/index.js +1 -1
- package/dist/{model-Bzi5i63l.d.ts → model-C8U0HZxL.d.ts} +1 -0
- package/dist/{model-dF2h4xT9.d.ts → model-DmdkIteM.d.ts} +19 -0
- package/dist/{model-BBwyOuWG.d.ts → model-DnfO_PdJ.d.ts} +25 -1
- package/dist/model-resolution/index.d.ts +10 -0
- package/dist/model-resolution/index.js.map +1 -1
- package/dist/{operations-DLIvvD00.d.ts → operations-ByJ6yXOW.d.ts} +20 -2
- package/dist/{parts-1sRYSPjF.d.ts → parts-n9XUon0a.d.ts} +29 -1
- package/dist/plans/index.d.ts +6 -0
- package/dist/plans/index.js +1 -1
- package/dist/platform/index.d.ts +24 -2
- package/dist/platform/index.js +3 -3
- package/dist/platform/index.js.map +1 -1
- package/dist/preflight/index.d.ts +3 -0
- package/dist/preflight/index.js +1 -1
- package/dist/preset-cloudflare/index.d.ts +4 -1
- package/dist/preset-cloudflare/index.js +2 -2
- package/dist/redact/index.d.ts +5 -0
- package/dist/redact/index.js +1 -1
- package/dist/{roles-BC1n4t37.d.ts → roles-B4nU3jDi.d.ts} +12 -0
- package/dist/run/index.d.ts +3 -0
- package/dist/run/index.js +1 -1
- package/dist/run/index.js.map +1 -1
- package/dist/runtime/index.d.ts +11 -4
- package/dist/runtime/index.js +4 -4
- package/dist/sandbox/index.d.ts +96 -3
- package/dist/sandbox/index.js +6 -6
- package/dist/{sandbox-terminal-BIIC__CP.d.ts → sandbox-terminal-ChNEdHF8.d.ts} +5 -0
- package/dist/sequences/drizzle.d.ts +11 -1
- package/dist/sequences/drizzle.js +1 -1
- package/dist/sequences/drizzle.js.map +1 -1
- package/dist/sequences/index.d.ts +32 -7
- package/dist/sequences/index.js +1562 -47
- package/dist/sequences/index.js.map +1 -1
- package/dist/sequences-react/index.d.ts +32 -2
- package/dist/sequences-react/index.js +3 -3
- package/dist/sequences-react/index.js.map +1 -1
- package/dist/{sso-CNOsARMJ.d.ts → sso-Cm00S61u.d.ts} +13 -0
- package/dist/{stale-turn-lock-C8Na1cFZ.d.ts → stale-turn-lock-DucQzvXu.d.ts} +2 -0
- package/dist/store/index.d.ts +5 -0
- package/dist/store/index.js.map +1 -1
- package/dist/{store-gckrNq-g.d.ts → store-B7LLlk9p.d.ts} +22 -1
- package/dist/{store-CFyxgTlD.d.ts → store-CqfDtnPQ.d.ts} +8 -2
- package/dist/stream/index.d.ts +10 -4
- package/dist/stream/index.js +4 -4
- package/dist/{stream-normalizer-DWvtmY6F.d.ts → stream-normalizer-C3M--r6y.d.ts} +17 -1
- package/dist/studio/index.d.ts +37 -0
- package/dist/studio/index.js +1 -1
- package/dist/studio-react/index.d.ts +3 -0
- package/dist/studio-react/index.js +1 -1
- package/dist/studio-react/index.js.map +1 -1
- package/dist/tangle/index.d.ts +3 -0
- package/dist/tangle/index.js +1 -1
- package/dist/teams/drizzle.d.ts +8 -4
- package/dist/teams/drizzle.js +1 -1
- package/dist/teams/drizzle.js.map +1 -1
- package/dist/teams/index.d.ts +4 -2
- package/dist/teams/index.js +3 -3
- package/dist/teams/invitations-api.d.ts +10 -4
- package/dist/teams/invitations-api.js +4 -4
- package/dist/teams/invitations-api.js.map +1 -1
- package/dist/teams/members-api.d.ts +4 -2
- package/dist/teams/members-api.js +3 -3
- package/dist/teams/resend.d.ts +5 -4
- package/dist/teams/resend.js +1 -1
- package/dist/teams/resend.js.map +1 -1
- package/dist/teams-react/index.d.ts +7 -2
- package/dist/teams-react/index.js +3 -3
- package/dist/teams-react/lazy.d.ts +5 -2
- package/dist/teams-react/lazy.js +2 -2
- package/dist/teams-react/lazy.js.map +1 -1
- package/dist/theme/index.d.ts +2 -0
- package/dist/theme/index.js +1 -1
- package/dist/theme/tailwind-preset.d.ts +1 -0
- package/dist/theme/tailwind-preset.js.map +1 -1
- package/dist/theme-contract/cli.js +1 -1
- package/dist/theme-contract/index.d.ts +3 -0
- package/dist/theme-contract/index.js +1 -1
- package/dist/tools/index.d.ts +13 -6
- package/dist/tools/index.js +3 -3
- package/dist/trace/index.d.ts +6 -1
- package/dist/trace/index.js +2 -2
- package/dist/{turn-buffer-C9mEgoop.d.ts → turn-buffer-DGnAPKwa.d.ts} +6 -0
- package/dist/turn-stream/index.d.ts +28 -2
- package/dist/turn-stream/index.js +1 -1
- package/dist/turn-stream/index.js.map +1 -1
- package/dist/{types-BEOvc_ue.d.ts → types-CBRyqijY.d.ts} +10 -0
- package/dist/vault/index.d.ts +1 -1
- package/dist/vault/lazy.d.ts +3 -2
- package/dist/vault/lazy.js.map +1 -1
- package/dist/web/index.d.ts +4 -0
- package/dist/web/index.js +1 -1
- package/dist/web-react/index.d.ts +51 -8
- package/dist/web-react/index.js +9 -9
- package/dist/web-react/terminal.d.ts +2 -2
- package/dist/web-react/terminal.js +1 -1
- package/package.json +3 -2
- package/dist/DesignCanvas-5DJAFDNC.js +0 -12
- package/dist/DesignCanvasEditor-XUJZARSN.js +0 -13
- package/dist/IntakeInterview-WHVKMNQW.js +0 -8
- package/dist/InvitationsPanel-XUUWZBPV.js +0 -8
- package/dist/MembersPanel-FOVHWUQ3.js +0 -8
- package/dist/chunk-23LUHHHN.js.map +0 -1
- package/dist/chunk-3SVAA3MA.js.map +0 -1
- package/dist/chunk-47C6PJCW.js.map +0 -1
- package/dist/chunk-4K7BZYO7.js.map +0 -1
- package/dist/chunk-4QN75UHX.js.map +0 -1
- package/dist/chunk-54GSK2KK.js.map +0 -1
- package/dist/chunk-5MG74GVQ.js.map +0 -1
- package/dist/chunk-5PTGEJZL.js.map +0 -1
- package/dist/chunk-5RMIUJDI.js.map +0 -1
- package/dist/chunk-63CE7FEZ.js.map +0 -1
- package/dist/chunk-65P3HJY3.js.map +0 -1
- package/dist/chunk-6KVH5SP5.js.map +0 -1
- package/dist/chunk-7EVZUIHW.js.map +0 -1
- package/dist/chunk-7SUPA2YT.js.map +0 -1
- package/dist/chunk-7W5XSTUF.js.map +0 -1
- package/dist/chunk-BN2USWIR.js.map +0 -1
- package/dist/chunk-BQV42AFK.js.map +0 -1
- package/dist/chunk-CCRVDLV3.js.map +0 -1
- package/dist/chunk-EEPJGZJW.js.map +0 -1
- package/dist/chunk-G3HCU7TA.js.map +0 -1
- package/dist/chunk-GQRL6VNU.js.map +0 -1
- package/dist/chunk-GUSCYE3S.js.map +0 -1
- package/dist/chunk-H5FM75MR.js.map +0 -1
- package/dist/chunk-HFC4BTWJ.js.map +0 -1
- package/dist/chunk-HRJJH3RX.js.map +0 -1
- package/dist/chunk-K63OWUDJ.js.map +0 -1
- package/dist/chunk-L2TG5DBW.js.map +0 -1
- package/dist/chunk-M2EBUDZ7.js.map +0 -1
- package/dist/chunk-MCJSS6SM.js.map +0 -1
- package/dist/chunk-MFI4ZM7W.js.map +0 -1
- package/dist/chunk-MFRCM32T.js.map +0 -1
- package/dist/chunk-ND3XEGBE.js.map +0 -1
- package/dist/chunk-NEOV2NQ3.js +0 -1617
- package/dist/chunk-NEOV2NQ3.js.map +0 -1
- package/dist/chunk-NYATNLRK.js.map +0 -1
- package/dist/chunk-PJC4NXPA.js.map +0 -1
- package/dist/chunk-Q4TKVF3L.js.map +0 -1
- package/dist/chunk-RVUISYUE.js.map +0 -1
- package/dist/chunk-SIXYZ2FB.js.map +0 -1
- package/dist/chunk-TKVJE63N.js.map +0 -1
- package/dist/chunk-WEBBJBDH.js.map +0 -1
- package/dist/chunk-WFMUDX5J.js +0 -1092
- package/dist/chunk-WFMUDX5J.js.map +0 -1
- package/dist/chunk-XAWFPMAR.js.map +0 -1
- package/dist/chunk-XKBIYSSM.js.map +0 -1
- package/dist/chunk-XWJXZ6WE.js.map +0 -1
- package/dist/chunk-YGUNTIT5.js.map +0 -1
- package/dist/chunk-YNALYIQ3.js.map +0 -1
- package/dist/chunk-ZU5GNSOJ.js.map +0 -1
- package/dist/chunk-ZXNXAQAH.js.map +0 -1
- package/dist/chunk-ZYBWGSAZ.js.map +0 -1
- /package/dist/{DesignCanvas-5DJAFDNC.js.map → DesignCanvas-UNL34LS2.js.map} +0 -0
- /package/dist/{DesignCanvasEditor-XUJZARSN.js.map → DesignCanvasEditor-FJIVYOA2.js.map} +0 -0
- /package/dist/{IntakeInterview-WHVKMNQW.js.map → IntakeInterview-3ZIQMZS6.js.map} +0 -0
- /package/dist/{InvitationsPanel-XUUWZBPV.js.map → InvitationsPanel-CJ7DR4F7.js.map} +0 -0
- /package/dist/{MembersPanel-FOVHWUQ3.js.map → MembersPanel-BB4OD2KB.js.map} +0 -0
- /package/dist/{TimelineEditor-I35SJJAJ.js.map → TimelineEditor-2ECKSU6P.js.map} +0 -0
- /package/dist/{chunk-7C64WKAH.js.map → chunk-5SXS3YAB.js.map} +0 -0
- /package/dist/{chunk-R4NOKZMD.js.map → chunk-ROLFDOMJ.js.map} +0 -0
- /package/dist/{chunk-GNL3MG5J.js.map → chunk-S564OFTL.js.map} +0 -0
- /package/dist/{chunk-5I72FRZY.js.map → chunk-VOJB7MVG.js.map} +0 -0
- /package/dist/{chunk-USYXHDKE.js.map → chunk-YZ6KQULN.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/missions/service.ts","../src/missions/events.ts","../src/missions/engine.ts","../src/missions/plan-parse.ts","../src/missions/agent-activity.ts"],"sourcesContent":["/**\n * Durable mission state — the guarded status machine for a multi-step agent run.\n *\n * A mission is a persisted plan (ordered steps), a cursor (count of completed\n * steps), a cost ledger + budget, and a status machine. This module owns the\n * legal transitions and the guarded mutation surface; it does NOT execute steps\n * (the engine in `./engine` does) and it does NOT own persistence — products\n * implement {@link MissionStorePort} over their own tables. Every state change\n * appends a {@link MissionAuditEvent} so the run timeline is a single durable\n * audit trail.\n *\n * Concurrency contract: a mission MUST be driven by a single serialized owner\n * (a Durable Object, a Cloudflare Workflow, a queue consumer — one per\n * mission). The service is the typed guard layer, not a serializer: every\n * mutation re-reads the record and asks the store for a compare-and-set write\n * guarded on the values it read. When the guard misses, the row changed under\n * us and the caller gets `{ succeeded: false, conflict: true }` — never a\n * silent clobber, never a stale overwrite.\n */\n\nexport type MissionStatus =\n | 'scheduled'\n | 'running'\n | 'paused'\n | 'waiting_approval'\n | 'blocked'\n | 'succeeded'\n | 'failed'\n | 'aborted'\n | 'cancelled'\n\n/** Define possible statuses for a mission step during its execution lifecycle */\nexport type MissionStepStatus = 'pending' | 'running' | 'waiting_approval' | 'done' | 'failed'\n\n/** Define the structure and state details of a mission step within a workflow system */\nexport interface MissionStep {\n id: string\n /** What the step should accomplish — an intent, never an implementation. */\n intent: string\n /** Product-defined kind label. Labels intent for gating/UX; it never selects\n * a different execution path. */\n kind: string\n status: MissionStepStatus\n /** Count of genuine `* -> running` edges (retries inflate this; idempotent\n * re-asserts do not). */\n attempts: number\n /** One-line live status surfaced on the step row (\"7/15 refs\"). */\n sublabel?: string\n /** Small pointer at the produced artifact (vault path, asset id) — never the\n * full payload. */\n resultRef?: string\n}\n\n/** Define the structure for tracking mission token usage, cost, duration, and LLM call counts */\nexport interface MissionCostLedger {\n tokensIn: number\n tokensOut: number\n costUsd: number\n wallMs: number\n llmCalls: number\n}\n\n/** The durable mission row, shape-normalized. Timestamps are epoch ms. */\nexport interface MissionRecord {\n id: string\n workspaceId: string\n status: MissionStatus\n /** Product-defined origin label ('chat', 'manual', 'cron', …). */\n trigger: string\n summary: string | null\n plan: MissionStep[]\n /** Count of durably-completed steps; the next step to run is `plan[cursor]`. */\n cursor: number\n cost: MissionCostLedger | null\n budgetUsd: number | null\n spentUsd: number\n pauseReason: string | null\n /** The single owning engine's instance id, write-once (see `setEngineRef`). */\n engineRef: string | null\n scheduledAt: number | null\n startedAt: number\n completedAt: number | null\n metadata: Record<string, unknown> | null\n}\n\n/**\n * Discriminated outcome for guarded operations. Callers MUST inspect\n * `succeeded` before reading `value` — illegal transitions and missing rows\n * surface here, never as a throw-and-swallow or a silent no-op. `conflict`\n * distinguishes a lost guarded race (retryable — re-read and re-apply) from a\n * logic rejection (illegal edge, missing step — deterministic, never retried).\n */\nexport type MissionOutcome<T> =\n | { succeeded: true; value: T }\n | { succeeded: false; error: string; conflict: boolean }\n\n/** Fields a guarded write compares against the values the caller read. A SQL\n * implementation compares JSON columns as serialized text\n * (`coalesce(col, 'null') = JSON.stringify(expected)`), matching how the\n * in-memory store compares. An absent field is unguarded. */\nexport interface MissionUpdateGuard {\n status?: MissionStatus\n cursor?: number\n plan?: MissionStep[]\n cost?: MissionCostLedger | null\n metadata?: Record<string, unknown> | null\n /** Guard that no engine has bound yet (the write-once bind). */\n engineRefIsNull?: true\n}\n\n/** Fields a guarded write sets when the guard holds. `null` values are real\n * writes (clear the column), not skips. */\nexport interface MissionUpdatePatch {\n status?: MissionStatus\n pauseReason?: string | null\n summary?: string\n completedAt?: number | null\n plan?: MissionStep[]\n cursor?: number\n cost?: MissionCostLedger\n spentUsd?: number\n metadata?: Record<string, unknown>\n engineRef?: string\n}\n\n/** One audit-trail row. Appended after every committed state change, so an\n * event always denotes a real transition (no phantom rows on rejected or\n * no-op calls). */\nexport interface MissionAuditEvent {\n missionId: string\n workspaceId: string\n level: 'info' | 'warn' | 'error'\n /** Machine-readable transition name ('mission.created', 'mission.step.done',\n * 'mission.cursor', 'mission.cost', 'mission.paused', …). */\n step: string\n message: string\n metadata: Record<string, unknown>\n at: number\n}\n\n/**\n * Persistence seam — the product implements this over its own tables. The\n * invariant the implementation MUST keep: `update` applies `patch` ONLY when\n * every guard field still equals the stored value, and returns `null` when the\n * guard misses. That null is how a concurrent write surfaces as a typed\n * failure instead of a clobber.\n */\nexport interface MissionStorePort {\n load(id: string): Promise<MissionRecord | null>\n /** `extras` are the opaque product-column values from\n * `CreateMissionInput.extras` — write them in the SAME statement as the\n * record (single-write creation) or ignore them if the table has no extra\n * columns. */\n insert(record: MissionRecord, extras?: Record<string, unknown>): Promise<MissionRecord>\n update(id: string, guard: MissionUpdateGuard, patch: MissionUpdatePatch): Promise<MissionRecord | null>\n appendEvent(event: MissionAuditEvent): Promise<void>\n}\n\nconst TERMINAL_STATUSES: ReadonlySet<MissionStatus> = new Set<MissionStatus>([\n 'succeeded',\n 'failed',\n 'aborted',\n 'cancelled',\n])\n\n/** Statuses a mission can never leave — the run is done. */\nexport function isMissionTerminal(status: MissionStatus): boolean {\n return TERMINAL_STATUSES.has(status)\n}\n\n/** The cooperative kill switch: a stop request rides metadata so it survives\n * any status and is honored by the engine before the next side effect. */\nexport function isMissionStopRequested(mission: MissionRecord): boolean {\n return (mission.metadata ?? {}).stopRequested === true\n}\n\n// Legal mission status transitions. A target absent from a source's set is\n// rejected by the guarded helpers. Terminal statuses have no outgoing edges.\nconst MISSION_TRANSITIONS: Record<MissionStatus, ReadonlySet<MissionStatus>> = {\n scheduled: new Set<MissionStatus>(['running', 'cancelled', 'aborted']),\n running: new Set<MissionStatus>([\n 'paused',\n 'waiting_approval',\n 'blocked',\n 'succeeded',\n 'failed',\n 'aborted',\n ]),\n paused: new Set<MissionStatus>(['running', 'aborted', 'cancelled']),\n waiting_approval: new Set<MissionStatus>(['running', 'aborted', 'cancelled']),\n blocked: new Set<MissionStatus>(['running', 'aborted', 'cancelled']),\n succeeded: new Set<MissionStatus>(),\n failed: new Set<MissionStatus>(),\n aborted: new Set<MissionStatus>(),\n cancelled: new Set<MissionStatus>(),\n}\n\n// Legal per-step transitions inside a plan.\nconst STEP_TRANSITIONS: Record<MissionStepStatus, ReadonlySet<MissionStepStatus>> = {\n pending: new Set<MissionStepStatus>(['running', 'waiting_approval', 'failed']),\n running: new Set<MissionStepStatus>(['done', 'failed', 'waiting_approval']),\n waiting_approval: new Set<MissionStepStatus>(['running', 'done', 'failed']),\n done: new Set<MissionStepStatus>([]),\n failed: new Set<MissionStepStatus>(['pending', 'running']),\n}\n\nconst ZERO_LEDGER: MissionCostLedger = {\n tokensIn: 0,\n tokensOut: 0,\n costUsd: 0,\n wallMs: 0,\n llmCalls: 0,\n}\n\n/** Define input parameters for creating a mission including optional deterministic id and unique plan steps */\nexport interface CreateMissionInput {\n /** Explicit row id. Omit to use the service's id generator. Pass a\n * DETERMINISTIC id (derived from the originating turn) when the caller may\n * re-create the same mission under at-least-once delivery — the duplicate\n * insert then trips the store's uniqueness instead of spawning a second run. */\n id?: string\n workspaceId: string\n /** Becomes the mission summary. */\n title: string\n /** Plan step ids MUST be unique: the owning workflow keys its durable step\n * cache by step id, so a collision would silently replay the wrong result.\n * A duplicate is rejected here (fail loud). */\n plan: MissionStep[]\n budgetUsd?: number | null\n /** Epoch ms. Present → the mission starts `scheduled` instead of `running`. */\n scheduledAt?: number | null\n trigger: string\n /** Caller-defined context stamped onto the record (thread ids, source turn,\n * model). Read back via `mission.metadata`; the engine only reads\n * `stopRequested` from it. */\n metadata?: Record<string, unknown> | null\n /**\n * Opaque product-column values handed VERBATIM to `MissionStorePort.insert`\n * (e.g. a `workflowId` FK on the product's mission table), so creation is a\n * single write — no post-insert stamp. The service never reads, validates,\n * or persists them itself; they exist only on the insert call.\n */\n extras?: Record<string, unknown>\n}\n\n/** Define a patch to update the status and optional metadata of a step in a process */\nexport interface SetStepStatusPatch {\n sublabel?: string\n resultRef?: string\n error?: string\n /**\n * Spend committed ATOMICALLY with the step transition. Folding the cost into\n * the SAME guarded write makes step completion the per-step idempotency key:\n * a same-status replay (the step is already `done`) short-circuits as a no-op\n * BEFORE the write, so the charge lands exactly once even when an engine\n * RESUME re-dispatches a step whose cost previously committed. `deltaUsd` is\n * the marginal spend; `ledgerDelta` carries the token/wall/llm breakdown.\n */\n cost?: {\n deltaUsd: number\n ledgerDelta?: Partial<MissionCostLedger>\n }\n}\n\n/** Define input parameters to complete a mission with status and optional summary */\nexport interface CompleteMissionInput {\n ok: boolean\n summary?: string\n}\n\n/** Define methods to create, retrieve, and update missions with controlled engine binding and metadata merging */\nexport interface MissionService {\n createMission(input: CreateMissionInput): Promise<MissionRecord>\n getMission(id: string): Promise<MissionRecord | null>\n /** Bind the executing engine's instance id, write-once from the single\n * owner: re-asserting the same ref is a no-op; binding a DIFFERENT ref over\n * an existing one is rejected so a second owner can never steal the run. */\n setEngineRef(id: string, engineRef: string): Promise<MissionOutcome<MissionRecord>>\n /** Shallow-merge keys into metadata. Guarded on the metadata read, so racing\n * merges surface as conflicts instead of silently dropping keys. */\n mergeMetadata(id: string, patch: Record<string, unknown>): Promise<MissionOutcome<MissionRecord>>\n /** Mutate one plan step's status (+ optional sublabel/resultRef) and append a\n * transition event. Rejects unknown steps and illegal step edges. Does NOT\n * move the cursor — call `advanceCursor` for that. */\n setStepStatus(\n id: string,\n stepId: string,\n status: MissionStepStatus,\n patch?: SetStepStatusPatch,\n ): Promise<MissionOutcome<MissionRecord>>\n /** Move the done-count cursor forward by one. Rejects advancing past the end\n * of the plan so the caller learns the mission has no further work. */\n advanceCursor(id: string): Promise<MissionOutcome<MissionRecord>>\n /** Increment spentUsd and merge a partial ledger into the cumulative ledger.\n * `deltaUsd` is the marginal spend; `ledgerDelta` carries the token/wall/\n * llm-call breakdown for the same unit of work. */\n addCost(\n id: string,\n deltaUsd: number,\n ledgerDelta?: Partial<MissionCostLedger>,\n ): Promise<MissionOutcome<MissionRecord>>\n pause(id: string, reason: string): Promise<MissionOutcome<MissionRecord>>\n resume(id: string): Promise<MissionOutcome<MissionRecord>>\n abort(id: string): Promise<MissionOutcome<MissionRecord>>\n /** Flip one step and the whole mission to waiting_approval together. The\n * mission transition is validated FIRST so an illegal source is rejected\n * without mutating the step — no half-applied state. */\n markWaitingApproval(id: string, stepId: string): Promise<MissionOutcome<MissionRecord>>\n complete(id: string, input: CompleteMissionInput): Promise<MissionOutcome<MissionRecord>>\n}\n\n/** Define options for configuring mission service behavior including storage, time, and ID generation */\nexport interface MissionServiceOptions {\n store: MissionStorePort\n /** Injectable clock (epoch ms). Default `Date.now`. */\n now?: () => number\n /**\n * Row-id generator when `CreateMissionInput.id` is omitted. Default\n * `crypto.randomUUID()` — a 36-char dashed UUID. Inject your own when your\n * mission table already has an id shape (e.g. 32-hex matching D1 row\n * defaults): the service stamps the generated id verbatim onto the inserted\n * record, so a mismatch with the rest of your schema surfaces only at the\n * product layer. Keep id shape parity here, not in the store.\n */\n generateId?: () => string\n}\n\nfunction rejected<T>(error: string): MissionOutcome<T> {\n return { succeeded: false, error, conflict: false }\n}\n\nfunction lostRace<T>(id: string): MissionOutcome<T> {\n return { succeeded: false, error: `Mission ${id} changed concurrently`, conflict: true }\n}\n\n/** Create a mission service that manages mission records and audit events with customizable options */\nexport function createMissionService(options: MissionServiceOptions): MissionService {\n const { store } = options\n const now = options.now ?? (() => Date.now())\n const generateId = options.generateId ?? (() => crypto.randomUUID())\n\n async function appendEvent(\n mission: MissionRecord,\n level: MissionAuditEvent['level'],\n step: string,\n message: string,\n metadata: Record<string, unknown> = {},\n ): Promise<void> {\n await store.appendEvent({\n missionId: mission.id,\n workspaceId: mission.workspaceId,\n level,\n step,\n message,\n metadata,\n at: now(),\n })\n }\n\n // Guarded status transition: load → validate against the transition table →\n // CAS-write guarded on the status we read → event. The loser of a racing\n // transition gets a conflict instead of silently violating the machine.\n async function transition(\n id: string,\n to: MissionStatus,\n patch: Omit<MissionUpdatePatch, 'status'> = {},\n eventMeta: Record<string, unknown> = {},\n ): Promise<MissionOutcome<MissionRecord>> {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n\n const from = mission.status\n if (TERMINAL_STATUSES.has(from)) {\n return rejected(`Mission ${id} is terminal (${from}); cannot transition to ${to}`)\n }\n // Same-status calls are a true no-op (resume-while-running, idempotent\n // re-assert under at-least-once delivery): no patch re-apply, no phantom\n // event, so an event row always denotes a real state change.\n if (from === to) return { succeeded: true, value: mission }\n if (!MISSION_TRANSITIONS[from].has(to)) {\n return rejected(`Illegal mission transition ${from} -> ${to} for mission ${id}`)\n }\n\n const updated = await store.update(id, { status: from }, { status: to, ...patch })\n if (!updated) return lostRace(id)\n await appendEvent(updated, to === 'failed' ? 'error' : 'info', `mission.${to}`, `Mission ${from} -> ${to}`, {\n from,\n to,\n ...eventMeta,\n })\n return { succeeded: true, value: updated }\n }\n\n const createMission: MissionService['createMission'] = async (input) => {\n const seen = new Set<string>()\n for (const step of input.plan) {\n if (seen.has(step.id)) {\n throw new Error(`Duplicate plan step id \"${step.id}\" — mission plan step ids must be unique`)\n }\n seen.add(step.id)\n }\n\n const scheduledAt = input.scheduledAt ?? null\n const status: MissionStatus = scheduledAt !== null ? 'scheduled' : 'running'\n const plan: MissionStep[] = input.plan.map((step) => ({\n id: step.id,\n intent: step.intent,\n kind: step.kind,\n status: step.status,\n attempts: step.attempts,\n ...(step.sublabel === undefined ? {} : { sublabel: step.sublabel }),\n ...(step.resultRef === undefined ? {} : { resultRef: step.resultRef }),\n }))\n\n const record = await store.insert(\n {\n id: input.id ?? generateId(),\n workspaceId: input.workspaceId,\n status,\n trigger: input.trigger,\n summary: input.title,\n plan,\n cursor: 0,\n cost: { ...ZERO_LEDGER },\n budgetUsd: input.budgetUsd ?? null,\n spentUsd: 0,\n pauseReason: null,\n engineRef: null,\n scheduledAt,\n startedAt: now(),\n completedAt: null,\n metadata: input.metadata ?? null,\n },\n input.extras,\n )\n\n await appendEvent(record, 'info', 'mission.created', `Mission \"${input.title}\" ${status}`, {\n status,\n stepCount: plan.length,\n budgetUsd: input.budgetUsd ?? null,\n scheduledAt,\n })\n return record\n }\n\n const getMission: MissionService['getMission'] = (id) => store.load(id)\n\n const setEngineRef: MissionService['setEngineRef'] = async (id, engineRef) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n if (TERMINAL_STATUSES.has(mission.status) || isMissionStopRequested(mission)) {\n return rejected(`Mission ${id} is not writable in status ${mission.status}`)\n }\n if (mission.engineRef === engineRef) return { succeeded: true, value: mission }\n if (mission.engineRef !== null) {\n return rejected(`Mission ${id} is already bound to engine ${mission.engineRef}`)\n }\n\n // Guard on the null engineRef we read so two racing owners cannot both\n // bind: the second write misses the guard and reports the lost race —\n // distinct from the deterministic already-bound rejection above.\n const updated = await store.update(id, { engineRefIsNull: true }, { engineRef })\n if (!updated) return lostRace(id)\n await appendEvent(updated, 'info', 'mission.engine', `Engine bound: ${engineRef}`, { engineRef })\n return { succeeded: true, value: updated }\n }\n\n const mergeMetadata: MissionService['mergeMetadata'] = async (id, patch) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n if (TERMINAL_STATUSES.has(mission.status) || isMissionStopRequested(mission)) {\n return rejected(`Mission ${id} is not writable in status ${mission.status}`)\n }\n\n const updated = await store.update(\n id,\n { metadata: mission.metadata },\n { metadata: { ...(mission.metadata ?? {}), ...patch } },\n )\n if (!updated) return lostRace(id)\n return { succeeded: true, value: updated }\n }\n\n const setStepStatus: MissionService['setStepStatus'] = async (id, stepId, status, patch = {}) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n\n const plan = mission.plan\n const index = plan.findIndex((step) => step.id === stepId)\n const current = index < 0 ? undefined : plan[index]\n if (!current) return rejected(`Step ${stepId} not found in mission ${id}`)\n\n const sameStatus = current.status === status\n if (!sameStatus && !STEP_TRANSITIONS[current.status].has(status)) {\n return rejected(`Illegal step transition ${current.status} -> ${status} for step ${stepId}`)\n }\n\n // True no-op: re-asserting the same status with no patch field change must\n // not bump attempts or write a duplicate event (at-least-once delivery,\n // retries, and reconnects re-assert state). A patch on the same status\n // still persists, but does not count a new attempt.\n const sublabelChanges = patch.sublabel !== undefined && patch.sublabel !== current.sublabel\n const resultRefChanges = patch.resultRef !== undefined && patch.resultRef !== current.resultRef\n if (sameStatus && !sublabelChanges && !resultRefChanges) {\n return { succeeded: true, value: mission }\n }\n\n // Only a genuine `* -> running` edge counts an attempt; a running->running\n // re-assert (handled as a no-op above) never inflates the counter.\n const nextStep: MissionStep = {\n ...current,\n status,\n attempts: status === 'running' && !sameStatus ? current.attempts + 1 : current.attempts,\n ...(patch.sublabel === undefined ? {} : { sublabel: patch.sublabel }),\n ...(patch.resultRef === undefined ? {} : { resultRef: patch.resultRef }),\n }\n const nextPlan = plan.slice()\n nextPlan[index] = nextStep\n\n // Spend folded into the SAME guarded write as the transition. Because the\n // same-status no-op above returns BEFORE this point, a step that is already\n // `done` never reaches here — so an engine RESUME re-dispatching a committed\n // step cannot charge a second time. The ledger CAS guard preserves the\n // over-spend protection `addCost` carried: an undercounted concurrent merge\n // surfaces as a lost race, never a clobber.\n const cost = patch.cost\n const ledgerDelta = cost?.ledgerDelta\n const base = mission.cost ?? { ...ZERO_LEDGER }\n const nextCost: MissionCostLedger | undefined =\n cost === undefined\n ? undefined\n : {\n tokensIn: base.tokensIn + (ledgerDelta?.tokensIn ?? 0),\n tokensOut: base.tokensOut + (ledgerDelta?.tokensOut ?? 0),\n costUsd: base.costUsd + (ledgerDelta?.costUsd ?? cost.deltaUsd),\n wallMs: base.wallMs + (ledgerDelta?.wallMs ?? 0),\n llmCalls: base.llmCalls + (ledgerDelta?.llmCalls ?? 0),\n }\n\n // The plan write is guarded on the status, plan, metadata, AND (when a cost\n // rides along) the ledger we read: a concurrent stop request (a metadata\n // write), any plan mutation, or a racing spend flips a guard and the loser\n // reports the race instead of clobbering it.\n const updated = await store.update(\n id,\n {\n status: mission.status,\n plan: mission.plan,\n metadata: mission.metadata,\n ...(cost === undefined ? {} : { cost: mission.cost }),\n },\n {\n plan: nextPlan,\n ...(cost === undefined ? {} : { cost: nextCost!, spentUsd: mission.spentUsd + cost.deltaUsd }),\n },\n )\n if (!updated) return lostRace(id)\n await appendEvent(\n updated,\n status === 'failed' ? 'error' : 'info',\n `mission.step.${status}`,\n patch.error ?? `Step ${stepId} (${current.intent}) -> ${status}`,\n {\n stepId,\n from: current.status,\n to: status,\n attempts: nextStep.attempts,\n ...(patch.resultRef ? { resultRef: patch.resultRef } : {}),\n },\n )\n if (cost !== undefined) {\n await appendEvent(updated, 'info', 'mission.cost', `Spent +$${cost.deltaUsd.toFixed(4)}`, {\n deltaUsd: cost.deltaUsd,\n spentUsd: updated.spentUsd,\n budgetUsd: updated.budgetUsd,\n })\n }\n return { succeeded: true, value: updated }\n }\n\n const advanceCursor: MissionService['advanceCursor'] = async (id) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n\n const next = mission.cursor + 1\n if (next > mission.plan.length) {\n return rejected(`Cursor ${mission.cursor} is already at the end of mission ${id}`)\n }\n\n // Guarded on the cursor (and status) we read: a concurrent advance shifts\n // the cursor, the guard misses, and we report the lost race rather than\n // writing a stale value that drops the other increment.\n const updated = await store.update(\n id,\n { status: mission.status, cursor: mission.cursor },\n { cursor: next },\n )\n if (!updated) return lostRace(id)\n await appendEvent(updated, 'info', 'mission.cursor', `Cursor ${mission.cursor} -> ${updated.cursor}`, {\n from: mission.cursor,\n to: updated.cursor,\n })\n return { succeeded: true, value: updated }\n }\n\n const addCost: MissionService['addCost'] = async (id, deltaUsd, ledgerDelta) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n\n const base = mission.cost ?? { ...ZERO_LEDGER }\n const nextCost: MissionCostLedger = {\n tokensIn: base.tokensIn + (ledgerDelta?.tokensIn ?? 0),\n tokensOut: base.tokensOut + (ledgerDelta?.tokensOut ?? 0),\n costUsd: base.costUsd + (ledgerDelta?.costUsd ?? deltaUsd),\n wallMs: base.wallMs + (ledgerDelta?.wallMs ?? 0),\n llmCalls: base.llmCalls + (ledgerDelta?.llmCalls ?? 0),\n }\n\n // Guarded on the ledger we read: an undercounted spend is an over-spend\n // against the budget gate, so a concurrent merge must surface as a lost\n // race (retry re-reads and re-merges), never be clobbered.\n const updated = await store.update(\n id,\n { cost: mission.cost },\n { cost: nextCost, spentUsd: mission.spentUsd + deltaUsd },\n )\n if (!updated) return lostRace(id)\n await appendEvent(updated, 'info', 'mission.cost', `Spent +$${deltaUsd.toFixed(4)}`, {\n deltaUsd,\n spentUsd: updated.spentUsd,\n budgetUsd: updated.budgetUsd,\n })\n return { succeeded: true, value: updated }\n }\n\n const markWaitingApproval: MissionService['markWaitingApproval'] = async (id, stepId) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n if (!MISSION_TRANSITIONS[mission.status].has('waiting_approval')) {\n return rejected(`Illegal mission transition ${mission.status} -> waiting_approval for mission ${id}`)\n }\n\n const stepResult = await setStepStatus(id, stepId, 'waiting_approval')\n if (!stepResult.succeeded) return stepResult\n return transition(id, 'waiting_approval', {}, { stepId })\n }\n\n return {\n createMission,\n getMission,\n setEngineRef,\n mergeMetadata,\n setStepStatus,\n advanceCursor,\n addCost,\n markWaitingApproval,\n pause: (id, reason) => transition(id, 'paused', { pauseReason: reason }),\n resume: (id) => transition(id, 'running', { pauseReason: null }),\n abort: (id) => transition(id, 'aborted', { completedAt: now() }),\n complete: (id, input) =>\n transition(id, input.ok ? 'succeeded' : 'failed', {\n completedAt: now(),\n ...(input.summary === undefined ? {} : { summary: input.summary }),\n }),\n }\n}\n\n// ── in-memory store ──────────────────────────────────────────────────────────\n\n/** Define an in-memory mission store that tracks events and allows direct record writes */\nexport interface InMemoryMissionStore extends MissionStorePort {\n /** The full audit trail, append order. */\n events(): MissionAuditEvent[]\n /** Unguarded direct write — simulates a concurrent owner or a crash-shaped\n * state in tests. Production writers go through the guarded `update`. */\n put(record: MissionRecord): void\n}\n\n/**\n * In-memory {@link MissionStorePort} — the portable backend for tests and\n * sandbox/eval shells. Guard comparison uses JSON serialization of the read\n * value, the same contract a SQL implementation honors by comparing stored\n * JSON text. Records are deep-copied on every boundary so callers can never\n * mutate stored state around the guards.\n */\nexport function createInMemoryMissionStore(): InMemoryMissionStore {\n const rows = new Map<string, MissionRecord>()\n const events: MissionAuditEvent[] = []\n\n return {\n async load(id) {\n const record = rows.get(id)\n return record ? structuredClone(record) : null\n },\n async insert(record) {\n if (rows.has(record.id)) throw new Error(`Mission ${record.id} already exists`)\n rows.set(record.id, structuredClone(record))\n return structuredClone(record)\n },\n async update(id, guard, patch) {\n const current = rows.get(id)\n if (!current) return null\n if (guard.status !== undefined && current.status !== guard.status) return null\n if (guard.cursor !== undefined && current.cursor !== guard.cursor) return null\n if (guard.plan !== undefined && JSON.stringify(current.plan) !== JSON.stringify(guard.plan)) return null\n if (guard.cost !== undefined && JSON.stringify(current.cost) !== JSON.stringify(guard.cost)) return null\n if (\n guard.metadata !== undefined &&\n JSON.stringify(current.metadata) !== JSON.stringify(guard.metadata)\n ) {\n return null\n }\n if (guard.engineRefIsNull && current.engineRef !== null) return null\n\n const next: MissionRecord = { ...current }\n if (patch.status !== undefined) next.status = patch.status\n if (patch.pauseReason !== undefined) next.pauseReason = patch.pauseReason\n if (patch.summary !== undefined) next.summary = patch.summary\n if (patch.completedAt !== undefined) next.completedAt = patch.completedAt\n if (patch.plan !== undefined) next.plan = patch.plan\n if (patch.cursor !== undefined) next.cursor = patch.cursor\n if (patch.cost !== undefined) next.cost = patch.cost\n if (patch.spentUsd !== undefined) next.spentUsd = patch.spentUsd\n if (patch.metadata !== undefined) next.metadata = patch.metadata\n if (patch.engineRef !== undefined) next.engineRef = patch.engineRef\n rows.set(id, structuredClone(next))\n return structuredClone(next)\n },\n async appendEvent(event) {\n events.push(structuredClone(event))\n },\n events() {\n return events.map((event) => structuredClone(event))\n },\n put(record) {\n rows.set(record.id, structuredClone(record))\n },\n }\n}\n","/**\n * Shared mission realtime contract — the single source of truth for the typed\n * events the engine BROADCASTS over a live channel and the client REDUCES into\n * live mission state. Server emit and client reduce import the same module so\n * the wire shape can never drift between the two ends.\n *\n * This module is CLIENT-SAFE: no server imports, no platform globals, no DB\n * types. It is pure data + a pure reducer. Keep it that way — a server-only\n * import here would leak into the browser bundle.\n *\n * Sink contract — best-effort UI notification, never load-bearing:\n * - fire-and-forget: the engine never awaits `emit` and a sink failure can\n * never fail a step (the engine wraps every emit). The durable audit-event\n * row is the authoritative timeline; the socket is a convenience.\n * - replay-safe: the engine re-emits on a resume/replay. The reducer below is\n * idempotent + order-tolerant, so a re-sent or duplicated event converges.\n * The sink itself does no dedupe.\n */\n\nimport type { StepAgentActivity } from './agent-activity'\n\n/** Handle mission stream events by processing emitted MissionStreamEvent objects */\nexport interface MissionEventSink {\n emit(event: MissionStreamEvent): void\n}\n\n/** A sink that drops every event — the engine default when no live channel is\n * wired (and the unit-test default). */\nexport const noopEventSink: MissionEventSink = { emit() {} }\n\n/** Workspace-wide channel id missions broadcast on (alongside any per-thread\n * channel the product keys). */\nexport const MISSION_CONTROL_CHANNEL_ID = 'missions'\n\n/** One plan step as it appears on the wire — only what a live UI needs\n * (`sublabel` updates travel separately via `step.updated` so the snapshot\n * stays small). */\nexport interface MissionStreamStep {\n id: string\n intent: string\n kind: string\n status: MissionStreamStepStatus\n}\n\n/** Define possible status values for a mission stream step */\nexport type MissionStreamStepStatus =\n | 'pending'\n | 'running'\n | 'done'\n | 'failed'\n | 'waiting_approval'\n\n/** Define possible statuses representing the current state of a mission stream */\nexport type MissionStreamStatus =\n | 'scheduled'\n | 'running'\n | 'paused'\n | 'waiting_approval'\n | 'succeeded'\n | 'aborted'\n | 'cancelled'\n | 'failed'\n\n/**\n * Discriminated union of every live mission event. Every member carries\n * `missionId` (one channel may multiplex several missions) and a `type` the\n * client switches on. `at` is the emitter's wall-clock ms — used only for\n * display ordering; the reducer never trusts it for causality.\n */\nexport type MissionStreamEvent =\n | {\n type: 'mission.created'\n missionId: string\n at: number\n title: string\n status?: MissionStreamStatus\n steps: MissionStreamStep[]\n budgetUsd?: number | null\n }\n | { type: 'mission.started'; missionId: string; at: number }\n | { type: 'step.started'; missionId: string; at: number; stepId: string }\n | {\n type: 'step.updated'\n missionId: string\n at: number\n stepId: string\n sublabel?: string\n /**\n * Full CURRENT snapshot of the step's delegated runs — never a delta.\n * The reducer replaces the whole lane (latest snapshot wins by `at`), so\n * emitters re-send everything they know each time and at-least-once /\n * out-of-order delivery converges.\n */\n agentActivity?: StepAgentActivity[]\n }\n | {\n type: 'step.completed'\n missionId: string\n at: number\n stepId: string\n ok: boolean\n reason?: string\n durationMs?: number\n }\n | {\n type: 'cost.updated'\n missionId: string\n at: number\n spentUsd: number\n capUsd?: number | null\n }\n | { type: 'mission.paused'; missionId: string; at: number; reason?: string }\n | { type: 'mission.waiting_approval'; missionId: string; at: number; reason?: string }\n | { type: 'mission.resumed'; missionId: string; at: number }\n | {\n type: 'mission.plan.updated'\n missionId: string\n at: number\n title: string\n steps: MissionStreamStep[]\n budgetUsd?: number | null\n }\n | {\n type: 'mission.completed'\n missionId: string\n at: number\n ok: boolean\n status?: Extract<MissionStreamStatus, 'succeeded' | 'failed' | 'aborted' | 'cancelled'>\n summary?: string\n }\n\n// All mission event `type` discriminants. Used to cheaply reject the non-mission\n// events that share a channel without a full switch.\nconst MISSION_EVENT_TYPES: ReadonlySet<string> = new Set<MissionStreamEvent['type']>([\n 'mission.created',\n 'mission.started',\n 'step.started',\n 'step.updated',\n 'step.completed',\n 'cost.updated',\n 'mission.paused',\n 'mission.waiting_approval',\n 'mission.resumed',\n 'mission.plan.updated',\n 'mission.completed',\n])\n\n/**\n * Reconstruct the flat MissionStreamEvent from a broadcast envelope of shape\n * `{ type, data: { ...missionFields } }` (transports may also stamp routing\n * fields like workspaceId/threadId into `data`). The envelope `type` is the\n * AUTHORITATIVE discriminant set by the server, so it is spread LAST — a\n * payload that happens to carry a top-level `type` inside `data` cannot shadow\n * it and mis-render as a mission event. Non-mission envelopes and malformed\n * payloads return null and are simply skipped, so one channel can carry both\n * streams.\n */\nexport function parseSessionStreamEnvelope(raw: unknown): MissionStreamEvent | null {\n if (!raw || typeof raw !== 'object') return null\n const envelope = raw as { type?: unknown; data?: unknown }\n if (typeof envelope.type !== 'string') return null\n const data = envelope.data && typeof envelope.data === 'object' ? envelope.data : {}\n return asMissionStreamEvent({ ...(data as Record<string, unknown>), type: envelope.type })\n}\n\n/** Narrow an arbitrary channel payload to a MissionStreamEvent. Returns null\n * for non-mission events and anything malformed — the reducer skips those. */\nexport function asMissionStreamEvent(value: unknown): MissionStreamEvent | null {\n if (!value || typeof value !== 'object') return null\n const record = value as Record<string, unknown>\n const type = record.type\n if (typeof type !== 'string' || !MISSION_EVENT_TYPES.has(type)) return null\n if (typeof record.missionId !== 'string' || !record.missionId) return null\n // `at` is best-effort: this narrows the type but does not fill a default —\n // the reducer tolerates a missing `at` and treats it as 0.\n return value as MissionStreamEvent\n}\n\n/** Live per-step view the reducer maintains. `status` only ever moves FORWARD\n * (see STEP_RANK) so a duplicate/out-of-order event can never regress a step\n * from done back to running. */\nexport interface MissionStepState {\n id: string\n intent: string\n kind: string\n status: MissionStreamStepStatus\n sublabel?: string\n reason?: string\n durationMs?: number\n /** Latest delegated-run snapshot for the step (see `step.updated`). */\n agentActivity?: StepAgentActivity[]\n /** The `at` of the snapshot currently held — an older snapshot arriving\n * late never replaces a newer one. */\n agentActivityAt?: number\n}\n\n/** Live per-mission view the reducer folds events into. */\nexport interface MissionState {\n missionId: string\n title?: string\n status: MissionStreamStatus\n steps: MissionStepState[]\n spentUsd: number\n capUsd?: number | null\n pauseReason?: string\n summary?: string\n /** The largest `at` folded so far — purely for display; never gates folding. */\n lastEventAt: number\n /** The largest pause/resume control `at` folded — lets a newer resume beat an\n * older pause that arrives late. */\n lastControlAt?: number\n}\n\n// Monotonic rank for step status. A fold NEVER moves a step to a lower rank, so\n// a late/duplicate `step.started` arriving after `step.completed` is a no-op —\n// the reducer tolerates out-of-order and at-least-once delivery.\nconst STEP_RANK: Record<MissionStreamStepStatus, number> = {\n pending: 0,\n running: 1,\n waiting_approval: 2,\n // done and failed are both TERMINAL for a step; rank them equal-and-highest\n // so neither can be overwritten by the other or regressed to running.\n done: 3,\n failed: 3,\n}\n\n// Monotonic rank for mission status. Same forward-only guarantee: a stray\n// `mission.started` after `mission.completed` cannot un-finish a mission.\nconst MISSION_RANK: Record<MissionStreamStatus, number> = {\n scheduled: 0,\n running: 1,\n paused: 2,\n waiting_approval: 2,\n succeeded: 3,\n aborted: 3,\n cancelled: 3,\n failed: 3,\n}\n\nfunction maxStepStatus(\n current: MissionStreamStepStatus,\n next: MissionStreamStepStatus,\n): MissionStreamStepStatus {\n // Equal rank but different terminal kind (done vs failed): the FIRST terminal\n // wins — a step.completed is authoritative and a later contradicting one is a\n // duplicate-class artifact we ignore.\n if (STEP_RANK[next] <= STEP_RANK[current]) return current\n return next\n}\n\nfunction maxMissionStatus(\n current: MissionStreamStatus,\n next: MissionStreamStatus,\n): MissionStreamStatus {\n if (MISSION_RANK[next] <= MISSION_RANK[current]) return current\n return next\n}\n\nfunction emptyMission(missionId: string): MissionState {\n // Seed at the LOWEST status rank so a fold-in is purely monotonic. A\n // step.started that arrives before mission.created must not pin the mission\n // at `running`: were the create to carry `scheduled`, the reducer would then\n // diverge by arrival order (created-first → scheduled, started-first →\n // running). Seeding `scheduled` makes every status edge climb upward only, so\n // both orders converge to the same state.\n return {\n missionId,\n status: 'scheduled',\n steps: [],\n spentUsd: 0,\n lastEventAt: 0,\n lastControlAt: 0,\n }\n}\n\nfunction stepStateFrom(step: MissionStreamStep): MissionStepState {\n return { id: step.id, intent: step.intent, kind: step.kind, status: step.status }\n}\n\n/**\n * Fold one event into one mission's state. PURE: returns a new state, mutates\n * nothing. Idempotent + order-tolerant — every status move is clamped through\n * the monotonic ranks above, so duplicates and out-of-order delivery converge\n * to the same terminal state regardless of arrival order.\n */\nexport function applyMissionEvent(\n prev: MissionState | undefined,\n event: MissionStreamEvent,\n): MissionState {\n const at = typeof event.at === 'number' ? event.at : 0\n const base = prev ?? emptyMission(event.missionId)\n const lastEventAt = Math.max(base.lastEventAt, at)\n const lastControlAt = base.lastControlAt ?? 0\n\n switch (event.type) {\n case 'mission.created': {\n // The authoritative plan snapshot. Merge rather than overwrite so a\n // late-arriving create (e.g. after the client already saw a step.started\n // from a reconnect race) does not clobber forward step progress.\n const merged = event.steps.map((incoming) => {\n const existing = base.steps.find((s) => s.id === incoming.id)\n if (!existing) return stepStateFrom(incoming)\n return {\n ...existing,\n intent: incoming.intent,\n kind: incoming.kind,\n status: maxStepStatus(existing.status, incoming.status),\n }\n })\n // Keep any steps the client already knew that the snapshot omits (it\n // never should, but never drop known progress).\n for (const known of base.steps) {\n if (!merged.some((s) => s.id === known.id)) merged.push(known)\n }\n return {\n ...base,\n title: event.title || base.title,\n status: maxMissionStatus(base.status, event.status ?? base.status),\n capUsd: event.budgetUsd ?? base.capUsd,\n steps: merged,\n lastEventAt,\n }\n }\n\n case 'mission.started':\n return { ...base, status: maxMissionStatus(base.status, 'running'), lastEventAt }\n\n case 'step.started':\n return {\n ...base,\n steps: upsertStep(base.steps, event.stepId, (step) => ({\n ...step,\n status: maxStepStatus(step.status, 'running'),\n })),\n lastEventAt,\n }\n\n case 'step.updated':\n return {\n ...base,\n steps: upsertStep(base.steps, event.stepId, (step) => ({\n ...step,\n // A sublabel is a live counter (\"7/15\") — always take the latest; it\n // does not move status.\n ...(event.sublabel !== undefined ? { sublabel: event.sublabel } : {}),\n // agentActivity is a full snapshot, replaced wholesale. Guarded by\n // `at` so a stale snapshot delivered late never erases newer rows;\n // an equal-`at` replay rewrites identical content (idempotent).\n ...(event.agentActivity !== undefined && at >= (step.agentActivityAt ?? 0)\n ? { agentActivity: event.agentActivity, agentActivityAt: at }\n : {}),\n })),\n lastEventAt,\n }\n\n case 'step.completed':\n return {\n ...base,\n steps: upsertStep(base.steps, event.stepId, (step) => ({\n ...step,\n status: maxStepStatus(step.status, event.ok ? 'done' : 'failed'),\n ...(event.reason !== undefined ? { reason: event.reason } : {}),\n ...(event.durationMs !== undefined ? { durationMs: event.durationMs } : {}),\n })),\n lastEventAt,\n }\n\n case 'cost.updated':\n return {\n ...base,\n // spentUsd is cumulative and monotonically non-decreasing at the\n // source; clamp so an out-of-order older value never lowers the\n // displayed spend.\n spentUsd: Math.max(base.spentUsd, event.spentUsd),\n capUsd: event.capUsd ?? base.capUsd,\n lastEventAt,\n }\n\n case 'mission.paused':\n if (at <= lastControlAt) return { ...base, lastEventAt }\n return {\n ...base,\n status: maxMissionStatus(base.status, 'paused'),\n ...(event.reason !== undefined ? { pauseReason: event.reason } : {}),\n lastEventAt,\n lastControlAt: Math.max(lastControlAt, at),\n }\n\n case 'mission.waiting_approval':\n if (at <= lastControlAt) return { ...base, lastEventAt }\n return {\n ...base,\n status: maxMissionStatus(base.status, 'waiting_approval'),\n ...(event.reason !== undefined ? { pauseReason: event.reason } : {}),\n lastEventAt,\n lastControlAt: Math.max(lastControlAt, at),\n }\n\n case 'mission.resumed':\n if (at <= lastControlAt) return { ...base, lastEventAt }\n return {\n ...base,\n status: isTerminalStreamStatus(base.status) ? base.status : 'running',\n pauseReason: undefined,\n lastEventAt,\n lastControlAt: Math.max(lastControlAt, at),\n }\n\n case 'mission.plan.updated':\n return {\n ...base,\n title: event.title || base.title,\n capUsd: event.budgetUsd ?? base.capUsd,\n steps: event.steps.map((incoming) => {\n const existing = base.steps.find((s) => s.id === incoming.id)\n if (!existing) return stepStateFrom(incoming)\n return {\n ...stepStateFrom(incoming),\n status: maxStepStatus(existing.status, incoming.status),\n ...(existing.sublabel !== undefined ? { sublabel: existing.sublabel } : {}),\n ...(existing.reason !== undefined ? { reason: existing.reason } : {}),\n ...(existing.durationMs !== undefined ? { durationMs: existing.durationMs } : {}),\n ...(existing.agentActivity !== undefined ? { agentActivity: existing.agentActivity } : {}),\n ...(existing.agentActivityAt !== undefined ? { agentActivityAt: existing.agentActivityAt } : {}),\n }\n }),\n lastEventAt,\n }\n\n case 'mission.completed':\n return {\n ...base,\n status: maxMissionStatus(base.status, event.status ?? (event.ok ? 'succeeded' : 'failed')),\n ...(event.summary !== undefined ? { summary: event.summary } : {}),\n lastEventAt,\n }\n }\n}\n\nfunction isTerminalStreamStatus(status: MissionStreamStatus): boolean {\n return status === 'succeeded' || status === 'failed' || status === 'aborted' || status === 'cancelled'\n}\n\n// Upsert one step by id, applying `patch`. A step unknown to the client (e.g. a\n// step.started arriving before the create snapshot on a reconnect) is created\n// as a minimal `pending` row so progress is never dropped; the create snapshot\n// fills in intent/kind when it arrives.\nfunction upsertStep(\n steps: MissionStepState[],\n stepId: string,\n patch: (step: MissionStepState) => MissionStepState,\n): MissionStepState[] {\n const index = steps.findIndex((s) => s.id === stepId)\n const existing = index < 0 ? undefined : steps[index]\n if (!existing) {\n const placeholder: MissionStepState = {\n id: stepId,\n intent: '',\n kind: '',\n status: 'pending',\n }\n return [...steps, patch(placeholder)]\n }\n const next = steps.slice()\n next[index] = patch(existing)\n return next\n}\n\n/**\n * Merge a loader SEED into the live state for one mission, advancing through\n * the SAME monotonic clamps the event reducer uses. The durable mission row is\n * the authoritative converged state: while the live channel is down the row\n * advances but the frozen live state does not, and nothing re-fires the gap to\n * a reconnecting client. Folding the seed THROUGH the clamps backfills that gap\n * on reconnect while never regressing a more-advanced live value:\n * - a stale seed for a more-advanced live mission is a no-op,\n * - an advanced seed after an outage fills the gap (status/steps/spend move\n * forward to the row's converged state).\n * `live === undefined` (mission unknown to the client) just adopts the seed.\n */\nexport function mergeMissionState(live: MissionState | undefined, seed: MissionState): MissionState {\n if (!live) return seed\n\n // The loader seed carries the authoritative current plan. Rebuild from it,\n // while preserving live progress fields for matching ids and retaining\n // live-only steps that have real progress evidence.\n const steps: MissionStepState[] = []\n for (const seededStep of seed.steps) {\n const current = live.steps.find((s) => s.id === seededStep.id)\n if (!current) {\n steps.push(seededStep)\n continue\n }\n steps.push({\n ...seededStep,\n intent: current.intent || seededStep.intent,\n kind: current.kind || seededStep.kind,\n status: maxStepStatus(current.status, seededStep.status),\n ...(current.sublabel !== undefined ? { sublabel: current.sublabel } : seededStep.sublabel !== undefined ? { sublabel: seededStep.sublabel } : {}),\n ...(current.reason !== undefined ? { reason: current.reason } : seededStep.reason !== undefined ? { reason: seededStep.reason } : {}),\n ...(current.durationMs !== undefined ? { durationMs: current.durationMs } : seededStep.durationMs !== undefined ? { durationMs: seededStep.durationMs } : {}),\n // The newer snapshot wins by its stamped `at`; an unstamped lane (loader\n // seed copied from the settled artifact) only fills an empty live lane.\n ...(mergeActivity(current, seededStep)),\n })\n }\n for (const current of live.steps) {\n if (seed.steps.some((step) => step.id === current.id)) continue\n if (hasStepProgressEvidence(current)) steps.push(current)\n }\n\n const status = mergeSeedMissionStatus(live.status, seed.status, live.lastControlAt ?? 0, seed.lastControlAt ?? 0)\n return {\n ...live,\n title: live.title ?? seed.title,\n status,\n steps,\n spentUsd: Math.max(live.spentUsd, seed.spentUsd),\n capUsd: live.capUsd ?? seed.capUsd,\n pauseReason: status === 'running' || status === 'scheduled' ? undefined : seed.pauseReason ?? live.pauseReason,\n summary: live.summary ?? seed.summary,\n lastEventAt: Math.max(live.lastEventAt, seed.lastEventAt),\n lastControlAt: Math.max(live.lastControlAt ?? 0, seed.lastControlAt ?? 0),\n }\n}\n\nfunction hasStepProgressEvidence(step: MissionStepState): boolean {\n return step.status !== 'pending' ||\n step.sublabel !== undefined ||\n step.reason !== undefined ||\n step.durationMs !== undefined ||\n step.agentActivity !== undefined\n}\n\n// Pick the activity lane to keep when merging a loader seed into live state:\n// the snapshot with the larger `at` wins; ties keep the live side.\nfunction mergeActivity(\n live: MissionStepState,\n seeded: MissionStepState,\n): Pick<MissionStepState, 'agentActivity' | 'agentActivityAt'> {\n const winner = (seeded.agentActivityAt ?? 0) > (live.agentActivityAt ?? 0) ? seeded : live\n if (winner.agentActivity === undefined) {\n const fallback = winner === live ? seeded : live\n if (fallback.agentActivity === undefined) return {}\n return {\n agentActivity: fallback.agentActivity,\n ...(fallback.agentActivityAt !== undefined ? { agentActivityAt: fallback.agentActivityAt } : {}),\n }\n }\n return {\n agentActivity: winner.agentActivity,\n ...(winner.agentActivityAt !== undefined ? { agentActivityAt: winner.agentActivityAt } : {}),\n }\n}\n\nfunction mergeSeedMissionStatus(\n liveStatus: MissionStreamStatus,\n seedStatus: MissionStreamStatus,\n liveControlAt: number,\n seedControlAt: number,\n): MissionStreamStatus {\n if (isTerminalStreamStatus(liveStatus)) return liveStatus\n if (isTerminalStreamStatus(seedStatus)) return seedStatus\n if ((seedStatus === 'paused' || seedStatus === 'waiting_approval') && liveControlAt > seedControlAt) {\n return liveStatus\n }\n if (seedStatus === 'running') return 'running'\n if (seedStatus === 'scheduled' && liveStatus !== 'scheduled') return liveStatus\n return maxMissionStatus(liveStatus, seedStatus)\n}\n\n/**\n * Fold a whole event sequence into a Map<missionId, MissionState>. PURE and\n * order-tolerant: feeding the same events in any order (with duplicates)\n * converges to the same map. `seed` lets a reload start from loader-rehydrated\n * state before live events arrive.\n */\nexport function reduceMissionEvents(\n events: MissionStreamEvent[],\n seed?: Map<string, MissionState>,\n): Map<string, MissionState> {\n const next = new Map<string, MissionState>(seed ?? [])\n for (const event of events) {\n next.set(event.missionId, applyMissionEvent(next.get(event.missionId), event))\n }\n return next\n}\n","/**\n * Mission execution engine — drives one mission's plan to completion under a\n * SINGLE serialized owner (a Cloudflare Workflow, a Durable Object alarm, a\n * queue consumer — one per mission). The owner wraps each `runStep` call in its\n * durable-step primitive (e.g. Workflows `step.do(step.id, …)`) so a completed\n * step's result is persisted and replayed instead of re-run after a mid-run\n * restart. This module holds the logic that must be correct independent of any\n * runtime, so it is injectable and unit-testable with the dispatch mocked.\n *\n * Idempotency is layered, belt-and-suspenders:\n * 1. The owner's durable-step cache replays a completed step's result.\n * 2. `runStep` re-reads the mission first; a step already `done` (with a\n * resultRef) returns the cached pointer WITHOUT re-dispatching — this\n * closes the at-least-once window where a callback re-runs after the\n * side effect committed but before the owner durably recorded it.\n * 3. The cursor advances only after a step is `done`, so a fresh run resumes\n * from `mission.cursor` and never re-touches earlier steps.\n *\n * Seams (the product supplies domain; the engine owns mechanism):\n * - {@link SandboxDispatch} — how a step actually executes.\n * - {@link MissionEngineOptions.estimateStepCostUsd} — per-step USD estimate.\n * - {@link MissionGateOptions.classifyStep} — which steps need approval.\n * - {@link MissionApprovalsPort} — where gate proposals live and how they\n * resolve.\n */\n\nimport type {\n MissionCostLedger,\n MissionOutcome,\n MissionRecord,\n MissionService,\n MissionStatus,\n MissionStep,\n} from './service'\nimport { isMissionStopRequested, isMissionTerminal } from './service'\nimport { noopEventSink, type MissionEventSink, type MissionStreamEvent } from './events'\n\n/**\n * A side-effecting unit of per-step work. The owner supplies the real\n * implementation (e.g. a detached sandbox-session dispatcher); tests supply a\n * mock. MUST return a SMALL pointer — large output is written to the product's\n * storage and only the resultRef is returned.\n */\nexport type SandboxDispatch = (input: SandboxDispatchInput) => Promise<SandboxDispatchResult>\n\n/** Define input parameters for dispatching a mission step in the sandbox environment */\nexport interface SandboxDispatchInput {\n mission: MissionRecord\n step: MissionStep\n stepIndex: number\n}\n\n/** Define the result of a completed sandbox dispatch including artifact reference and optional cost details */\nexport interface SandboxDispatchDoneResult {\n kind?: 'done'\n /** Small pointer at the produced artifact/output (vault path, asset id, exec\n * digest). Stored on the step as `resultRef`; never the full payload. */\n resultRef: string\n /** Optional one-line status surfaced on the step row. */\n sublabel?: string\n /** Optional marginal spend for this step. `ledgerDelta` carries platform-\n * reported truth (real token counts, wall time); `deltaUsd` is set ONLY when\n * a provider-authored price is known. Omit fields rather than synthesizing\n * zeros — the engine substitutes its injected per-step estimate for a\n * missing deltaUsd and records that estimate in the ledger. */\n cost?: {\n deltaUsd?: number\n ledgerDelta?: Partial<MissionCostLedger>\n }\n}\n\n/** The dispatched step's detached session is still executing on the platform.\n * The owner sleeps `pollAfterMs` and re-invokes the step; the dispatch is\n * idempotent on the session ref, so the re-invocation settles the same session\n * rather than starting a second run. */\nexport interface SandboxDispatchInProgressResult {\n kind: 'in_progress'\n sessionRef: string\n pollAfterMs: number\n sublabel?: string\n}\n\n/** Resolve the result of a sandbox dispatch as done or in progress */\nexport type SandboxDispatchResult = SandboxDispatchDoneResult | SandboxDispatchInProgressResult\n\n/** Outcome of running a single step. `cached` distinguishes a replay/skip\n * (step was already done) from a fresh execution so the engine and its tests\n * can assert the dispatch was NOT re-invoked. */\nexport type StepOutcome =\n | { kind: 'done'; resultRef: string; cached: boolean }\n | { kind: 'in_progress'; sessionRef: string; pollAfterMs: number; sublabel?: string }\n | { kind: 'skipped-cursor'; reason: string }\n | { kind: 'failed'; error: string; fatal: boolean }\n\n/** Outcome of running the whole plan from the cursor to the end. */\nexport type PlanOutcome =\n | { kind: 'completed'; summary: string }\n | { kind: 'in_progress'; stepId: string; sessionRef: string; pollAfterMs: number; sublabel?: string }\n | { kind: 'failed'; failedStepId: string; error: string }\n | { kind: 'halted'; status: MissionStatus; reason?: string | null }\n | { kind: 'terminal'; status: MissionStatus }\n | { kind: 'not-found' }\n\ntype StepGateOutcome =\n | { kind: 'continue' }\n | { kind: 'halted'; status: MissionStatus; reason: string }\n\n/** Define options to control mission plan execution with optional pre-step veto logic */\nexport interface MissionPlanRunOptions {\n /** Pre-step veto (kill switch, schedule window). A non-null return pauses the\n * mission with that reason before the step's side effect starts. */\n beforeStep?: (mission: MissionRecord, step: MissionStep) => Promise<string | null>\n}\n\n/** Thrown to make the owner's durable-step wrapper retry. The single-owner\n * invariant makes a genuine concurrent change rare (it means another writer\n * touched the row), so retrying — rather than corrupting state by forcing a\n * stale write — is the correct response. Distinct from a task failure, which\n * is recorded on the step. */\nexport class MissionConcurrencyError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'MissionConcurrencyError'\n }\n}\n\n/** Thrown by a {@link SandboxDispatch} for a TRANSIENT failure (platform blip,\n * exec-time network fault) that should be re-attempted. `runStep` RE-THROWS it\n * so the owner engages its bounded retry+backoff; the step is left `running`\n * and the re-dispatch is made idempotent by the cached-done guard. A\n * deterministic failure must be a plain Error instead — that is recorded as a\n * fatal `failed` step and is never retried (no money-burning loop on a\n * deterministic error). */\nexport class RetryableStepError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'RetryableStepError'\n }\n}\n\n/** Resolution states a gate proposal can be in. `approved`/`executed` unblock\n * the gated step; everything else keeps the mission parked. */\nexport type MissionProposalResolution = 'pending' | 'approved' | 'rejected' | 'executed' | 'ignored'\n\n/** Define mission gate categories as step, budget, or volume */\nexport type MissionGateKind = 'step' | 'budget' | 'volume'\n\n/** Product classification of one step. Returned by\n * {@link MissionGateOptions.classifyStep}; the matching rules (regexes, intent\n * vocabularies, path allowlists) are product domain and never live here. */\nexport interface StepGateClassification {\n /** Product approval-type label persisted on the proposal ('generate',\n * 'integration_invoke', …). */\n type: string\n /** Counted against the per-mission external-action volume cap. */\n externalAction?: boolean\n estCostUsd?: number | null\n}\n\n/** A gate proposal the engine asks the product to persist. The id is\n * deterministic per (gate, mission, step) — see the `*ProposalId` helpers —\n * so a replay re-finds the same proposal instead of duplicating it. The\n * product composes its own title/description from the structured fields. */\nexport interface MissionGateProposal {\n id: string\n missionId: string\n stepId: string\n gate: MissionGateKind\n mission: MissionRecord\n step: MissionStep\n /** Present for `gate: 'step'` — the classification that triggered the gate. */\n classification?: StepGateClassification\n /** Present for `gate: 'budget'`. */\n budget?: { spentUsd: number; budgetUsd: number; estimatedCostUsd: number }\n /** Present for `gate: 'volume'`. */\n volume?: { externalActionCount: number; cap: number }\n}\n\n/** Approval persistence seam — the product implements this over its own\n * proposal table and resolution flow. */\nexport interface MissionApprovalsPort {\n /** Resolution of the proposal with this id, or null when none exists. */\n findResolution(proposalId: string): Promise<MissionProposalResolution | null>\n /** Persist a new gate proposal (id is deterministic; called at most once per\n * (gate, mission, step) absent a resolution). */\n createProposal(proposal: MissionGateProposal): Promise<void>\n /** Count of this mission's `gate: 'step'` proposals whose classification was\n * `externalAction: true` — the denominator of the volume cap. */\n countExternalActionProposals(missionId: string): Promise<number>\n}\n\n/** Define configuration options for mission gating including approvals, step classification, and action limits */\nexport interface MissionGateOptions {\n approvals: MissionApprovalsPort\n /** Which steps need human approval, and as what. Return null for an ungated\n * step. The rules are product domain (intent regexes, kind tables). */\n classifyStep: (step: MissionStep) => StepGateClassification | null\n /** Max external-action approvals per mission before an approved override is\n * required to request another. Default 5. */\n externalActionCap?: number\n}\n\n/** Define configuration options for initializing and controlling the mission engine behavior */\nexport interface MissionEngineOptions {\n service: MissionService\n /** Per-step USD estimate. Load-bearing twice: the budget gate parks on it\n * BEFORE a step runs, and the engine records it as the step's spend when the\n * dispatch reports no provider-authored price — using one estimator keeps\n * spend and gate consistent. */\n estimateStepCostUsd: (step: MissionStep) => number\n /** Best-effort live notifier. Fired AFTER each guarded write commits, so a\n * broadcast always reflects persisted state; re-fired on idempotent replays\n * so a reconnecting client converges. Never awaited; a throwing sink can\n * never fail a step. Default: drop everything. */\n sink?: MissionEventSink\n /** Approval gating. Omitted → no classification/volume gates, and a budget\n * overrun pauses the mission (fail closed) instead of parking it\n * waiting_approval behind an override proposal. */\n gates?: MissionGateOptions\n /** Step kinds whose failure does NOT abort the whole mission — enrichment\n * steps the plan can complete without. Every other kind is fatal-on-failure.\n * Default `['optional', 'best-effort']`. */\n nonFatalStepKinds?: readonly string[]\n}\n\n/** Resolve mission plan steps with concurrency control and durable state management */\nexport interface MissionEngine {\n /** Run exactly one plan step. Idempotent: re-invoking for a step already\n * `done` returns the cached pointer without re-dispatching. A lost guarded\n * race throws {@link MissionConcurrencyError} so the owner's durable-step\n * wrapper retries instead of writing a stale value. */\n runStep(missionId: string, stepId: string, dispatch: SandboxDispatch): Promise<StepOutcome>\n /** Walk the plan from the durable cursor to the end, re-reading the mission\n * between steps so a pause/stop control that lands while a step is running\n * is honored before the next side effect. `runStep` is the owner's boundary:\n * in production `(step) => durableStep.do(step.id, () => engine.runStep(…))`;\n * in tests `engine.runStep` directly. */\n runPlan(\n missionId: string,\n runStep: (step: MissionStep, stepIndex: number) => Promise<StepOutcome>,\n options?: MissionPlanRunOptions,\n ): Promise<PlanOutcome>\n /** Record spend durable-first, live second: the guarded ledger write commits,\n * then the sink sees the new total. A guarded failure returns unchanged. */\n recordCost(\n missionId: string,\n deltaUsd: number,\n ledgerDelta?: Partial<MissionCostLedger>,\n ): Promise<MissionOutcome<MissionRecord>>\n /** Pause durable-first, live second (the paused event fires only on a real\n * edge, not an idempotent re-pause). */\n pauseMission(missionId: string, reason: string): Promise<MissionOutcome<MissionRecord>>\n}\n\nconst DEFAULT_EXTERNAL_ACTION_CAP = 5\nconst DEFAULT_NON_FATAL_STEP_KINDS: readonly string[] = ['optional', 'best-effort']\n\n/** Deterministic proposal id for a step-classification gate. */\nexport function stepGateProposalId(missionId: string, stepId: string): string {\n return `mission-step-gate:${missionId}:${stepId}`\n}\n\n/** Deterministic proposal id for a budget-overrun override. */\nexport function budgetGateProposalId(missionId: string, stepId: string): string {\n return `mission-budget-gate:${missionId}:${stepId}`\n}\n\n/** Deterministic proposal id for an external-action volume-cap override. */\nexport function volumeGateProposalId(missionId: string, stepId: string): string {\n return `mission-volume-gate:${missionId}:${stepId}`\n}\n\n// Emit a live event without letting the sink's failure touch the step path.\n// The engine owns the fire-and-forget guarantee at this boundary: a sink that\n// throws synchronously can NEVER fail a step. The durable audit row is the\n// authoritative timeline; the live channel is a convenience.\nfunction safeEmit(sink: MissionEventSink, event: MissionStreamEvent): void {\n try {\n sink.emit(event)\n } catch {\n // Best-effort UI notification — a broadcast fault never fails the step.\n }\n}\n\nfunction unblocked(resolution: MissionProposalResolution | null): boolean {\n return resolution === 'approved' || resolution === 'executed'\n}\n\n// Map a durable terminal status onto the live `mission.completed` event. Only\n// `succeeded` is ok=true; aborted/cancelled/failed fold to ok=false with the\n// terminal status preserved so the reducer keeps user stops distinct from\n// failures.\nfunction terminalMissionEvent(\n missionId: string,\n status: MissionStatus,\n): Extract<MissionStreamEvent, { type: 'mission.completed' }> {\n const terminal = status === 'succeeded' || status === 'failed' || status === 'aborted' || status === 'cancelled'\n ? status\n : 'failed'\n return {\n type: 'mission.completed',\n missionId,\n at: Date.now(),\n ok: terminal === 'succeeded',\n status: terminal,\n ...(terminal === 'succeeded' ? {} : { summary: `Mission ${status}` }),\n }\n}\n\n/** Create a mission engine configured with options to manage mission execution and error handling */\nexport function createMissionEngine(options: MissionEngineOptions): MissionEngine {\n const { service, estimateStepCostUsd, gates } = options\n const sink = options.sink ?? noopEventSink\n const nonFatalStepKinds = new Set(options.nonFatalStepKinds ?? DEFAULT_NON_FATAL_STEP_KINDS)\n const externalActionCap = gates?.externalActionCap ?? DEFAULT_EXTERNAL_ACTION_CAP\n\n function isFatalStepKind(kind: string): boolean {\n return !nonFatalStepKinds.has(kind)\n }\n\n // A guarded service rejection is one of two things:\n // - a lost race (conflict: true) — THROW so the owner's durable-step\n // wrapper retries; the single owner makes this rare and a retry re-reads\n // fresh state rather than forcing a stale write.\n // - a logic rejection (illegal transition, missing step) — that step is\n // structurally broken; return a fatal `failed` outcome instead of\n // looping a retry forever (no money-burning retry on a deterministic\n // error).\n function rejectStep(failure: { error: string; conflict: boolean }): StepOutcome {\n if (failure.conflict) throw new MissionConcurrencyError(failure.error)\n return { kind: 'failed', error: failure.error, fatal: true }\n }\n\n function isStepCurrentOrFuture(mission: MissionRecord, stepId: string): boolean {\n const index = mission.plan.findIndex((candidate) => candidate.id === stepId)\n return index >= mission.cursor\n }\n\n const recordCost: MissionEngine['recordCost'] = async (missionId, deltaUsd, ledgerDelta) => {\n const recorded = await service.addCost(missionId, deltaUsd, ledgerDelta)\n if (!recorded.succeeded) return recorded\n safeEmit(sink, {\n type: 'cost.updated',\n missionId,\n at: Date.now(),\n spentUsd: recorded.value.spentUsd,\n capUsd: recorded.value.budgetUsd,\n })\n return recorded\n }\n\n const pauseMission: MissionEngine['pauseMission'] = async (missionId, reason) => {\n const before = await service.getMission(missionId)\n const paused = await service.pause(missionId, reason)\n if (!paused.succeeded) return paused\n if (before?.status !== 'paused') {\n safeEmit(sink, {\n type: 'mission.paused',\n missionId,\n at: Date.now(),\n reason: paused.value.pauseReason ?? reason,\n })\n }\n return paused\n }\n\n const runStep: MissionEngine['runStep'] = async (missionId, stepId, dispatch) => {\n const mission = await service.getMission(missionId)\n if (!mission) throw new MissionConcurrencyError(`Mission ${missionId} not found mid-run`)\n\n const stepIndex = mission.plan.findIndex((candidate) => candidate.id === stepId)\n const step = stepIndex < 0 ? undefined : mission.plan[stepIndex]\n if (!step) {\n return { kind: 'skipped-cursor', reason: `Step ${stepId} is no longer in mission plan` }\n }\n if (isMissionStopRequested(mission)) {\n return { kind: 'failed', error: mission.pauseReason ?? 'Mission stop requested', fatal: true }\n }\n if (mission.status !== 'running') {\n return { kind: 'skipped-cursor', reason: mission.pauseReason ?? `Mission is ${mission.status}` }\n }\n\n // (2) Idempotent short-circuit: a step that already reached `done` keeps\n // its resultRef. Return it WITHOUT re-running the side effect. Reconcile\n // the cursor first: a crash in the window between setStepStatus('done')\n // and advanceCursor leaves the cursor one slot behind this done step; a\n // fresh resume would otherwise end with cursor = plan.length - 1. Advance\n // it here (guarded — a lost race throws for the owner to retry) so the\n // cursor stays a true done-count.\n if (step.status === 'done' && step.resultRef) {\n if (stepIndex === mission.cursor) {\n const reconciled = await service.advanceCursor(missionId)\n if (!reconciled.succeeded) return rejectStep(reconciled)\n }\n // Re-emit the terminal event on replay so a client that connected after\n // the step finished still folds it (idempotent at the reducer).\n safeEmit(sink, { type: 'step.completed', missionId, at: Date.now(), stepId, ok: true })\n return { kind: 'done', resultRef: step.resultRef, cached: true }\n }\n\n // (3) The cursor has already moved past this step but it is not `done` —\n // the owner is replaying a step the engine no longer considers active.\n // Skip it rather than re-run; the cursor is the source of truth.\n if (stepIndex < mission.cursor) {\n return { kind: 'skipped-cursor', reason: `Step ${stepId} is behind cursor ${mission.cursor}` }\n }\n\n const startedAt = Date.now()\n if (step.status !== 'running') {\n const running = await service.setStepStatus(missionId, stepId, 'running')\n if (!running.succeeded) return rejectStep(running)\n }\n safeEmit(sink, { type: 'step.started', missionId, at: startedAt, stepId })\n\n let dispatched: SandboxDispatchResult\n try {\n dispatched = await dispatch({ mission, step, stepIndex })\n } catch (error) {\n // A TRANSIENT failure re-throws so the owner engages its bounded\n // retry+backoff: leave the step `running` (the re-dispatch is idempotent\n // via the cached-done guard) and let the owner re-attempt up to its limit.\n if (error instanceof RetryableStepError) throw error\n const latest = await service.getMission(missionId)\n if (\n latest &&\n !isMissionTerminal(latest.status) &&\n !isMissionStopRequested(latest) &&\n !isStepCurrentOrFuture(latest, stepId)\n ) {\n return { kind: 'skipped-cursor', reason: `Step ${stepId} is no longer active` }\n }\n // A deterministic failure is recorded as a fatal `failed` step and is\n // NOT retried — looping a retry on a deterministic error burns money.\n const message = error instanceof Error ? error.message : 'Sandbox dispatch failed'\n const failed = await service.setStepStatus(missionId, stepId, 'failed', { error: message })\n if (!failed.succeeded) return rejectStep(failed)\n safeEmit(sink, {\n type: 'step.completed',\n missionId,\n at: Date.now(),\n stepId,\n ok: false,\n reason: message,\n durationMs: Date.now() - startedAt,\n })\n return { kind: 'failed', error: message, fatal: isFatalStepKind(step.kind) }\n }\n\n const afterDispatch = await service.getMission(missionId)\n if (!afterDispatch) throw new MissionConcurrencyError(`Mission ${missionId} not found after dispatch`)\n if (isMissionTerminal(afterDispatch.status) || isMissionStopRequested(afterDispatch)) {\n return { kind: 'failed', error: afterDispatch.pauseReason ?? 'Mission stop requested', fatal: true }\n }\n if (afterDispatch.status !== 'running') {\n return { kind: 'skipped-cursor', reason: afterDispatch.pauseReason ?? `Mission is ${afterDispatch.status}` }\n }\n if (!isStepCurrentOrFuture(afterDispatch, stepId)) {\n return { kind: 'skipped-cursor', reason: `Step ${stepId} is no longer active` }\n }\n\n // The detached session is still running: surface the elapsed sublabel and\n // hand the poll cadence to the owner. The step row stays `running`.\n if (dispatched.kind === 'in_progress') {\n if (dispatched.sublabel !== undefined) {\n safeEmit(sink, { type: 'step.updated', missionId, at: Date.now(), stepId, sublabel: dispatched.sublabel })\n }\n return {\n kind: 'in_progress',\n sessionRef: dispatched.sessionRef,\n pollAfterMs: dispatched.pollAfterMs,\n ...(dispatched.sublabel === undefined ? {} : { sublabel: dispatched.sublabel }),\n }\n }\n\n // A live counter (\"7/15\") the dispatch surfaced — push it before the\n // terminal event so a long step shows progress, then settles to done.\n if (dispatched.sublabel !== undefined) {\n safeEmit(sink, { type: 'step.updated', missionId, at: Date.now(), stepId, sublabel: dispatched.sublabel })\n }\n\n // USD: provider-authored price when the dispatch reports one, the injected\n // estimate otherwise (the budget gate runs on the same estimate, so spend\n // and gate stay consistent). Token counts/wall time come from the\n // dispatch's ledgerDelta when the platform reported them. The spend is\n // folded into the SAME guarded pending->done write below so step completion\n // is the per-step idempotency key: a RESUME re-dispatching an already-done\n // step short-circuits as a no-op and never charges twice.\n const deltaUsd = dispatched.cost?.deltaUsd ?? estimateStepCostUsd(step)\n const chargeable = deltaUsd > 0 || Boolean(dispatched.cost?.ledgerDelta)\n const spentBefore = afterDispatch.spentUsd\n\n const done = await service.setStepStatus(missionId, stepId, 'done', {\n resultRef: dispatched.resultRef,\n ...(dispatched.sublabel === undefined ? {} : { sublabel: dispatched.sublabel }),\n ...(chargeable\n ? {\n cost: {\n deltaUsd,\n ledgerDelta: {\n costUsd: deltaUsd,\n llmCalls: 1,\n ...(dispatched.cost?.ledgerDelta ?? {}),\n },\n },\n }\n : {}),\n })\n if (!done.succeeded) return rejectStep(done)\n // Emit cost.updated only when the spend actually committed (a real\n // transition, not a replayed no-op that re-asserted an already-done step).\n if (done.value.spentUsd !== spentBefore) {\n safeEmit(sink, {\n type: 'cost.updated',\n missionId,\n at: Date.now(),\n spentUsd: done.value.spentUsd,\n capUsd: done.value.budgetUsd,\n })\n }\n safeEmit(sink, {\n type: 'step.completed',\n missionId,\n at: Date.now(),\n stepId,\n ok: true,\n durationMs: Date.now() - startedAt,\n })\n\n // Advance the cursor only after the step is durably `done`. A racing\n // advance (single owner ⇒ should not happen) surfaces as a retry.\n const advanced = await service.advanceCursor(missionId)\n if (!advanced.succeeded) return rejectStep(advanced)\n\n return { kind: 'done', resultRef: dispatched.resultRef, cached: false }\n }\n\n async function parkForApproval(\n mission: MissionRecord,\n step: MissionStep,\n reason: string,\n ): Promise<StepGateOutcome> {\n const waiting = await service.markWaitingApproval(mission.id, step.id)\n if (!waiting.succeeded) {\n if (waiting.conflict) throw new MissionConcurrencyError(waiting.error)\n return { kind: 'halted', status: mission.status, reason: waiting.error }\n }\n safeEmit(sink, {\n type: 'mission.waiting_approval',\n missionId: mission.id,\n at: Date.now(),\n reason,\n })\n safeEmit(sink, {\n type: 'mission.plan.updated',\n missionId: mission.id,\n at: Date.now(),\n title: waiting.value.summary ?? 'Mission',\n steps: waiting.value.plan.map((candidate) => ({\n id: candidate.id,\n intent: candidate.intent,\n kind: candidate.kind,\n status: candidate.status,\n })),\n budgetUsd: waiting.value.budgetUsd,\n })\n return { kind: 'halted', status: 'waiting_approval', reason }\n }\n\n // A budgeted mission never starts a step whose estimate would push spend\n // past the cap. With an approvals port the overrun parks behind a\n // deterministic override proposal; without one it pauses (fail closed) for a\n // manual budget raise + resume.\n async function enforceBudget(mission: MissionRecord, step: MissionStep): Promise<StepGateOutcome> {\n const capUsd = mission.budgetUsd\n if (capUsd === null) return { kind: 'continue' }\n const estimatedCostUsd = estimateStepCostUsd(step)\n if (estimatedCostUsd <= 0) return { kind: 'continue' }\n // Compare in integer cents: USD amounts accumulate binary-float error (e.g.\n // 0.1 + 0.2 > 0.3), so a raw float compare can spuriously trip the gate at\n // the exact cap. Cent-rounding makes the boundary deterministic.\n if (Math.round((mission.spentUsd + estimatedCostUsd) * 100) <= Math.round(capUsd * 100)) {\n return { kind: 'continue' }\n }\n\n if (!gates) {\n const reason = `Budget cap reached before step ${step.id}: $${mission.spentUsd.toFixed(2)} spent of $${capUsd.toFixed(2)}, next step estimated $${estimatedCostUsd.toFixed(2)}`\n const paused = await pauseMission(mission.id, reason)\n if (!paused.succeeded) {\n if (paused.conflict) throw new MissionConcurrencyError(paused.error)\n return { kind: 'halted', status: mission.status, reason: paused.error }\n }\n return { kind: 'halted', status: paused.value.status, reason }\n }\n\n const proposalId = budgetGateProposalId(mission.id, step.id)\n const resolution = await gates.approvals.findResolution(proposalId)\n if (unblocked(resolution)) return { kind: 'continue' }\n if (resolution === null) {\n await gates.approvals.createProposal({\n id: proposalId,\n missionId: mission.id,\n stepId: step.id,\n gate: 'budget',\n mission,\n step,\n budget: { spentUsd: mission.spentUsd, budgetUsd: capUsd, estimatedCostUsd },\n })\n }\n return parkForApproval(mission, step, `Budget approval required for step ${step.id}`)\n }\n\n async function enforceVolumeCap(mission: MissionRecord, step: MissionStep): Promise<StepGateOutcome> {\n if (!gates) return { kind: 'continue' }\n const overrideId = volumeGateProposalId(mission.id, step.id)\n const override = await gates.approvals.findResolution(overrideId)\n if (unblocked(override)) return { kind: 'continue' }\n\n const externalCount = await gates.approvals.countExternalActionProposals(mission.id)\n if (externalCount < externalActionCap) return { kind: 'continue' }\n\n if (override === null) {\n await gates.approvals.createProposal({\n id: overrideId,\n missionId: mission.id,\n stepId: step.id,\n gate: 'volume',\n mission,\n step,\n volume: { externalActionCount: externalCount, cap: externalActionCap },\n })\n }\n return parkForApproval(mission, step, `External action cap approval required for step ${step.id}`)\n }\n\n async function enforceStepGate(mission: MissionRecord, step: MissionStep): Promise<StepGateOutcome> {\n if (!gates) return { kind: 'continue' }\n const classification = gates.classifyStep(step)\n if (!classification) return { kind: 'continue' }\n\n if (classification.externalAction) {\n const volume = await enforceVolumeCap(mission, step)\n if (volume.kind === 'halted') return volume\n }\n\n const proposalId = stepGateProposalId(mission.id, step.id)\n const resolution = await gates.approvals.findResolution(proposalId)\n if (unblocked(resolution)) return { kind: 'continue' }\n\n if (resolution === null) {\n await gates.approvals.createProposal({\n id: proposalId,\n missionId: mission.id,\n stepId: step.id,\n gate: 'step',\n mission,\n step,\n classification,\n })\n }\n return parkForApproval(mission, step, `Approval required for step ${step.id}`)\n }\n\n const runPlan: MissionEngine['runPlan'] = async (missionId, runStepFn, planOptions = {}) => {\n const mission = await service.getMission(missionId)\n if (!mission) return { kind: 'not-found' }\n if (isMissionTerminal(mission.status)) {\n // Already terminal on entry (a replay after the row finished, or a\n // resume of an aborted/cancelled run). Re-emit the terminal event so a\n // client that connected during an outage still converges off this run.\n // Idempotent at the reducer.\n safeEmit(sink, terminalMissionEvent(missionId, mission.status))\n return { kind: 'terminal', status: mission.status }\n }\n\n // Resume from the durable cursor. Re-read the mission between steps so a\n // pause/stop control that lands while a step is running is honored before\n // the next side effect starts.\n while (true) {\n const currentMission = await service.getMission(missionId)\n if (!currentMission) return { kind: 'not-found' }\n if (isMissionTerminal(currentMission.status)) {\n safeEmit(sink, terminalMissionEvent(missionId, currentMission.status))\n return { kind: 'terminal', status: currentMission.status }\n }\n if (isMissionStopRequested(currentMission)) {\n return {\n kind: 'halted',\n status: currentMission.status,\n reason: currentMission.pauseReason ?? 'Mission stop requested',\n }\n }\n if (currentMission.status !== 'running') {\n return {\n kind: 'halted',\n status: currentMission.status,\n reason: currentMission.pauseReason,\n }\n }\n\n const index = currentMission.cursor\n if (index >= currentMission.plan.length) break\n const step = currentMission.plan[index]\n if (!step) break\n\n const haltReason = await planOptions.beforeStep?.(currentMission, step)\n if (haltReason) {\n const paused = await pauseMission(missionId, haltReason)\n if (!paused.succeeded) throw new MissionConcurrencyError(paused.error)\n return { kind: 'halted', status: paused.value.status, reason: paused.value.pauseReason ?? haltReason }\n }\n\n if (step.status !== 'done') {\n const budget = await enforceBudget(currentMission, step)\n if (budget.kind === 'halted') {\n return { kind: 'halted', status: budget.status, reason: budget.reason }\n }\n const gate = await enforceStepGate(currentMission, step)\n if (gate.kind === 'halted') {\n return { kind: 'halted', status: gate.status, reason: gate.reason }\n }\n }\n\n const outcome = await runStepFn(step, index)\n if (outcome.kind === 'failed' && outcome.fatal) {\n // The owner flips the mission row to `failed` on this outcome; emit the\n // live terminal event here so a watcher sees the failure without\n // waiting for the next loader poll. Idempotent at the reducer.\n safeEmit(sink, {\n type: 'mission.completed',\n missionId,\n at: Date.now(),\n ok: false,\n summary: `Step ${step.id} failed: ${outcome.error}`,\n })\n return { kind: 'failed', failedStepId: step.id, error: outcome.error }\n }\n if (outcome.kind === 'failed' && !outcome.fatal) {\n const advanced = await service.advanceCursor(missionId)\n if (!advanced.succeeded) throw new MissionConcurrencyError(advanced.error)\n }\n if (outcome.kind === 'in_progress') {\n return {\n kind: 'in_progress',\n stepId: step.id,\n sessionRef: outcome.sessionRef,\n pollAfterMs: outcome.pollAfterMs,\n ...(outcome.sublabel === undefined ? {} : { sublabel: outcome.sublabel }),\n }\n }\n // Cursor-skips fall through: another pass already moved past it. The next\n // loop re-reads the authoritative cursor and plan.\n }\n\n const finalMission = await service.getMission(missionId)\n const planLength = finalMission?.plan.length ?? 0\n const summary = `Completed ${planLength} step${planLength === 1 ? '' : 's'}`\n const completed = await service.complete(missionId, { ok: true, summary })\n if (!completed.succeeded) {\n // complete() is guarded; a concurrent terminal transition (abort/cancel)\n // wins the race. Re-read to report the real terminal status rather than\n // claim a completion that did not land.\n const after = await service.getMission(missionId)\n if (after && isMissionTerminal(after.status)) {\n safeEmit(sink, terminalMissionEvent(missionId, after.status))\n return { kind: 'terminal', status: after.status }\n }\n throw new MissionConcurrencyError(completed.error)\n }\n safeEmit(sink, { type: 'mission.completed', missionId, at: Date.now(), ok: true, summary })\n return { kind: 'completed', summary }\n }\n\n return { runStep, runPlan, recordCost, pauseMission }\n}\n","/**\n * Parsing for the agent-authored `:::mission` block — the bridge from a chat\n * prompt contract to the engine's MissionStep[] shape. The block format:\n *\n * :::mission\n * title: <mission title>\n * <id>: <kind> | <intent>\n * :::\n *\n * The allowed kind vocabulary is a PARAMETER — products pass their own list to\n * match their prompt directive; {@link DEFAULT_MISSION_STEP_KINDS} is the\n * default. Kinds label intent for gating and UX; they never select a different\n * execution path.\n */\n\nimport type { MissionStep } from './service'\n\n/** Default step-kind vocabulary. `best-effort` matches the engine's default\n * non-fatal kind (a failure does not abort the mission); the rest are\n * fatal-on-failure agent sub-tasks. */\nexport const DEFAULT_MISSION_STEP_KINDS: readonly string[] = [\n 'research',\n 'generate',\n 'analyze',\n 'write',\n 'best-effort',\n]\n\n/** Define the structure representing a parsed mission step with id, kind, and intent fields */\nexport interface ParsedMissionStep {\n id: string\n kind: string\n intent: string\n}\n\n/** Describe a mission with a title and an ordered list of parsed steps */\nexport interface ParsedMission {\n title: string\n steps: ParsedMissionStep[]\n}\n\n/** Define options to specify allowed lowercase step kinds for parsing mission blocks */\nexport interface ParseMissionBlocksOptions {\n /** Allowed step kinds (lowercase). Default {@link DEFAULT_MISSION_STEP_KINDS}. */\n kinds?: readonly string[]\n}\n\n/**\n * Parse every well-formed `:::mission` block. A block without a title or\n * without at least one valid step yields nothing (it is malformed — never\n * guess a plan from loose prose). Unknown kinds and malformed step lines are\n * dropped; an empty result lets the caller skip the block rather than start an\n * empty mission.\n */\nexport function parseMissionBlocks(\n fullContent: string,\n options: ParseMissionBlocksOptions = {},\n): ParsedMission[] {\n const kinds = new Set(options.kinds ?? DEFAULT_MISSION_STEP_KINDS)\n const missionRegex = /:::mission\\s*\\n([\\s\\S]*?)\\n\\s*:::/g\n const missions: ParsedMission[] = []\n let match\n while ((match = missionRegex.exec(fullContent)) !== null) {\n const body = match[1]\n if (body === undefined) continue\n const parsed = parseMissionBody(body, kinds)\n if (parsed) missions.push(parsed)\n }\n return missions\n}\n\nfunction parseMissionBody(body: string, kinds: ReadonlySet<string>): ParsedMission | null {\n let title: string | null = null\n const steps: ParsedMissionStep[] = []\n for (const rawLine of body.split('\\n')) {\n const line = rawLine.trim()\n if (!line) continue\n const titleMatch = /^title\\s*:\\s*(.+)$/i.exec(line)\n if (titleMatch?.[1] !== undefined) {\n if (title === null) title = titleMatch[1].trim()\n continue\n }\n // `<id>: <kind> | <intent>`\n const stepMatch = /^([A-Za-z0-9][A-Za-z0-9_-]*)\\s*:\\s*([A-Za-z-]+)\\s*\\|\\s*(.+)$/.exec(line)\n if (!stepMatch) continue\n const id = stepMatch[1]?.trim()\n const kind = stepMatch[2]?.trim().toLowerCase()\n const intent = stepMatch[3]?.trim()\n if (!id || !kind || !intent) continue\n if (!kinds.has(kind)) continue\n steps.push({ id, kind, intent })\n }\n if (!title || steps.length === 0) return null\n return { title, steps }\n}\n\n/**\n * Materialize parsed steps into the engine's MissionStep[] shape. Rejects a\n * duplicate step id (fail loud — the owner keys its durable step cache by\n * step id and `createMission` rejects duplicates anyway; catching it here\n * gives a clearer diagnostic). Every step starts `pending` with zero attempts.\n */\nexport function buildAgentMissionPlan(steps: ParsedMissionStep[]): MissionStep[] {\n if (steps.length === 0) throw new Error('mission plan must have at least one step')\n const seen = new Set<string>()\n for (const step of steps) {\n if (seen.has(step.id)) {\n throw new Error(`duplicate mission step id \"${step.id}\" — step ids must be unique`)\n }\n seen.add(step.id)\n }\n return steps.map((step) => ({\n id: step.id,\n intent: step.intent,\n kind: step.kind,\n status: 'pending' as const,\n attempts: 0,\n }))\n}\n","/**\n * Canonical shape for the per-step agent-activity lane — the delegated runs\n * (delegation MCP registry entries) a mission step spawned, journaled onto the\n * step so a live UI can render \"what the agent is actually doing\" under the\n * step row.\n *\n * CLIENT-SAFE, like everything in `./events`: pure data + a validator. The\n * server attaches `StepAgentActivity[]` when a step settles (and live via\n * `step.updated`); the client re-validates with {@link stepAgentActivity}\n * because the value rides loader/JSON boundaries as untyped metadata.\n */\n\nexport interface StepAgentActivity {\n taskId: string\n /** Delegation profile: coder | researcher | ui-auditor | product-defined. */\n tool: string\n status: string\n /** Title-ish excerpt of the delegation's args (goal / question / audit root). */\n detail: string\n costUsd?: number\n durationMs?: number\n /** ISO timestamp the delegation started. */\n startedAt: string\n /** Live loop progress (DelegationProgress.iteration) while the run is in flight. */\n iteration?: number\n /** Live loop progress (DelegationProgress.phase) while the run is in flight. */\n phase?: string\n /** 32-hex trace id when the delegation joined a mission trace (see `/trace`). */\n traceId?: string\n /** 16-hex span id of the delegation's span inside that trace. */\n spanId?: string\n}\n\n/** Step state extended with the activity lane a loader/seed route attaches. */\nexport type WithAgentActivity<Step> = Step & { agentActivity?: StepAgentActivity[] }\n\n/**\n * Re-validate an `agentActivity` lane that crossed a JSON boundary. Entries\n * missing any required field are dropped (a torn row must not crash the lane);\n * optional fields are kept only when well-typed.\n */\nexport function stepAgentActivity(step: object): StepAgentActivity[] {\n const value = (step as { agentActivity?: unknown }).agentActivity\n if (!Array.isArray(value)) return []\n const items: StepAgentActivity[] = []\n for (const entry of value) {\n if (!entry || typeof entry !== 'object') continue\n const record = entry as Record<string, unknown>\n if (\n typeof record.taskId !== 'string' ||\n typeof record.tool !== 'string' ||\n typeof record.status !== 'string' ||\n typeof record.detail !== 'string' ||\n typeof record.startedAt !== 'string'\n ) {\n continue\n }\n items.push({\n taskId: record.taskId,\n tool: record.tool,\n status: record.status,\n detail: record.detail,\n startedAt: record.startedAt,\n ...(typeof record.costUsd === 'number' && Number.isFinite(record.costUsd) ? { costUsd: record.costUsd } : {}),\n ...(typeof record.durationMs === 'number' && Number.isFinite(record.durationMs) ? { durationMs: record.durationMs } : {}),\n ...(typeof record.iteration === 'number' && Number.isFinite(record.iteration) ? { iteration: record.iteration } : {}),\n ...(typeof record.phase === 'string' ? { phase: record.phase } : {}),\n ...(typeof record.traceId === 'string' ? { traceId: record.traceId } : {}),\n ...(typeof record.spanId === 'string' ? { spanId: record.spanId } : {}),\n })\n }\n return items\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/web/index.ts"],"sourcesContent":["/**\n * Web-boundary utilities every agent app's routes hand-roll: JSON body parsing\n * + narrowing, request-context extraction (real client IP behind Cloudflare),\n * a KV-backed sliding-window rate limiter, and security response headers. Pure\n * mechanism — no DB, no domain. The KV is a structural interface so this needs\n * no `@cloudflare/workers-types` dependency.\n */\n\nexport type JsonObject = Record<string, unknown>\n\n/** Parse + object-narrow a Request body. `[body, null]` on success, `[null,\n * errorResponse]` on a non-object body (callers `if (err) return err`). */\nexport async function parseJsonObjectBody(request: Request): Promise<[JsonObject, null] | [null, Response]> {\n let raw: unknown\n try {\n raw = await request.json()\n } catch {\n return [null, Response.json({ error: 'Invalid JSON body' }, { status: 400 })]\n }\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n return [null, Response.json({ error: 'Body must be a JSON object' }, { status: 400 })]\n }\n return [raw as JsonObject, null]\n}\n\n/** Narrow one required string field, 400 if missing/empty. */\nexport function requireString(body: JsonObject, field: string): string | Response {\n const v = body[field]\n if (typeof v !== 'string' || v.length === 0) {\n return Response.json({ error: `Missing or non-string field: ${field}` }, { status: 400 })\n }\n return v\n}\n\n/** Define the context of a request including IP address, user agent, timestamp, and request ID */\nexport interface RequestContext {\n ipAddress: string\n userAgent: string\n timestamp: string\n requestId: string\n}\n\n/** Extract request context for audit trails. Uses `CF-Connecting-IP` for the\n * real client IP behind Cloudflare. */\nexport function extractRequestContext(request: Request): RequestContext {\n const ipAddress =\n request.headers.get('CF-Connecting-IP') ??\n request.headers.get('X-Forwarded-For')?.split(',')[0]?.trim() ??\n '0.0.0.0'\n return {\n ipAddress,\n userAgent: request.headers.get('User-Agent') ?? '',\n timestamp: new Date().toISOString(),\n requestId: crypto.randomUUID(),\n }\n}\n\n/** Minimal KV contract (Cloudflare `KVNamespace` satisfies it structurally). */\nexport interface KvLike {\n get(key: string): Promise<string | null>\n put(key: string, value: string, options?: { expirationTtl?: number }): Promise<void>\n}\n\n/** Describe the outcome of a rate limit check including allowance, remaining count, and reset time */\nexport interface RateLimitResult {\n allowed: boolean\n remaining: number\n resetAt: number\n}\n\n/** KV-backed sliding-window rate limit. Stores recent timestamps per key,\n * prunes the window, allows until `limit` is hit.\n *\n * Read-modify-write is best-effort, NOT atomic: KV has no compare-and-swap, so\n * two requests racing on the same key can each read the same pre-state and both\n * write — a concurrent burst can momentarily admit up to one extra request per\n * racing writer. This is acceptable for coarse abuse limiting; it is NOT a hard\n * quota gate.\n *\n * Fail-CLOSED on unreadable state: corrupt/non-array KV (a poisoned or\n * truncated value) is treated as a full window, so a tampered key cannot reset\n * the count and bypass the limiter. A bare `JSON.parse` here would throw and\n * abort the request handler, silently disabling the limit (fail-open). */\nexport async function checkRateLimit(kv: KvLike, key: string, limit: number, windowSeconds: number): Promise<RateLimitResult> {\n const now = Math.floor(Date.now() / 1000)\n const windowStart = now - windowSeconds\n const kvKey = `rl:${key}`\n const raw = await kv.get(kvKey)\n const parsed = parseRateLimitState(raw)\n if (parsed === POISONED_STATE) {\n // Unreadable state (parse threw or value is not a JSON array): deny rather\n // than reset the window to empty. A poisoned key must not become a bypass.\n return { allowed: false, remaining: 0, resetAt: now + windowSeconds }\n }\n const valid = parsed.filter((t) => t > windowStart)\n if (valid.length >= limit) return { allowed: false, remaining: 0, resetAt: (valid[0] ?? now) + windowSeconds }\n valid.push(now)\n await kv.put(kvKey, JSON.stringify(valid), { expirationTtl: windowSeconds * 2 })\n return { allowed: true, remaining: limit - valid.length, resetAt: now + windowSeconds }\n}\n\n/** Sentinel returned by `parseRateLimitState` when the stored value cannot be\n * read as a timestamp array — distinct from an empty window so the limiter can\n * fail closed instead of treating corruption as a fresh window. */\nconst POISONED_STATE = Symbol('rate-limit-poisoned-state')\n\n/** Parse stored rate-limit state into a timestamp array. Absent state is a\n * fresh (empty) window. A value that fails to parse, or parses to a non-array,\n * returns `POISONED_STATE`; numeric junk inside a valid array is dropped. */\nfunction parseRateLimitState(raw: string | null): number[] | typeof POISONED_STATE {\n if (raw === null) return []\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch {\n return POISONED_STATE\n }\n if (!Array.isArray(parsed)) return POISONED_STATE\n return parsed.filter((t): t is number => typeof t === 'number' && Number.isFinite(t))\n}\n\n/** Define options for configuring cookie attributes and behavior */\nexport interface CookieOptions {\n name: string\n /** Default '/'. */\n path?: string\n /** Default true. */\n httpOnly?: boolean\n /** Adds the `Secure` attribute. Default false. */\n secure?: boolean\n /** Default 'Lax'. */\n sameSite?: 'Lax' | 'Strict' | 'None'\n maxAgeSeconds?: number\n}\n\n/** Serialize a Set-Cookie header value: `name=encodeURIComponent(value)` plus\n * attributes in Path / HttpOnly / SameSite / Max-Age / Secure order.\n * Throws on `SameSite=None` without `secure` — browsers silently drop that\n * combination, which would otherwise fail invisibly. */\nexport function serializeCookie(value: string, opts: CookieOptions): string {\n if (opts.sameSite === 'None' && !opts.secure) {\n throw new Error('SameSite=None cookies require secure: true (browsers reject them otherwise)')\n }\n const parts = [`${opts.name}=${encodeURIComponent(value)}`, `Path=${opts.path ?? '/'}`]\n if (opts.httpOnly !== false) parts.push('HttpOnly')\n parts.push(`SameSite=${opts.sameSite ?? 'Lax'}`)\n if (opts.maxAgeSeconds !== undefined) parts.push(`Max-Age=${opts.maxAgeSeconds}`)\n if (opts.secure) parts.push('Secure')\n return parts.join('; ')\n}\n\n/** Set-Cookie header value that deletes the cookie (empty value, Max-Age=0). */\nexport function clearCookieHeader(opts: Omit<CookieOptions, 'maxAgeSeconds'>): string {\n return serializeCookie('', { ...opts, maxAgeSeconds: 0 })\n}\n\n/** Read + decode one cookie from a Cookie request header; null when absent. */\nexport function readCookieValue(cookieHeader: string | null, name: string): string | null {\n if (!cookieHeader) return null\n for (const part of cookieHeader.split(/;\\s*/)) {\n const [cookieName, ...rest] = part.split('=')\n if (cookieName === name) {\n try {\n return decodeURIComponent(rest.join('='))\n } catch {\n return null\n }\n }\n }\n return null\n}\n\n/** Define options for configuring security-related HTTP headers including disclaimers and retention labels */\nexport interface SecurityHeaderOptions {\n /** Product disclaimer (e.g. \"AI-powered tool. Not legal advice.\"). Omitted if absent. */\n disclaimer?: string\n /** Data-retention label (e.g. \"7-years\"). Omitted if absent. */\n retention?: string\n /** Extra headers to set. */\n extra?: Record<string, string>\n}\n\n/** Set standard security headers on a response (HSTS, nosniff, frame-options,\n * referrer-policy, XSS) + optional product disclaimer/retention. The security\n * set is generic; the disclaimer/retention are the product's. */\nexport function addSecurityHeaders(response: Response, opts: SecurityHeaderOptions = {}): Response {\n response.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload')\n response.headers.set('X-Content-Type-Options', 'nosniff')\n response.headers.set('X-Frame-Options', 'SAMEORIGIN')\n response.headers.set('Referrer-Policy', 'same-origin')\n response.headers.set('X-XSS-Protection', '1; mode=block')\n if (opts.disclaimer) response.headers.set('X-AI-Disclaimer', opts.disclaimer)\n if (opts.retention) response.headers.set('X-Data-Retention', opts.retention)\n for (const [k, v] of Object.entries(opts.extra ?? {})) response.headers.set(k, v)\n return response\n}\n\n/** Local-sandbox / inline schemes a stored media reference must never use.\n * Reachable from neither a browser nor the product worker, and a `file:`/`data:`\n * url is the tell of an agent substituting local ffmpeg output for a real\n * provider artifact. `blob:` and `javascript:` are inert/active client schemes\n * with no server reachability. */\nconst REJECTED_MEDIA_SCHEMES = ['file:', 'data:', 'blob:', 'javascript:', 'vbscript:'] as const\n\n/**\n * Canonical media-reference boundary shared by every surface that persists a\n * media url (sequences clips, design-canvas image/video src). The ONE rule:\n * remote `http(s)` or a rooted `/api/` path are allowed; everything else is\n * rejected, with a named reason for known-bad local/inline schemes so the\n * thrown message is actionable for an LLM planner. The url is trimmed before\n * the scheme check so leading whitespace cannot smuggle a rejected scheme past\n * a naive `startsWith`.\n *\n * @param what - noun for the error message (e.g. 'media url', 'src').\n */\nexport function assertMediaUrl(url: string, what = 'media url'): void {\n const trimmed = url.trim()\n if (/^https?:\\/\\//i.test(trimmed)) return\n if (trimmed.startsWith('/api/')) return\n const shown = trimmed.length > 96 ? `${trimmed.slice(0, 96)}…` : trimmed\n const lower = trimmed.toLowerCase()\n if (\n REJECTED_MEDIA_SCHEMES.some((scheme) => lower.startsWith(scheme)) ||\n lower.startsWith('/tmp/') ||\n lower.startsWith('/home/')\n ) {\n throw new Error(`${what} must reference a provider http(s) URL or a rooted /api/ path, not a local sandbox file (${shown})`)\n }\n throw new Error(`${what} must be http(s) or a rooted /api/ path (${shown})`)\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// src/design-canvas-react/lazy.tsx
|
|
2
2
|
import { lazy } from "react";
|
|
3
3
|
var DesignCanvasLazy = lazy(
|
|
4
|
-
() => import("./DesignCanvasEditor-
|
|
4
|
+
() => import("./DesignCanvasEditor-FJIVYOA2.js").then((m) => ({ default: m.DesignCanvasEditor }))
|
|
5
5
|
);
|
|
6
6
|
var DesignCanvasChromeLazy = lazy(
|
|
7
|
-
() => import("./DesignCanvas-
|
|
7
|
+
() => import("./DesignCanvas-UNL34LS2.js").then((m) => ({ default: m.DesignCanvas }))
|
|
8
8
|
);
|
|
9
9
|
|
|
10
10
|
export {
|
|
11
11
|
DesignCanvasLazy,
|
|
12
12
|
DesignCanvasChromeLazy
|
|
13
13
|
};
|
|
14
|
-
//# sourceMappingURL=chunk-
|
|
14
|
+
//# sourceMappingURL=chunk-ROLFDOMJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/theme-contract/index.ts"],"sourcesContent":["/**\n * Exportable theme-token contract checker — the incident guard for the\n * invisible-popover class of bugs.\n *\n * The failure mode (tax-agent's transparent model dropdown; the whole\n * `bg-surface-container-*` family): a consumer app ships a component that\n * references a theme token — either as `var(--popover)` or as a Tailwind class\n * like `bg-surface-container-high` that the agent-app preset maps to\n * `hsl(var(--popover))` — but the app's OWN build never emits that custom\n * property (it forgot `import '@tangle-network/agent-app/styles'`, or dropped a\n * token in its local tokens.css). CSS resolves the missing var to nothing, the\n * surface paints transparent, and NOTHING errors. It ships invisible.\n *\n * `tests/theme/tokens-contract.test.ts` guards agent-app's OWN components. This\n * module lifts that walking logic into a function every CONSUMER app can run\n * against ITS OWN source in CI, comparing references to the tokens.css agent-app\n * ships plus any extra CSS the app defines.\n *\n * ── What each check covers (scope is deliberately honest) ────────────────────\n *\n * 1. var(--…) check — COMPLETE. Every `var(--name)` literal in the scanned\n * source (inline styles, `bg-[var(--name)]` arbitrary Tailwind values, CSS\n * template strings) is matched and compared against the defined token set.\n * This is exact: a `var(--x)` reference is unambiguous. It is a raw-text\n * scan (no AST), so a `var(--x)` written inside a comment or string literal\n * counts too — deliberate: it keeps the single-source logic identical to the\n * agent-app self-test, and a dangling `var(--x)` in a comment is a smell\n * worth surfacing. Suppress a deliberate one with `allowlist`.\n *\n * 2. Tailwind-utility check — INTENTIONALLY PARTIAL. Bare classes like\n * `bg-card` carry no `var(--)` and so are invisible to check 1; Tailwind\n * resolves them to `hsl(var(--card))` at build via the preset. Fully\n * resolving arbitrary Tailwind config is out of scope (it would mean\n * re-implementing Tailwind). Instead we check the SPECIFIC known-dangerous\n * families that have actually shipped invisible: the MD3 surface ladder\n * (`surface-container` / `-high` / `-highest`) and the `card` / `popover`\n * elevation pairs — exactly the utilities the agent-app tailwind-preset\n * registers onto elevation tokens (see src/theme/tailwind-preset.ts, the\n * source of truth for this mapping). The canvas/sequence aliases\n * (`--bg-input`, `--text-primary`, …) are consumed as `bg-[var(--…)]`\n * arbitrary values and so are already covered fully by check 1 — they need\n * no entry here.\n *\n * Node-only (reads the filesystem) → this lives in the `./theme-contract`\n * subpath, NOT `./theme`, which must stay browser-clean (it's in the\n * browser-safe manifest test).\n */\n\nimport { type Dirent, existsSync, readFileSync, readdirSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/** Define options for scanning source directories and CSS token files in a theme contract */\nexport interface ThemeContractOptions {\n /** Consumer source directories to scan for token references (recursively). */\n srcDirs: string[]\n /**\n * Path to the base tokens.css whose `--name:` definitions are the ground\n * truth. Defaults to the tokens.css agent-app ships (`./styles`) — the set a\n * consumer gets from `import '@tangle-network/agent-app/styles'`.\n */\n tokensCss?: string\n /**\n * Additional CSS files whose `--name:` definitions also count as defined —\n * the app's own overrides/extensions layered on top of the base tokens.\n */\n extraTokensCss?: string[]\n /**\n * Token names (e.g. `--my-app-accent`) to treat as always-defined, suppressing\n * them from the missing list. For app-specific vars defined outside any CSS\n * the checker can see (injected at runtime, from a third-party stylesheet, …).\n */\n allowlist?: string[]\n}\n\n/** Describe a missing theme contract variable and where it was referenced */\nexport interface ThemeContractMiss {\n /** The undefined custom property, e.g. `--popover`. */\n varName: string\n /**\n * Where it was referenced: `path/to/file.tsx`, or\n * `path/to/file.tsx (via bg-surface-container-high)` when the reference is a\n * Tailwind utility that resolves to the token rather than a literal var().\n */\n referencedIn: string\n}\n\n/** Describe the result of validating a theme contract including success status and missing items */\nexport interface ThemeContractResult {\n ok: boolean\n missing: ThemeContractMiss[]\n}\n\n/** Source extensions scanned for token references. */\nconst SOURCE_RE = /\\.(ts|tsx|js|jsx|mjs|cjs)$/\nconst SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', 'coverage'])\n\n/**\n * Known-dangerous Tailwind utility families and the elevation token each\n * resolves to, mirroring src/theme/tailwind-preset.ts. Ordered longest-suffix\n * first so `surface-container-highest` is matched before `surface-container`.\n * The negative look-around in {@link buildUtilityRe} makes ordering belt-and-\n * suspenders rather than load-bearing.\n */\nconst DANGEROUS_UTILITIES: ReadonlyArray<{ suffix: string; varName: string }> = [\n { suffix: 'surface-container-highest', varName: '--secondary' },\n { suffix: 'surface-container-high', varName: '--popover' },\n { suffix: 'surface-container', varName: '--card' },\n { suffix: 'card-foreground', varName: '--card-foreground' },\n { suffix: 'popover-foreground', varName: '--popover-foreground' },\n { suffix: 'card', varName: '--card' },\n { suffix: 'popover', varName: '--popover' },\n]\n\n/** Tailwind color-utility prefixes that can carry a background/text/border color. */\nconst UTILITY_PREFIXES = 'bg|text|border|ring|fill|stroke'\n\n/**\n * Match a whole utility class for `suffix`, tolerant of variants (`hover:`,\n * `dark:`) and opacity (`/95`) but not of longer siblings: the trailing\n * `(?![\\w-])` stops `bg-surface-container` from matching inside\n * `bg-surface-container-high`, and `bg-card` from matching inside\n * `bg-card-foreground`.\n */\nfunction buildUtilityRe(suffix: string): RegExp {\n return new RegExp(`(?<![\\\\w-])(?:${UTILITY_PREFIXES})-${suffix}(?![\\\\w-])`, 'g')\n}\n\n/** Recursively collect scannable source files under a directory. */\nfunction walkSources(dir: string): string[] {\n let entries: Dirent[]\n try {\n entries = readdirSync(dir, { withFileTypes: true })\n } catch {\n return []\n }\n return entries.flatMap((e) => {\n if (e.isDirectory()) return SKIP_DIRS.has(e.name) ? [] : walkSources(join(dir, e.name))\n return SOURCE_RE.test(e.name) && !e.name.endsWith('.d.ts') ? [join(dir, e.name)] : []\n })\n}\n\n/**\n * Every `--name:` DEFINITION across the given CSS files. A definition is\n * `--name:` at the start of a (trimmed) line; RHS references like\n * `hsl(var(--card))` are mid-line and are never counted as definitions.\n */\nfunction definedVars(cssFiles: string[]): Set<string> {\n const defs = new Set<string>()\n for (const file of cssFiles) {\n let css: string\n try {\n css = readFileSync(file, 'utf8')\n } catch {\n continue\n }\n for (const m of css.matchAll(/^\\s*(--[a-z0-9-]+)\\s*:/gim)) if (m[1]) defs.add(m[1])\n }\n return defs\n}\n\n/**\n * Default tokens.css: the one agent-app ships as `./styles`. Resolved relative\n * to this module's URL, but tolerant of where the bundler lands the running\n * code — tsup code-splits shared logic into a chunk at the dist ROOT, so the\n * tokens.css sits one directory DIFFERENTLY depending on layout:\n * - source (src/theme-contract/index.ts) → ../theme/tokens.css (src/theme)\n * - split chunk (dist/contract-*.js) → ./theme/tokens.css (dist/theme)\n * - unsplit entry (dist/theme-contract/index.js) → ../theme/tokens.css\n * Probe both and return the one that exists; fall back to the first for a\n * sensible error path if neither is present.\n */\nfunction defaultTokensCss(): string {\n const candidates = ['../theme/tokens.css', './theme/tokens.css'].map((rel) =>\n fileURLToPath(new URL(rel, import.meta.url)),\n )\n return candidates.find((p) => existsSync(p)) ?? candidates[0]!\n}\n\n/**\n * Check that every theme token a consumer's source references is actually\n * defined in the CSS that consumer ships. Returns the full missing set; the\n * caller decides how to fail (the bin exits non-zero on any miss).\n */\nexport function checkThemeContract(opts: ThemeContractOptions): ThemeContractResult {\n const tokensCss = opts.tokensCss ?? defaultTokensCss()\n const defined = definedVars([tokensCss, ...(opts.extraTokensCss ?? [])])\n const allow = new Set(opts.allowlist ?? [])\n const isDefined = (name: string) => defined.has(name) || allow.has(name)\n\n const files = opts.srcDirs.flatMap(walkSources)\n const utilityMatchers = DANGEROUS_UTILITIES.map((u) => ({ ...u, re: buildUtilityRe(u.suffix) }))\n\n // Dedupe by varName (literal check) and by varName+utility (utility check),\n // keeping the FIRST referencing file — enough to locate the offender without\n // drowning the report when one token is referenced across many files.\n const seenVar = new Map<string, string>()\n const seenUtility = new Map<string, string>()\n\n for (const file of files) {\n let text: string\n try {\n text = readFileSync(file, 'utf8')\n } catch {\n continue\n }\n const where = displayPath(file)\n\n // Check 1 — literal var(--…) references.\n for (const m of text.matchAll(/var\\(\\s*(--[a-z0-9-]+)/gi)) {\n const name = m[1]\n if (!name || isDefined(name) || seenVar.has(name)) continue\n seenVar.set(name, where)\n }\n\n // Check 2 — known-dangerous Tailwind utility classes.\n for (const u of utilityMatchers) {\n if (isDefined(u.varName)) continue\n const key = `${u.varName}::${u.suffix}`\n if (seenUtility.has(key)) continue\n u.re.lastIndex = 0\n if (u.re.test(text)) seenUtility.set(key, `${where} (via ${firstUtilityHit(text, u.suffix)})`)\n }\n }\n\n const missing: ThemeContractMiss[] = [\n ...[...seenVar].map(([varName, referencedIn]) => ({ varName, referencedIn })),\n ...[...seenUtility].map(([key, referencedIn]) => ({ varName: key.split('::')[0]!, referencedIn })),\n ]\n return { ok: missing.length === 0, missing }\n}\n\n/** The literal utility class (with prefix) first seen in `text` for `suffix`, for the report. */\nfunction firstUtilityHit(text: string, suffix: string): string {\n const m = buildUtilityRe(suffix).exec(text)\n return m?.[0] ?? `<utility>-${suffix}`\n}\n\n/** Path relative to cwd when it stays inside it, else the path as given — for readable reports. */\nfunction displayPath(file: string): string {\n const rel = relative(process.cwd(), file)\n return rel && !rel.startsWith('..') ? rel : file\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
hasWorkspaceRole
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6XIAPIW6.js";
|
|
4
4
|
|
|
5
5
|
// src/teams-react/components/MembersPanel.tsx
|
|
6
6
|
import { useState } from "react";
|
|
@@ -160,4 +160,4 @@ function MemberRow({ member, canManage, onChangeRole, onRemove }) {
|
|
|
160
160
|
export {
|
|
161
161
|
MembersPanel
|
|
162
162
|
};
|
|
163
|
-
//# sourceMappingURL=chunk-
|
|
163
|
+
//# sourceMappingURL=chunk-S564OFTL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/model-catalog.ts"],"sourcesContent":["/**\n * Model catalogue — computed live from the Tangle Router, never hand-curated.\n * Lifted from tuner-agent so every agent app's model picker shares one\n * filter/dedupe/rank/feature pipeline instead of re-deriving it.\n *\n * The router's /models endpoint returns every routeable model (~200), which is\n * unusable as a picker list: it mixes chat models with TTS/embedding/realtime\n * endpoints, dated snapshots alias their parents, and provider-prefixed ids\n * duplicate canonical ones. This module turns that into a product catalogue:\n *\n * filter (chat-capable, routeable) → dedupe (snapshot/prefix/:free aliases)\n * → rank (provider tier, family, version) → feature (best model per family)\n * → default (env override or first featured)\n *\n * Freshness is automatic: everything is derived from the live router response,\n * so new models surface as soon as the router lists them. The only static\n * knowledge here is slow-moving: provider display order and family name\n * patterns (e.g. \"claude-sonnet-*\", \"gpt-N\"). A new Sonnet or GPT release\n * outranks its predecessor by version comparison with zero code change; only\n * a brand-new *family name* (rare) needs a one-line rule addition.\n */\n\nexport interface RouterModel {\n id: string\n name?: string\n description?: string\n _provider?: string\n pricing?: { prompt?: string | null; completion?: string | null }\n context_length?: number\n architecture?: {\n modality?: string\n input_modalities?: string[]\n output_modalities?: string[]\n }\n supported_parameters?: string[]\n routeability?: {\n status?: string\n routeable?: boolean\n provider?: string\n }\n}\n\n/** Define the structure and capabilities of a catalog item with optional pricing and feature flags */\nexport interface CatalogModel {\n id: string\n name: string\n provider: string\n description?: string\n contextLength?: number\n pricing?: { prompt?: string; completion?: string }\n supportsTools: boolean\n supportsReasoning: boolean\n featured: boolean\n}\n\n/** Define a catalog containing models with a default ID and fetch timestamp */\nexport interface ModelCatalog {\n defaultModelId: string | null\n fetchedAt: string\n models: CatalogModel[]\n}\n\n/** Display order. Unlisted providers sort after these, alphabetically. */\nconst PROVIDER_TIER: string[] = [\n 'anthropic',\n 'openai',\n 'google',\n 'xai',\n 'deepseek',\n 'moonshotai',\n 'moonshot',\n 'zai',\n 'z-ai',\n 'mistral',\n 'groq',\n 'nvidia',\n 'cohere',\n 'cerebras',\n]\n\n/** Non-chat endpoints that pollute the router list (matched on normalized id). */\nconst EXCLUDED_ID = /(embedding|tts|transcribe|whisper|audio|realtime|image|lyria|sora|dall-e|moderation|content-safety|search-preview|search-api|deep-research)/\n\n/**\n * Featured families, in display order. Each rule surfaces the highest-version\n * routeable model whose normalized id matches. Patterns anchor on the family\n * name and stop before specialty suffixes (codex, nano, lite, …) so the\n * mainline model wins.\n */\nconst FEATURED_RULES: Array<{ providers: string[]; match: RegExp }> = [\n { providers: ['anthropic'], match: /^claude-sonnet-[\\d-]+$/ },\n { providers: ['anthropic'], match: /^claude-opus-[\\d-]+$/ },\n { providers: ['anthropic'], match: /^claude-haiku-[\\d-]+$/ },\n { providers: ['openai'], match: /^gpt-\\d+(\\.\\d+)?$/ },\n { providers: ['openai'], match: /^gpt-\\d+(\\.\\d+)?-mini$/ },\n { providers: ['google'], match: /^gemini-[\\d.]+-pro(-preview)?$/ },\n { providers: ['google'], match: /^gemini-[\\d.]+-flash(-preview)?$/ },\n { providers: ['xai'], match: /^grok-[\\d.]+$/ },\n { providers: ['deepseek'], match: /^deepseek-(chat|v[\\d.]+(-\\w+)?)$/ },\n { providers: ['moonshotai', 'moonshot'], match: /^kimi-k[\\d.]+$/ },\n { providers: ['zai', 'z-ai'], match: /^glm-[\\d.]+$/ },\n { providers: ['mistral'], match: /^mistral-(large|medium)-?[\\d.-]*$/ },\n]\n\n/** Families known to support tool calls even when router metadata omits it\n * (dated snapshots often lack the supported_parameters of their parent). */\nconst TOOL_CAPABLE_FAMILY = /^(claude|gpt-[45]|gpt-oss|o[134]|gemini|grok|deepseek|glm|kimi|mistral|ministral|magistral|command|nemotron|llama)/\n\n/** Strip provider prefix, :free suffix, and trailing date stamps. */\nexport function normalizeModelId(id: string): string {\n let tail = id.split('/').pop() ?? id\n tail = tail.replace(/:free$/, '')\n tail = tail.replace(/-\\d{8}$/, '')\n tail = tail.replace(/-\\d{4}-\\d{2}-\\d{2}$/, '')\n return tail\n}\n\n/** All numeric groups in a normalized id, for version comparison. */\nfunction versionOf(normId: string): number[] {\n return (normId.match(/\\d+/g) ?? []).map(Number)\n}\n\nfunction compareVersions(a: number[], b: number[]): number {\n const len = Math.max(a.length, b.length)\n for (let i = 0; i < len; i++) {\n const d = (a[i] ?? -1) - (b[i] ?? -1)\n if (d !== 0) return d\n }\n return 0\n}\n\n/** Lower = preferred representative for an alias group. */\nfunction aliasPenalty(id: string): number {\n let p = 0\n if (id.includes('/')) p += 4\n if (/-\\d{8}$|-\\d{4}-\\d{2}-\\d{2}$/.test(id.replace(/:free$/, ''))) p += 2\n if (id.endsWith(':free')) p += 1\n return p\n}\n\nfunction providerRank(provider: string): number {\n const i = PROVIDER_TIER.indexOf(provider)\n return i === -1 ? PROVIDER_TIER.length : i\n}\n\nfunction isChatModel(m: RouterModel): boolean {\n const arch = m.architecture\n if (!arch?.input_modalities || !arch?.output_modalities) return true\n return arch.input_modalities.includes('text') && arch.output_modalities.includes('text')\n}\n\nfunction isRouteable(m: RouterModel): boolean {\n return m.routeability?.routeable !== false && m.routeability?.status !== 'unavailable'\n}\n\nfunction familyOf(normId: string): string {\n return normId.replace(/[\\d.]+/g, '').replace(/-+/g, '-').replace(/-$/, '')\n}\n\n/**\n * Pure catalogue pipeline. `preferredDefault` (typically the MODEL_NAME env\n * var) wins when it survives filtering; otherwise the first featured model.\n */\nexport function buildCatalog(raw: RouterModel[], opts?: { preferredDefault?: string }): ModelCatalog {\n // Filter to chat-capable, routeable, non-specialty models\n const candidates = raw.filter(\n (m) => m.id && isRouteable(m) && isChatModel(m) && !EXCLUDED_ID.test(normalizeModelId(m.id)),\n )\n\n // Dedupe alias groups (dated snapshots, provider prefixes, :free variants).\n // Within a group, merge metadata so the representative keeps the richest\n // supported_parameters claim (snapshots often omit what the parent lists).\n const groups = new Map<string, RouterModel[]>()\n for (const m of candidates) {\n const key = `${m._provider ?? ''}::${normalizeModelId(m.id)}`\n const g = groups.get(key)\n if (g) g.push(m)\n else groups.set(key, [m])\n }\n\n const reps: Array<{ model: RouterModel; normId: string; mergedParams: Set<string> }> = []\n for (const group of groups.values()) {\n group.sort((a, b) => aliasPenalty(a.id) - aliasPenalty(b.id) || a.id.length - b.id.length)\n const rep = group[0]!\n const mergedParams = new Set<string>(group.flatMap((m) => m.supported_parameters ?? []))\n reps.push({ model: rep, normId: normalizeModelId(rep.id), mergedParams })\n }\n\n // Featured: best version per family rule, in rule order\n const featuredIds: string[] = []\n for (const rule of FEATURED_RULES) {\n const matches = reps.filter(\n (r) =>\n rule.providers.includes(r.model._provider ?? '') &&\n rule.match.test(r.normId) &&\n !featuredIds.includes(r.model.id),\n )\n if (!matches.length) continue\n matches.sort(\n (a, b) =>\n compareVersions(versionOf(b.normId), versionOf(a.normId)) ||\n Number(a.normId.includes('preview')) - Number(b.normId.includes('preview')) ||\n a.model.id.length - b.model.id.length,\n )\n featuredIds.push(matches[0]!.model.id)\n }\n\n const toCatalogModel = (r: (typeof reps)[number]): CatalogModel => {\n const m = r.model\n const provider = m._provider ?? 'unknown'\n return {\n id: m.id,\n name: m.name ?? m.id,\n provider,\n description: m.description ? m.description.slice(0, 160) : undefined,\n contextLength: m.context_length,\n pricing:\n m.pricing?.prompt || m.pricing?.completion\n ? { prompt: m.pricing.prompt ?? undefined, completion: m.pricing.completion ?? undefined }\n : undefined,\n supportsTools: r.mergedParams.has('tools') || TOOL_CAPABLE_FAMILY.test(r.normId),\n supportsReasoning: r.mergedParams.has('reasoning') || r.mergedParams.has('include_reasoning'),\n featured: featuredIds.includes(m.id),\n }\n }\n\n // Sort: featured first (rule order), then provider tier → family → version desc\n const featured = featuredIds\n .map((id) => reps.find((r) => r.model.id === id)!)\n .map(toCatalogModel)\n const rest = reps\n .filter((r) => !featuredIds.includes(r.model.id))\n .sort((a, b) => {\n const pa = providerRank(a.model._provider ?? '')\n const pb = providerRank(b.model._provider ?? '')\n if (pa !== pb) return pa - pb\n const fa = familyOf(a.normId)\n const fb = familyOf(b.normId)\n if (fa !== fb) return fa.localeCompare(fb)\n return compareVersions(versionOf(b.normId), versionOf(a.normId)) || a.model.id.localeCompare(b.model.id)\n })\n .map(toCatalogModel)\n\n const models = [...featured, ...rest]\n\n const preferred = opts?.preferredDefault\n const defaultModelId =\n (preferred && models.find((m) => m.id === preferred || normalizeModelId(m.id) === normalizeModelId(preferred))?.id) ||\n featured.find((m) => m.supportsTools)?.id ||\n models[0]?.id ||\n null\n\n return { defaultModelId, fetchedAt: new Date().toISOString(), models }\n}\n\n// ── Cached fetch ─────────────────────────────────────────────────────────\n\nconst CATALOG_TTL_MS = 5 * 60 * 1000\n\nlet _cache: { catalog: ModelCatalog; at: number } | null = null\n\n/**\n * Fetch the router model list and build the catalogue, with an in-isolate\n * cache (TTL 5 min). On router failure a stale catalogue is served rather\n * than erroring the picker.\n */\nexport async function fetchModelCatalog(cfg: {\n baseUrl: string\n apiKey: string\n preferredDefault?: string\n}): Promise<ModelCatalog> {\n if (_cache && Date.now() - _cache.at < CATALOG_TTL_MS) {\n return _cache.catalog\n }\n try {\n const res = await fetch(`${cfg.baseUrl}/models`, {\n headers: { Authorization: `Bearer ${cfg.apiKey}` },\n })\n if (!res.ok) throw new Error(`Router /models returned ${res.status}`)\n const data = (await res.json()) as { data?: RouterModel[] }\n const catalog = buildCatalog(data.data ?? [], { preferredDefault: cfg.preferredDefault })\n _cache = { catalog, at: Date.now() }\n return catalog\n } catch (err) {\n if (_cache) return _cache.catalog\n throw err\n }\n}\n\n/** Test-only: clear the catalogue cache. */\nexport function __resetCatalogCache(): void {\n _cache = null\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/theme/theme.ts"],"sourcesContent":["/**\n * Typed mirror of tokens.css for runtime/JS theming. The canonical source is\n * tokens.css (`import '@tangle-network/agent-app/styles'`); this module is for\n * apps that compute theme variables in JS, or read color values where CSS\n * custom properties cannot reach — notably Konva canvas render code, which\n * paints to a bitmap and cannot resolve `var(--…)`.\n *\n * Values are shadcn-style HSL channel triples (\"H S% L%\"); wrap with `color()`.\n */\n\nexport interface AgentAppTheme {\n background: string\n foreground: string\n card: string\n cardForeground: string\n popover: string\n popoverForeground: string\n primary: string\n primaryForeground: string\n secondary: string\n secondaryForeground: string\n muted: string\n mutedForeground: string\n accent: string\n accentForeground: string\n destructive: string\n destructiveForeground: string\n border: string\n input: string\n ring: string\n success: string\n successForeground: string\n warning: string\n warningForeground: string\n /** Full CSS color (not a triple) — the canvas/scene backdrop. */\n canvasBackdrop: string\n /** Konva render palette — full hex colors the bitmap canvas paints with\n * (it cannot resolve `var(--…)`). NOT emitted by themeToCssVars. */\n canvasRender: CanvasRenderPalette\n}\n\n/**\n * Colors the Konva design-canvas paints directly. Konva renders to a bitmap\n * and cannot read CSS custom properties, so these are full hex strings sourced\n * from the active theme and threaded through the canvas components.\n */\nexport interface CanvasRenderPalette {\n /** Grid line color (GridLayer). */\n grid: string\n /** Grid-snap guide line (SnapGuidesOverlay, kind 'grid'). */\n snapGrid: string\n /** Saved ruler-guide snap line (kind 'guide'). */\n snapGuide: string\n /** Page edge/center snap line (kinds 'page-edge'/'page-center'). */\n snapPage: string\n /** Element edge/center snap line (kinds 'element-edge'/'element-center'). */\n snapElement: string\n /** Transformer border + anchor stroke (SelectionLayer). */\n selectionStroke: string\n /** Transformer anchor fill (SelectionLayer). */\n selectionAnchorFill: string\n /** Video placeholder fill (ElementNode VideoNode). */\n placeholderFill: string\n /** Video placeholder stroke (ElementNode VideoNode). */\n placeholderStroke: string\n /** Broken/loading image placeholder fill (ElementNode ImageNode). */\n brokenFill: string\n /** Broken/loading image placeholder stroke (ElementNode ImageNode). */\n brokenStroke: string\n}\n\n/** Define a light color theme with specific background, foreground, and accent color values */\nexport const lightTheme: AgentAppTheme = {\n background: '0 0% 100%',\n foreground: '0 0% 5%',\n card: '240 7% 97%',\n cardForeground: '0 0% 5%',\n popover: '0 0% 100%',\n popoverForeground: '0 0% 5%',\n primary: '245 62% 57%',\n primaryForeground: '0 0% 100%',\n secondary: '240 6% 93%',\n secondaryForeground: '0 0% 10%',\n muted: '240 6% 93%',\n mutedForeground: '240 5% 38%',\n accent: '240 6% 93%',\n accentForeground: '0 0% 10%',\n destructive: '0 72% 41%',\n destructiveForeground: '0 0% 100%',\n border: '240 6% 89%',\n input: '240 6% 89%',\n ring: '245 62% 57%',\n success: '160 84% 26%',\n successForeground: '0 0% 100%',\n warning: '41 96% 38%',\n warningForeground: '38 92% 12%',\n canvasBackdrop: 'hsl(240 7% 90%)',\n canvasRender: {\n grid: '#c0c0c0',\n snapGrid: '#a0a0a0',\n snapGuide: '#3b82f6',\n snapPage: '#f59e0b',\n snapElement: '#f43f5e',\n selectionStroke: '#00a1ff',\n selectionAnchorFill: '#ffffff',\n placeholderFill: '#1f2937',\n placeholderStroke: '#374151',\n brokenFill: '#e5e7eb',\n brokenStroke: '#9ca3af',\n },\n}\n\n/** Define a dark color scheme for the Agent app interface with specific background and foreground hues */\nexport const darkTheme: AgentAppTheme = {\n background: '240 8% 5%',\n foreground: '240 6% 93%',\n card: '240 5% 8%',\n cardForeground: '240 6% 93%',\n popover: '240 4% 13%',\n popoverForeground: '240 6% 93%',\n primary: '239 84% 74%',\n primaryForeground: '0 0% 100%',\n secondary: '240 5% 11%',\n secondaryForeground: '240 6% 93%',\n muted: '240 5% 11%',\n mutedForeground: '240 4% 62%',\n accent: '240 5% 11%',\n accentForeground: '240 6% 93%',\n destructive: '348 90% 68%',\n destructiveForeground: '0 0% 12%',\n border: '240 3% 13%',\n input: '240 5% 11%',\n ring: '239 84% 74%',\n success: '160 70% 52%',\n successForeground: '160 84% 10%',\n warning: '40 94% 56%',\n warningForeground: '38 92% 12%',\n canvasBackdrop: 'hsl(240 8% 5%)',\n canvasRender: {\n grid: '#3a3a3a',\n snapGrid: '#5a5a5a',\n snapGuide: '#3b82f6',\n snapPage: '#f59e0b',\n snapElement: '#f43f5e',\n selectionStroke: '#00a1ff',\n selectionAnchorFill: '#e5e7eb',\n placeholderFill: '#2a2f3a',\n placeholderStroke: '#3f4654',\n brokenFill: '#262b33',\n brokenStroke: '#4b5563',\n },\n}\n\n/** Wrap a channel triple in `hsl()`; pass through values already in a color form. */\nexport function themeColor(value: string): string {\n return value.startsWith('hsl') || value.startsWith('#') || value.startsWith('rgb')\n ? value\n : `hsl(${value})`\n}\n\n/**\n * Map a theme to the full CSS-variable set (shadcn triples + canvas/sequences\n * aliases + canvas surface). Apply at runtime to scope a theme without loading\n * tokens.css: `Object.assign(el.style, themeToCssVars(darkTheme))`.\n */\nexport function themeToCssVars(theme: AgentAppTheme): Record<string, string> {\n return {\n '--background': theme.background,\n '--foreground': theme.foreground,\n '--card': theme.card,\n '--card-foreground': theme.cardForeground,\n '--popover': theme.popover,\n '--popover-foreground': theme.popoverForeground,\n '--primary': theme.primary,\n '--primary-foreground': theme.primaryForeground,\n '--secondary': theme.secondary,\n '--secondary-foreground': theme.secondaryForeground,\n '--muted': theme.muted,\n '--muted-foreground': theme.mutedForeground,\n '--accent': theme.accent,\n '--accent-foreground': theme.accentForeground,\n '--destructive': theme.destructive,\n '--destructive-foreground': theme.destructiveForeground,\n '--border': theme.border,\n '--input': theme.input,\n '--ring': theme.ring,\n '--success': theme.success,\n '--success-foreground': theme.successForeground,\n '--warning': theme.warning,\n '--warning-foreground': theme.warningForeground,\n '--bg-input': `hsl(${theme.card})`,\n '--text-primary': `hsl(${theme.foreground})`,\n '--text-secondary': `hsl(${theme.secondaryForeground})`,\n '--text-muted': `hsl(${theme.mutedForeground})`,\n '--text-danger': `hsl(${theme.destructive})`,\n '--border-default': `hsl(${theme.border})`,\n '--brand-primary': `hsl(${theme.primary})`,\n '--editor-selection-background': `hsl(${theme.primary})`,\n '--editor-selection-foreground': `hsl(${theme.background})`,\n '--canvas-backdrop': theme.canvasBackdrop,\n }\n}\n"],"mappings":";AAwEO,IAAM,aAA4B;AAAA,EACvC,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,mBAAmB;AAAA,EACnB,SAAS;AAAA,EACT,mBAAmB;AAAA,EACnB,WAAW;AAAA,EACX,qBAAqB;AAAA,EACrB,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,QAAQ;AAAA,EACR,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,uBAAuB;AAAA,EACvB,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,mBAAmB;AAAA,EACnB,SAAS;AAAA,EACT,mBAAmB;AAAA,EACnB,gBAAgB;AAAA,EAChB,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,WAAW;AAAA,IACX,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,qBAAqB;AAAA,IACrB,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,IACnB,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB;AACF;AAGO,IAAM,YAA2B;AAAA,EACtC,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,mBAAmB;AAAA,EACnB,SAAS;AAAA,EACT,mBAAmB;AAAA,EACnB,WAAW;AAAA,EACX,qBAAqB;AAAA,EACrB,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,QAAQ;AAAA,EACR,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,uBAAuB;AAAA,EACvB,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,mBAAmB;AAAA,EACnB,SAAS;AAAA,EACT,mBAAmB;AAAA,EACnB,gBAAgB;AAAA,EAChB,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,WAAW;AAAA,IACX,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,qBAAqB;AAAA,IACrB,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,IACnB,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB;AACF;AAGO,SAAS,WAAW,OAAuB;AAChD,SAAO,MAAM,WAAW,KAAK,KAAK,MAAM,WAAW,GAAG,KAAK,MAAM,WAAW,KAAK,IAC7E,QACA,OAAO,KAAK;AAClB;AAOO,SAAS,eAAe,OAA8C;AAC3E,SAAO;AAAA,IACL,gBAAgB,MAAM;AAAA,IACtB,gBAAgB,MAAM;AAAA,IACtB,UAAU,MAAM;AAAA,IAChB,qBAAqB,MAAM;AAAA,IAC3B,aAAa,MAAM;AAAA,IACnB,wBAAwB,MAAM;AAAA,IAC9B,aAAa,MAAM;AAAA,IACnB,wBAAwB,MAAM;AAAA,IAC9B,eAAe,MAAM;AAAA,IACrB,0BAA0B,MAAM;AAAA,IAChC,WAAW,MAAM;AAAA,IACjB,sBAAsB,MAAM;AAAA,IAC5B,YAAY,MAAM;AAAA,IAClB,uBAAuB,MAAM;AAAA,IAC7B,iBAAiB,MAAM;AAAA,IACvB,4BAA4B,MAAM;AAAA,IAClC,YAAY,MAAM;AAAA,IAClB,WAAW,MAAM;AAAA,IACjB,UAAU,MAAM;AAAA,IAChB,aAAa,MAAM;AAAA,IACnB,wBAAwB,MAAM;AAAA,IAC9B,aAAa,MAAM;AAAA,IACnB,wBAAwB,MAAM;AAAA,IAC9B,cAAc,OAAO,MAAM,IAAI;AAAA,IAC/B,kBAAkB,OAAO,MAAM,UAAU;AAAA,IACzC,oBAAoB,OAAO,MAAM,mBAAmB;AAAA,IACpD,gBAAgB,OAAO,MAAM,eAAe;AAAA,IAC5C,iBAAiB,OAAO,MAAM,WAAW;AAAA,IACzC,oBAAoB,OAAO,MAAM,MAAM;AAAA,IACvC,mBAAmB,OAAO,MAAM,OAAO;AAAA,IACvC,iCAAiC,OAAO,MAAM,OAAO;AAAA,IACrD,iCAAiC,OAAO,MAAM,UAAU;AAAA,IACxD,qBAAqB,MAAM;AAAA,EAC7B;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tangle/index.ts"],"sourcesContent":["/**\n * Tangle login + the developer self-service app-registration → broker-token\n * flow, for apps built on agent-app.\n *\n * The platform (agent-dev-container integration hub) lets a developer register\n * their client app and obtain a `sk-tan-broker-` bearer to call `/v1/hub/exec`\n * on a user's connected integrations — WITHOUT being a hard-coded \"trusted app\".\n * The wire client (`TangleAppsClient` — registerApp / exchangeAuthCode /\n * mintBrokerToken) lives in `@tangle-network/agent-integrations`; this module is\n * the app-shell layer on top, and is intentionally **structural**: it depends on\n * the minter CONTRACT, not the concrete client, so it installs without the\n * agent-integrations publish and is trivially testable. A consumer constructs\n * the real client and passes it in.\n *\n * 1. {@link buildConsentUrl} — send the user through the ONE-TIME consent\n * (their Tangle session authorizes the app for a connection + scopes).\n * 2. On the callback, the consumer's client `exchangeAuthCode`s the `agc_`\n * code into the first broker token + a durable grant.\n * 3. {@link createBrokerTokenProvider} — the runtime path: a cached provider\n * that re-mints a fresh single-use broker token per `/v1/hub/exec` from the\n * durable grant using only the app credentials (no user session). Caches\n * until just before expiry so a burst of hub calls shares one mint.\n */\n\n/** A single-use hub bearer minted from a durable grant — mirrors\n * `@tangle-network/agent-integrations`'s `BrokerToken`. */\nexport interface BrokerToken {\n /** The `sk-tan-broker-…` bearer for a single `/v1/hub/exec` call. */\n accessToken: string\n /** Seconds until expiry. */\n expiresIn: number\n scope: string\n connectionId?: string\n}\n\n/** The one method the provider needs — `TangleAppsClient` satisfies it\n * structurally, so `createBrokerTokenProvider({ client: tangleAppsClient, … })`\n * type-checks without importing the concrete class. */\nexport interface BrokerTokenMinter {\n mintBrokerToken(input: { clientId: string; clientSecret: string; grantId: string; ttlSeconds?: number }): Promise<BrokerToken>\n}\n\n/** Define input parameters required to generate a consent URL for OAuth authorization */\nexport interface ConsentUrlInput {\n /** Platform base URL (e.g. https://id.tangle.tools). */\n endpoint: string\n clientId: string\n /** Must match one of the app's registered redirect URIs. */\n redirectUri: string\n /** Scopes the app is requesting for this connection (e.g. ['gmail.read']). */\n scopes: string[]\n /** Opaque CSRF/state value the callback echoes back — verify it on return. */\n state: string\n /** Optionally pre-select a specific connection to authorize. */\n connectionId?: string\n}\n\n/**\n * Build the URL to send the user to for the one-time app-consent. The user's\n * Tangle session (not the app's credentials) authorizes it; on approval the\n * platform redirects to `redirectUri?code=agc_…&state=…`.\n */\nexport function buildConsentUrl(input: ConsentUrlInput): string {\n const base = input.endpoint.replace(/\\/+$/, '')\n const params = new URLSearchParams({\n client_id: input.clientId,\n redirect_uri: input.redirectUri,\n scope: input.scopes.join(' '),\n state: input.state,\n response_type: 'code',\n })\n if (input.connectionId) params.set('connection_id', input.connectionId)\n return `${base}/cross-site/app-consent?${params.toString()}`\n}\n\n/** Define options for configuring a broker token provider including client credentials and token management settings */\nexport interface BrokerTokenProviderOptions {\n client: BrokerTokenMinter\n clientId: string\n clientSecret: string\n /** The durable grant id from the consent exchange. */\n grantId: string\n /** Requested token TTL (seconds). */\n ttlSeconds?: number\n /** Re-mint this many ms BEFORE expiry so an in-flight call never uses a\n * just-expired token. Default 30s. */\n refreshSkewMs?: number\n /** Injectable clock (ms). Default `Date.now`. */\n now?: () => number\n}\n\n/** Provide and refresh broker bearer tokens, allowing forced token invalidation */\nexport interface BrokerTokenProvider {\n /** A valid `sk-tan-broker-` bearer, minting/refreshing as needed. */\n getToken(): Promise<string>\n /** Force the next `getToken` to re-mint (e.g. after a 401 from the hub). */\n invalidate(): void\n}\n\n/**\n * Cache + auto-refresh a broker token for one grant. A burst of hub calls\n * shares a single mint; the token is re-minted once it's within `refreshSkewMs`\n * of expiry, or on demand via {@link BrokerTokenProvider.invalidate}.\n * Concurrent `getToken` calls during a mint share the same in-flight promise\n * (no thundering herd).\n */\nexport function createBrokerTokenProvider(opts: BrokerTokenProviderOptions): BrokerTokenProvider {\n const now = opts.now ?? (() => Date.now())\n const skew = opts.refreshSkewMs ?? 30_000\n let cached: { token: string; expiresAt: number } | null = null\n let inflight: Promise<string> | null = null\n\n async function mint(): Promise<string> {\n const t = await opts.client.mintBrokerToken({\n clientId: opts.clientId,\n clientSecret: opts.clientSecret,\n grantId: opts.grantId,\n ttlSeconds: opts.ttlSeconds,\n })\n cached = { token: t.accessToken, expiresAt: now() + t.expiresIn * 1000 }\n return t.accessToken\n }\n\n return {\n async getToken() {\n if (cached && now() < cached.expiresAt - skew) return cached.token\n if (inflight) return inflight\n inflight = mint().finally(() => {\n inflight = null\n })\n return inflight\n },\n invalidate() {\n cached = null\n },\n }\n}\n"],"mappings":";AA8DO,SAAS,gBAAgB,OAAgC;AAC9D,QAAM,OAAO,MAAM,SAAS,QAAQ,QAAQ,EAAE;AAC9C,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,WAAW,MAAM;AAAA,IACjB,cAAc,MAAM;AAAA,IACpB,OAAO,MAAM,OAAO,KAAK,GAAG;AAAA,IAC5B,OAAO,MAAM;AAAA,IACb,eAAe;AAAA,EACjB,CAAC;AACD,MAAI,MAAM,aAAc,QAAO,IAAI,iBAAiB,MAAM,YAAY;AACtE,SAAO,GAAG,IAAI,2BAA2B,OAAO,SAAS,CAAC;AAC5D;AAiCO,SAAS,0BAA0B,MAAuD;AAC/F,QAAM,MAAM,KAAK,QAAQ,MAAM,KAAK,IAAI;AACxC,QAAM,OAAO,KAAK,iBAAiB;AACnC,MAAI,SAAsD;AAC1D,MAAI,WAAmC;AAEvC,iBAAe,OAAwB;AACrC,UAAM,IAAI,MAAM,KAAK,OAAO,gBAAgB;AAAA,MAC1C,UAAU,KAAK;AAAA,MACf,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,MACd,YAAY,KAAK;AAAA,IACnB,CAAC;AACD,aAAS,EAAE,OAAO,EAAE,aAAa,WAAW,IAAI,IAAI,EAAE,YAAY,IAAK;AACvE,WAAO,EAAE;AAAA,EACX;AAEA,SAAO;AAAA,IACL,MAAM,WAAW;AACf,UAAI,UAAU,IAAI,IAAI,OAAO,YAAY,KAAM,QAAO,OAAO;AAC7D,UAAI,SAAU,QAAO;AACrB,iBAAW,KAAK,EAAE,QAAQ,MAAM;AAC9B,mBAAW;AAAA,MACb,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IACA,aAAa;AACX,eAAS;AAAA,IACX;AAAA,EACF;AACF;","names":[]}
|