chainlesschain 0.162.82 → 0.162.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-C1dEj8Gv.js → AIOps-CUPGM_Kx.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-CzXXDnBr.js → ActionButton-Gpo_8hNk.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-hdY26dTn.js → Analytics-gUz2I_9P.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-D6vzt_W0.js → AppLayout-8S6XH1S0.js} +3 -3
  6. package/src/assets/web-panel/assets/{Audit-CyavCkKh.js → Audit-DspRXpoj.js} +1 -1
  7. package/src/assets/web-panel/assets/{Backup-CN26lWvD.js → Backup-CrO4UCdI.js} +1 -1
  8. package/src/assets/web-panel/assets/{BaseInput-DuztEFOJ.js → BaseInput-BkEVF4o4.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-BtR0IxAD.js → Chat-B9EvII6G.js} +5 -5
  10. package/src/assets/web-panel/assets/{ChatBubbleRenderer-3Fqx4Hr2.js → ChatBubbleRenderer-BfXGm9lb.js} +1 -1
  11. package/src/assets/web-panel/assets/{Checkbox-DpZIx_RE.js → Checkbox-BrDQRcU4.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-CHTbeOJl.js → Codegen-CqBoplXd.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col-BpLy4N8M.js → Col-CbQa3Dci.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-Bjj8jRSr.js → Community-C03LI8ue.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-JRM5b_T8.js → Compact-D_XPXg9d.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-BhgyZEaK.js → Compliance-B5sBpZMO.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-DptkLSOO.js → Cowork-WnDWZ2l6.js} +2 -2
  18. package/src/assets/web-panel/assets/{Cron-DnKASPO9.js → Cron-CMIJXLkp.js} +2 -2
  19. package/src/assets/web-panel/assets/{Crosschain-DyutKwBT.js → Crosschain-DLptM6-5.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-26hGyWNl.js → DID-svP2FmRz.js} +2 -2
  21. package/src/assets/web-panel/assets/{Dashboard-TR0q4qh-.js → Dashboard-CO5hKuwh.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dropdown-xvwRnUkx.js → Dropdown-CQIK5Gf6.js} +1 -1
  23. package/src/assets/web-panel/assets/{EmailListRenderer-FMmTGI6O.js → EmailListRenderer-CBBq-Mwz.js} +1 -1
  24. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BZA7U05u.js → FamilyGuardDashboard-DgcxYtqz.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-CyDZU8Vb.js → Federation-Di9NPJ6Y.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-Bwpx_qG1.js → FormItemContext-CwjF4P1N.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-BOmjGKYM.js → GenericCardRenderer-BZRv0AD7.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-BnAkhaIF.js → Git-BUpxPamc.js} +2 -2
  29. package/src/assets/web-panel/assets/{Governance-C47Eochw.js → Governance-D-RKals0.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-CFUir5Av.js → Inference-CEoHP1e3.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-C-rgIKPB.js → KnowledgeGraph-Bzb02Le2.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-B2ECWeOD.js → Logs-C80J5aHm.js} +2 -2
  33. package/src/assets/web-panel/assets/{Marketplace-Bd24otS-.js → Marketplace-CLeqc86x.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-BEh50uI4.js → McpTools-C6_3jvji.js} +5 -5
  35. package/src/assets/web-panel/assets/{Memory-ClE1QgWc.js → Memory-CB1VmvbL.js} +2 -2
  36. package/src/assets/web-panel/assets/{MobileBridge-DMsIqebF.js → MobileBridge-PPO8M2EY.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileProjects-yFLjy9f4.js → MobileProjects-Bu-ul-bk.js} +1 -1
  38. package/src/assets/web-panel/assets/{Mtc-lnmQdmxN.js → Mtc-CY0506vU.js} +4 -4
  39. package/src/assets/web-panel/assets/{MtcAudit-DEdi9KlB.js → MtcAudit-BV-IqDNO.js} +6 -6
  40. package/src/assets/web-panel/assets/{Multisig-EMtJxjIi.js → Multisig-8YiHk7pd.js} +3 -3
  41. package/src/assets/web-panel/assets/{NLProgramming-BiUmSSO5.js → NLProgramming-DCFi8iuv.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-3lpy_K7F.js → Notes-pcK3qdfo.js} +4 -4
  43. package/src/assets/web-panel/assets/{NotificationSettings-DdV8KQ2W.js → NotificationSettings-DrU6Ac_p.js} +1 -1
  44. package/src/assets/web-panel/assets/{OrderTableRenderer-RiYZWIrY.js → OrderTableRenderer-tCu7jh7L.js} +1 -1
  45. package/src/assets/web-panel/assets/{Organization-D4pXjOf7.js → Organization-r7rkShQ7.js} +4 -4
  46. package/src/assets/web-panel/assets/{Overflow-Bgiby6WK.js → Overflow-DGOBthyf.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-BHxhcSMm.js → P2P-DalC-NnK.js} +2 -2
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-BdUjqoZX.js → PdhVaultBrowser-4xxODAFn.js} +5 -5
  49. package/src/assets/web-panel/assets/{Permissions-DiT7TAuH.js → Permissions-C15WSlI2.js} +4 -4
  50. package/src/assets/web-panel/assets/{PersonalDataHub-yX_ynMWf.js → PersonalDataHub-DAGK05sL.js} +3 -3
  51. package/src/assets/web-panel/assets/{Pipeline-DSqiTMMR.js → Pipeline-NAji3Ktw.js} +1 -1
  52. package/src/assets/web-panel/assets/{Privacy-CEqDwNuF.js → Privacy-CCRUEGFK.js} +1 -1
  53. package/src/assets/web-panel/assets/{ProjectInit-5QUyd1ln.js → ProjectInit-DVmQTOeF.js} +2 -2
  54. package/src/assets/web-panel/assets/{ProjectSettings-CC6VlFAp.js → ProjectSettings-1NVdULNr.js} +2 -2
  55. package/src/assets/web-panel/assets/{Projects-CxRagMyT.js → Projects-ws0_zzCP.js} +1 -1
  56. package/src/assets/web-panel/assets/{Providers-MmzWjI1Z.js → Providers-Bsi5PeyG.js} +1 -1
  57. package/src/assets/web-panel/assets/{QuickAsk-BvC_QGmG.js → QuickAsk-Teh85bAz.js} +1 -1
  58. package/src/assets/web-panel/assets/{Recommend--uuUiAeR.js → Recommend-CnBrDiNE.js} +1 -1
  59. package/src/assets/web-panel/assets/{Reputation-D4mGDQhe.js → Reputation-BRuy4vvz.js} +1 -1
  60. package/src/assets/web-panel/assets/{Row-DEHR17nP.js → Row-AknoXYnV.js} +1 -1
  61. package/src/assets/web-panel/assets/{RssFeed-Dhd5LlvJ.js → RssFeed-BYhR3Mr1.js} +2 -2
  62. package/src/assets/web-panel/assets/{Search-BKKaAsdv.js → Search-CCVNTHhz.js} +1 -1
  63. package/src/assets/web-panel/assets/{Security-BNEZq_5G.js → Security-DtxuRS8f.js} +3 -3
  64. package/src/assets/web-panel/assets/{Services-DnnD83Th.js → Services-CH9pcUuR.js} +2 -2
  65. package/src/assets/web-panel/assets/{Skeleton-DRN2nuYV.js → Skeleton-BVCIpTSu.js} +1 -1
  66. package/src/assets/web-panel/assets/{Skills-CZ8v6lET.js → Skills-Bh0_27fL.js} +1 -1
  67. package/src/assets/web-panel/assets/{Sla-fWXby0dV.js → Sla-Dt2ZjTIC.js} +1 -1
  68. package/src/assets/web-panel/assets/{SpeechSettings-W3mT-TCc.js → SpeechSettings-3oB4vztB.js} +1 -1
  69. package/src/assets/web-panel/assets/{SyncSettings-Cf6KahGB.js → SyncSettings-3EakfvY5.js} +2 -2
  70. package/src/assets/web-panel/assets/{Tasks-2y3yFyI7.js → Tasks-DrkWRRuK.js} +1 -1
  71. package/src/assets/web-panel/assets/{Templates-C35-32qQ.js → Templates-bJMb7OuJ.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tenant-BmdzuATF.js → Tenant-9T-oGFha.js} +1 -1
  73. package/src/assets/web-panel/assets/Terminal-CPZGmFF2.js +3 -0
  74. package/src/assets/web-panel/assets/{TimelineRenderer-5tp0WznW.js → TimelineRenderer-C6IcefQF.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tokens-DirT8R87.js → Tokens-DRBbMZ6n.js} +1 -1
  76. package/src/assets/web-panel/assets/{Trigger-DYnzdL_p.js → Trigger-CfBK_gR2.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trust-C_q6P5WI.js → Trust-Csi86O_m.js} +1 -1
  78. package/src/assets/web-panel/assets/{UkeySign-BUeK4hfx.js → UkeySign-Dh0RMWm3.js} +1 -1
  79. package/src/assets/web-panel/assets/{VideoEditing-BS_--Xsn.js → VideoEditing-CGVRp-xp.js} +1 -1
  80. package/src/assets/web-panel/assets/{Wallet-Bq_HZQqC.js → Wallet-ul6ZpClL.js} +4 -4
  81. package/src/assets/web-panel/assets/{WebAuthn-CY3WJ6nC.js → WebAuthn-B96-wm1H.js} +4 -4
  82. package/src/assets/web-panel/assets/{WorkflowEditor-DlPUi0YB.js → WorkflowEditor-CHvvdMYl.js} +1 -1
  83. package/src/assets/web-panel/assets/{chat-jQtA2Ab3.js → chat-CrmxJLgQ.js} +1 -1
  84. package/src/assets/web-panel/assets/{colors-ooTwaBud.js → colors-iw0H0UBl.js} +1 -1
  85. package/src/assets/web-panel/assets/{compact-item-BO5q8ejQ.js → compact-item-BoP3ciR6.js} +1 -1
  86. package/src/assets/web-panel/assets/{createContext-CQEFxH8G.js → createContext-CerRGWSU.js} +1 -1
  87. package/src/assets/web-panel/assets/devWarning-Cjez93XF.js +1 -0
  88. package/src/assets/web-panel/assets/{hasIn-Bfh8ym7j.js → hasIn-DsatQYJx.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-BLFFfptZ.js → index-0GW7IL2K.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-vz2NGSmb.js → index-4IhKBZK7.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-B_jRn2qu.js → index-7l1rmyaN.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-ttHp-hXj.js → index-8jD5bKYd.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-BDu0C8OQ.js → index-B19YcZYT.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-BRAE0gub.js → index-BMbuAe5D.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CWHoylCz.js → index-BP_JGTE7.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DIdJ8ejc.js → index-BWZD_xU3.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DYXirELz.js → index-BXvqWNro.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-_8VdJDx9.js → index-BbSrbGp1.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DSySRvTm.js → index-BcyvEERG.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-B4g0hyRy.js → index-BgHjBX0-.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-D-TuIKgh.js → index-Boik4lkC.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-wEzQ3n0H.js → index-BuhW7RGq.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-D67wRcX7.js → index-C1GAAh9I.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Vq5_4yRG.js → index-C1Ve7k2T.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-D184_rN5.js → index-C5r1VXhR.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Dplghama.js → index-CDkkLejB.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DpvBCXK2.js → index-CIKm0ljX.js} +1 -1
  108. package/src/assets/web-panel/assets/index-CN5J59xo.js +1 -0
  109. package/src/assets/web-panel/assets/{index-DenLVGTu.js → index-CSNbpxSb.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CXiTomlG.js → index-CSbZKYQE.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Bqd69z-n.js → index-CWjQpi8r.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-oEHdC_uw.js → index-Cxc0hflR.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DHKvkxN7.js → index-D0IzrHb1.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-U6Qz-HCH.js → index-D97QUify.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-B8GMkcIp.js → index-D9Sygqi9.js} +3 -3
  116. package/src/assets/web-panel/assets/{index-DBY9StK5.js → index-DAGrtlTV.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-B28q3m82.js → index-DDrhbD6H.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-kkC2JCE-.js → index-DKuaBG68.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-D_geVHb2.js → index-Dbpu7Y9S.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DBfaQNOv.js → index-DnQ7-JQY.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-wAc5u7Y-.js → index-DvQd5rvA.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BouXFnjK.js → index-Dx4n5tpI.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CnMG8pdx.js → index-KTJ86cMc.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-iknCkh9p.js → index-QwRb8Iww.js} +1 -1
  125. package/src/assets/web-panel/assets/index-RnzfY85C.js +1 -0
  126. package/src/assets/web-panel/assets/{index-CtbbCXw9.js → index-b43zAH3v.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-0InkUerO.js → index-ykZB3IUq.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-C0lDjMFG.js → initDefaultProps-C_C9YePV.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-CKqarhmp.js → motion-DsRTNcag.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-DU_4DXfe.js → move-CNQpgGaU.js} +1 -1
  131. package/src/assets/web-panel/assets/{omit-C486jH10.js → omit-B_C8ne12.js} +1 -1
  132. package/src/assets/web-panel/assets/{pickAttrs-Sivz4zN9.js → pickAttrs-D95no1kz.js} +1 -1
  133. package/src/assets/web-panel/assets/{placementArrow-Ca2RquTa.js → placementArrow-CqoJQLBC.js} +1 -1
  134. package/src/assets/web-panel/assets/{responsiveObserve-BvwPauMH.js → responsiveObserve-BvxPjVzY.js} +1 -1
  135. package/src/assets/web-panel/assets/{slide-B9onHJLI.js → slide-C1PZ8gjQ.js} +1 -1
  136. package/src/assets/web-panel/assets/{statusUtils-b_AgBw9w.js → statusUtils-CXlmu_Qa.js} +1 -1
  137. package/src/assets/web-panel/assets/{styleChecker-EtdQYqac.js → styleChecker-Yeh5dWMi.js} +1 -1
  138. package/src/assets/web-panel/assets/{useFlexGapSupport-Cp_Cx5AB.js → useFlexGapSupport-BA1uUeeV.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFs-Dwp7AWMH.js → useFs-TCL7sk7I.js} +1 -1
  140. package/src/assets/web-panel/assets/{usePersonalDataHub-BjHr52Az.js → usePersonalDataHub-DD0yPkw9.js} +1 -1
  141. package/src/assets/web-panel/assets/{vnode-CJkJ5hJu.js → vnode-Cxw9uvFj.js} +1 -1
  142. package/src/assets/web-panel/assets/{zoom-DJEBPq5j.js → zoom-DPRJdorO.js} +1 -1
  143. package/src/assets/web-panel/index.html +1 -1
  144. package/src/lib/image-input.js +90 -0
  145. package/src/repl/agent-repl.js +50 -5
  146. package/src/runtime/headless-stream.js +18 -2
  147. package/src/assets/web-panel/assets/Terminal-B6R_Mv2d.js +0 -3
  148. package/src/assets/web-panel/assets/devWarning-cfHzirPw.js +0 -1
  149. package/src/assets/web-panel/assets/index-CWB8kLmE.js +0 -1
  150. package/src/assets/web-panel/assets/index-DtaI9LEB.js +0 -1
