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.
- package/package.json +4 -3
- package/src/assets/web-panel/assets/{AIOps-CnHIXe2L.js → AIOps-1bl50Cen.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CzDyhTAp.js → ActionButton-Bw4kAx-1.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-rwvDMCd-.js → Analytics-junq7r42.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-D-Q1M-5V.js → AppLayout-vf1TVGeu.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-BIFSGnAE.js → Audit-CBzpgcL9.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CbUdiVeS.js → Backup-ChwGPeP4.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-D3P_pICH.js → BaseInput-2G2KGE6M.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DRrX7vVB.js → Chat-Cr9NwfDA.js} +4 -4
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DTC1ElV-.js → ChatBubbleRenderer-CJvlo_ay.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-Dpq9X8Xd.js → Checkbox-DN_5PBT7.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-yQQOSuHA.js → Codegen-D4bluAgT.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DLFLDxar.js → Col-DBP3UjYW.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DYbdX-GB.js → Community-ge-SvR3c.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-Dclrp1HE.js → Compact-DGXvlGNv.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-C9EQMgoP.js → Compliance-Cn7nBsFQ.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-B9IMFsMk.js → Cowork-DgHEO2de.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-V6TlbDey.js → Cron-DUbYxPs0.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-Cl_vG8R_.js → Crosschain-3J0MWPQq.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DoUUF9cz.js → DID-BR_Jpkh4.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DcA8WmrS.js → Dashboard-ffykLkAJ.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-wyGMYzw9.js → Dropdown-DaI38dXT.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-B1T3ziIH.js → EmailListRenderer-CnIzkUVG.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CW4CwYbZ.js → FamilyGuardDashboard-BxrgAAZg.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-9tEiw0KA.js → Federation-BuS_FRh3.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DbocfnNQ.js → FormItemContext-DBG-8g1w.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CGNsG2qI.js → GenericCardRenderer-CmEc8RgH.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BxA3dgT8.js → Git-CK10BYwY.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DqnhaC7n.js → Governance-C8cABM_I.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-D6Ll4t9c.js → Inference-Dr4_DIw3.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Bb8IXGl7.js → KnowledgeGraph-0_bLuvvb.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DRUUVMdc.js → Logs-PVGZxt9z.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-BYRM186w.js → Marketplace-BtVnS4HW.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DLbh7uS8.js → McpTools-BVulusCC.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-D2_uZEOI.js → Memory-DEMtNbJ9.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CXVJoMKk.js → MobileBridge-5QL654FQ.js} +3 -3
- package/src/assets/web-panel/assets/{MobileProjects-QRZYgIME.js → MobileProjects-G5Du9Thr.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-D3mv30gW.js → Mtc-CNFPJej0.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-__QPus22.js → MtcAudit-DAJ81Cej.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-Bu5gaTn6.js → Multisig-Cwj8tXfT.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-SjaViyfR.js → NLProgramming-Di0ptaL6.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-feXsl0dN.js → Notes-Cse_4Ox6.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BtVFokbM.js → NotificationSettings-ntpoi5ec.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CbKsOM0D.js +1 -0
- package/src/assets/web-panel/assets/{Organization-CewL60Rd.js → Organization-Bhjya5h9.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-2ZRChJ7E.js → Overflow-C0u3aive.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DuMr6G9y.js → P2P-BWrTwf_a.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BMOcHCWd.js → PdhVaultBrowser-pld2Qlqk.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-Cj94ql75.js → Permissions-CW5FG3mE.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BYK4mlk7.js → PersonalDataHub-BuBmqfDa.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-maRvdrbO.js → Pipeline-nZr06BNO.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CKb2zZJz.js → Privacy-B_XvMHYv.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-C2pfXv4l.js → ProjectInit-CbRHJy85.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DGeNwq0h.js → ProjectSettings-CCH8SpEa.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BRypTQUV.js → Projects-D5Min2Fu.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DYwMPXEO.js → Providers-DgCY5A8A.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DE6isYZV.js → QuickAsk-DEcKTiTV.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Rc-AE6y9.js → Recommend-BRaq2oKc.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-DDQ8_kmM.js → Reputation-DzdbBdVv.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Do5iQUFj.js → Row-Ds2JNh7c.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-cxG0ulSw.js → RssFeed-Bk1-t4Ie.js} +3 -3
- package/src/assets/web-panel/assets/{Search-D3-8EdNk.js → Search-DaVhRwoq.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DtBfws1D.js → Security-D9mXjPSu.js} +3 -3
- package/src/assets/web-panel/assets/{Services-DzeN8HsG.js → Services-D8sukL97.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BQCVeTUf.js → Skeleton-COCJt30X.js} +1 -1
- package/src/assets/web-panel/assets/Skills-DtFPSPSc.js +1 -0
- package/src/assets/web-panel/assets/{Sla-d2IOd-AA.js → Sla-DIab-wjR.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BjfopwT6.js → SpeechSettings-xxIH9hgB.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D5_c1_rI.js → SyncSettings-DrpxzJOH.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-maiJ3jAT.js +1 -0
- package/src/assets/web-panel/assets/{Templates-CCXFgNy3.js → Templates-COE-dSks.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-Duz6eEK3.js → Tenant-CLGObu4M.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-Buc6UbYo.js → Terminal-Bw8WBvW3.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Du5jKHJz.js → TimelineRenderer-D4gdk3Qb.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-B12vLtQm.js → Tokens-CSoL_uAw.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-s_pHOerx.js → Trigger-BCRcowFi.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-4sDhbAls.js → Trust-D5QPZ198.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-UeyFknip.js → UkeySign-C7SsMqfZ.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DsopM2S5.js → VideoEditing-CDQ4HNZL.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CreLjC5U.js → Wallet-VmG4P_Fm.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-CBuMMZbK.js → WebAuthn-qtC0dZxA.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-DWhHHSFW.js → WorkflowEditor-C_CSdk3l.js} +1 -1
- package/src/assets/web-panel/assets/{chat-Iurq_BMY.js → chat-BGm8YCIo.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BIOTIa89.js → colors-BScOmAwk.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-B7lPu9pO.js → compact-item-HDwaT31n.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-Bzzk7mrh.js → createContext-CheSjPm8.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CjO6o19j.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-B11xqp5O.js → hasIn-C1_NbX0O.js} +1 -1
- package/src/assets/web-panel/assets/index--jhpSxS1.js +1 -0
- package/src/assets/web-panel/assets/{index-vZCGFSJN.js → index-6C8mxO8j.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLhpFnSR.js → index-B94_DZF-.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlR8YVlt.js → index-BGROuzQG.js} +1 -1
- package/src/assets/web-panel/assets/{index-CxDOX4g6.js → index-BKQordyU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMrzHgE6.js → index-BMxPL1oG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsoyfOzW.js → index-Bd2zS3jK.js} +1 -1
- package/src/assets/web-panel/assets/{index-3LgeP4ij.js → index-ByoUjX8f.js} +1 -1
- package/src/assets/web-panel/assets/{index-Y4gmywZo.js → index-C4FSaMAu.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCxGEK8b.js → index-C6WXnaG4.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-lwsB2F.js → index-C71Z__li.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4wV32Dn.js → index-CSaztnyZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiXNVmcx.js → index-CYkezxqK.js} +1 -1
- package/src/assets/web-panel/assets/{index-D6q-fkXs.js → index-Clqj2JTi.js} +1 -1
- package/src/assets/web-panel/assets/{index-R-YSpvg4.js → index-Cm55PcYG.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dn_Ht3v2.js → index-CxoOEb-l.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEKeoJ6I.js → index-D2gy4IX0.js} +1 -1
- package/src/assets/web-panel/assets/{index-AmHa_sOB.js → index-D8xpncJ0.js} +1 -1
- package/src/assets/web-panel/assets/{index-CULCwI_H.js → index-DB6gbgU2.js} +1 -1
- package/src/assets/web-panel/assets/{index-tub9w6us.js → index-DDk0s3nO.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzY2mdiR.js → index-DQ01dBAS.js} +1 -1
- package/src/assets/web-panel/assets/{index-5BkUKVyj.js → index-DSuWcpsv.js} +1 -1
- package/src/assets/web-panel/assets/{index-D8tm9JnQ.js → index-DVyL9jbT.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLREN4NE.js → index-DaATJtzu.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEKRqrI-.js → index-DbfAaBxL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVIXYV1R.js → index-DhTELUKN.js} +1 -1
- package/src/assets/web-panel/assets/{index-BgmBZgU9.js → index-Dk2N6hxE.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4rXFIOG.js → index-DkiIxTIS.js} +1 -1
- package/src/assets/web-panel/assets/{index-CA-GPOd9.js → index-DoZwBGUX.js} +1 -1
- package/src/assets/web-panel/assets/{index-6QGNBg3m.js → index-DppSbT8L.js} +3 -3
- package/src/assets/web-panel/assets/{index-DwGVPCZb.js → index-DzE-95XH.js} +1 -1
- package/src/assets/web-panel/assets/index-J4Rzclyc.js +1 -0
- package/src/assets/web-panel/assets/{index-BIFscFlm.js → index-KG9xVANC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DyV9331f.js → index-KXrUKw1h.js} +1 -1
- package/src/assets/web-panel/assets/{index-D25-u3Q2.js → index-eSBoP6E_.js} +1 -1
- package/src/assets/web-panel/assets/{index-B93pM-xx.js → index-ggl9cj35.js} +1 -1
- package/src/assets/web-panel/assets/{index-htxy7qDG.js → index-i-xYmSsZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CS2UWrIt.js → index-lB5kN9Yc.js} +1 -1
- package/src/assets/web-panel/assets/{index-DX7HGwP_.js → index-yKmudyK7.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Bl6klCzd.js → initDefaultProps-D7Q7zDzP.js} +1 -1
- package/src/assets/web-panel/assets/{motion-B9IxS1-S.js → motion-B4DcaqPb.js} +1 -1
- package/src/assets/web-panel/assets/{move-NJ5XDNQe.js → move-CYxmYrFY.js} +1 -1
- package/src/assets/web-panel/assets/{omit-ByO8GI_H.js → omit-ze_9dKw3.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-W6iTcLuH.js → pickAttrs-D24HrRSv.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-JMndCFEP.js → placementArrow-BWJZVYyS.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DHV7-FuY.js → responsiveObserve-CseJia_L.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DioRUeNF.js → slide-CBWNEpey.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DMp2b9Ze.js → statusUtils-f4e6p7yd.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DamP5BTS.js → styleChecker-DZmfWZ8G.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-D338iARz.js → useFlexGapSupport-4RBaBnVI.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DE_HJgk6.js → useFs-Cv6bNoEA.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-914YxO9q.js → usePersonalDataHub-DOTETJIA.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CY39Ky7P.js → vnode-DqS1K6-J.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BEghlCth.js → zoom-C1MYzIJG.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/cowork.js +8 -0
- package/src/commands/crosschain.js +32 -4
- package/src/commands/init.js +10 -10
- package/src/commands/loop.js +9 -3
- package/src/commands/memory.js +6 -4
- package/src/commands/orchestrate.js +5 -2
- package/src/commands/video.js +5 -1
- package/src/lib/cowork-workflow.js +664 -129
- package/src/lib/hook-manager.js +3 -2
- package/src/lib/micro-compact.js +52 -0
- package/src/lib/permission-rules.cjs +39 -0
- package/src/lib/skill-loader.js +62 -43
- package/src/lib/workflow-state-reader.js +10 -1
- package/src/repl/agent-repl.js +228 -20
- package/src/repl/chat-repl.js +4 -2
- package/src/repl/permission-tier.js +60 -0
- package/src/repl/stream-decision.js +16 -0
- package/src/repl/think-command.js +36 -0
- package/src/runtime/agent-core.js +42 -6
- package/src/runtime/file-ref-expander.js +209 -18
- package/src/runtime/headless-runner.js +3 -3
- package/src/runtime/headless-stream.js +16 -3
- package/src/assets/web-panel/assets/OrderTableRenderer-CJJ2GWDf.js +0 -1
- package/src/assets/web-panel/assets/Skills-D6mCVKt7.js +0 -1
- package/src/assets/web-panel/assets/Tasks-BCmxDMZx.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CuIq7zBB.js +0 -1
- package/src/assets/web-panel/assets/index-BRsns-SP.js +0 -1
- package/src/assets/web-panel/assets/index-QTuoOvJA.js +0 -1
package/src/repl/chat-repl.js
CHANGED
|
@@ -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 {
|
|
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 =
|
|
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)
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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(
|
|
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
|
-
|
|
212
|
-
|
|
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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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 {
|
|
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 ||
|
|
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 {
|
|
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 ||
|
|
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};
|