chainlesschain 0.162.70 → 0.162.72

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 (171) hide show
  1. package/package.json +4 -3
  2. package/src/assets/web-panel/assets/{AIOps-CnHIXe2L.js → AIOps-1bl50Cen.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-CzDyhTAp.js → ActionButton-Bw4kAx-1.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-rwvDMCd-.js → Analytics-junq7r42.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-D-Q1M-5V.js → AppLayout-vf1TVGeu.js} +5 -5
  6. package/src/assets/web-panel/assets/{Audit-BIFSGnAE.js → Audit-CBzpgcL9.js} +1 -1
  7. package/src/assets/web-panel/assets/{Backup-CbUdiVeS.js → Backup-ChwGPeP4.js} +1 -1
  8. package/src/assets/web-panel/assets/{BaseInput-D3P_pICH.js → BaseInput-2G2KGE6M.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-DRrX7vVB.js → Chat-Cr9NwfDA.js} +4 -4
  10. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DTC1ElV-.js → ChatBubbleRenderer-CJvlo_ay.js} +1 -1
  11. package/src/assets/web-panel/assets/{Checkbox-Dpq9X8Xd.js → Checkbox-DN_5PBT7.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-yQQOSuHA.js → Codegen-D4bluAgT.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col-DLFLDxar.js → Col-DBP3UjYW.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-DYbdX-GB.js → Community-ge-SvR3c.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-Dclrp1HE.js → Compact-DGXvlGNv.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-C9EQMgoP.js → Compliance-Cn7nBsFQ.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-B9IMFsMk.js → Cowork-DgHEO2de.js} +2 -2
  18. package/src/assets/web-panel/assets/{Cron-V6TlbDey.js → Cron-DUbYxPs0.js} +2 -2
  19. package/src/assets/web-panel/assets/{Crosschain-Cl_vG8R_.js → Crosschain-3J0MWPQq.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-DoUUF9cz.js → DID-BR_Jpkh4.js} +2 -2
  21. package/src/assets/web-panel/assets/{Dashboard-DcA8WmrS.js → Dashboard-ffykLkAJ.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dropdown-wyGMYzw9.js → Dropdown-DaI38dXT.js} +1 -1
  23. package/src/assets/web-panel/assets/{EmailListRenderer-B1T3ziIH.js → EmailListRenderer-CnIzkUVG.js} +1 -1
  24. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CW4CwYbZ.js → FamilyGuardDashboard-BxrgAAZg.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-9tEiw0KA.js → Federation-BuS_FRh3.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-DbocfnNQ.js → FormItemContext-DBG-8g1w.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-CGNsG2qI.js → GenericCardRenderer-CmEc8RgH.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-BxA3dgT8.js → Git-CK10BYwY.js} +2 -2
  29. package/src/assets/web-panel/assets/{Governance-DqnhaC7n.js → Governance-C8cABM_I.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-D6Ll4t9c.js → Inference-Dr4_DIw3.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-Bb8IXGl7.js → KnowledgeGraph-0_bLuvvb.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-DRUUVMdc.js → Logs-PVGZxt9z.js} +2 -2
  33. package/src/assets/web-panel/assets/{Marketplace-BYRM186w.js → Marketplace-BtVnS4HW.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-DLbh7uS8.js → McpTools-BVulusCC.js} +3 -3
  35. package/src/assets/web-panel/assets/{Memory-D2_uZEOI.js → Memory-DEMtNbJ9.js} +2 -2
  36. package/src/assets/web-panel/assets/{MobileBridge-CXVJoMKk.js → MobileBridge-5QL654FQ.js} +3 -3
  37. package/src/assets/web-panel/assets/{MobileProjects-QRZYgIME.js → MobileProjects-G5Du9Thr.js} +1 -1
  38. package/src/assets/web-panel/assets/{Mtc-D3mv30gW.js → Mtc-CNFPJej0.js} +2 -2
  39. package/src/assets/web-panel/assets/{MtcAudit-__QPus22.js → MtcAudit-DAJ81Cej.js} +2 -2
  40. package/src/assets/web-panel/assets/{Multisig-Bu5gaTn6.js → Multisig-Cwj8tXfT.js} +3 -3
  41. package/src/assets/web-panel/assets/{NLProgramming-SjaViyfR.js → NLProgramming-Di0ptaL6.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-feXsl0dN.js → Notes-Cse_4Ox6.js} +3 -3
  43. package/src/assets/web-panel/assets/{NotificationSettings-BtVFokbM.js → NotificationSettings-ntpoi5ec.js} +1 -1
  44. package/src/assets/web-panel/assets/OrderTableRenderer-CbKsOM0D.js +1 -0
  45. package/src/assets/web-panel/assets/{Organization-CewL60Rd.js → Organization-Bhjya5h9.js} +4 -4
  46. package/src/assets/web-panel/assets/{Overflow-2ZRChJ7E.js → Overflow-C0u3aive.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-DuMr6G9y.js → P2P-BWrTwf_a.js} +2 -2
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-BMOcHCWd.js → PdhVaultBrowser-pld2Qlqk.js} +3 -3
  49. package/src/assets/web-panel/assets/{Permissions-Cj94ql75.js → Permissions-CW5FG3mE.js} +4 -4
  50. package/src/assets/web-panel/assets/{PersonalDataHub-BYK4mlk7.js → PersonalDataHub-BuBmqfDa.js} +4 -4
  51. package/src/assets/web-panel/assets/{Pipeline-maRvdrbO.js → Pipeline-nZr06BNO.js} +1 -1
  52. package/src/assets/web-panel/assets/{Privacy-CKb2zZJz.js → Privacy-B_XvMHYv.js} +1 -1
  53. package/src/assets/web-panel/assets/{ProjectInit-C2pfXv4l.js → ProjectInit-CbRHJy85.js} +2 -2
  54. package/src/assets/web-panel/assets/{ProjectSettings-DGeNwq0h.js → ProjectSettings-CCH8SpEa.js} +2 -2
  55. package/src/assets/web-panel/assets/{Projects-BRypTQUV.js → Projects-D5Min2Fu.js} +1 -1
  56. package/src/assets/web-panel/assets/{Providers-DYwMPXEO.js → Providers-DgCY5A8A.js} +1 -1
  57. package/src/assets/web-panel/assets/{QuickAsk-DE6isYZV.js → QuickAsk-DEcKTiTV.js} +1 -1
  58. package/src/assets/web-panel/assets/{Recommend-Rc-AE6y9.js → Recommend-BRaq2oKc.js} +1 -1
  59. package/src/assets/web-panel/assets/{Reputation-DDQ8_kmM.js → Reputation-DzdbBdVv.js} +1 -1
  60. package/src/assets/web-panel/assets/{Row-Do5iQUFj.js → Row-Ds2JNh7c.js} +1 -1
  61. package/src/assets/web-panel/assets/{RssFeed-cxG0ulSw.js → RssFeed-Bk1-t4Ie.js} +3 -3
  62. package/src/assets/web-panel/assets/{Search-D3-8EdNk.js → Search-DaVhRwoq.js} +1 -1
  63. package/src/assets/web-panel/assets/{Security-DtBfws1D.js → Security-D9mXjPSu.js} +3 -3
  64. package/src/assets/web-panel/assets/{Services-DzeN8HsG.js → Services-D8sukL97.js} +2 -2
  65. package/src/assets/web-panel/assets/{Skeleton-BQCVeTUf.js → Skeleton-COCJt30X.js} +1 -1
  66. package/src/assets/web-panel/assets/Skills-DtFPSPSc.js +1 -0
  67. package/src/assets/web-panel/assets/{Sla-d2IOd-AA.js → Sla-DIab-wjR.js} +1 -1
  68. package/src/assets/web-panel/assets/{SpeechSettings-BjfopwT6.js → SpeechSettings-xxIH9hgB.js} +1 -1
  69. package/src/assets/web-panel/assets/{SyncSettings-D5_c1_rI.js → SyncSettings-DrpxzJOH.js} +2 -2
  70. package/src/assets/web-panel/assets/Tasks-maiJ3jAT.js +1 -0
  71. package/src/assets/web-panel/assets/{Templates-CCXFgNy3.js → Templates-COE-dSks.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tenant-Duz6eEK3.js → Tenant-CLGObu4M.js} +1 -1
  73. package/src/assets/web-panel/assets/{Terminal-Buc6UbYo.js → Terminal-Bw8WBvW3.js} +2 -2
  74. package/src/assets/web-panel/assets/{TimelineRenderer-Du5jKHJz.js → TimelineRenderer-D4gdk3Qb.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tokens-B12vLtQm.js → Tokens-CSoL_uAw.js} +1 -1
  76. package/src/assets/web-panel/assets/{Trigger-s_pHOerx.js → Trigger-BCRcowFi.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trust-4sDhbAls.js → Trust-D5QPZ198.js} +1 -1
  78. package/src/assets/web-panel/assets/{UkeySign-UeyFknip.js → UkeySign-C7SsMqfZ.js} +1 -1
  79. package/src/assets/web-panel/assets/{VideoEditing-DsopM2S5.js → VideoEditing-CDQ4HNZL.js} +1 -1
  80. package/src/assets/web-panel/assets/{Wallet-CreLjC5U.js → Wallet-VmG4P_Fm.js} +3 -3
  81. package/src/assets/web-panel/assets/{WebAuthn-CBuMMZbK.js → WebAuthn-qtC0dZxA.js} +4 -4
  82. package/src/assets/web-panel/assets/{WorkflowEditor-DWhHHSFW.js → WorkflowEditor-C_CSdk3l.js} +1 -1
  83. package/src/assets/web-panel/assets/{chat-Iurq_BMY.js → chat-BGm8YCIo.js} +1 -1
  84. package/src/assets/web-panel/assets/{colors-BIOTIa89.js → colors-BScOmAwk.js} +1 -1
  85. package/src/assets/web-panel/assets/{compact-item-B7lPu9pO.js → compact-item-HDwaT31n.js} +1 -1
  86. package/src/assets/web-panel/assets/{createContext-Bzzk7mrh.js → createContext-CheSjPm8.js} +1 -1
  87. package/src/assets/web-panel/assets/devWarning-CjO6o19j.js +1 -0
  88. package/src/assets/web-panel/assets/{hasIn-B11xqp5O.js → hasIn-C1_NbX0O.js} +1 -1
  89. package/src/assets/web-panel/assets/index--jhpSxS1.js +1 -0
  90. package/src/assets/web-panel/assets/{index-vZCGFSJN.js → index-6C8mxO8j.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-CLhpFnSR.js → index-B94_DZF-.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-BlR8YVlt.js → index-BGROuzQG.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-CxDOX4g6.js → index-BKQordyU.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DMrzHgE6.js → index-BMxPL1oG.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DsoyfOzW.js → index-Bd2zS3jK.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-3LgeP4ij.js → index-ByoUjX8f.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-Y4gmywZo.js → index-C4FSaMAu.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CCxGEK8b.js → index-C6WXnaG4.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-C-lwsB2F.js → index-C71Z__li.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-C4wV32Dn.js → index-CSaztnyZ.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CiXNVmcx.js → index-CYkezxqK.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-D6q-fkXs.js → index-Clqj2JTi.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-R-YSpvg4.js → index-Cm55PcYG.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Dn_Ht3v2.js → index-CxoOEb-l.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DEKeoJ6I.js → index-D2gy4IX0.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-AmHa_sOB.js → index-D8xpncJ0.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CULCwI_H.js → index-DB6gbgU2.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-tub9w6us.js → index-DDk0s3nO.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CzY2mdiR.js → index-DQ01dBAS.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-5BkUKVyj.js → index-DSuWcpsv.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-D8tm9JnQ.js → index-DVyL9jbT.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BLREN4NE.js → index-DaATJtzu.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CEKRqrI-.js → index-DbfAaBxL.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BVIXYV1R.js → index-DhTELUKN.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BgmBZgU9.js → index-Dk2N6hxE.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-B4rXFIOG.js → index-DkiIxTIS.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CA-GPOd9.js → index-DoZwBGUX.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-6QGNBg3m.js → index-DppSbT8L.js} +3 -3
  119. package/src/assets/web-panel/assets/{index-DwGVPCZb.js → index-DzE-95XH.js} +1 -1
  120. package/src/assets/web-panel/assets/index-J4Rzclyc.js +1 -0
  121. package/src/assets/web-panel/assets/{index-BIFscFlm.js → index-KG9xVANC.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DyV9331f.js → index-KXrUKw1h.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-D25-u3Q2.js → index-eSBoP6E_.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B93pM-xx.js → index-ggl9cj35.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-htxy7qDG.js → index-i-xYmSsZ.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CS2UWrIt.js → index-lB5kN9Yc.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DX7HGwP_.js → index-yKmudyK7.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-Bl6klCzd.js → initDefaultProps-D7Q7zDzP.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-B9IxS1-S.js → motion-B4DcaqPb.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-NJ5XDNQe.js → move-CYxmYrFY.js} +1 -1
  131. package/src/assets/web-panel/assets/{omit-ByO8GI_H.js → omit-ze_9dKw3.js} +1 -1
  132. package/src/assets/web-panel/assets/{pickAttrs-W6iTcLuH.js → pickAttrs-D24HrRSv.js} +1 -1
  133. package/src/assets/web-panel/assets/{placementArrow-JMndCFEP.js → placementArrow-BWJZVYyS.js} +1 -1
  134. package/src/assets/web-panel/assets/{responsiveObserve-DHV7-FuY.js → responsiveObserve-CseJia_L.js} +1 -1
  135. package/src/assets/web-panel/assets/{slide-DioRUeNF.js → slide-CBWNEpey.js} +1 -1
  136. package/src/assets/web-panel/assets/{statusUtils-DMp2b9Ze.js → statusUtils-f4e6p7yd.js} +1 -1
  137. package/src/assets/web-panel/assets/{styleChecker-DamP5BTS.js → styleChecker-DZmfWZ8G.js} +1 -1
  138. package/src/assets/web-panel/assets/{useFlexGapSupport-D338iARz.js → useFlexGapSupport-4RBaBnVI.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFs-DE_HJgk6.js → useFs-Cv6bNoEA.js} +1 -1
  140. package/src/assets/web-panel/assets/{usePersonalDataHub-914YxO9q.js → usePersonalDataHub-DOTETJIA.js} +1 -1
  141. package/src/assets/web-panel/assets/{vnode-CY39Ky7P.js → vnode-DqS1K6-J.js} +1 -1
  142. package/src/assets/web-panel/assets/{zoom-BEghlCth.js → zoom-C1MYzIJG.js} +1 -1
  143. package/src/assets/web-panel/index.html +1 -1
  144. package/src/commands/cowork.js +8 -0
  145. package/src/commands/crosschain.js +32 -4
  146. package/src/commands/init.js +10 -10
  147. package/src/commands/loop.js +9 -3
  148. package/src/commands/memory.js +6 -4
  149. package/src/commands/orchestrate.js +5 -2
  150. package/src/commands/video.js +5 -1
  151. package/src/lib/cowork-workflow.js +664 -129
  152. package/src/lib/hook-manager.js +3 -2
  153. package/src/lib/micro-compact.js +52 -0
  154. package/src/lib/permission-rules.cjs +39 -0
  155. package/src/lib/skill-loader.js +62 -43
  156. package/src/lib/workflow-state-reader.js +10 -1
  157. package/src/repl/agent-repl.js +228 -20
  158. package/src/repl/chat-repl.js +4 -2
  159. package/src/repl/permission-tier.js +60 -0
  160. package/src/repl/stream-decision.js +16 -0
  161. package/src/repl/think-command.js +36 -0
  162. package/src/runtime/agent-core.js +42 -6
  163. package/src/runtime/file-ref-expander.js +209 -18
  164. package/src/runtime/headless-runner.js +3 -3
  165. package/src/runtime/headless-stream.js +16 -3
  166. package/src/assets/web-panel/assets/OrderTableRenderer-CJJ2GWDf.js +0 -1
  167. package/src/assets/web-panel/assets/Skills-D6mCVKt7.js +0 -1
  168. package/src/assets/web-panel/assets/Tasks-BCmxDMZx.js +0 -1
  169. package/src/assets/web-panel/assets/devWarning-CuIq7zBB.js +0 -1
  170. package/src/assets/web-panel/assets/index-BRsns-SP.js +0 -1
  171. package/src/assets/web-panel/assets/index-QTuoOvJA.js +0 -1