@@ -44,6 +44,96 @@ export function resolveImages(paths, deps = {}) {
44
44
  });
45
45
  }
46
46
 
47
+ /**
48
+ * Claude-Code-style auto-detection: find local image-file paths mentioned in a
49
+ * prompt so the turn can attach them as vision input. A token is treated as an
50
+ * image only when (a) its extension is one we support and (b) the file actually
51
+ * exists on disk — so prose like "I edited a.png" attaches only when a.png is
52
+ * really there, and a typo'd/remote path stays as plain text. Matched path
53
+ * tokens are stripped from the returned text ("describe ./a.png" → "describe" +
54
+ * an attachment); an all-path message leaves empty text, which the caller turns
55
+ * into an image-only turn. URLs / data: URIs are never auto-attached (local
56
+ * files only). Pure except for the existence check (inject `deps.existsSync`).
57
+ *
58
+ * @param {string} text
59
+ * @param {object} deps { existsSync }
60
+ * @returns {{ images: string[], text: string }}
61
+ */
62
+ export function detectImagePaths(text, deps = {}) {
63
+ const existsSync = deps.existsSync || fs.existsSync;
64
+ if (typeof text !== "string" || !text.trim()) {
65
+ return { images: [], text: typeof text === "string" ? text : "" };
66
+ }
67
+ const exts = Object.keys(EXT_MEDIA)
68
+ .map((e) => e.slice(1))
69
+ .join("|"); // png|jpg|jpeg|gif|webp
70
+ // Quoted ("…"/'…') paths first (may contain spaces), then bare
71
+ // whitespace-delimited tokens ending in a supported image extension.
72
+ const re = new RegExp(
73
+ `"([^"]+?\\.(?:${exts}))"|'([^']+?\\.(?:${exts}))'|(\\S+?\\.(?:${exts}))(?=$|[\\s)\\]'",])`,
74
+ "gi",
75
+ );
76
+ const images = [];
77
+ const seen = new Set();
78
+ const ranges = [];
79
+ let m;
80
+ while ((m = re.exec(text)) !== null) {
81
+ const raw = m[1] || m[2] || m[3];
82
+ if (!raw) continue;
83
+ // Local files only — never auto-attach URLs / data: URIs.
84
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(raw) || /^data:/i.test(raw)) continue;
85
+ let exists = false;
86
+ try {
87
+ exists = existsSync(raw);
88
+ } catch {
89
+ exists = false;
90
+ }
91
+ if (!exists) continue;
92
+ if (!seen.has(raw)) {
93
+ seen.add(raw);
94
+ images.push(raw);
95
+ }
96
+ ranges.push([m.index, m.index + m[0].length]);
97
+ }
98
+ if (!images.length) return { images: [], text };
99
+ let out = text;
100
+ for (let i = ranges.length - 1; i >= 0; i--) {
101
+ out = out.slice(0, ranges[i][0]) + out.slice(ranges[i][1]);
102
+ }
103
+ out = out.replace(/ {2,}/g, " ").trim();
104
+ return { images, text: out };
105
+ }
106
+
107
+ /**
108
+ * Compose a REPL/interactive turn from raw typed text: auto-detect local image
109
+ * paths, build the user-message content, and (on an image turn) the per-turn
110
+ * vision-LLM override. Pure orchestration over detectImagePaths/resolveImages/
111
+ * buildUserContent/resolveVisionLlm so the interactive wiring is unit-testable
112
+ * (the REPL itself forces a terminal and can't be driven headlessly).
113
+ *
114
+ * @param {string} text the user's typed message
115
+ * @param {object} llm { provider, baseUrl, apiKey, visionModel } current LLM
116
+ * @param {object} deps { existsSync, fs } injected for tests
117
+ * @returns {{ content, images: string[], visionLlm: object|null, text: string }}
118
+ * content is a plain string when no image is detected, else a multimodal
119
+ * array; visionLlm is the per-turn provider/model/baseUrl/apiKey override
120
+ * (null when there are no images); text is the path-stripped prompt.
121
+ */
122
+ export function prepareVisionTurn(text, llm = {}, deps = {}) {
123
+ const detected = detectImagePaths(text, deps);
124
+ if (!detected.images.length) {
125
+ return { content: text, images: [], visionLlm: null, text };
126
+ }
127
+ const finalText = detected.text || "Please look at the attached image(s).";
128
+ const resolved = resolveImages(detected.images, deps); // throws on bad ext
129
+ return {
130
+ content: buildUserContent(finalText, resolved),
131
+ images: detected.images,
132
+ visionLlm: resolveVisionLlm({ hasImage: true, flags: {}, llm }),
133
+ text: finalText,
134
+ };
135
+ }
136
+
47
137
  /**
48
138
  * Build a user-message `content`: the plain string when there are no images,
49
139
  * else an OpenAI-style multimodal array (the internal representation).
@@ -81,6 +81,7 @@ import {
81
81
  } from "../runtime/agent-core.js";
82
82
  import { formatBackgroundTasks } from "./tasks-status.js";
83
83
  import { expandFileRefsAsync } from "../runtime/file-ref-expander.js";
84
+ import { prepareVisionTurn } from "../lib/image-input.js";
84
85
  import { composeSystemPrompt } from "../runtime/system-prompt.js";
85
86
  import {
86
87
  makeFallbackChatFn,
@@ -279,6 +280,16 @@ export async function startAgentRepl(options = {}) {
279
280
  let _sessionTier = "strict";
280
281
  const baseUrl = options.baseUrl || "http://localhost:11434";
281
282
  const apiKey = options.apiKey || null;
283
+ // Configured vision model (config.llm.visionModel) — used when a turn carries
284
+ // an auto-detected image path so the REPL switches to a vision-capable model
285
+ // for that turn only (resolveVisionLlm falls back to the default when unset).
286
+ let _visionModel;
287
+ try {
288
+ const { loadConfig } = await import("../lib/config-manager.js");
289
+ _visionModel = loadConfig()?.llm?.visionModel || undefined;
290
+ } catch {
291
+ /* optional — resolveVisionLlm falls back to DEFAULT_VISION_MODEL */
292
+ }
282
293
  // Extra workspace roots (--add-dir): advertised in the system prompt and
