@tangle-network/agent-app 0.43.56 → 0.43.58
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/chat-routes/stale-turn-lock.ts"],"sourcesContent":["/**\n * Recovery policy for a `ChatTurnLock` whose holder died.\n *\n * `createChatTurnRoutes` takes the lock as a seam (`acquire`/`release`) and a\n * lock is a single-flight guard: while it is held, a second turn on the same\n * scope is refused. Products give it a TTL measured in tens of minutes, so a\n * turn that dies without releasing wedges chat for that whole window. Every\n * app on the seam inherits that wedge, which is why the way OUT of it is\n * policy this package owns rather than something each app rediscovers.\n *\n * The policy takes PROBES, not clients: it imports no sandbox SDK, opens no\n * connection, and knows nothing about how a product finds its box or talks to\n * a sidecar. That is what makes the rules testable and what keeps the concrete\n * probes — which box key, which session id, which sidecar endpoint — in the\n * product.\n *\n * The rules, in precedence order:\n *\n * 1. The session probe answered and the execution is TERMINAL ⇒ release, once\n * the lock is past a short grace period. The authority on \"is this turn\n * still running\" is whatever is actually running it; a terminal verdict is\n * proof the lock outlived its turn — but only if the verdict is about THIS\n * turn, which is what the grace buys (see\n * {@link DEFAULT_TERMINAL_TURN_LOCK_GRACE_MS}).\n * 2. The session probe answered and the execution is LIVE ⇒ hold, always.\n * Nothing below may override this. The lock is doing exactly its job.\n * 3. The probes could not reach that authority at all — the sandbox could not\n * be listed, is gone, is not running, or its session probe failed ⇒ fall\n * back on the physical argument: an execution runs INSIDE the box, so a box\n * that is not there is running nothing, and the lock is releasable. Without\n * this fallback the recovery would depend on the very subsystem whose\n * failure produced the stale lock.\n *\n * Rule 3 is gated on a grace period because it is an inference, not an\n * observation — see {@link DEFAULT_STALE_TURN_LOCK_GRACE_MS}.\n */\n\n/** Where the box is, as far as the caller can see. `state` on `not-running`\n * is the platform's own status string, carried through for the log. */\nexport type StaleTurnLockSandboxProbeResult =\n | { status: 'running' }\n | { status: 'absent' }\n | { status: 'not-running'; state?: string }\n\n/** What the thing running the turn says about it. `terminal: false` means an\n * execution is LIVE — the strongest signal in the policy. `diagnostics` rides\n * through to the result and the logs unread. */\nexport type StaleTurnLockSessionProbeResult =\n | { reachable: true; terminal: boolean; diagnostics?: Record<string, unknown> }\n | { reachable: false; reason?: string }\n\n/**\n * Minimum age a lock must reach before the \"sandbox unreachable ⇒ nothing can\n * be running\" fallback may force-release it.\n *\n * The lock is acquired BEFORE the box is ensured, so during a cold workspace's\n * first turn there is a real window in which the lock is held and no box exists\n * yet — indistinguishable, from a peek, from a box that vanished. The grace\n * period has to outlast that window (create + bootstrap + whatever the product\n * hydrates) or a concurrent request steals the lock from a turn that is merely\n * still provisioning. Five minutes clears observed cold starts with room to\n * spare while cutting the worst case from a TTL-length wedge down to five\n * minutes. Raising it makes recovery slower; lowering it risks stealing a lock\n * mid-provision.\n */\nexport const DEFAULT_STALE_TURN_LOCK_GRACE_MS = 5 * 60 * 1000\n\n/**\n * Minimum age a lock must reach before a TERMINAL session verdict may release\n * it.\n *\n * The session probe is keyed on the THREAD, not on the execution the lock\n * holds: a sidecar that has nothing running reports `terminal` with\n * `activeExecutionId: null`, so there is no id to match the lock against. The\n * lock, meanwhile, is acquired BEFORE the box is ensured and before the\n * execution registers with the sidecar. Between those two moments a second\n * request that reconciles the lock asks the sidecar about a turn it has not\n * heard of yet and gets back the PREVIOUS turn's terminal state — proof about\n * the wrong execution. Releasing on that verdict hands the second request a\n * lock the first one is still using, which is two concurrent turns on a scope\n * whose single-flight guard just voted for itself.\n *\n * One minute covers the acquire → box-ensure → sidecar-registration window on\n * a warm box (the cold-box case is Rule 3's, and has its own, much longer\n * grace). Deliberately NOT\n * {@link DEFAULT_STALE_TURN_LOCK_GRACE_MS}: this branch has a positive\n * observation behind it, so it should recover fast, and stretching it to five\n * minutes would leave a genuinely dead turn wedged for the whole window that\n * the session probe exists to shortcut. Raising it delays recovery from a\n * crashed turn; lowering it narrows the registration window it protects.\n */\nexport const DEFAULT_TERMINAL_TURN_LOCK_GRACE_MS = 60 * 1000\n\n/** Resolve options for probing and releasing stale TURN locks based on lock start time and sandbox state */\nexport interface ReconcileStaleTurnLockOptions {\n /** When the held lock was acquired (epoch ms). The grace period is measured\n * from here, so it must be the LOCK's start, not the turn's. */\n lockStartedAt: number\n /** Is the box there and running? Never provisions — a peek, not an ensure.\n * A throw is treated as unreachable, same as `absent`. */\n probeSandbox(): Promise<StaleTurnLockSandboxProbeResult>\n /** Ask the running box whether the execution is still live. Only called when\n * `probeSandbox` reported `running`. A throw is treated as unreachable. */\n probeSession(): Promise<StaleTurnLockSessionProbeResult>\n /** Release the lock, fenced by the instant the releasing evidence was\n * observed. `fence.observedAt` is snapshotted BEFORE the probe that\n * justified the release, so a store that can compare it against the held\n * lock's start refuses to delete a SUCCESSOR lock acquired while the probe\n * was in flight. A store that cannot make that comparison may ignore the\n * fence, but must not substitute its own `Date.now()` — that timestamp is\n * by construction newer than any successor and makes the check vacuous.\n *\n * Returns whether the release actually landed — `false` when the lock was\n * already gone (someone else got there first), which is reported, never\n * treated as a release. */\n release(fence: { observedAt: number }): boolean | Promise<boolean>\n /** Override {@link DEFAULT_STALE_TURN_LOCK_GRACE_MS} (Rule 3's fallback). */\n graceMs?: number\n /** Override {@link DEFAULT_TERMINAL_TURN_LOCK_GRACE_MS} (Rule 1's release). */\n terminalGraceMs?: number\n /** Identity fields merged into every log line (workspace, thread, execution\n * id — whatever makes the entry findable in the product's logs). */\n context?: Record<string, unknown>\n /** Defaults to `console.warn`. Both the withheld and the force-released\n * branches log; a force-release is never silent. */\n log?(message: string, meta: Record<string, unknown>): void\n /** Injectable clock, for tests. */\n now?(): number\n}\n\n/** Describe the outcome of reconciling a stale turn lock including release status and diagnostics */\nexport interface ReconcileStaleTurnLockResult {\n released: boolean\n /** Why the policy decided what it did — the probe's own diagnostics on the\n * reachable path, the unreachable reason and lock age on the fallback. */\n diagnostics: Record<string, unknown>\n}\n\nfunction messageOf(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n\n/**\n * Decide whether a held lock is stale and, if so, release it.\n *\n * Never provisions and never mutates anything but the lock: a reconciliation\n * attempt on a cold workspace leaves it cold.\n */\nexport async function reconcileStaleTurnLock(\n options: ReconcileStaleTurnLockOptions,\n): Promise<ReconcileStaleTurnLockResult> {\n let sandbox: StaleTurnLockSandboxProbeResult\n try {\n sandbox = await options.probeSandbox()\n } catch (err) {\n return forceReleaseUnreachable(options, {\n unreachableReason: 'SANDBOX_PROBE_FAILED',\n unreachableDetail: messageOf(err),\n })\n }\n if (sandbox.status !== 'running') {\n return forceReleaseUnreachable(options, {\n unreachableReason: sandbox.status === 'absent' ? 'SANDBOX_ABSENT' : 'SANDBOX_NOT_RUNNING',\n ...(sandbox.status === 'not-running' && sandbox.state !== undefined\n ? { sandboxState: sandbox.state }\n : {}),\n })\n }\n\n // Snapshot the clock BEFORE the probe and use that one instant for BOTH the\n // grace decision and the release fence: the release is only valid against the\n // lock as it was when the state was observed, not after an arbitrarily slow\n // round trip. Re-reading the clock after the probe would let a slow round\n // trip age the lock past the grace on paper, and would hand the store a fence\n // newer than a successor lock acquired meanwhile.\n const observedAt = (options.now ?? Date.now)()\n let session: StaleTurnLockSessionProbeResult\n try {\n session = await options.probeSession()\n } catch (err) {\n return forceReleaseUnreachable(options, {\n unreachableReason: 'SESSION_PROBE_FAILED',\n unreachableDetail: messageOf(err),\n })\n }\n if (!session.reachable) {\n return forceReleaseUnreachable(options, {\n unreachableReason: 'SESSION_UNREACHABLE',\n ...(session.reason !== undefined ? { sessionProbeError: session.reason } : {}),\n })\n }\n\n const diagnostics: Record<string, unknown> = {\n sandboxReachable: true,\n sessionTerminal: session.terminal,\n ...(session.diagnostics ?? {}),\n }\n // The authority answered and says the execution is live: the lock is doing\n // its job. Nothing below this point may override that.\n if (!session.terminal) return { released: false, diagnostics }\n\n // Terminal, but about WHICH execution? The probe is thread-keyed, so a lock\n // younger than the registration window may be reading the previous turn's\n // verdict — hold until it is old enough that the verdict has to be its own.\n const terminalGraceMs = options.terminalGraceMs ?? DEFAULT_TERMINAL_TURN_LOCK_GRACE_MS\n const lockAgeMs = observedAt - options.lockStartedAt\n if (lockAgeMs < terminalGraceMs) {\n const log = options.log ?? ((message, meta) => console.warn(message, meta))\n const withheld = {\n ...diagnostics,\n lockAgeMs,\n terminalReleaseGraceMs: terminalGraceMs,\n terminalReleaseWithheld: 'LOCK_WITHIN_TERMINAL_GRACE_PERIOD',\n }\n log('[chat-routes] stale turn lock held: terminal verdict but lock is younger than the registration window', {\n ...(options.context ?? {}),\n ...withheld,\n })\n return { released: false, diagnostics: withheld }\n }\n\n const released = await options.release({ observedAt })\n return {\n released,\n diagnostics: { ...diagnostics, lockAgeMs, terminalReleaseGraceMs: terminalGraceMs, released, observedAt },\n }\n}\n\n/**\n * The fallback: the session authority could not attest to the execution's\n * state because the box is gone, stopped, or unreachable. Release the lock\n * once it is old enough that it cannot belong to a turn still provisioning its\n * box — and say so either way, with the reason, the lock's age, and the grace\n * period it was measured against.\n *\n * One clock read (`at`) serves as the age gate AND the release fence here too,\n * for the same reason the terminal branch uses `observedAt` for both.\n */\nasync function forceReleaseUnreachable(\n options: ReconcileStaleTurnLockOptions,\n reason: Record<string, unknown> & { unreachableReason: string },\n): Promise<ReconcileStaleTurnLockResult> {\n const log = options.log ?? ((message, meta) => console.warn(message, meta))\n const graceMs = options.graceMs ?? DEFAULT_STALE_TURN_LOCK_GRACE_MS\n const at = (options.now ?? Date.now)()\n const lockAgeMs = at - options.lockStartedAt\n const diagnostics: Record<string, unknown> = {\n ...reason,\n sandboxReachable: false,\n lockAgeMs,\n forceReleaseGraceMs: graceMs,\n }\n\n if (lockAgeMs < graceMs) {\n log('[chat-routes] stale turn lock held: sandbox unreachable but lock is inside the grace period', {\n ...(options.context ?? {}),\n ...diagnostics,\n })\n return { released: false, diagnostics: { ...diagnostics, forceReleaseWithheld: 'LOCK_WITHIN_GRACE_PERIOD' } }\n }\n\n const released = await options.release({ observedAt: at })\n log('[chat-routes] force-released stale turn lock: sandbox unreachable, no turn can be executing', {\n ...(options.context ?? {}),\n ...diagnostics,\n released,\n })\n return { released, diagnostics: { ...diagnostics, forceReleased: released } }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/harness/index.ts"],"sourcesContent":["/**\n * Coding-agent harness selection — taxonomy, coercion, and the session-lock invariant.\n *\n * A \"harness\" is the coding-agent CLI a sandbox drives (opencode / codex /\n * claude-code / …). The shell governs WHICH harness a chat session uses and\n * enforces that a session is LOCKED to the harness it started with — the model\n * may change mid-session, the harness may not (swapping it mid-session would\n * orphan the session's running agent state). Every product otherwise hand-rolls\n * this and hard-codes a single harness; this is the one place the rule lives.\n *\n * Substrate-free: the harness list mirrors the sandbox SDK's `BackendType` as a\n * plain string union (no sandbox dependency). The consumer owns storage — which\n * harness a workspace defaults to, which one a session locked — and maps the\n * resolved value onto the SDK's `backend.type`.\n *\n * Harness↔model COMPATIBILITY (which models a harness can run, snapping) is NOT defined here — it\n * comes from `@tangle-network/agent-interface`, the single source of truth shared with the\n * sandbox-ui pickers and the cli-bridge backends. This module owns the harness TAXONOMY + the\n * session lock.\n */\n\nimport {\n harnessSupportsModel,\n modelProvider,\n preferredHarnessForModel,\n snapHarnessToModel as aiSnapHarnessToModel,\n snapModelToHarness as aiSnapModelToHarness,\n type HarnessType,\n} from '@tangle-network/agent-interface'\n\n/** The known coding-agent backends. Mirrors `@tangle-network/sandbox`'s\n * `BackendType`; kept structural so this module needs no sandbox dependency. */\nexport const KNOWN_HARNESSES = [\n 'opencode',\n 'claude-code',\n 'nanoclaw',\n 'kimi-code',\n 'codex',\n 'amp',\n 'factory-droids',\n 'pi',\n 'hermes',\n 'forge',\n 'openclaw',\n 'acp',\n 'cursor',\n 'cli-base',\n] as const\n\n/** Resolve a valid harness identifier from the predefined KNOWN_HARNESSES array */\nexport type Harness = (typeof KNOWN_HARNESSES)[number]\n\n/** Define the default harness to use for code execution and testing environments */\nexport const DEFAULT_HARNESS: Harness = 'opencode'\n\nconst HARNESS_SET: ReadonlySet<string> = new Set(KNOWN_HARNESSES)\n\n/** Determine if a value is a recognized harness string identifier */\nexport function isHarness(value: unknown): value is Harness {\n return typeof value === 'string' && HARNESS_SET.has(value)\n}\n\n/** Coerce an arbitrary value to a known harness, falling back (default `opencode`). */\nexport function coerceHarness(value: unknown, fallback: Harness = DEFAULT_HARNESS): Harness {\n return isHarness(value) ? value : fallback\n}\n\n/** Resolve input options to determine the appropriate session harness to use */\nexport interface ResolveSessionHarnessInput {\n /** The harness already locked to this session (recorded at its first turn). */\n sessionHarness?: unknown\n /** The harness requested now — a new session's choice, or a turn's attempt to switch. */\n requested?: unknown\n /** The workspace's default harness, used only when starting a fresh session. */\n workspaceDefault?: unknown\n /** Final fallback when nothing else resolves (default `opencode`). */\n fallback?: Harness\n}\n\n/** Represent resolved session state including harness, lock status, and swap attempt flag */\nexport interface ResolvedSessionHarness {\n /** The harness to actually run — the locked one when the session already has it. */\n harness: Harness\n /** True when the session already had a locked harness (this turn did not pick it). */\n locked: boolean\n /** True when `requested` differs from the locked harness — a forbidden mid-session\n * swap the caller should reject or warn on. The lock always wins regardless. */\n swapAttempted: boolean\n}\n\n/**\n * Resolve the harness for a turn, enforcing the session lock.\n *\n * - **Session already started** (`sessionHarness` is a known harness): that harness\n * wins (`locked: true`); a differing `requested` sets `swapAttempted` so the caller\n * can reject the swap. The model is a separate per-turn concern and is unaffected.\n * - **Fresh session**: pick `requested → workspaceDefault → fallback`. The caller\n * persists the result as the session's lock for every subsequent turn.\n */\nexport function resolveSessionHarness(input: ResolveSessionHarnessInput = {}): ResolvedSessionHarness {\n const fallback = input.fallback ?? DEFAULT_HARNESS\n if (isHarness(input.sessionHarness)) {\n const locked = input.sessionHarness\n const swapAttempted = isHarness(input.requested) && input.requested !== locked\n return { harness: locked, locked: true, swapAttempted }\n }\n const harness = coerceHarness(input.requested, coerceHarness(input.workspaceDefault, fallback))\n return { harness, locked: false, swapAttempted: false }\n}\n\n/**\n * Harness ↔ model compatibility + snapping — delegated to `@tangle-network/agent-interface`.\n *\n * agent-app's `Harness` taxonomy is a superset of agent-interface's `HarnessType` (it carries\n * `forge`/`cursor`, which agent-interface doesn't list). Those extra runners have no provider lock\n * there, so they resolve as router-backed (any model) — the correct behavior — which makes the\n * `as HarnessType` casts safe. The snap helpers only ever return a vendor-locked harness or\n * `opencode`, all of which are valid `Harness` values.\n */\n\nexport { modelProvider }\n\n/** Provider-less ids (sentinels like \"default\", or a session's own config) are\n * compatible everywhere — every harness honors its own configuration. */\nexport function isModelCompatibleWithHarness(harness: Harness, modelId: string): boolean {\n return harnessSupportsModel(harness as HarnessType, modelId)\n}\n\n/** Keep `modelId` when the harness can run it; else the harness's best compatible\n * catalog id (preferred patterns in order, highest version). When nothing in the\n * catalog fits, return the original so the caller sees the incompatibility. */\nexport function snapModelToHarness(harness: Harness, modelId: string, canonicalIds: readonly string[]): string {\n return aiSnapModelToHarness(harness as HarnessType, modelId, canonicalIds)\n}\n\n/** Keep the harness when it can run `modelId`; else the model's native harness\n * (anthropic → claude-code, openai → codex, moonshot → kimi-code), falling back to opencode. */\nexport function snapHarnessToModel(harness: Harness, modelId: string): Harness {\n return aiSnapHarnessToModel(harness as HarnessType, modelId) as Harness\n}\n\n/** Fail-loud server guard: throw when a harness is asked to run a model it can't.\n * Call before dispatching a sandbox turn so a bypassed UI can't reach the sidecar\n * with an incompatible pair. */\nexport function assertHarnessModelCompatible(harness: Harness, modelId: string): void {\n if (!isModelCompatibleWithHarness(harness, modelId)) {\n const provider = modelProvider(modelId)\n const native = preferredHarnessForModel(modelId)\n throw new Error(\n `Harness \"${harness}\" cannot run model \"${modelId}\" (provider \"${provider}\"). ` +\n `Use ${native ?? 'a router-backed harness (opencode)'} or an allowed model.`,\n )\n }\n}\n"],"mappings":";AAqBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,OAEjB;AAIA,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAMO,IAAM,kBAA2B;AAExC,IAAM,cAAmC,IAAI,IAAI,eAAe;AAGzD,SAAS,UAAU,OAAkC;AAC1D,SAAO,OAAO,UAAU,YAAY,YAAY,IAAI,KAAK;AAC3D;AAGO,SAAS,cAAc,OAAgB,WAAoB,iBAA0B;AAC1F,SAAO,UAAU,KAAK,IAAI,QAAQ;AACpC;AAkCO,SAAS,sBAAsB,QAAoC,CAAC,GAA2B;AACpG,QAAM,WAAW,MAAM,YAAY;AACnC,MAAI,UAAU,MAAM,cAAc,GAAG;AACnC,UAAM,SAAS,MAAM;AACrB,UAAM,gBAAgB,UAAU,MAAM,SAAS,KAAK,MAAM,cAAc;AACxE,WAAO,EAAE,SAAS,QAAQ,QAAQ,MAAM,cAAc;AAAA,EACxD;AACA,QAAM,UAAU,cAAc,MAAM,WAAW,cAAc,MAAM,kBAAkB,QAAQ,CAAC;AAC9F,SAAO,EAAE,SAAS,QAAQ,OAAO,eAAe,MAAM;AACxD;AAgBO,SAAS,6BAA6B,SAAkB,SAA0B;AACvF,SAAO,qBAAqB,SAAwB,OAAO;AAC7D;AAKO,SAAS,mBAAmB,SAAkB,SAAiB,cAAyC;AAC7G,SAAO,qBAAqB,SAAwB,SAAS,YAAY;AAC3E;AAIO,SAAS,mBAAmB,SAAkB,SAA0B;AAC7E,SAAO,qBAAqB,SAAwB,OAAO;AAC7D;AAKO,SAAS,6BAA6B,SAAkB,SAAuB;AACpF,MAAI,CAAC,6BAA6B,SAAS,OAAO,GAAG;AACnD,UAAM,WAAW,cAAc,OAAO;AACtC,UAAM,SAAS,yBAAyB,OAAO;AAC/C,UAAM,IAAI;AAAA,MACR,YAAY,OAAO,uBAAuB,OAAO,gBAAgB,QAAQ,WAChE,UAAU,oCAAoC;AAAA,IACzD;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/teams/invite.ts"],"sourcesContent":["/**\n * Pure invite-token helpers — generation, shape validation, and expiry math.\n * No I/O: the members API persists/looks up tokens; these functions only\n * produce well-formed tokens and decide, given values the caller already\n * loaded, whether an invite is usable.\n *\n * A token is an opaque high-entropy URL-safe string. It is the bearer secret\n * in `/invite/:token`, so it must be unguessable and never derived from the\n * email or workspace. `generateInviteToken` uses Web Crypto (`crypto`), which\n * is present in Workers, Node 18+, Deno, and browsers — no Node-only import,\n * so this stays a pure leaf.\n */\n\nconst INVITE_TOKEN_BYTES = 24\nconst INVITE_TOKEN_PATTERN = /^[A-Za-z0-9_-]{16,}$/\n\n/** Cryptographically-random, URL-safe (base64url) invite token. */\nexport function generateInviteToken(): string {\n const bytes = new Uint8Array(INVITE_TOKEN_BYTES)\n crypto.getRandomValues(bytes)\n return base64UrlEncode(bytes)\n}\n\n/** True when `value` has the shape of an invite token (not whether it exists). */\nexport function isInviteTokenShape(value: unknown): value is string {\n return typeof value === 'string' && INVITE_TOKEN_PATTERN.test(value)\n}\n\n/** A pending invite row, narrowed to the fields invite acceptance reasons over. */\nexport interface InviteTokenState {\n /** null until accepted — a non-null value means the invite was already used. */\n acceptedAt: Date | number | null | undefined\n /** Email the invite was addressed to, if any. */\n inviteEmail?: string | null\n /** Optional hard expiry; omit/undefined for invites that never expire. */\n expiresAt?: Date | number | null\n}\n\n/** Define possible reasons for rejecting an invite including acceptance, expiration, or email mismatch */\nexport type InviteRejectionReason = 'already-accepted' | 'expired' | 'email-mismatch'\n\n/** Represent the outcome of validating an invite with success status and optional rejection reason */\nexport interface InviteValidationResult {\n ok: boolean\n reason?: InviteRejectionReason\n}\n\n/**\n * Decide whether a loaded invite can be accepted by `acceptingEmail` at `now`.\n * Pure: the caller has already fetched the row by token; this only judges it.\n * Email match is case-insensitive and only enforced when the invite was\n * addressed to a specific email (an open invite has no `inviteEmail`).\n */\nexport function validateInviteToken(\n invite: InviteTokenState,\n opts: { acceptingEmail?: string | null; now?: Date } = {},\n): InviteValidationResult {\n if (invite.acceptedAt != null) return { ok: false, reason: 'already-accepted' }\n\n if (invite.expiresAt != null) {\n const now = (opts.now ?? new Date()).getTime()\n const expires = invite.expiresAt instanceof Date ? invite.expiresAt.getTime() : Number(invite.expiresAt)\n if (Number.isFinite(expires) && now >= expires) return { ok: false, reason: 'expired' }\n }\n\n if (invite.inviteEmail && opts.acceptingEmail) {\n if (invite.inviteEmail.trim().toLowerCase() !== opts.acceptingEmail.trim().toLowerCase()) {\n return { ok: false, reason: 'email-mismatch' }\n }\n }\n\n return { ok: true }\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let binary = ''\n for (const byte of bytes) binary += String.fromCharCode(byte)\n const base64 = typeof btoa === 'function' ? btoa(binary) : bufferToBase64(binary)\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction bufferToBase64(binary: string): string {\n // Node without a global `btoa` (older runtimes); Buffer is always present there.\n return Buffer.from(binary, 'binary').toString('base64')\n}\n"],"mappings":";AAaA,IAAM,qBAAqB;AAC3B,IAAM,uBAAuB;AAGtB,SAAS,sBAA8B;AAC5C,QAAM,QAAQ,IAAI,WAAW,kBAAkB;AAC/C,SAAO,gBAAgB,KAAK;AAC5B,SAAO,gBAAgB,KAAK;AAC9B;AAGO,SAAS,mBAAmB,OAAiC;AAClE,SAAO,OAAO,UAAU,YAAY,qBAAqB,KAAK,KAAK;AACrE;AA2BO,SAAS,oBACd,QACA,OAAuD,CAAC,GAChC;AACxB,MAAI,OAAO,cAAc,KAAM,QAAO,EAAE,IAAI,OAAO,QAAQ,mBAAmB;AAE9E,MAAI,OAAO,aAAa,MAAM;AAC5B,UAAM,OAAO,KAAK,OAAO,oBAAI,KAAK,GAAG,QAAQ;AAC7C,UAAM,UAAU,OAAO,qBAAqB,OAAO,OAAO,UAAU,QAAQ,IAAI,OAAO,OAAO,SAAS;AACvG,QAAI,OAAO,SAAS,OAAO,KAAK,OAAO,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,UAAU;AAAA,EACxF;AAEA,MAAI,OAAO,eAAe,KAAK,gBAAgB;AAC7C,QAAI,OAAO,YAAY,KAAK,EAAE,YAAY,MAAM,KAAK,eAAe,KAAK,EAAE,YAAY,GAAG;AACxF,aAAO,EAAE,IAAI,OAAO,QAAQ,iBAAiB;AAAA,IAC/C;AAAA,EACF;AAEA,SAAO,EAAE,IAAI,KAAK;AACpB;AAEA,SAAS,gBAAgB,OAA2B;AAClD,MAAI,SAAS;AACb,aAAW,QAAQ,MAAO,WAAU,OAAO,aAAa,IAAI;AAC5D,QAAM,SAAS,OAAO,SAAS,aAAa,KAAK,MAAM,IAAI,eAAe,MAAM;AAChF,SAAO,OAAO,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AACzE;AAEA,SAAS,eAAe,QAAwB;AAE9C,SAAO,OAAO,KAAK,QAAQ,QAAQ,EAAE,SAAS,QAAQ;AACxD;","names":[]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
persistedPartToInteraction
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YKOM4ZLQ.js";
|
|
4
4
|
import {
|
|
5
5
|
persistedPartToPlan,
|
|
6
6
|
planToPersistedPart
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YJMCRXQQ.js";
|
|
8
8
|
|
|
9
9
|
// src/chat-routes/wire.ts
|
|
10
10
|
function chatTurnRequestInit(payload) {
|
|
@@ -358,4 +358,4 @@ export {
|
|
|
358
358
|
buildAttachmentPromptBlock,
|
|
359
359
|
historyContentWithAttachments
|
|
360
360
|
};
|
|
361
|
-
//# sourceMappingURL=chunk-
|
|
361
|
+
//# sourceMappingURL=chunk-DTSPS2HV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/chat-routes/wire.ts","../src/chat-store/parts.ts"],"sourcesContent":["/**\n * Wire contract between the chat client (composer + `streamChatTurn`) and the\n * assembled server vertical (`createChatTurnRoutes`). Import-free on purpose:\n * `/web-react` re-exports these types into browser bundles, so nothing here may\n * reach a Node builtin or an engine package.\n *\n * The part shape mirrors the sandbox SDK's `PromptInputPart` structurally\n * (text | image | file with filename/mediaType/url/path/content) — derived\n * here, not imported, so the client bundle never touches the SDK.\n */\n\nexport interface ChatTurnTextPartInput {\n type: 'text'\n text: string\n}\n\n/** A non-text prompt part the upload route hands back and the client echoes\n * on send. `url` carries an inline `data:` URI for small files; `path` is a\n * sandbox workspace reference for large ones (the >1 MiB gateway body cap\n * makes the two-step upload mandatory). */\nexport interface ChatTurnFilePartInput {\n type: 'image' | 'file'\n filename?: string\n mediaType?: string\n url?: string\n path?: string\n content?: string\n}\n\n/** Resolve input as either a text part or a file part of a chat turn */\nexport type ChatTurnPartInput = ChatTurnTextPartInput | ChatTurnFilePartInput\n\n// ── producer stream vocabulary ───────────────────────────────────────────────\n\n/** Represent a text event produced by a source with a fixed type and associated text content */\nexport interface ProducerTextEvent {\n type: 'text'\n text: string\n}\n\n/** Define an event representing reasoning output with a fixed type and associated text */\nexport interface ProducerReasoningEvent {\n type: 'reasoning'\n text: string\n}\n\n/** Represent an event triggered by a producer tool call with its identifier, name, and arguments */\nexport interface ProducerToolCallEvent {\n type: 'tool_call'\n call: {\n toolCallId: string\n toolName: string\n args: Record<string, unknown>\n }\n}\n\n/** Describe the structure of an event representing the result of a producer tool call */\nexport interface ProducerToolResultEvent {\n type: 'tool_result'\n toolCallId: string\n toolName: string\n outcome: {\n ok: boolean\n result?: unknown\n message?: string\n }\n}\n\n/** Describe usage event with prompt and completion token counts for a producer */\nexport interface ProducerUsageEvent {\n type: 'usage'\n usage: {\n promptTokens: number\n completionTokens: number\n }\n}\n\n/** Define the structure for a producer notice event with type, id, kind, and text fields */\nexport interface ProducerNoticeEvent {\n type: 'notice'\n id: string\n /** Kept inline with `/interactions`' `NoticeKind` so this file stays import-free. */\n noticeKind: 'warning' | 'auto-declined'\n text: string\n}\n\n/** Represent an error event emitted by a producer containing message, code, and optional details */\nexport interface ProducerErrorEvent {\n type: 'error'\n data: {\n message: string\n code?: string\n details?: Record<string, unknown>\n }\n}\n\n/** Stable raw lifecycle/interaction/plan/route events forwarded unchanged. */\nexport type ProducerPassthroughEventType =\n | 'turn'\n | 'metadata'\n | 'interaction'\n | 'interaction.cancel'\n | 'plan.submitted'\n | 'done'\n | 'warning'\n | 'session.run.started'\n | 'session.run.completed'\n | 'session.run.failed'\n | 'turn_status'\n\n/** Define an event carrying passthrough data with flexible properties for producer communication */\nexport interface ProducerPassthroughEvent {\n type: ProducerPassthroughEventType\n data?: Record<string, unknown>\n /** Route markers and raw passthroughs may carry `turnId`, `status`, `seq`, etc. */\n [key: string]: unknown\n}\n\n/** Represent events emitted by a producer during its operation for processing and handling */\nexport type ProducerWireEvent =\n | ProducerTextEvent\n | ProducerReasoningEvent\n | ProducerToolCallEvent\n | ProducerToolResultEvent\n | ProducerUsageEvent\n | ProducerNoticeEvent\n | ProducerErrorEvent\n | ProducerPassthroughEvent\n\n/** The image/file split an attachment is rendered and persisted under — the\n * same discriminant as {@link ChatMentionKind}, but a distinct name because an\n * attachment carries content the product uploaded (`ChatAttachmentInput`)\n * while a mention points at a file the box already has. Defined HERE (the\n * import-free layer) so `ChatAttachmentInput` can reference it and the client\n * composer imports it without pulling the persisted-part vocabulary;\n * `/chat-store`'s parts module re-exports it alongside the attachment helpers. */\nexport type ChatAttachmentKind = 'image' | 'file'\n\n/** `POST` turn-body entry describing a file already uploaded to the product's\n * store (vault/object-store) — distinct from an inline {@link\n * ChatTurnFilePartInput} (which carries bytes) and from a {@link FileMention}\n * (a sandbox path the box already holds). The route resolves this field with\n * {@link resolveChatAttachments}: every path is re-validated and every size is\n * re-derived from the stored body, so nothing here is trusted as sent. */\nexport interface ChatAttachmentInput {\n path: string\n name: string\n size: number\n mediaType: string\n kind: ChatAttachmentKind\n}\n\n/** POST body for the turn route. `content` may be empty when `parts` carry the\n * message (an image-only send). Product routing fields (workspaceId etc.) ride\n * alongside and are read by the product's `authorize` seam. */\nexport interface ChatTurnRequestPayload {\n threadId: string\n content?: string\n /** Non-text parts from the upload route, echoed back verbatim. */\n parts?: ChatTurnFilePartInput[]\n /** `@`-picked file mentions for this turn — path references into the\n * workspace sandbox, NOT uploads, so they travel in their own field rather\n * than as `parts` entries. A product whose `parts` field is already spoken\n * for (an attachment sentinel) can still send mentions, and mentions\n * persist as their own `ChatMentionPart`s so a retry rebuilds them. The\n * route validates this field with {@link parseFileMentions} and replaces it\n * on the payload with the validated, deduped list. */\n mentions?: FileMention[]\n /** Files uploaded to the product's store ahead of the turn — path\n * references, NOT inline bytes (those ride `parts`). Validated and\n * size-re-derived by {@link resolveChatAttachments} into persistable\n * attachment parts; a product whose `parts` field is spoken for by inline\n * uploads still sends store-backed files here. */\n attachments?: ChatAttachmentInput[]\n model?: string\n effort?: 'auto' | 'low' | 'medium' | 'high'\n harness?: string\n /** Client-generated idempotency key for the logical turn (retry-safe). */\n turnId?: string\n [key: string]: unknown\n}\n\n/** `fetch` init for the turn route — the one place the client wire shape is\n * serialized, so composer glue and products never drift from the server's\n * parser. */\nexport function chatTurnRequestInit(payload: ChatTurnRequestPayload): RequestInit {\n return {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n }\n}\n\n// ── inline-part byte budget ─────────────────────────────────────────────────\n//\n// The sandbox gateway caps request bodies at 1 MiB; a turn body whose inline\n// `data:` parts exceed it dies at the gateway with an opaque 413. Enforce the\n// budget at the route boundary instead, with headroom for the JSON envelope\n// (same fail-loud-at-the-choke-point style as /sandbox's provision-payload and\n// env-size gates).\n\n/** Define the maximum byte size allowed for inline parts in data processing */\nexport const INLINE_PARTS_MAX_BYTES = 950_000\n\n// ── dispatch (parts[]) budget vocabulary ────────────────────────────────────\n//\n// The default caps `buildDispatchParts` sizes an attachment/mention dispatch\n// against — the sidecar/proxy limits an assembled `parts` array crosses, one\n// step past `INLINE_PARTS_MAX_BYTES` (which gates the raw turn BODY). Grouped\n// here, in the import-free layer, so the numbers are one overridable\n// vocabulary the client can read and a product can tune per call rather than\n// constants buried in the server module. NOTE: these model sidecar/proxy caps,\n// not a product's MIME accept-list or vault bucketing — those are DOMAIN\n// values the product supplies, never defaulted here.\n\n/** Hard cap on the whole `/prompt` request body as it crosses the sandbox\n * proxy — smaller in practice than a raw-file write cap because a dispatch\n * carries several inline parts plus the flattened history in one request. */\nexport const DISPATCH_REQUEST_MAX_BYTES = 1024 * 1024\n\n/** Bytes reserved off the top of {@link DISPATCH_REQUEST_MAX_BYTES} for the\n * JSON structure around the parts array (keys, delimiters, per-part\n * `type`/`filename`/`mediaType` fields) that {@link base64WireLen} does not\n * account for — keeps the inline budget off the exact proxy cap where one\n * stray byte trips the 413. */\nexport const DISPATCH_STRUCTURAL_RESERVE_BYTES = 64 * 1024\n\n/** Sidecar's hard cap on the `parts` array of one prompt request — a dispatch\n * must never assemble more parts than this or the whole turn 400s. */\nexport const DISPATCH_MAX_PARTS = 64\n\n/** Product-side cap on media parts per dispatch (current turn + carried\n * history), well under {@link DISPATCH_MAX_PARTS}. History trimming that keeps\n * a transcript's native media under this is a PRODUCT concern (the pointer\n * block keeps trimmed media reachable); `buildDispatchParts` enforces only the\n * total {@link DISPATCH_MAX_PARTS} cap. */\nexport const DISPATCH_MAX_MEDIA_PARTS = 24\n\n/** Size a base64-encoded string occupies on the wire given the raw\n * (pre-encoding) byte length: base64 packs 3 raw bytes into 4 output\n * characters, rounded up to the next multiple of 4. */\nexport function base64WireLen(byteLen: number): number {\n return Math.ceil(byteLen / 3) * 4\n}\n\n/**\n * Render a raw byte count as a human-readable size (`512B`, `3KB`, `12MB\n * 500KB`). Ported EXACTLY from gtm-agent's `attachment-limits.ts` — byte-\n * identical implementation, not a reinterpretation — so `resolve-attachments`'s\n * and `promote-file-part`'s error strings match gtm's wording verbatim. Lives\n * in the import-free wire layer (not `resolve-attachments.ts` alone) because\n * BOTH the aggregate-cap message here and the per-file oversize message in\n * `promote-file-part.ts` need it; a browser composer wanting the same\n * formatting for a client-side pre-check can also import it with no engine\n * pulled in.\n */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`\n if (bytes >= 1024 * 1024) {\n const megabytes = Math.floor(bytes / (1024 * 1024))\n const remainder = bytes % (1024 * 1024)\n return remainder === 0 ? `${megabytes}MB` : `${megabytes}MB ${formatBytes(remainder)}`\n }\n return `${Math.round(bytes / 1024)}KB`\n}\n\n/** Represent errors for invalid chat turn inputs with status and code properties */\nexport class ChatTurnInputError extends Error {\n constructor(message: string, readonly status = 400, readonly code = 'INVALID_CHAT_TURN') {\n super(message)\n this.name = 'ChatTurnInputError'\n }\n}\n\nfunction partByteSize(part: ChatTurnPartInput): number {\n let bytes = 0\n if (part.type === 'text') return part.text.length\n if (part.url) bytes += part.url.length\n if (part.content) bytes += part.content.length\n if (part.path) bytes += part.path.length\n return bytes\n}\n\n/** Calculate the total byte size of an array of chat turn parts */\nexport function promptPartsByteSize(parts: ChatTurnPartInput[]): number {\n return parts.reduce((total, part) => total + partByteSize(part), 0)\n}\n\n/** Throws `ChatTurnInputError` (413) when the parts' inline payload would blow\n * the gateway cap. Path-ref parts are tiny by construction and always pass. */\nexport function assertPromptPartsWithinCap(\n parts: ChatTurnPartInput[],\n maxBytes = INLINE_PARTS_MAX_BYTES,\n): void {\n const total = promptPartsByteSize(parts)\n if (total <= maxBytes) return\n const largest = [...parts].sort((a, b) => partByteSize(b) - partByteSize(a))[0]\n const largestName = largest && largest.type !== 'text' ? largest.filename ?? largest.path ?? largest.type : 'text'\n throw new ChatTurnInputError(\n `Inline prompt parts total ${total}B, over the ${maxBytes}B budget (largest: ${largestName}, ${largest ? partByteSize(largest) : 0}B). ` +\n 'Upload large files through the upload route so they travel as sandbox path references.',\n 413,\n 'PROMPT_PARTS_TOO_LARGE',\n )\n}\n\n// ── file mentions ────────────────────────────────────────────────────────\n//\n// A file mention (`@`-picked in the composer, sandbox-ui#184) is a path\n// reference into the workspace sandbox — no byte upload. These helpers turn\n// a resolved mention list into wire parts and the prompt pointer block that\n// tells the agent where to read them from.\n\n/** A file mention resolved from the composer's `@`-picker: the\n * workspace-relative path plus enough metadata to build a prompt part and\n * pointer text. `path` is the canonical identity — the mention pill's\n * `MentionItem.id` for the file kind (`/web-react`'s `useFileMentions`). */\nexport interface FileMention {\n path: string\n name: string\n size?: number\n}\n\nconst MENTION_IMAGE_MEDIA_TYPES: ReadonlyMap<string, string> = new Map([\n ['.png', 'image/png'],\n ['.jpg', 'image/jpeg'],\n ['.jpeg', 'image/jpeg'],\n ['.gif', 'image/gif'],\n ['.webp', 'image/webp'],\n ['.svg', 'image/svg+xml'],\n ['.bmp', 'image/bmp'],\n ['.heic', 'image/heic'],\n ['.heif', 'image/heif'],\n ['.avif', 'image/avif'],\n])\n\nfunction extensionOf(path: string): string {\n const base = path.split('/').filter(Boolean).pop() ?? path\n const dot = base.lastIndexOf('.')\n return dot > 0 ? base.slice(dot).toLowerCase() : ''\n}\n\n/** The `image/*` mime for a mention path by extension, or `undefined` for\n * anything not in the known image set (dispatched as `type: 'file'`). */\nexport function mediaTypeForMentionPath(path: string): string | undefined {\n return MENTION_IMAGE_MEDIA_TYPES.get(extensionOf(path))\n}\n\n/** The image/file split a mention is rendered and persisted under — the\n * composer pill's icon, the dispatched part's `type`, and\n * `ChatMentionPart.mentionKind` are all this one value. */\nexport type ChatMentionKind = 'image' | 'file'\n\n/** `image` when the path's extension is in the known image set (the same table\n * {@link mediaTypeForMentionPath} reads), `file` otherwise. Exported so a\n * client that needs only the discriminant — a pill icon, a persisted part's\n * `mentionKind` — never re-declares the extension table; two frozen copies of\n * one mime table is how one gains a format and the other doesn't. */\nexport function mentionKindForPath(path: string): ChatMentionKind {\n return mediaTypeForMentionPath(path) ? 'image' : 'file'\n}\n\n/** Define options to resolve mention paths when converting file mentions to parts */\nexport interface FileMentionsToPartsOptions {\n /** Resolve a mention's workspace-relative path to the absolute path the\n * dispatched part should carry (e.g. a host prefixing the in-box vault\n * root). Default: identity — the path travels unchanged. */\n resolvePath?: (path: string) => string\n}\n\n/** Maps resolved file mentions to path-only `ChatTurnFilePartInput`s —\n * `image` vs `file` by extension, and always a `path`, never a `url` (the\n * url/path XOR invariant: a mention is a sandbox path reference, never\n * inline bytes). */\nexport function fileMentionsToParts(\n mentions: readonly FileMention[],\n opts: FileMentionsToPartsOptions = {},\n): ChatTurnFilePartInput[] {\n const resolvePath = opts.resolvePath ?? ((path: string) => path)\n return mentions.map((mention) => {\n const mediaType = mediaTypeForMentionPath(mention.path)\n const part: ChatTurnFilePartInput = {\n type: mediaType ? 'image' : 'file',\n filename: mention.name,\n path: resolvePath(mention.path),\n }\n if (mediaType) part.mediaType = mediaType\n return part\n })\n}\n\n/** The agent-facing pointer block appended to the dispatched prompt — never\n * persisted in message `content`. Empty array → `''` so callers can append\n * unconditionally. This is the sole producer of that text: the current\n * turn's dispatch and any history projection built from the same mention\n * list both route through here, so the two can't drift apart. */\nexport function buildMentionPromptBlock(\n mentions: readonly Pick<FileMention, 'name' | 'path'>[],\n): string {\n if (mentions.length === 0) return ''\n const lines = mentions.map((m) => `- ${m.name} (${m.path})`)\n return `\\n\\nMentioned files — read them from these paths:\\n${lines.join('\\n')}`\n}\n\n// ── mention validation ───────────────────────────────────────────────────\n//\n// This package owns BOTH ends of the mention path contract — it emits paths\n// from `createSandboxFileIndexRoute` and consumes them in `fileMentionsToParts`\n// / `buildMentionPromptBlock` — so the validation belongs here rather than in\n// each app that wires the pair up. A mention names a file that already exists\n// in the sandbox, so validation is a pure path/charset/count check with no\n// I/O: existence is proven later, when the agent reads the path and the turn\n// fails loudly if it is gone.\n\n/** Hard cap on mentions per turn. Bounds the prompt pointer block, the\n * persisted parts, and whatever media budget a dispatch draws from them. */\nexport const MENTION_MAX_COUNT = 16\n\n/** Longest mention display name accepted — bounds the pointer-block text and\n * the transcript pill label. */\nconst MAX_MENTION_NAME_LENGTH = 256\n/** Longest mention path accepted. */\nconst MAX_MENTION_PATH_LENGTH = 1024\n\n/** Represent the result of a sandbox mention path check indicating success or failure with an error message */\nexport type SandboxMentionPathCheck =\n | { succeeded: true }\n | { succeeded: false; error: string }\n\n/**\n * Validate a workspace-relative sandbox mention path. Rejects traversal (a\n * `..` path segment), absolute paths (leading `/`), backslashes, and null\n * bytes — the four ways a path picked in a client can escape the root the\n * index route scanned.\n *\n * Spaces and unicode are deliberately ALLOWED: in-box filenames are arbitrary,\n * and an ASCII-only charset would silently drop real files from a feature\n * whose whole job is naming them.\n */\nexport function validateSandboxMentionPath(path: unknown): SandboxMentionPathCheck {\n if (typeof path !== 'string' || path.length === 0) {\n return { succeeded: false, error: 'mention path must be a non-empty string' }\n }\n if (path.length > MAX_MENTION_PATH_LENGTH) {\n return { succeeded: false, error: `mention path must not exceed ${MAX_MENTION_PATH_LENGTH} characters` }\n }\n if (path.includes('\\0')) {\n return { succeeded: false, error: 'mention path must not contain null bytes' }\n }\n if (path.includes('\\\\')) {\n return { succeeded: false, error: 'mention path must not contain backslashes' }\n }\n if (path.startsWith('/')) {\n return { succeeded: false, error: 'mention path must be workspace-relative, not absolute' }\n }\n if (path.split('/').some((segment) => segment === '..')) {\n return { succeeded: false, error: 'mention path must not contain \"..\" segments' }\n }\n return { succeeded: true }\n}\n\nfunction parseFileMention(value: unknown, index: number): FileMention {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new ChatTurnInputError(`mentions[${index}] must be an object`)\n }\n const record = value as Record<string, unknown>\n\n const pathCheck = validateSandboxMentionPath(record.path)\n if (!pathCheck.succeeded) throw new ChatTurnInputError(`mentions[${index}]: ${pathCheck.error}`)\n\n const name = record.name\n if (typeof name !== 'string' || !name.trim()) {\n throw new ChatTurnInputError(`mentions[${index}].name must be a non-empty string`)\n }\n if (name.length > MAX_MENTION_NAME_LENGTH) {\n throw new ChatTurnInputError(`mentions[${index}].name must not exceed ${MAX_MENTION_NAME_LENGTH} characters`)\n }\n\n const size = record.size\n if (size !== undefined) {\n if (typeof size !== 'number' || !Number.isFinite(size)) {\n throw new ChatTurnInputError(`mentions[${index}].size must be a finite number`)\n }\n if (size < 0) {\n throw new ChatTurnInputError(`mentions[${index}].size must not be negative`)\n }\n }\n\n return { path: record.path as string, name, ...(typeof size === 'number' ? { size } : {}) }\n}\n\n/**\n * Validates the untyped `mentions` array off the wire, mirroring\n * {@link parseChatTurnParts}: the typed list, or `ChatTurnInputError` (400)\n * naming the offending entry. Never sanitizes-and-continues — a traversal path\n * is a rejected request, not a trimmed one.\n *\n * A path repeated within one turn is deduped to its first occurrence rather\n * than rejected: mentioning the same file twice is plausible user input, not\n * an attack.\n */\nexport function parseFileMentions(raw: unknown): FileMention[] {\n if (raw === undefined || raw === null) return []\n if (!Array.isArray(raw)) throw new ChatTurnInputError('mentions must be an array')\n if (raw.length > MENTION_MAX_COUNT) {\n throw new ChatTurnInputError(`mentions must not exceed ${MENTION_MAX_COUNT} entries`)\n }\n\n const mentions: FileMention[] = []\n const seenPaths = new Set<string>()\n for (let index = 0; index < raw.length; index += 1) {\n const mention = parseFileMention(raw[index], index)\n if (seenPaths.has(mention.path)) continue\n seenPaths.add(mention.path)\n mentions.push(mention)\n }\n return mentions\n}\n\n/** Validates the untyped `parts` array off the wire. Returns the typed parts\n * or throws `ChatTurnInputError` (400) naming the offending entry. */\nexport function parseChatTurnParts(raw: unknown): ChatTurnFilePartInput[] {\n if (raw === undefined || raw === null) return []\n if (!Array.isArray(raw)) throw new ChatTurnInputError('parts must be an array')\n return raw.map((entry, index) => {\n const part = entry as Record<string, unknown> | null\n if (!part || typeof part !== 'object') {\n throw new ChatTurnInputError(`parts[${index}] must be an object`)\n }\n if (part.type !== 'image' && part.type !== 'file') {\n throw new ChatTurnInputError(`parts[${index}].type must be 'image' or 'file'`)\n }\n for (const key of ['filename', 'mediaType', 'url', 'path', 'content'] as const) {\n if (part[key] !== undefined && typeof part[key] !== 'string') {\n throw new ChatTurnInputError(`parts[${index}].${key} must be a string`)\n }\n }\n if (!part.url && !part.path && !part.content) {\n throw new ChatTurnInputError(`parts[${index}] needs a url, path, or content`)\n }\n return {\n type: part.type,\n ...(part.filename !== undefined ? { filename: part.filename as string } : {}),\n ...(part.mediaType !== undefined ? { mediaType: part.mediaType as string } : {}),\n ...(part.url !== undefined ? { url: part.url as string } : {}),\n ...(part.path !== undefined ? { path: part.path as string } : {}),\n ...(part.content !== undefined ? { content: part.content as string } : {}),\n }\n })\n}\n","/**\n * The stored shape of `message.parts` — one typed vocabulary for every part a\n * product persists into a chat transcript. NOT an ad-hoc union reverse-\n * engineered from product schemas; each member is matched field-for-field to\n * its canonical source:\n *\n * - `text` / `reasoning` / `tool`: the persisted projection `/stream`'s\n * `normalizePersistedPart` produces from the harness lane's\n * `message.part.updated` events (ADC sidecar\n * `apps/sidecar/src/events/session-events.ts:56` wraps the canonical part in\n * an `{id, sessionID, messageID}` envelope; the projection strips the\n * session/message ids and keeps the per-segment part id).\n * - `file` / `image` / `step-start` / `step-finish`: the sidecar's canonical\n * `MessagePartSchema` members (ADC\n * `apps/sidecar/src/schemas/agent-schemas.ts:50-154`); `step-finish` carries\n * the harness's per-step usage receipt — tokens\n * `{total, input, output, reasoning, cache{write, read}}` + `cost` — which is\n * also the shape the message-level token/cost columns mirror.\n * - `subtask`: `@tangle-network/agent-interface`'s `SubtaskPart` (a spawned\n * sub-agent task).\n * - `interaction` / `notice`: the persisted-part codecs in\n * `/web-react`'s chat-interactions contract (`interactionToPersistedPart`,\n * `noticePart`) — type-only imports, one source of truth for their statuses\n * and field shapes.\n * - `plan`: the durable-plan projection in `/plans`, derived from the sandbox\n * SDK's authoritative plan lifecycle.\n * - `mention`: an `@`-picked reference to a file that already lives in the\n * workspace sandbox (`FileMention` in `/chat-routes`'s wire contract, plus\n * the image/file discriminant). Neither transport lane produces it — the\n * turn route persists it from the request's `mentions` field — but it is a\n * part a product persists into a transcript, so it belongs in this\n * vocabulary rather than in a parallel one.\n *\n * `@tangle-network/agent-interface` exports the canonical wire `Part` union,\n * but its `PartBase` requires the `sessionID`/`messageID` stream envelope that\n * is deliberately NOT persisted, so the stored union is defined here as the\n * envelope-free projection (a type-level coverage check against the peer's\n * `Part['type']` lives in the tests). Contribute-down candidate: if\n * agent-interface grows envelope-free persisted-part types, re-export them\n * here and delete these definitions.\n *\n * Two transport lanes serialize into this SAME stored shape:\n * - harness lane: canonical `message.part.updated` parts, merged/normalized by\n * `/stream` (`mergePersistedPart`, `finalizeAssistantParts`);\n * - router/openai-compat lane: `text_delta`/`tool_call` stream events are\n * mapped INTO canonical part events first (`/runtime`'s `toLoopEvents` +\n * `/stream`'s `normalizeToolEvent`) and then persisted identically — the\n * store never sees a router-specific shape.\n */\n\nimport type { Part as HarnessWirePart } from '@tangle-network/agent-interface'\nimport type {\n ChatInteractionField,\n ChatInteractionStatus,\n InteractionAnswers,\n InteractionPersistedPart,\n NoticeKind,\n NoticePersistedPart,\n} from '../web-react/chat-interactions'\nimport { persistedPartToInteraction } from '../interactions/contract'\nimport {\n persistedPartToPlan,\n planToPersistedPart,\n type ChatPlanPersistedPart,\n} from '../plans/index'\n// `./wire` is import-free by construction, so this edge costs the store\n// nothing and keeps ONE image-extension table for the whole mention layer.\nimport {\n mentionKindForPath,\n type ChatAttachmentInput,\n type ChatAttachmentKind,\n type ChatMentionKind,\n type FileMention,\n} from '../chat-routes/wire'\n\nexport type { ChatMentionKind, ChatAttachmentKind }\n\n/** Start/end wall-clock millis, as normalized by `/stream`'s `normalizeTime`. */\nexport interface ChatPartTime {\n start?: number\n end?: number\n}\n\n/** `id` is the harness's per-segment identity; absent on legacy/router parts,\n * which collapse to a single logical text stream. Never invented client-side. */\nexport interface ChatTextPart {\n type: 'text'\n text: string\n id?: string\n}\n\n/** Define a reasoning part of a chat with text content and optional metadata fields */\nexport interface ChatReasoningPart {\n type: 'reasoning'\n text: string\n id?: string\n time?: ChatPartTime\n}\n\n/** Superset of the sidecar's status enum (`pending|running|completed|failed`)\n * and agent-interface's `ToolState` statuses; `error` is the persisted\n * terminal form `/stream`'s `normalizePersistedPart` settles on. */\nexport type ChatToolStatus = 'pending' | 'running' | 'completed' | 'error' | 'failed'\n\n/** Describe the current state and data of a chat tool including status, input, output, and metadata */\nexport interface ChatToolState {\n status: ChatToolStatus\n input?: unknown\n output?: unknown\n error?: string\n title?: string\n metadata?: Record<string, unknown>\n time?: ChatPartTime\n}\n\n/** Define a chat component representing a tool with its state and optional call identifier */\nexport interface ChatToolPart {\n type: 'tool'\n id: string\n tool: string\n callID?: string\n state: ChatToolState\n}\n\n/** Union of the sidecar's legacy (path-based) and AI-SDK (url-based) file\n * shapes; response-side every field besides `type` is optional. `name` is the\n * attachment-flavored display label an uploaded/promoted file carries (see\n * {@link ChatAttachmentPart}) — absent on a harness-emitted file part, which\n * uses `filename`. */\nexport interface ChatFilePart {\n type: 'file'\n id?: string\n filename?: string\n name?: string\n mediaType?: string\n url?: string\n path?: string\n content?: string\n}\n\n/** Define properties for an image part within a chat message including optional metadata fields */\nexport interface ChatImagePart {\n type: 'image'\n filename?: string\n name?: string\n mediaType?: string\n url?: string\n path?: string\n}\n\n/** Define a subtask part of a chat with prompt, description, agent, and optional identifier */\nexport interface ChatSubtaskPart {\n type: 'subtask'\n prompt: string\n description: string\n agent: string\n id?: string\n}\n\n/** OpenCode step-boundary marker — no renderable text; preserved so mappers\n * never coerce it into a \"[object Object]\" text part. */\nexport interface ChatStepStartPart {\n type: 'step-start'\n}\n\n/** Per-step usage receipt as the harness reports it (sidecar\n * `StepFinishPartSchema`). The message-level token/cost columns are this\n * shape flattened. */\nexport interface ChatUsageTokens {\n total?: number\n input?: number\n output?: number\n reasoning?: number\n cache?: {\n write?: number\n read?: number\n }\n}\n\n/** Define a chat step finish part indicating completion with optional reason, tokens, and cost */\nexport interface ChatStepFinishPart {\n type: 'step-finish'\n reason?: string\n tokens?: ChatUsageTokens\n cost?: number\n}\n\n/** Persisted human-in-the-loop ask — byte-matches\n * `interactionToPersistedPart` in `/web-react`'s chat-interactions contract. */\nexport interface ChatInteractionPart {\n type: 'interaction'\n id: string\n kind: string\n title: string\n body?: string\n answerSpec: { fields: ChatInteractionField[] }\n status: ChatInteractionStatus\n answers?: InteractionAnswers\n cancelReason?: string\n}\n\n/** Resolve a chat plan part by aliasing it to the persisted chat plan part type */\nexport type ChatPlanPart = ChatPlanPersistedPart\n\n/** Persisted one-line transcript notice — byte-matches `noticePart` in\n * `/web-react`'s chat-interactions contract. */\nexport interface ChatNoticePart {\n type: 'notice'\n id: string\n noticeKind: NoticeKind\n text: string\n}\n\n/**\n * A file the user `@`-mentioned on this turn: a workspace-relative path into\n * the sandbox, never bytes. `type: 'mention'` is its own discriminant\n * precisely so it does NOT collide with the `file`/`image` attachment parts —\n * an attachment carries content the product uploaded, a mention points at\n * something the box already has, and a transcript renders them differently\n * (an inline pill, not an attachment card).\n *\n * `path` is the identity: mentioning one file twice in a turn folds to one\n * part. `turnId` is optional and set by products that rebuild a turn's\n * mentions on retry.\n */\nexport interface ChatMentionPart {\n type: 'mention'\n mentionKind: ChatMentionKind\n path: string\n name: string\n size?: number\n turnId?: string\n}\n\n// The \"byte-matches\" claims above, enforced at compile time: the interaction\n// contract's codec output types and the stored part types must stay mutually\n// assignable, so a codec field added on one side without the other fails here.\ntype MutuallyAssignable<A extends B, B> = A\ntype _CodecEmitsStorableInteractionPart = MutuallyAssignable<InteractionPersistedPart, ChatInteractionPart>\ntype _StoredInteractionPartFeedsCodec = MutuallyAssignable<ChatInteractionPart, InteractionPersistedPart>\ntype _CodecEmitsStorableNoticePart = MutuallyAssignable<NoticePersistedPart, ChatNoticePart>\ntype _StoredNoticePartFeedsCodec = MutuallyAssignable<ChatNoticePart, NoticePersistedPart>\ntype _CodecEmitsStorablePlanPart = MutuallyAssignable<ChatPlanPersistedPart, ChatPlanPart>\ntype _StoredPlanPartFeedsCodec = MutuallyAssignable<ChatPlanPart, ChatPlanPersistedPart>\n\n/** Represent parts of a chat message including text, reasoning, tools, files, images, subtasks, steps, interactions, notices, plans, and mentions */\nexport type ChatMessagePart =\n | ChatTextPart\n | ChatReasoningPart\n | ChatToolPart\n | ChatFilePart\n | ChatImagePart\n | ChatSubtaskPart\n | ChatStepStartPart\n | ChatStepFinishPart\n | ChatInteractionPart\n | ChatNoticePart\n | ChatPlanPart\n | ChatMentionPart\n\n/** Every canonical harness wire-part kind must be storable — compile-time\n * guarantee that a new agent-interface part kind cannot silently fall out of\n * the persisted vocabulary. */\nexport type StorableHarnessPartKind = HarnessWirePart['type'] & ChatMessagePart['type']\n\n/**\n * The typed projection at the `/stream` → `/chat-store` boundary. The stream\n * normalizers (`normalizePersistedPart`/`mergePersistedPart`/\n * `finalizeAssistantParts`) deliberately produce untyped `JsonRecord`s — they\n * normalize wire shapes and do not own the stored vocabulary. THIS module\n * owns it, so this is where rows gain the `ChatMessagePart` type: each entry\n * is validated against its kind's required fields and narrowed, junk is\n * dropped, and — enforced by the exhaustiveness check below — no storable\n * kind can silently fall out (the step-finish/interaction trap).\n */\nexport function toChatMessageParts(parts: Array<Record<string, unknown>>): ChatMessagePart[] {\n const out: ChatMessagePart[] = []\n for (const part of parts) {\n const typed = toChatMessagePart(part)\n if (typed) out.push(typed)\n }\n return out\n}\n\nconst str = (value: unknown): value is string => typeof value === 'string'\n\nfunction toChatMessagePart(part: Record<string, unknown>): ChatMessagePart | null {\n if (!part || typeof part !== 'object') return null\n const type = part.type as ChatMessagePart['type'] | undefined\n switch (type) {\n case 'text':\n case 'reasoning':\n return str(part.text) ? (part as unknown as ChatTextPart | ChatReasoningPart) : null\n case 'tool':\n return str(part.id) && str(part.tool) && part.state && typeof part.state === 'object'\n ? (part as unknown as ChatToolPart)\n : null\n case 'file':\n case 'image':\n return part as unknown as ChatFilePart | ChatImagePart\n case 'subtask':\n return str(part.prompt) && str(part.description) && str(part.agent)\n ? (part as unknown as ChatSubtaskPart)\n : null\n case 'step-start':\n return { type: 'step-start' }\n case 'step-finish':\n return part as unknown as ChatStepFinishPart\n case 'interaction':\n return persistedPartToInteraction(part) ? (part as unknown as ChatInteractionPart) : null\n case 'notice':\n return str(part.id) && str(part.noticeKind) && str(part.text)\n ? (part as unknown as ChatNoticePart)\n : null\n case 'plan': {\n const plan = persistedPartToPlan(part)\n return plan ? ({ ...part, ...planToPersistedPart(plan) } as ChatPlanPart) : null\n }\n case 'mention':\n return isChatMentionPart(part) ? part : null\n case undefined:\n return null\n default: {\n // Compile-time exhaustiveness: a new ChatMessagePart kind that is not\n // handled above makes `type` non-never here and this line fails.\n const _exhaustive: never = type\n void _exhaustive\n return null\n }\n }\n}\n\n/** Resolve whether a ChatMessagePart is specifically a ChatToolPart based on its type property */\nexport function isChatToolPart(part: ChatMessagePart): part is ChatToolPart {\n return part.type === 'tool'\n}\n\n/** Resolve whether a chat message part is a text part based on its type property */\nexport function isChatTextPart(part: ChatMessagePart): part is ChatTextPart {\n return part.type === 'text'\n}\n\n/** Resolve whether a ChatMessagePart is a ChatInteractionPart based on its type property */\nexport function isChatInteractionPart(part: ChatMessagePart): part is ChatInteractionPart {\n return part.type === 'interaction'\n}\n\n/** Resolve whether a chat message part is a persisted chat plan part */\nexport function isChatPlanPart(part: ChatMessagePart): part is ChatPlanPart {\n return part.type === 'plan'\n}\n\n/** Determine if a chat message part represents the completion of a chat step */\nexport function isChatStepFinishPart(part: ChatMessagePart): part is ChatStepFinishPart {\n return part.type === 'step-finish'\n}\n\n/** Widened to `unknown` — unlike its siblings this guard also runs over raw\n * untyped stored rows (a transcript renderer reads `message.parts` before the\n * typed projection), which is exactly what {@link mentionPartsFromMessageParts}\n * needs. `path` and `name` carry the pill; a row missing either is unrenderable.\n *\n * Mirrors the write contract exactly (`parseFileMention` in `/chat-routes`,\n * then {@link mentionInputToPart}): a blank `name` is rejected there and so is\n * rejected here, and `size` — optional, but typed `number` once present — is\n * type-checked so `'12'` or `null` cannot ride through the guard wearing a\n * type it does not have. Negative sizes are NOT re-rejected: the wire screens\n * them, `mentionInputToPart` trusts its input, and a read guard stricter than\n * what the writer can emit would drop rows it produced itself. */\nexport function isChatMentionPart(part: unknown): part is ChatMentionPart {\n if (!part || typeof part !== 'object') return false\n const record = part as Record<string, unknown>\n if (record.size !== undefined && (typeof record.size !== 'number' || !Number.isFinite(record.size))) {\n return false\n }\n return (\n record.type === 'mention' &&\n typeof record.path === 'string' &&\n record.path.length > 0 &&\n typeof record.name === 'string' &&\n record.name.trim().length > 0 &&\n (record.mentionKind === 'image' || record.mentionKind === 'file')\n )\n}\n\n/** Every mention part on one message, in stored order. The projection a\n * transcript renderer runs before deciding which mentions the message text\n * already shows inline (see `segmentMentionContent` in `/web-react`). */\nexport function mentionPartsFromMessageParts(\n parts: ReadonlyArray<Record<string, unknown>> | ReadonlyArray<ChatMessagePart> | null | undefined,\n): ChatMentionPart[] {\n if (!parts) return []\n return (parts as ReadonlyArray<unknown>).filter(isChatMentionPart)\n}\n\n/** A validated wire mention (`parseFileMentions` in `/chat-routes`) as the\n * part the turn route persists. An absent/non-finite `size` is DROPPED rather\n * than stored as `undefined`, so a stored row never carries a key that means\n * nothing. */\nexport function mentionInputToPart(input: FileMention): ChatMentionPart {\n const part: ChatMentionPart = {\n type: 'mention',\n mentionKind: mentionKindForPath(input.path),\n path: input.path,\n name: input.name,\n }\n if (typeof input.size === 'number' && Number.isFinite(input.size)) part.size = input.size\n return part\n}\n\n// ── attachments ──────────────────────────────────────────────────────────\n//\n// An attachment is a file the product uploaded into its own store (vault /\n// object-store) ahead of the turn — content the box did not already have,\n// unlike a `@`-mention. It persists NOT as a new `ChatMessagePart` union\n// member but as an attachment-flavored `file`/`image` part: `type` is the\n// existing `image`/`file` discriminant, with `path` + `name` load-bearing.\n// That reuse is deliberate — a transcript already renders `file`/`image`\n// parts, and `toChatMessagePart` passes them through by cast (fields survive),\n// so an attachment slots into the same stored `parts` array without a parallel\n// vocabulary. These helpers own the input→part projection, the path-keyed\n// stream key, and the agent-facing prompt block, all byte-identical to the gtm\n// source they were lifted from (gtm-agent#618 adoption reproduces its rows and\n// dispatched prompt bytes through them).\n\n/** `image` for any `image/*` mime, `file` for everything else (including an\n * absent/empty mime) — the same split the composer and the persisted-part\n * discriminant both key on. */\nexport function attachmentKindForMime(mime?: string): ChatAttachmentKind {\n return mime?.startsWith('image/') ? 'image' : 'file'\n}\n\n/** Stream/transcript part key for an attachment — ONE declaration, owned by\n * `/stream`'s normalizer (whose `getPartKey` routes path-bearing file/image\n * parts through it). Re-exported here (not redefined) so the root barrel's\n * star-exports of both modules resolve to the same symbol instead of\n * colliding (TS2308 in the dts build). */\nexport { attachmentPartKey } from '../stream/stream-normalizer'\n\n/**\n * Persisted attachment part: structurally an attachment-flavored `ChatFilePart`\n * / `ChatImagePart` (`path` + `name` promoted to required) rather than a\n * separate union member — see the section note above. The index signature keeps\n * it a `Record<string, unknown>` so it drops into the same untyped `parts`\n * arrays every other stored part rides in, and lets the `type`-driven\n * constructors below build it without union-discriminant gymnastics.\n */\nexport interface ChatAttachmentPart {\n type: ChatAttachmentKind\n path: string\n name: string\n size?: number\n mediaType?: string\n turnId?: string\n [key: string]: unknown\n}\n\n/** True for any `image`/`file` part carrying a non-empty string `path`.\n * DELIBERATELY also matches a sidecar path-bearing file part (a mention-style\n * `file` part with only `path`) — the guard is a structural attachment shape,\n * not proof of provenance; a caller that needs to exclude those filters\n * upstream. The `path.length > 0` check diverges from gtm's original guard\n * (which only checked `typeof`), mirroring sibling {@link isChatMentionPart}:\n * read-side robustness against a stored `{type:'file', path:''}` row — which\n * would otherwise render a malformed `(vault: )` pointer line — justifies the\n * small divergence from byte-for-byte gtm parity here; no legitimate write\n * path ever produces an empty `path`. */\nexport function isChatAttachmentPart(part: unknown): part is ChatAttachmentPart {\n if (!part || typeof part !== 'object') return false\n const record = part as Record<string, unknown>\n return (\n (record.type === 'image' || record.type === 'file') &&\n typeof record.path === 'string' &&\n record.path.length > 0\n )\n}\n\n/** Drop absent/empty optional fields rather than persisting `undefined`/`''`\n * — keeps stored parts minimal. */\nexport function attachmentInputToPart(input: ChatAttachmentInput): ChatAttachmentPart {\n const part: ChatAttachmentPart = { type: input.kind, path: input.path, name: input.name }\n if (typeof input.size === 'number' && Number.isFinite(input.size)) part.size = input.size\n if (input.mediaType) part.mediaType = input.mediaType\n return part\n}\n\n/** Every attachment part on one message's stored `parts`, in stored order. */\nexport function attachmentPartsFromMessageParts(\n parts: ReadonlyArray<Record<string, unknown>> | null | undefined,\n): ChatAttachmentPart[] {\n if (!parts) return []\n return parts.filter(isChatAttachmentPart)\n}\n\n/** gtm's exact default header for {@link buildAttachmentPromptBlock} — kept\n * byte-identical because it feeds dispatched prompts (gtm-agent#618\n * reproduces the prompt bytes through this module). Override only with intent. */\nexport const DEFAULT_ATTACHMENT_PROMPT_HEADER =\n 'Attached files (already saved to the workspace vault — read them from these paths):'\n\n/** Same C0-control/DEL sweep as `/chat-routes`'s `resolve-attachments.ts`\n * (kept as a separate literal rather than a shared import — this module is\n * the read side and must stay usable over PERSISTED rows the wire validator\n * never saw). Replaced with a single space, not stripped or collapsed: this\n * is defense-in-depth for legacy/corrupt stored rows that bypassed wire\n * validation, not the primary control — a name/path a `resolveChatAttachments`\n * call validated today never contains one, so this is a no-op on legitimate\n * input and gtm byte-compat holds. */\nconst PROMPT_BLOCK_CONTROL_CHARS = /[\\x00-\\x1F\\x7F]/g\n\n/**\n * The agent-facing pointer block appended to the dispatched prompt — never\n * persisted in `message.content`. Empty array → `''` so callers can append\n * unconditionally. The sole producer of this text: both the current turn's\n * dispatch and history projection ({@link historyContentWithAttachments}) build\n * it from here, so the two can never drift. `header` overrides the first line\n * ({@link DEFAULT_ATTACHMENT_PROMPT_HEADER}); the per-file line format is fixed.\n *\n * Every interpolated `name`/`path` is swept for control characters first\n * (newline/carriage-return/tab included) and each one replaced with a single\n * space — a single-line-per-file guarantee. Without it, a control character\n * riding through on a PERSISTED row (`historyContentWithAttachments` reads\n * whatever is already stored; a legacy or hand-edited row was never re-checked\n * against `resolveChatAttachments`) could inject extra lines — fabricated\n * pointer entries or instructions — into a prompt the agent trusts verbatim.\n * The wire-side validator is the primary control; this is the backstop for\n * rows it never saw.\n */\nexport function buildAttachmentPromptBlock(\n atts: ReadonlyArray<Pick<ChatAttachmentPart, 'name' | 'path'>>,\n header: string = DEFAULT_ATTACHMENT_PROMPT_HEADER,\n): string {\n if (atts.length === 0) return ''\n const clean = (value: string) => value.replace(PROMPT_BLOCK_CONTROL_CHARS, ' ')\n const lines = atts.map((a) => `- ${clean(a.name)} (vault: ${clean(a.path)})`)\n return `\\n\\n${header}\\n${lines.join('\\n')}`\n}\n\n/**\n * History projection for a persisted message: `content` unchanged when it\n * carries no attachment parts, else the block is appended once. The\n * no-double-annotation guarantee holds under the invariant that the block is\n * never persisted INTO `message.content` — it is appended only at read time,\n * by this function and the current turn's dispatch, both sourcing from\n * `attachmentPartsFromMessageParts` — never by content inspection. This\n * function does not (and cannot) detect an already-embedded block by scanning\n * `content` for its text: a message whose stored `content` happens to already\n * contain the block's bytes, alongside attachment parts, still gets the block\n * appended a second time. See the pinned test for that boundary.\n */\nexport function historyContentWithAttachments(\n message: {\n content: string\n parts?: ReadonlyArray<Record<string, unknown>> | null\n },\n header?: string,\n): string {\n const attachmentParts = attachmentPartsFromMessageParts(message.parts)\n if (attachmentParts.length === 0) return message.content\n return `${message.content}${buildAttachmentPromptBlock(attachmentParts, header)}`\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
DesignCanvas,
|
|
5
5
|
PlusGlyph,
|
|
6
6
|
ShapesGlyph
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-22GLESKE.js";
|
|
8
8
|
import {
|
|
9
9
|
DEFAULT_INSERT_TEMPLATES,
|
|
10
10
|
collectGridTargets,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
isExportHiddenNodeName,
|
|
17
17
|
marqueeSelect,
|
|
18
18
|
resolveExportParams
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-VYECQAGM.js";
|
|
20
20
|
import {
|
|
21
21
|
addElementCommand,
|
|
22
22
|
createSceneCommandStack,
|
|
@@ -25,16 +25,16 @@ import {
|
|
|
25
25
|
multiSetAttrsCommand,
|
|
26
26
|
setAttrsCommand,
|
|
27
27
|
ungroupElementCommand
|
|
28
|
-
} from "./chunk-
|
|
29
|
-
import {
|
|
30
|
-
lightTheme
|
|
31
|
-
} from "./chunk-47C6PJCW.js";
|
|
28
|
+
} from "./chunk-VTWKH4YU.js";
|
|
32
29
|
import {
|
|
33
30
|
SCENE_SCHEMA_VERSION,
|
|
34
31
|
boundsIntersect,
|
|
35
32
|
elementAabb,
|
|
36
33
|
findElement
|
|
37
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-VWOPOLVO.js";
|
|
35
|
+
import {
|
|
36
|
+
lightTheme
|
|
37
|
+
} from "./chunk-TZO3SOUL.js";
|
|
38
38
|
|
|
39
39
|
// src/design-canvas-react/components/Workspace.tsx
|
|
40
40
|
import {
|
|
@@ -1911,4 +1911,4 @@ export {
|
|
|
1911
1911
|
Workspace,
|
|
1912
1912
|
DesignCanvasEditor
|
|
1913
1913
|
};
|
|
1914
|
-
//# sourceMappingURL=chunk-
|
|
1914
|
+
//# sourceMappingURL=chunk-E7N67FYI.js.map
|