@@ -12,7 +12,7 @@ import readline from "readline";
12
12
  import chalk from "chalk";
13
13
  import { logger } from "../lib/logger.js";
14
14
  import { BUILT_IN_PROVIDERS } from "../lib/llm-providers.js";
15
- import { expandFileRefs } from "../runtime/file-ref-expander.js";
15
+ import { expandFileRefsAsync } from "../runtime/file-ref-expander.js";
16
16
  import {
17
17
  streamOllama,
18
18
  streamOpenAI,
@@ -159,7 +159,9 @@ export async function startChatRepl(options = {}) {
159
159
  // content; the JSONL log keeps the original line for readability.
160
160
  let userContent = trimmed;
161
161
  try {
162
- const fileRefs = expandFileRefs(trimmed, { cwd: process.cwd() });
162
+ const fileRefs = await expandFileRefsAsync(trimmed, {
163
+ cwd: process.cwd(),
164
+ });
163
165
  userContent = fileRefs.prompt;
164
166
  for (const w of fileRefs.warnings) {
165
167
  logger.info(chalk.yellow(`[@ref] ${w}`));
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Map a `/permissions <arg>` tier alias to an ApprovalGate session-policy tier
3
+ * (strict | trusted | autopilot), or null when the arg is not a known tier.
4
+ * Pure → unit-testable. Mirrors `cc session policy --set` and the headless
5
+ * --permission-mode mapping (default → strict, acceptEdits → trusted,
6
+ * bypassPermissions → autopilot), giving the interactive REPL a mid-session
7
+ * permission-mode toggle (Claude-Code Shift+Tab mode-cycling parity).
8
+ */
9
+ const TIER_ALIASES = {
10
+ // strict — every risky action asks (the default)
11
+ strict: "strict",
12
+ default: "strict",
13
+ normal: "strict",
14
+ off: "strict",
15
+ // trusted — low/medium-risk auto-approved, high-risk still asks (acceptEdits)
16
+ trusted: "trusted",
17
+ accept: "trusted",
18
+ "accept-edits": "trusted",
19
+ acceptedits: "trusted",
20
+ // autopilot — everything auto-approved (bypassPermissions)
21
+ autopilot: "autopilot",
22
+ bypass: "autopilot",
23
+ bypasspermissions: "autopilot",
24
+ yolo: "autopilot",
25
+ };
26
+
27
+ /** Cycle order for Shift+Tab mode cycling (Claude-Code parity). */
28
+ export const TIER_CYCLE = Object.freeze(["strict", "trusted", "autopilot"]);
29
+
30
+ /**
31
+ * Next tier in the Shift+Tab cycle: strict → trusted → autopilot → strict.
32
+ * An unrecognized current tier resets to the first (strict).
33
+ */
34
+ export function nextTier(current) {
35
+ const i = TIER_CYCLE.indexOf(current);
36
+ return TIER_CYCLE[(i + 1) % TIER_CYCLE.length];
37
+ }
38
+
39
+ export function parsePermissionTier(arg) {
40
+ const a = String(arg == null ? "" : arg)
41
+ .trim()
42
+ .toLowerCase();
43
+ return Object.prototype.hasOwnProperty.call(TIER_ALIASES, a)
44
+ ? TIER_ALIASES[a]
45
+ : null;
46
+ }
47
+
48
+ /** One-line description of what a tier auto-approves. */
49
+ export function describeTier(tier) {
50
+ switch (tier) {
51
+ case "autopilot":
52
+ return "everything auto-approved (no prompts)";
53
+ case "trusted":
54
+ return "low/medium-risk auto-approved; high-risk still asks";
55
+ case "strict":
56
+ return "every risky action asks";
57
+ default:
58
+ return "";
59
+ }
60
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Decide whether the REPL may stream the answer live (token-by-token) instead of
3
+ * replaying the finished text. Pure → unit-testable.
4
+ *
5
+ * Safety invariant: live streaming is ONLY allowed when no AssistantResponse
6
+ * hook is registered. Such a hook can rewrite or suppress the final answer, and
7
+ * once tokens are on screen they can't be un-printed. If we can't determine the
8
+ * hook count (a query error → pass arHookCount < 0), we stay safe (no streaming).
9
+ * `CC_REPL_STREAM=0` forces the replay regardless.
10
+ */
11
+ export function shouldStreamLive({ streamEnv, arHookCount = 0 } = {}) {
12
+ if (streamEnv === "0") return false;
13
+ // 0 → no rewrite/suppress hook → safe. Anything else (hooks present, or -1 for
14
+ // "unknown") → fall back to the replay.
15
+ return arHookCount === 0;
16
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Pure parser for the REPL `/think` · `/ultrathink` extended-thinking toggle.
3
+ * Maps the slash-command string to the next `thinking` value the agent loop
4
+ * reads (true | "ultra" | <level> | null) plus a human label. Returns null when
5
+ * the input is not a think command, so the REPL falls through to its other
6
+ * handlers. vscode/readline-free → unit-testable (mirrors the chat panel's
7
+ * /think; extended thinking is Anthropic-only, ignored by other providers).
8
+ *
9
+ * /think → on (default budget) /think off → off
10
+ * /think ultra → max budget /think <level> → that level
11
+ * /think-off → off (panel-style alias)
12
+ * /ultrathink → max budget (alias)
13
+ */
14
+ export function parseThinkCommand(trimmed) {
15
+ const t = String(trimmed == null ? "" : trimmed).trim();
16
+ const isThink =
17
+ t === "/think" || t.startsWith("/think ") || t.startsWith("/think-");
18
+ const isUltra = t === "/ultrathink" || t.startsWith("/ultrathink ");
19
+ if (!isThink && !isUltra) return null;
20
+
21
+ const arg = isUltra
22
+ ? "ultra"
23
+ : t.slice(6).replace(/^-+/, "").trim().toLowerCase();
24
+
25
+ if (arg === "ultra") {
26
+ return { thinking: "ultra", label: "ultra (max budget)", anthropic: true };
27
+ }
28
+ if (arg === "off" || arg === "false" || arg === "0" || arg === "none") {
29
+ return { thinking: null, label: "off", anthropic: false };
30
+ }
31
+ if (!arg || arg === "on" || arg === "true") {
32
+ return { thinking: true, label: "on", anthropic: true };
33
+ }
34
+ // An explicit effort level (low/medium/high/…) — passed through to the engine.
35
+ return { thinking: arg, label: arg, anthropic: true };
36
+ }
@@ -2658,6 +2658,7 @@ export async function chatWithTools(rawMessages, options) {
2658
2658
  { "x-api-key": key, "anthropic-version": "2023-06-01" },
2659
2659
  options.onToken,
2660
2660
  signal,
2661
+ options.onThinking,
2661
2662
  );
2662
2663
  }
2663
2664
 
@@ -2899,7 +2900,7 @@ function _anthropicInitState() {
2899
2900
  return { text: "", blocks: {}, inputTokens: 0, outputTokens: 0 };
2900
2901
  }
2901
2902
 
2902
- function _anthropicReduceLine(state, raw, onToken) {
2903
+ function _anthropicReduceLine(state, raw, onToken, onThinking) {
2903
2904
  const line = (raw || "").trim();
2904
2905
  if (!line.startsWith("data:")) return state;
2905
2906
  const payload = line.slice(5).trim();
@@ -2939,6 +2940,13 @@ function _anthropicReduceLine(state, raw, onToken) {
2939
2940
  } else if (d.type === "thinking_delta" && state.blocks[obj.index]) {
2940
2941
  state.blocks[obj.index].thinking =
2941
2942
  (state.blocks[obj.index].thinking || "") + (d.thinking || "");
2943
+ if (typeof onThinking === "function" && d.thinking) {
2944
+ try {
2945
+ onThinking(d.thinking);
2946
+ } catch {
2947
+ // a failing UI hook must never break the run
2948
+ }
2949
+ }
2942
2950
  } else if (d.type === "signature_delta" && state.blocks[obj.index]) {
2943
2951
  state.blocks[obj.index].signature =
2944
2952
  (state.blocks[obj.index].signature || "") + (d.signature || "");
@@ -2994,9 +3002,10 @@ function _anthropicFinalize(state) {
2994
3002
  }
2995
3003
 
2996
3004
  /** Pure reducer over Anthropic SSE lines — exported for tests (no HTTP). */
2997
- export function _accumulateAnthropicStream(lines, onToken) {
3005
+ export function _accumulateAnthropicStream(lines, onToken, onThinking) {
2998
3006
  const state = _anthropicInitState();
2999
- for (const line of lines) _anthropicReduceLine(state, line, onToken);
3007
+ for (const line of lines)
3008
+ _anthropicReduceLine(state, line, onToken, onThinking);
3000
3009
  return _anthropicFinalize(state);
3001
3010
  }
3002
3011
 
@@ -3006,6 +3015,7 @@ async function _chatAnthropicStreaming(
3006
3015
  extraHeaders,
3007
3016
  onToken,
3008
3017
  signal,
3018
+ onThinking,
3009
3019
  ) {
3010
3020
  const response = await fetch(apiUrl, {
3011
3021
  method: "POST",
@@ -3026,9 +3036,10 @@ async function _chatAnthropicStreaming(
3026
3036
  buf += decoder.decode(value, { stream: true });
3027
3037
  const lines = buf.split("\n");
3028
3038
  buf = lines.pop() || "";
3029
- for (const line of lines) _anthropicReduceLine(state, line, onToken);
3039
+ for (const line of lines)
3040
+ _anthropicReduceLine(state, line, onToken, onThinking);
3030
3041
  }
3031
- if (buf.trim()) _anthropicReduceLine(state, buf, onToken);
3042
+ if (buf.trim()) _anthropicReduceLine(state, buf, onToken, onThinking);
3032
3043
  return _anthropicFinalize(state);
3033
3044
  }
3034
3045
 
@@ -3699,7 +3710,20 @@ export async function* agentLoop(messages, options) {
3699
3710
  const toolCalls = msg.tool_calls;
3700
3711
 
3701
3712
  if (!toolCalls || toolCalls.length === 0) {
3702
- yield { type: "response-complete", content: msg.content || "" };
3713
+ // Surface the final answer's extended-thinking reasoning (Anthropic, when
3714
+ // --think is on) so non-streaming consumers (the REPL) can show it. The
3715
+ // streaming path forwards reasoning live via onThinking instead.
3716
+ const _thinking = Array.isArray(msg._thinkingBlocks)
3717
+ ? msg._thinkingBlocks
3718
+ .map((b) => b.thinking || "")
3719
+ .join("")
3720
+ .trim()
3721
+ : "";
3722
+ yield {
3723
+ type: "response-complete",
3724
+ content: msg.content || "",
3725
+ ...(_thinking ? { thinking: _thinking } : {}),
3726
+ };
3703
3727
  // settings.json Stop hooks: a `block` decision FORCES the agent to keep
3704
3728
  // going instead of stopping — the reason is injected as a new instruction.
3705
3729
  // `stop_hook_active` lets the hook avoid an infinite loop; the iteration
@@ -3741,6 +3765,18 @@ export async function* agentLoop(messages, options) {
3741
3765
  return;
3742
3766
  }
3743
3767
 
3768
+ // Intermediate-step reasoning (Anthropic, --think): the model's reasoning
3769
+ // before it chose these tool calls. Streaming consumers already get it live
3770
+ // via onThinking, so only surface it as an event for non-streaming consumers
3771
+ // (the REPL) — keeps it out of the --include-partial-messages stream.
3772
+ if (!options.onThinking && Array.isArray(msg._thinkingBlocks)) {
3773
+ const _stepThinking = msg._thinkingBlocks
3774
+ .map((b) => b.thinking || "")
3775
+ .join("")
3776
+ .trim();
3777
+ if (_stepThinking) yield { type: "thinking", text: _stepThinking };
3778
+ }
3779
+
3744
3780
  // Add assistant message with tool calls
3745
3781
  messages.push(msg);
3746
3782
 
@@ -38,11 +38,51 @@ function looksPathLike(raw) {
38
38
  return /[\\/]/.test(raw) || /\.[A-Za-z0-9]+$/.test(raw);
39
39
  }
40
40
 
41
+ /** Whether a resolved path is a PDF (text extraction is async, opt-in). */
42
+ function isPdf(p) {
43
+ return /\.pdf$/i.test(String(p || ""));
44
+ }
45
+
41
46
  /** Strip trailing sentence punctuation that is unlikely to be part of a path. */
42
47
  function trimTrailingPunct(raw) {
43
48
  return raw.replace(/[),;:!?'".]+$/g, "");
44
49
  }
45
50
 
51
+ /**
52
+ * Split a token into its path and an optional trailing line range — Claude-Code
53
+ * `@file#L5-10` parity. Accepts `#L5-10`, `#5-10`, `#L5`, `#5` (1-based,
54
+ * inclusive). Returns `{ path, start, end }`, or null when there is no valid
55
+ * range suffix (so plain `@path` falls through to whole-file expansion).
56
+ */
57
+ export function parseLineRange(raw) {
58
+ const m = /^(.+?)#[Ll]?(\d+)(?:-(\d+))?$/.exec(String(raw || ""));
59
+ if (!m) return null;
60
+ const start = parseInt(m[2], 10);
61
+ if (!Number.isFinite(start) || start < 1) return null;
62
+ let end = m[3] != null ? parseInt(m[3], 10) : start;
63
+ if (!Number.isFinite(end) || end < start) end = start;
64
+ return { path: m[1], start, end };
65
+ }
66
+
67
+ /**
68
+ * Take lines [start, end] (1-based, inclusive) from `content`, clamped to the
69
+ * file, then cap the slice at `maxBytes`. Returns the text plus the actual
70
+ * (clamped) line bounds and whether the byte cap truncated it.
71
+ */
72
+ function sliceLines(content, start, end, maxBytes) {
73
+ const lines = content.split("\n");
74
+ const total = lines.length;
75
+ const s = Math.max(1, Math.min(start, total));
76
+ const e = Math.min(Math.max(end, s), total);
77
+ let text = lines.slice(s - 1, e).join("\n");
78
+ let truncated = false;
79
+ if (Buffer.byteLength(text, "utf-8") > maxBytes) {
80
+ text = text.slice(0, maxBytes);
81
+ truncated = true;
82
+ }
83
+ return { text, start: s, end: e, truncated };
84
+ }
85
+
46
86
  /**
47
87
  * Find unique `@path` candidates in the text, in first-seen order.
48
88
  * @returns {Array<{raw:string}>}
@@ -81,7 +121,10 @@ function resolveRef(raw, { cwd, fs, path, maxBytes, maxDirEntries }) {
81
121
  if (trimmed && trimmed !== raw) candidates.push(trimmed);
82
122
 
83
123
  for (const cand of candidates) {
84
- const abs = path.resolve(cwd, cand);
124
+ // `@path#L5-10` resolve the bare path, slice the lines below.
125
+ const range = parseLineRange(cand);
126
+ const fsPath = range ? range.path : cand;
127
+ const abs = path.resolve(cwd, fsPath);
85
128
  let stat;
86
129
  try {
87
130
  stat = fs.statSync(abs);
@@ -89,6 +132,7 @@ function resolveRef(raw, { cwd, fs, path, maxBytes, maxDirEntries }) {
89
132
  continue; // not this candidate
90
133
  }
91
134
  if (stat.isDirectory()) {
135
+ // A line range on a directory is meaningless — ignore it and list the dir.
92
136
  let entries;
93
137
  try {
94
138
  entries = fs.readdirSync(abs, { withFileTypes: true });
@@ -96,7 +140,7 @@ function resolveRef(raw, { cwd, fs, path, maxBytes, maxDirEntries }) {
96
140
  return {
97
141
  kind: "error",
98
142
  raw: cand,
99
- rel: cand,
143
+ rel: fsPath,
100
144
  message: `cannot read directory: ${err.message}`,
101
145
  };
102
146
  }
@@ -108,7 +152,7 @@ function resolveRef(raw, { cwd, fs, path, maxBytes, maxDirEntries }) {
108
152
  return {
109
153
  kind: "dir",
110
154
  raw: cand,
111
- rel: cand,
155
+ rel: fsPath,
112
156
  entries: names,
113
157
  total: entries.length,
114
158
  truncated,
@@ -122,18 +166,46 @@ function resolveRef(raw, { cwd, fs, path, maxBytes, maxDirEntries }) {
122
166
  return {
123
167
  kind: "error",
124
168
  raw: cand,
125
- rel: cand,
169
+ rel: fsPath,
126
170
  message: `cannot read file: ${err.message}`,
127
171
  };
128
172
  }
173
+ if (isPdf(fsPath)) {
174
+ // PDF text extraction is async (pdf-parse). Return a deferred marker
175
+ // carrying the bytes + requested page range; expandFileRefsAsync fills
176
+ // in `content`. The sync path renders it as a note (no extraction).
177
+ return {
178
+ kind: "pdf",
179
+ raw: cand,
180
+ rel: fsPath,
181
+ buf,
182
+ bytes: stat.size,
183
+ pageStart: range ? range.start : null,
184
+ pageEnd: range ? range.end : null,
185
+ };
186
+ }
129
187
  if (looksBinary(buf)) {
130
188
  return {
131
189
  kind: "binary",
132
190
  raw: cand,
133
- rel: cand,
191
+ rel: fsPath,
134
192
  bytes: stat.size,
135
193
  };
136
194
  }
195
+ if (range) {
196
+ // Slice the requested lines (clamped to the file), then byte-cap them.
197
+ const sl = sliceLines(buf.toString("utf-8"), range.start, range.end, maxBytes);
198
+ return {
199
+ kind: "file",
200
+ raw: cand,
201
+ rel: fsPath,
202
+ bytes: stat.size,
203
+ content: sl.text,
204
+ truncated: sl.truncated,
205
+ lineStart: sl.start,
206
+ lineEnd: sl.end,
207
+ };
208
+ }
137
209
  const truncated = buf.length > maxBytes;
138
210
  const content = (truncated ? buf.slice(0, maxBytes) : buf).toString(
139
211
  "utf-8",
@@ -141,7 +213,7 @@ function resolveRef(raw, { cwd, fs, path, maxBytes, maxDirEntries }) {
141
213
  return {
142
214
  kind: "file",
143
215
  raw: cand,
144
- rel: cand,
216
+ rel: fsPath,
145
217
  bytes: stat.size,
146
218
  content,
147
219
  truncated,
@@ -164,15 +236,23 @@ function renderBlock(refs) {
164
236
  ];
165
237
  for (const ref of refs) {
166
238
  if (ref.kind === "file") {
239
+ const lineAttr = ref.lineStart
240
+ ? ` lines="${ref.lineStart}-${ref.lineEnd}"`
241
+ : "";
167
242
  const attrs =
168
243
  `path="${escapeAttr(ref.rel)}" bytes="${ref.bytes}"` +
244
+ lineAttr +
169
245
  (ref.truncated
170
246
  ? ` truncated="true" shown-bytes="${DEFAULT_MAX_BYTES}"`
171
247
  : "");
172
248
  parts.push(`<file ${attrs}>`);
173
249
  parts.push(ref.content);
174
250
  if (ref.truncated) {
175
- parts.push(`\n… [truncated — file is ${ref.bytes} bytes]`);
251
+ parts.push(
252
+ ref.lineStart
253
+ ? `\n… [truncated — line range exceeds ${DEFAULT_MAX_BYTES} bytes]`
254
+ : `\n… [truncated — file is ${ref.bytes} bytes]`,
255
+ );
176
256
  }
177
257
  parts.push("</file>");
178
258
  } else if (ref.kind === "dir") {
@@ -185,6 +265,30 @@ function renderBlock(refs) {
185
265
  parts.push(`… [truncated — ${ref.total} entries total]`);
186
266
  }
187
267
  parts.push("</dir>");
268
+ } else if (ref.kind === "pdf") {
269
+ if (typeof ref.content === "string") {
270
+ const pageAttr =
271
+ ref.pageStart != null ? ` pages="${ref.pageStart}-${ref.pageEnd}"` : "";
272
+ const attrs =
273
+ `path="${escapeAttr(ref.rel)}" bytes="${ref.bytes}" type="pdf"` +
274
+ pageAttr +
275
+ (ref.truncated
276
+ ? ` truncated="true" shown-bytes="${DEFAULT_MAX_BYTES}"`
277
+ : "");
278
+ parts.push(`<file ${attrs}>`);
279
+ parts.push(ref.content);
280
+ if (ref.truncated) {
281
+ parts.push(`\n… [truncated — PDF text exceeds ${DEFAULT_MAX_BYTES} bytes]`);
282
+ }
283
+ parts.push("</file>");
284
+ } else {
285
+ // Sync context (or extraction failed): no page text inlined.
286
+ const note =
287
+ ref.pdfNote || "PDF — page text not extracted in this context";
288
+ parts.push(
289
+ `<file path="${escapeAttr(ref.rel)}" bytes="${ref.bytes}" type="pdf" note="${escapeAttr(note)}" />`,
290
+ );
291
+ }
188
292
  } else if (ref.kind === "binary") {
189
293
  parts.push(
190
294
  `<file path="${escapeAttr(ref.rel)}" bytes="${ref.bytes}" binary="true" note="binary file — contents omitted" />`,
@@ -208,11 +312,8 @@ function renderBlock(refs) {
208
312
  * `prompt` is unchanged when nothing resolved; otherwise it is the
209
313
  * original text + a trailing `<referenced-files>` block.
210
314
  */
211
- export function expandFileRefs(prompt, opts = {}) {
212
- const text = typeof prompt === "string" ? prompt : "";
213
- if (!text || !text.includes("@")) {
214
- return { prompt: text, refs: [], warnings: [] };
215
- }
315
+ /** Resolve every @token to a ref (no rendering). Shared by sync + async. */
316
+ function _resolveAllRefs(text, opts) {
216
317
  const fs = opts.deps?.fs || fsDefault;
217
318
  const path = opts.deps?.path || pathDefault;
218
319
  const cwd = opts.cwd || process.cwd();
@@ -242,17 +343,107 @@ export function expandFileRefs(prompt, opts = {}) {
242
343
  }
243
344
  refs.push(ref);
244
345
  }
346
+ return { refs, warnings, maxBytes };
347
+ }
348
+
349
+ export function expandFileRefs(prompt, opts = {}) {
350
+ const text = typeof prompt === "string" ? prompt : "";
351
+ if (!text || !text.includes("@")) {
352
+ return { prompt: text, refs: [], warnings: [] };
353
+ }
354
+ const { refs, warnings } = _resolveAllRefs(text, opts);
355
+ if (refs.length === 0) {
356
+ return { prompt: text, refs: [], warnings };
357
+ }
358
+ return { prompt: `${text}\n\n${renderBlock(refs)}`, refs, warnings };
359
+ }
360
+
361
+ /**
362
+ * Async superset of {@link expandFileRefs} that ALSO extracts text from
363
+ * `@file.pdf` (optionally a page range, e.g. `@doc.pdf#1-3`). Needs the optional
364
+ * `pdf-parse` dependency; when it's absent each PDF ref degrades to a note and a
365
+ * warning (never throws). Used by the agent / REPL paths; the sync expandFileRefs
366
+ * leaves PDFs un-extracted.
367
+ */
368
+ export async function expandFileRefsAsync(prompt, opts = {}) {
369
+ const text = typeof prompt === "string" ? prompt : "";
370
+ if (!text || !text.includes("@")) {
371
+ return { prompt: text, refs: [], warnings: [] };
372
+ }
373
+ const { refs, warnings, maxBytes } = _resolveAllRefs(text, opts);
374
+ const extractPdf = opts.deps?.extractPdfPages || _extractPdfPages;
375
+
376
+ for (const ref of refs) {
377
+ if (ref.kind !== "pdf" || !ref.buf) continue;
378
+ try {
379
+ const out = await extractPdf(ref.buf, {
380
+ firstPage: ref.pageStart,
381
+ lastPage: ref.pageEnd,
382
+ maxBytes,
383
+ });
384
+ ref.content = String(out?.text || "");
385
+ ref.truncated = !!out?.truncated;
386
+ } catch (err) {
387
+ if (err && err.code === "PDF_LIB_MISSING") {
388
+ ref.pdfNote =
389
+ "PDF — install the optional `pdf-parse` dependency to extract page text";
390
+ warnings.push(
391
+ `@${ref.raw} — PDF extraction needs the optional \`pdf-parse\` dep (left as a reference)`,
392
+ );
393
+ } else {
394
+ ref.pdfNote = `PDF — could not extract text (${err?.message || "error"})`;
395
+ warnings.push(
396
+ `@${ref.raw} — PDF extraction failed: ${err?.message || "error"} (left as a reference)`,
397
+ );
398
+ }
399
+ }
400
+ delete ref.buf; // don't keep the raw bytes around once consumed
401
+ }
245
402
 
246
403
  if (refs.length === 0) {
247
404
  return { prompt: text, refs: [], warnings };
248
405
  }
406
+ return { prompt: `${text}\n\n${renderBlock(refs)}`, refs, warnings };
407
+ }
249
408
 
250
- const block = renderBlock(refs);
251
- return {
252
- prompt: `${text}\n\n${block}`,
253
- refs,
254
- warnings,
255
- };
409
+ /**
410
+ * Default PDF text extractor (optional `pdf-parse`). Returns
411
+ * `{ text, numpages, truncated }` for pages [firstPage, lastPage] (all pages
412
+ * when those are null), byte-capped at maxBytes. Throws code `PDF_LIB_MISSING`
413
+ * when the optional dependency is not installed.
414
+ */
415
+ async function _extractPdfPages(buffer, { firstPage, lastPage, maxBytes } = {}) {
416
+ let pdfParse;
417
+ try {
418
+ // pdf-parse's index.js runs debug code under ESM (no module.parent), so
419
+ // import the lib entry directly to avoid it.
420
+ const mod = await import("pdf-parse/lib/pdf-parse.js");
421
+ pdfParse = mod.default || mod;
422
+ } catch {
423
+ const e = new Error("pdf-parse not installed");
424
+ e.code = "PDF_LIB_MISSING";
425
+ throw e;
426
+ }
427
+ const data = await pdfParse(buffer, {
428
+ pagerender: (pageData) => {
429
+ const n = pageData && pageData.pageNumber;
430
+ if (typeof n === "number") {
431
+ if (firstPage && n < firstPage) return "";
432
+ if (lastPage && n > lastPage) return "";
433
+ }
434
+ return pageData.getTextContent().then((tc) =>
435
+ (tc.items || []).map((it) => it.str).join(" "),
436
+ );
437
+ },
438
+ });
439
+ let out = String(data?.text || "").trim();
440
+ let truncated = false;
441
+ const cap = Number.isFinite(maxBytes) ? maxBytes : DEFAULT_MAX_BYTES;
442
+ if (Buffer.byteLength(out, "utf-8") > cap) {
443
+ out = out.slice(0, cap);
444
+ truncated = true;
445
+ }
446
+ return { text: out, numpages: data?.numpages || 0, truncated };
256
447
  }
257
448
 
258
449
  export const _deps = { fs: fsDefault, path: pathDefault };
@@ -43,7 +43,7 @@ import {
43
43
  sessionExists as jsonlSessionExists,
44
44
  getLastSessionId as jsonlGetLastSessionId,
45
45
  } from "../harness/jsonl-session-store.js";
46
- import { expandFileRefs } from "./file-ref-expander.js";
46
+ import { expandFileRefsAsync } from "./file-ref-expander.js";
47
47
  import { composeSystemPrompt } from "./system-prompt.js";
48
48
  import { buildUserContent } from "../lib/image-input.js";
49
49
  import { withQuietStdout } from "./quiet-stdout.js";
@@ -330,8 +330,8 @@ export async function runAgentHeadless(options = {}, deps = {}) {
330
330
  // cat-pipe. Opt out with `--no-file-refs` (options.expandFileRefs === false).
331
331
  let userContent = prompt;
332
332
  if (options.expandFileRefs !== false) {
333
- const doExpand = deps.expandFileRefs || expandFileRefs;
334
- const expanded = doExpand(prompt, { cwd });
333
+ const doExpand = deps.expandFileRefs || expandFileRefsAsync;
334
+ const expanded = await doExpand(prompt, { cwd });
335
335
  userContent = expanded.prompt;
336
336
  // Warnings (typo'd paths, unreadable files) go to stderr in every output
337
337
  // format so stdout stays a clean machine payload.
@@ -19,7 +19,7 @@
19
19
  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
- import { expandFileRefs } from "./file-ref-expander.js";
22
+ import { expandFileRefsAsync } from "./file-ref-expander.js";
23
23
  import {
24
24
  resolveAgentMcp,
25
25
  resolvePermissionPromptTool,
@@ -260,7 +260,7 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
260
260
  const input = deps.input || process.stdin;
261
261
  const runLoop = deps.agentLoop || coreAgentLoop;
262
262
  const doBootstrap = deps.bootstrap || bootstrap;
263
- const doExpand = deps.expandFileRefs || expandFileRefs;
263
+ const doExpand = deps.expandFileRefs || expandFileRefsAsync;
264
264
  const writeOut = deps.writeOut || ((s) => process.stdout.write(s));
265
265
  const writeErr = deps.writeErr || ((s) => process.stderr.write(s));
266
266
  const emit = (obj) => writeOut(JSON.stringify(obj) + "\n");
@@ -578,6 +578,19 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
578
578
  },
579
579
  })
580
580
  : undefined,
581
+ // Extended-thinking reasoning deltas (Anthropic; only when thinking is on).
582
+ // Surfaced as a thinking_delta so consumers can render a dimmed/collapsed
583
+ // reasoning block — the visible half of the /think toggle.
584
+ onThinking: options.includePartialMessages
585
+ ? (thinking) =>
586
+ emit({
587
+ type: "stream_event",
588
+ event: {
589
+ type: "content_block_delta",
590
+ delta: { type: "thinking_delta", thinking },
591
+ },
592
+ })
593
+ : undefined,
581
594
  };
582
595
 
583
596
  // --max-budget-usd: a SESSION-WIDE USD spend cap across all turns. Folded
@@ -745,7 +758,7 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
745
758
  // @file expansion per user event (parity with single-turn headless).
746
759
  let userContent = parsed.text;
747
760
  if (options.expandFileRefs !== false) {
748
- const expanded = doExpand(parsed.text, { cwd });
761
+ const expanded = await doExpand(parsed.text, { cwd });
749
762
  userContent = expanded.prompt;
750
763
  for (const w of expanded.warnings) writeErr(` @ref: ${w}\n`);
751
764
  }
@@ -1 +0,0 @@
1
- import{I as w,P as d,J as u,U as s,R as n,c as i,K as v,Q as _,V as g,b as o}from"./vendor-BvqAck49.js";import{_ as S}from"./index-6QGNBg3m.js";import"./icons-DP3uiYxy.js";const k={__name:"OrderTableRenderer",props:{event:{type:Object,required:!0}},setup(p,{expose:r}){r();const a=p,e=o(()=>a.event.content||{}),l=o(()=>a.event.extra||{}),m=o(()=>l.value.merchant||e.value.merchant||e.value.counterparty||"—"),c=o(()=>e.value.title||e.value.name||e.value.itemName||e.value.text||"—"),y=o(()=>{const t=e.value.amount??e.value.price??e.value.total;return t==null?"—":`${e.value.currency||"¥"} ${typeof t=="number"?t.toFixed(2):t}`}),T=o(()=>l.value.orderNo||e.value.orderNo||e.value.orderId),f=o(()=>e.value.status||e.value.state),b=o(()=>{const t=(f.value||"").toLowerCase();return t.includes("成功")||t.includes("succe")||t.includes("paid")?"green":t.includes("退")||t.includes("refund")?"orange":t.includes("失败")||t.includes("fail")?"red":"default"}),h=o(()=>{if(!a.event.occurredAt)return"";const t=new Date(a.event.occurredAt);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`}),x={props:a,c:e,e:l,merchantText:m,itemText:c,amountText:y,orderNo:T,statusText:f,statusColor:b,formattedTime:h,computed:o};return Object.defineProperty(x,"__isScriptSetup",{enumerable:!1,value:!0}),x}},N={class:"order-card"},C={class:"head"},O={class:"time"},V={class:"row"},R={class:"val"},B={class:"row"},D={class:"val"},I={class:"row amount-row"},j={class:"val amount"},A={key:0,class:"row"},E={class:"val mono"},F={key:1,class:"row"};function M(p,r,a,e,l,m){const c=w("a-tag");return d(),u("div",N,[s("div",C,[s("span",O,n(e.formattedTime),1),i(c,{color:"gold"},{default:v(()=>[_(n(a.event.source.adapter),1)]),_:1}),i(c,null,{default:v(()=>[_(n(a.event.subtype),1)]),_:1})]),s("div",V,[r[0]||(r[0]=s("span",{class:"key"},"商户",-1)),s("span",R,n(e.merchantText),1)]),s("div",B,[r[1]||(r[1]=s("span",{class:"key"},"商品/项目",-1)),s("span",D,n(e.itemText),1)]),s("div",I,[r[2]||(r[2]=s("span",{class:"key"},"金额",-1)),s("span",j,n(e.amountText),1)]),e.orderNo?(d(),u("div",A,[r[3]||(r[3]=s("span",{class:"key"},"单号",-1)),s("span",E,n(e.orderNo),1)])):g("v-if",!0),e.statusText?(d(),u("div",F,[r[4]||(r[4]=s("span",{class:"key"},"状态",-1)),i(c,{color:e.statusColor},{default:v(()=>[_(n(e.statusText),1)]),_:1},8,["color"])])):g("v-if",!0)])}const K=S(k,[["render",M],["__scopeId","data-v-5ed5524d"],["__file","/tmp/cc-web-panel-WsolcE/repo/packages/web-panel/src/components/pdh/renderers/OrderTableRenderer.vue"]]);export{K as default};
@@ -1 +0,0 @@
1
- import{E,b as w,r as b,I as m,J as y,U as a,R as g,c as i,K as u,V as h,F as C,Z as L,N as O,o as M,P as p,Q as S,S as R}from"./vendor-BvqAck49.js";import{u as I,_ as N}from"./index-6QGNBg3m.js";import{u as P}from"./useShellMode-CgR0wCYM.js";import{p as V}from"./parsers-UEvh_ShA.js";import{u as z}from"./chat-Iurq_BMY.js";import{a9 as j,ap as q,R as B}from"./icons-DP3uiYxy.js";const A=E("skills",()=>{const _=b(!1),t=b([]),n=b(""),e=b("all"),k=w(()=>{const o=new Set(["all"]);return t.value.forEach(l=>{l.category&&o.add(l.category),l.executionMode&&o.add(l.executionMode)}),[...o]}),v=w(()=>{let o=t.value;if(e.value!=="all"&&(o=o.filter(l=>l.category===e.value||l.executionMode===e.value)),n.value){const l=n.value.toLowerCase();o=o.filter(d=>d.name?.toLowerCase().includes(l)||d.description?.toLowerCase().includes(l)||d.title?.toLowerCase().includes(l))}return o});async function f(){const o=I();_.value=!0;try{if(P().isEmbedded){const c=await o.sendRaw({type:"skill.list"},2e4);c?.ok&&Array.isArray(c.result?.skills)?t.value=c.result.skills:(console.error("skill.list custom topic failed:",c?.error),t.value=[]);return}const{output:l}=await o.execute("skill list",2e4),d=V(l);t.value=d}catch(l){console.error("Failed to load skills:",l)}finally{_.value=!1}}return{loading:_,allSkills:t,searchQuery:n,selectedCategory:e,categories:k,filteredSkills:v,loadSkills:f}}),F={__name:"Skills",setup(_,{expose:t}){t();const n=A(),e=z(),k=O(),v=w(()=>n.categories.filter(r=>r!=="all").slice(0,6));function f(r){return{"built-in":"内置","cli-direct":"CLI",agent:"Agent","llm-query":"LLM",hybrid:"混合",workspace:"工作区",marketplace:"市场"}[r]||r}function o(r){return{"cli-direct":"CLI",agent:"Agent","llm-query":"LLM",hybrid:"混合","built-in":"内置"}[r]||r||"未知"}function l(r){return{"cli-direct":"cyan",agent:"purple","llm-query":"blue",hybrid:"orange","built-in":"green"}[r]||"default"}async function d(r){await e.createSession("agent"),k.push("/chat"),setTimeout(()=>{e.currentSessionId&&e.sendMessage(e.currentSessionId,`/skill run ${r}`)},300)}M(()=>{n.allSkills.length||n.loadSkills()});const c={skillsStore:n,chatStore:e,router:k,displayCategories:v,catLabel:f,modeLabel:o,modeColor:l,runSkill:d,computed:w,onMounted:M,get useRouter(){return O},get ReloadOutlined(){return B},get PlayCircleOutlined(){return q},get AppstoreOutlined(){return j},get useSkillsStore(){return A},get useChatStore(){return z}};return Object.defineProperty(c,"__isScriptSetup",{enumerable:!1,value:!0}),c}},Q={style:{display:"flex","align-items":"center","justify-content":"space-between","margin-bottom":"24px"}},U={class:"page-sub"},T={style:{display:"flex",gap:"12px","margin-bottom":"20px","flex-wrap":"wrap"}},W={key:0,style:{"text-align":"center",padding:"60px"}},D={class:"skills-grid"},G={style:{display:"flex","justify-content":"space-between","align-items":"flex-start"}},J={style:{flex:"1","min-width":"0"}},K={style:{"font-weight":"500",color:"#e0e0e0","font-size":"14px","margin-bottom":"4px","font-family":"monospace"}},Z={style:{color:"var(--text-secondary)","font-size":"12px","line-height":"1.5"}},H={style:{"margin-left":"12px","flex-shrink":"0"}},X={style:{"margin-top":"10px",display:"flex","justify-content":"flex-end"}},Y={style:{"text-align":"center",padding:"60px",color:"var(--text-muted)"}};function $(_,t,n,e,k,v){const f=m("a-button"),o=m("a-input-search"),l=m("a-radio-button"),d=m("a-radio-group"),c=m("a-spin"),r=m("a-tag"),x=m("a-card");return p(),y("div",null,[a("div",Q,[a("div",null,[t[3]||(t[3]=a("h2",{class:"page-title"},"技能管理",-1)),a("p",U,g(e.skillsStore.allSkills.length)+" 个可用技能",1)]),i(f,{type:"primary",ghost:"",loading:e.skillsStore.loading,onClick:t[0]||(t[0]=s=>e.skillsStore.loadSkills())},{icon:u(()=>[i(e.ReloadOutlined)]),default:u(()=>[t[4]||(t[4]=S(" 刷新 ",-1))]),_:1},8,["loading"])]),h(" Search & Filter "),a("div",T,[i(o,{value:e.skillsStore.searchQuery,"onUpdate:value":t[1]||(t[1]=s=>e.skillsStore.searchQuery=s),placeholder:"搜索技能名称或描述...",style:{width:"300px"},"allow-clear":""},null,8,["value"]),i(d,{value:e.skillsStore.selectedCategory,"onUpdate:value":t[2]||(t[2]=s=>e.skillsStore.selectedCategory=s),"button-style":"solid"},{default:u(()=>[i(l,{value:"all"},{default:u(()=>[...t[5]||(t[5]=[S("全部",-1)])]),_:1}),(p(!0),y(C,null,L(e.displayCategories,s=>(p(),R(l,{key:s,value:s},{default:u(()=>[S(g(e.catLabel(s)),1)]),_:2},1032,["value"]))),128))]),_:1},8,["value"])]),h(" Loading State "),e.skillsStore.loading?(p(),y("div",W,[i(c,{size:"large"}),t[6]||(t[6]=a("div",{style:{color:"var(--text-muted)","margin-top":"12px"}},"加载技能中...",-1))])):e.skillsStore.filteredSkills.length?(p(),y(C,{key:1},[h(" Skills Grid "),a("div",D,[(p(!0),y(C,null,L(e.skillsStore.filteredSkills,s=>(p(),R(x,{key:s.name,class:"skill-card",size:"small",hoverable:"",style:{background:"var(--bg-card)","border-color":"var(--border-color)",cursor:"default"}},{default:u(()=>[a("div",G,[a("div",J,[a("div",K,g(s.name),1),a("div",Z,g(s.description||"暂无描述"),1)]),a("div",H,[i(r,{color:e.modeColor(s.executionMode),style:{"font-size":"10px"}},{default:u(()=>[S(g(e.modeLabel(s.executionMode)),1)]),_:2},1032,["color"])])]),a("div",X,[i(f,{size:"small",type:"primary",ghost:"",onClick:ee=>e.runSkill(s.name)},{icon:u(()=>[i(e.PlayCircleOutlined)]),default:u(()=>[t[7]||(t[7]=S(" 运行 ",-1))]),_:1},8,["onClick"])])]),_:2},1024))),128))])],2112)):(p(),y(C,{key:2},[h(" Empty State "),a("div",Y,[i(e.AppstoreOutlined,{style:{"font-size":"48px","margin-bottom":"16px",display:"block"}}),a("div",null,g(e.skillsStore.allSkills.length?"没有匹配的技能":"点击刷新加载技能列表"),1)])],2112))])}const ie=N(F,[["render",$],["__scopeId","data-v-d64d43f5"],["__file","/tmp/cc-web-panel-WsolcE/repo/packages/web-panel/src/views/Skills.vue"]]);export{ie as default};