283
294
  // spanned by search_files.
284
295
  const additionalDirectories = Array.isArray(options.additionalDirectories)
@@ -3075,8 +3086,40 @@ export async function startAgentRepl(options = {}) {
3075
3086
  // optional polish — never fail the turn over it
3076
3087
  }
3077
3088
 
3089
+ // Claude-Code-style: auto-attach local image paths typed in the message so
3090
+ // "describe ./shot.png" reads the image via the vision model (same as the
3091
+ // chat panels). CC_AUTO_IMAGE=0 opts out. `_visionLlm` (truthy on an image
3092
+ // turn) overrides this turn's provider/model/baseUrl/apiKey below. The
3093
+ // composition is the unit-tested `prepareVisionTurn` helper.
3094
+ let _visionLlm = null;
3095
+ let _userMessageContent = userContent;
3096
+ if (process.env.CC_AUTO_IMAGE !== "0") {
3097
+ try {
3098
+ const turn = prepareVisionTurn(userContent, {
3099
+ provider,
3100
+ baseUrl,
3101
+ apiKey,
3102
+ visionModel: _visionModel,
3103
+ });
3104
+ if (turn.visionLlm) {
3105
+ _userMessageContent = turn.content;
3106
+ _visionLlm = turn.visionLlm;
3107
+ logger.info(
3108
+ chalk.gray(
3109
+ `[image] ${turn.images.length} attached → vision model ${turn.visionLlm.model}`,
3110
+ ),
3111
+ );
3112
+ }
3113
+ } catch (e) {
3114
+ // Bad attachment (e.g. unreadable file) → send as plain text.
3115
+ _visionLlm = null;
3116
+ _userMessageContent = userContent;
3117
+ logger.info(chalk.yellow(`[image] ${e.message} — sending as text`));
3118
+ }
3119
+ }
3120
+
3078
3121
  // Add user message
3079
- messages.push({ role: "user", content: userContent });
3122
+ messages.push({ role: "user", content: _userMessageContent });
3080
3123
 
3081
3124
  // Slot-filling: detect intent and fill missing parameters interactively
3082
3125
  try {
@@ -3202,12 +3245,14 @@ export async function startAgentRepl(options = {}) {
3202
3245
  ),
3203
3246
  ),
3204
3247
  signal: _turnAbort.signal,
3205
- provider,
3206
- model: activeModel,
3248
+ // On an auto-detected image turn, switch to the vision LLM for this
3249
+ // turn only (provider/baseUrl/apiKey unchanged, model → vision model).
3250
+ provider: _visionLlm ? _visionLlm.provider : provider,
3251
+ model: _visionLlm ? _visionLlm.model : activeModel,
3207
3252
  thinking,
3208
3253
  thinkingBudget,
3209
- baseUrl,
3210
- apiKey,
3254
+ baseUrl: _visionLlm ? _visionLlm.baseUrl : baseUrl,
3255
+ apiKey: _visionLlm ? _visionLlm.apiKey : apiKey,
3211
3256
  contextEngine,
3212
3257
  iterationBudget,
3213
3258
  sessionId,
@@ -20,6 +20,7 @@ import { bootstrap } from "./bootstrap.js";
20
20
  import { buildSystemPrompt, agentLoop as coreAgentLoop } from "./agent-core.js";
21
21
  import { composeSystemPrompt } from "./system-prompt.js";
22
22
  import { expandFileRefsAsync } from "./file-ref-expander.js";
23
+ import { detectImagePaths } from "../lib/image-input.js";
23
24
  import {
24
25
  resolveAgentMcp,
25
26
  resolvePermissionPromptTool,
@@ -113,9 +114,24 @@ export function parseInputEvent(line) {
113
114
  // the same image-input pipeline as `cc agent --image`.
114
115
  const rawImages =
115
116
  obj && typeof obj === "object" ? obj.images || msg.images : null;
116
- const images = Array.isArray(rawImages)
117
- ? rawImages.filter((p) => typeof p === "string" && p.trim()).slice(0, 8)
117
+ let images = Array.isArray(rawImages)
118
+ ? rawImages.filter((p) => typeof p === "string" && p.trim())
118
119
  : [];
120
+ // Claude-Code-style: auto-attach local image-file paths the user typed into
121
+ // the message (so "describe ./shot.png" reads the image, like Claude Code).
122
+ // Opt out with CC_AUTO_IMAGE=0. Explicit `images` (paste) still win.
123
+ if (
124
+ typeof content === "string" &&
125
+ content.trim() &&
126
+ process.env.CC_AUTO_IMAGE !== "0"
127
+ ) {
128
+ const detected = detectImagePaths(content);
129
+ if (detected.images.length) {
130
+ images = [...images, ...detected.images];
131
+ content = detected.text;
132
+ }
133
+ }
134
+ images = [...new Set(images)].slice(0, 8);
119
135
  if (typeof content !== "string" || !content.trim()) {
120
136
  // An image-only turn is valid — give the model something to act on.
121
137
  if (images.length)
@@ -1,3 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./xterm-BZcWGsqw.js","./markdown-CsiA8-E5.js","./markdown-Dfs9RUU9.css","./addon-fit-CK6X9sAG.js","./xterm-DFuMZ0ql.css"])))=>i.map(i=>d[i]);
2
- import{u as fe,_ as me,d as U,e as V}from"./index-B8GMkcIp.js";import{I as z,J as x,U as _,Q as q,S as K,K as T,V as R,c as A,F as Q,Z as Y,R as E,o as Z,f as G,w as $,n as D,b as ee,r as S,P as y,_ as ve,a2 as we,a3 as he,a4 as _e}from"./vendor-BvqAck49.js";import{R as ye,b as pe,as as ge}from"./icons-DP3uiYxy.js";const k=new Map,C=new Map,I=new Set,N=new Set;let te=!1;function xe(c){te||(te=!0,c.onMessage(o=>{if(!(!o||typeof o.type!="string")){if(o.type==="terminal.stdout"){const{sessionId:s,data:e,seq:i}=o.payload||{};if(!s)return;let w;try{const d=atob(e||""),f=new Uint8Array(d.length);for(let n=0;n<d.length;n++)f[n]=d.charCodeAt(n);w=new TextDecoder("utf-8").decode(f)}catch{w=""}const u={sessionId:s,data:w,seq:i};k.get(s)?.forEach(d=>d(u)),I.forEach(d=>d(u))}else if(o.type==="terminal.exit"){const{sessionId:s,exitCode:e,signal:i}=o.payload||{};if(!s)return;const w={sessionId:s,exitCode:e,signal:i};C.get(s)?.forEach(u=>u(w)),N.forEach(u=>u(w))}}}))}function ne(c){const o=new TextEncoder().encode(c);let s="";for(let e=0;e<o.length;e++)s+=String.fromCharCode(o[e]);return btoa(s)}function ae(c){const o=atob(c||""),s=new Uint8Array(o.length);for(let e=0;e<o.length;e++)s[e]=o.charCodeAt(e);return new TextDecoder("utf-8").decode(s)}function oe(){const c=fe();xe(c);async function o(n={}){const a=await c.sendRaw({type:"terminal.create",payload:{shell:n.shell,cwd:n.cwd,env:n.env,cols:n.cols,rows:n.rows}});if(a.ok===!1)throw new Error(a.error||"terminal_create_failed");return a.result??a}async function s(){const n=await c.sendRaw({type:"terminal.list",payload:{}});if(n.ok===!1)throw new Error(n.error||"terminal_list_failed");const a=n.result??n;return Array.isArray(a.sessions)?a.sessions:[]}async function e(n,a){const r=await c.sendRaw({type:"terminal.stdin",payload:{sessionId:n,data:ne(String(a))}});if(r.ok===!1)throw new Error(r.error||"terminal_stdin_failed");return r.result??r}async function i(n,a,r){const h=await c.sendRaw({type:"terminal.resize",payload:{sessionId:n,cols:a,rows:r}});if(h.ok===!1)throw new Error(h.error||"terminal_resize_failed");return h.result??h}async function w(n){const a=await c.sendRaw({type:"terminal.close",payload:{sessionId:n}});if(a.ok===!1)throw new Error(a.error||"terminal_close_failed");return a.result??a}async function u(n,a=0){const r=await c.sendRaw({type:"terminal.history",payload:{sessionId:n,fromSeq:a}});if(r.ok===!1)throw new Error(r.error||"terminal_history_failed");const h=r.result??r;return{truncated:!!h.truncated,chunks:(h.chunks||[]).map(L=>({seq:L.seq,data:ae(L.data)}))}}function d(n,a){return n?(k.has(n)||k.set(n,new Set),k.get(n).add(a),()=>{k.get(n)?.delete(a),k.get(n)?.size===0&&k.delete(n)}):(I.add(a),()=>I.delete(a))}function f(n,a){return n?(C.has(n)||C.set(n,new Set),C.get(n).add(a),()=>{C.get(n)?.delete(a),C.get(n)?.size===0&&C.delete(n)}):(N.add(a),()=>N.delete(a))}return{create:o,list:s,stdin:e,resize:i,close:w,history:u,onStdout:d,onExit:f,_internal:{stdoutSubs:k,exitSubs:C,toBase64Utf8:ne,fromBase64Utf8:ae}}}const Se={__name:"Terminal",setup(c,{expose:o}){o();const s=oe(),e=S([]),i=S(null),w=S("pwsh"),u=S(!1),d=S(!1),f=S(""),n=S(""),a=S([]),r=[{value:"pwsh",label:"PowerShell"},{value:"cmd",label:"CMD"},{value:"bash",label:"Bash"},{value:"wsl",label:"WSL"}],h=ee(()=>e.value.find(t=>t.id===i.value));function L(t){return t?t.slice(0,8):""}let O=null,M=null;async function F(){if(O)return{xtermMod:O,fitAddonMod:M};try{O=await V(()=>import("./xterm-BZcWGsqw.js").then(t=>t.x),__vite__mapDeps([0,1,2]),import.meta.url),M=await V(()=>import("./addon-fit-CK6X9sAG.js").then(t=>t.a),__vite__mapDeps([3,1,2]),import.meta.url),await V(()=>Promise.resolve({}),__vite__mapDeps([4]),import.meta.url)}catch(t){throw n.value="xterm 资源加载失败:"+(t?.message||"未知错误"),t}return{xtermMod:O,fitAddonMod:M}}async function B(t){await D();const{xtermMod:l,fitAddonMod:p}=await F(),b=a.value.find(v=>v?.dataset?.sessionId===t.id);if(!b)return;const m=new l.Terminal({cursorBlink:!0,fontFamily:'Consolas, "Courier New", monospace',fontSize:13,theme:{background:"#1e1e1e",foreground:"#d4d4d4"},convertEol:!1}),P=new p.FitAddon;m.loadAddon(P),m.open(b);try{P.fit()}catch{}t.xterm=m,t.fitAddon=P;const ie=m.onData(v=>{s.stdin(t.id,v).catch(g=>{String(g?.message||"").includes("dangerous_keyword_blocked")?U.warning("该命令被桌面端拦截(高危关键字)"):U.error("stdin 失败: "+(g?.message||g))})}),ce=s.onStdout(t.id,({data:v,seq:g})=>{t.lastSeq=g,m.write(v)}),de=s.onExit(t.id,({exitCode:v,signal:g})=>{t.alive=!1,t.exitCode=v,t.signal=g,m.writeln(`\r
3
- \x1B[33m[session exited, code=${v}, signal=${g??"-"}]\x1B[0m`)});t.offs=()=>{try{ie.dispose?.()}catch{}ce(),de()};try{const{chunks:v,truncated:g}=await s.history(t.id,0);g&&m.writeln("\x1B[2m[history truncated — earlier output was evicted]\x1B[0m");for(const J of v)m.write(J.data),t.lastSeq=J.seq}catch(v){m.writeln(`\x1B[31m[history fetch failed: ${v?.message||v}]\x1B[0m`)}const H=new ResizeObserver(()=>{try{P.fit(),s.resize(t.id,m.cols,m.rows).catch(()=>{})}catch{}});H.observe(b);const ue=t.offs;t.offs=()=>{try{H.disconnect()}catch{}ue()}}async function re(){u.value=!0,f.value="";try{const t=await s.create({shell:w.value,cols:80,rows:24}),l={id:t.sessionId,shell:t.shell,cwd:"",alive:!0,lastSeq:0,exitCode:null,xterm:null,fitAddon:null,offs:()=>{}};e.value.push(l),i.value=l.id,await B(l)}catch(t){f.value=t?.message||String(t)}finally{u.value=!1}}async function se(t){try{await s.close(t)}catch(l){f.value=l?.message||String(l)}setTimeout(()=>W(t),500)}function W(t){const l=e.value.findIndex(b=>b.id===t);if(l===-1)return;const p=e.value[l];try{p.offs?.()}catch{}try{p.xterm?.dispose?.()}catch{}e.value.splice(l,1),i.value===t&&(i.value=e.value[0]?.id||null)}function le(t){i.value=t,D(()=>{const l=e.value.find(p=>p.id===t);try{l?.fitAddon?.fit()}catch{}})}async function X(){d.value=!0,f.value="";try{const t=await s.list();for(const l of t){const p=e.value.find(m=>m.id===l.id);if(p){p.alive=l.alive,p.lastSeq=l.lastSeq;continue}const b={id:l.id,shell:l.shell,cwd:l.cwd,alive:l.alive,lastSeq:l.lastSeq,exitCode:null,xterm:null,fitAddon:null,offs:()=>{}};e.value.push(b),await B(b)}!i.value&&e.value.length>0&&(i.value=e.value[0].id)}catch(t){f.value=t?.message||String(t)}finally{d.value=!1}}Z(async()=>{await X()}),G(()=>{for(const t of e.value){try{t.offs?.()}catch{}try{t.xterm?.dispose?.()}catch{}}}),$(i,()=>{D(()=>{const t=h.value;try{t?.fitAddon?.fit()}catch{}})});const j={term:s,sessions:e,activeId:i,newShell:w,creating:u,loadingList:d,error:f,warning:n,xtermContainers:a,shellOptions:r,active:h,shortId:L,get xtermMod(){return O},set xtermMod(t){O=t},get fitAddonMod(){return M},set fitAddonMod(t){M=t},loadXterm:F,mountXterm:B,onCreate:re,onClose:se,removeSession:W,activate:le,refreshList:X,ref:S,computed:ee,onMounted:Z,onBeforeUnmount:G,nextTick:D,watch:$,get message(){return U},get PlusOutlined(){return ge},get CloseOutlined(){return pe},get ReloadOutlined(){return ye},get useTerminal(){return oe}};return Object.defineProperty(j,"__isScriptSetup",{enumerable:!1,value:!0}),j}},be={class:"terminal-page"},ke={class:"terminal-header"},Ce={class:"page-sub"},Ae={class:"terminal-body"},Ee={class:"session-tabs"},Oe=["onClick"],Te={class:"session-shell"},Re={class:"session-id"},Me={key:0,class:"session-empty"},ze={class:"xterm-host"},Le=["data-session-id"],Pe={key:0,class:"xterm-placeholder"},qe={key:1,class:"terminal-footer"},De={key:0,class:"footer-exit"};function Be(c,o,s,e,i,w){const u=z("a-tag"),d=z("a-select"),f=z("a-button"),n=z("a-space"),a=z("a-alert");return y(),x("div",be,[_("div",ke,[_("div",null,[o[3]||(o[3]=_("h2",{class:"page-title"},"远程终端",-1)),_("p",Ce,[o[2]||(o[2]=q(" 桌面端托管的 PTY 会话;Android 端可远程操控同一通道 ",-1)),e.warning?(y(),K(u,{key:0,color:"orange",style:{"margin-left":"8px"}},{default:T(()=>[q(E(e.warning),1)]),_:1})):R("v-if",!0)])]),A(n,null,{default:T(()=>[A(d,{value:e.newShell,"onUpdate:value":o[0]||(o[0]=r=>e.newShell=r),style:{width:"130px"},size:"small",options:e.shellOptions},null,8,["value"]),A(f,{type:"primary",size:"small",loading:e.creating,onClick:e.onCreate},{icon:T(()=>[A(e.PlusOutlined)]),default:T(()=>[o[4]||(o[4]=q(" 新会话 ",-1))]),_:1},8,["loading"]),A(f,{size:"small",loading:e.loadingList,onClick:e.refreshList},{icon:T(()=>[A(e.ReloadOutlined)]),default:T(()=>[o[5]||(o[5]=q(" 刷新 ",-1))]),_:1},8,["loading"])]),_:1})]),e.error?(y(),K(a,{key:0,message:e.error,type:"error","show-icon":"",closable:"",style:{"margin-bottom":"12px"},onClose:o[1]||(o[1]=r=>e.error="")},null,8,["message"])):R("v-if",!0),_("div",Ae,[_("div",Ee,[(y(!0),x(Q,null,Y(e.sessions,r=>(y(),x("div",{key:r.id,class:ve(["session-tab",{active:r.id===e.activeId,dead:!r.alive}]),onClick:h=>e.activate(r.id)},[_("span",Te,E(r.shell),1),_("span",Re,E(e.shortId(r.id)),1),A(e.CloseOutlined,{class:"session-close",onClick:we(h=>e.onClose(r.id),["stop"])},null,8,["onClick"])],10,Oe))),128)),e.sessions.length===0?(y(),x("div",Me,' 点击 "新会话" 创建第一个终端 ')):R("v-if",!0)]),_("div",ze,[(y(!0),x(Q,null,Y(e.sessions,r=>he((y(),x("div",{key:r.id,ref_for:!0,ref:"xtermContainers","data-session-id":r.id,class:"xterm-container"},null,8,Le)),[[_e,r.id===e.activeId]])),128)),e.sessions.length===0?(y(),x("div",Pe,[...o[6]||(o[6]=[_("span",null,"无活跃会话",-1)])])):R("v-if",!0)])]),e.active?(y(),x("div",qe,[_("span",null,E(e.active.shell)+" · "+E(e.active.cwd||"(默认 cwd)")+" · seq "+E(e.active.lastSeq),1),e.active.alive?R("v-if",!0):(y(),x("span",De,"已退出 (code="+E(e.active.exitCode??"-")+")",1))])):R("v-if",!0)])}const Ne=me(Se,[["render",Be],["__scopeId","data-v-65366a29"],["__file","/tmp/cc-web-panel-dUwIHq/repo/packages/web-panel/src/views/Terminal.vue"]]);export{Ne as default};
@@ -1 +0,0 @@
1
- import{O as r}from"./index-B8GMkcIp.js";const o=((n,a,e)=>{r(n,`[ant-design-vue: ${a}] ${e}`)});export{o as d};
@@ -1 +0,0 @@
1
- import{A as o}from"./Row-DEHR17nP.js";import{U as t}from"./index-B8GMkcIp.js";import"./vendor-BvqAck49.js";import"./responsiveObserve-BvwPauMH.js";import"./useFlexGapSupport-Cp_Cx5AB.js";import"./styleChecker-EtdQYqac.js";import"./index-DBfaQNOv.js";import"./icons-DP3uiYxy.js";const l=t(o);export{l as default};
@@ -1 +0,0 @@
1
- import{C as o}from"./Col-BpLy4N8M.js";import{U as t}from"./index-B8GMkcIp.js";import"./vendor-BvqAck49.js";import"./index-DBfaQNOv.js";import"./icons-DP3uiYxy.js";const s=t(o);export{s as default};