chainlesschain 0.162.108 → 0.162.109
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 +1 -1
- package/src/assets/web-panel/assets/{AIOps-B8T7xeuV.js → AIOps-DOMx_gkg.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BDxBsVYw.js → ActionButton-C3V6-KN7.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-5cYrjLzN.js → Analytics-DhcH00zB.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DkanJ7a5.js → AppLayout-DxaARGVc.js} +4 -4
- package/src/assets/web-panel/assets/{Audit-8r3yG5R0.js → Audit-B5v_yPs_.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DmdJpAwc.js → Backup-y0trXy2c.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CCTC_ROJ.js → BaseInput-BKmOrsWP.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BE4xBoCH.js → Chat-YxvzoXfA.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-ita18CO8.js → ChatBubbleRenderer-BT502wFg.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DVUDDyL5.js → Checkbox-CWlobcH7.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-C07Q688Z.js → Codegen-DyDnpB9x.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Bfmb67bb.js → Col-vSk5m8e6.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DvUS6LOy.js → Community-ms1KDyit.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-B2jvuCf2.js → Compact-CgOlMunA.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-B4VnGZOX.js → Compliance-BtmnlvkF.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Bf1UR9uf.js → Cowork-oEgcjamv.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-BYU0OhXx.js → Cron-C45dOmbZ.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DhtbpJ8Y.js → Crosschain-COCW4N4S.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DFtdsXjV.js → DID-D6L3jdjB.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BOqMlucS.js → Dashboard-CZO_nWR9.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DWcGi1lU.js → Dropdown-BHogrwxg.js} +1 -1
- package/src/assets/web-panel/assets/EmailListRenderer-DVU5KA6Y.js +1 -0
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-_qKQM8YZ.js → FamilyGuardDashboard-BpJuZRlS.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DxaaO_Pn.js → Federation-C8XUIu9h.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Bijk4-hV.js → FormItemContext-Dkv__07L.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CXs5JGAs.js → GenericCardRenderer-PI4Hv6J7.js} +1 -1
- package/src/assets/web-panel/assets/{Git-vopq_XAi.js → Git-DwoiOK7x.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-D6lEr1e2.js → Governance-CZMrVnKF.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-D716knRp.js → Inference-DGMCR5fY.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Mt7L5B8p.js → KnowledgeGraph-CHnyTP7N.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BmY4Aozy.js → Logs-aHwf1vTL.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-Bk7yghOV.js → Marketplace-DrrJWlYy.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BdEGNK8B.js → McpTools-FsvEQItl.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-DaGxWezP.js → Memory-BaLEbZZU.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-D2QqiIXV.js → MobileBridge-Cbpe-UlH.js} +2 -2
- package/src/assets/web-panel/assets/{MobileProjects-JQQ60jth.js → MobileProjects-WpHJ6m0I.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-DHp0q17f.js → Mtc-B2-MRjt7.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-RWrCriuc.js → MtcAudit-BPKg4478.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-BR_5j51g.js → Multisig-DL-OMvg9.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-MF3ZkPYq.js → NLProgramming-BFbo7QJF.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BDWwfQ-t.js → Notes-DxlawFPj.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-Xk8BtRL5.js → NotificationSettings-mXTgzIC1.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DaVlfAsv.js → OrderTableRenderer-BUcf5CYv.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-CNdU6Jxx.js → Organization-FeID8_Wf.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DOf8iK6T.js → Overflow-DTFS4Vaz.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DvOwefeB.js → P2P-BkylEhA8.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-D8bxEEcj.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-DM_Cha_-.js → Permissions-BSQ09sRI.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DGOiN1md.js → PersonalDataHub-Ba5iRuIi.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-BMZcrOpC.js → Pipeline-DmL3Gpxg.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CcBD3ZJ_.js → Privacy-DAvd_DSh.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BQmkVkFp.js → ProjectInit-AxBuFwBR.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BL0iqE5h.js → ProjectSettings-BZ40fAt5.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-7iyoLF6T.js → Projects-BHNbHuBF.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-IQqLZ_11.js → Providers-CDkzXPon.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Cp0Lpic1.js → QuickAsk-CzKbOUkP.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DN-26aef.js → Recommend-D82aHoFt.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-ChdhhVGu.js → Reputation-zpuCi7ft.js} +1 -1
- package/src/assets/web-panel/assets/{Row-2RBgBLEz.js → Row-BDq7p96-.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CfogWRiS.js → RssFeed-pAo6EJPy.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CrREUZpz.js → Search-BXv5RRMM.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CcDdV-6d.js → Security-Gn-HnKp5.js} +4 -4
- package/src/assets/web-panel/assets/{Services-qG5L2PHE.js → Services-BlfMJ4ZL.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-7CVgHJwr.js → Skeleton-Q8cKUNTJ.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BEbdNS7s.js → Skills-BjOLODy1.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-C4ENvrVM.js → Sla-TbklgbfG.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DuvM2AsQ.js → SpeechSettings-DzKKyV6J.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Bm9Np5Yt.js → SyncSettings-Bq-76qhL.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-K2ZtJ47C.js → Tasks-DWBUnqic.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-C3GJa4ya.js → Templates-CS77urAR.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BLQnLb8K.js → Tenant-B1hx8tjz.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-CNX2SAIP.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-BD8NwNAK.js → TimelineRenderer-CS_iGXXB.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BIsO2iiX.js → Tokens-CPfe_Ndx.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-FJv5zeym.js → Trigger-DARLZ6w5.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-9FRFEAhW.js → Trust-R_mlmd7H.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-aOdZVwgi.js → UkeySign-CysEd--z.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C1KyY7ZB.js → VideoEditing-eQ46o05K.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CH5edY0l.js → Wallet-C5788NxS.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-BUPlkFe0.js → WebAuthn-CK_CPM4k.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-BoH8bG7y.js → WorkflowEditor-DhvdLkE6.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DRS34SZq.js → chat-CcKQhy1e.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DxdhROQh.js → colors-B4d3_MCE.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-C5f4y3lM.js → compact-item-DC-Xf92z.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DkyzEt2y.js → createContext-KBHDmwRY.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Da3Hdd0q.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CIRkRQ69.js → hasIn-DEuYFeVL.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgJf60U0.js → index-2Csz0FGD.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3WhDg1s.js → index-8BCBAwoA.js} +3 -3
- package/src/assets/web-panel/assets/{index-B_ytTrAE.js → index-B4RknAfS.js} +1 -1
- package/src/assets/web-panel/assets/{index-CyMVWOwq.js → index-B5cOdkHy.js} +1 -1
- package/src/assets/web-panel/assets/{index-X7QcpsDO.js → index-BB9f50Gt.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnAbauX-.js → index-BCbc993k.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHACYX-h.js → index-BJbBr6_l.js} +1 -1
- package/src/assets/web-panel/assets/{index-CSHsX3UZ.js → index-BK8vM7es.js} +1 -1
- package/src/assets/web-panel/assets/index-BMeXnsEn.js +1 -0
- package/src/assets/web-panel/assets/{index-btOwnCpT.js → index-BZjC0VTy.js} +1 -1
- package/src/assets/web-panel/assets/index-BdqPeWvV.js +1 -0
- package/src/assets/web-panel/assets/{index-dtUUXiuv.js → index-Bm39r2a4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BrNCtBLG.js → index-BwD3FKsC.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLLkL7YU.js → index-BxlEfskb.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cv9YEv3M.js → index-C4Ose5wC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSpV3T2E.js → index-CCnk_-ug.js} +1 -1
- package/src/assets/web-panel/assets/{index-BStiYEJX.js → index-CTgs-ddr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfZal2SO.js → index-CdabmsmR.js} +1 -1
- package/src/assets/web-panel/assets/{index-fgBegFCK.js → index-ChJ_B7b9.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpHZisZn.js → index-Cl0As6V5.js} +1 -1
- package/src/assets/web-panel/assets/{index-DR_uUG6x.js → index-CxtHBFqA.js} +1 -1
- package/src/assets/web-panel/assets/{index-1Vd3bgPH.js → index-Cyb-NAiR.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dz-Xl9WT.js → index-DE0-JQay.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0VYKzdU.js → index-DQMDCc8g.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXE4fjlC.js → index-DQXNz0yD.js} +1 -1
- package/src/assets/web-panel/assets/{index-CO4PAGWX.js → index-DSXxbnHU.js} +1 -1
- package/src/assets/web-panel/assets/{index-2uQYIxL1.js → index-DTudiivg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BzC5O6l6.js → index-DZN3yAE2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bc59VV8o.js → index-DhGYNT5Y.js} +1 -1
- package/src/assets/web-panel/assets/{index-BdzFfdXs.js → index-DkoDAyug.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdLAPzIl.js → index-EnBI6ILp.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLjIL9vZ.js → index-JXP0mbdx.js} +1 -1
- package/src/assets/web-panel/assets/{index-Kyd48UEO.js → index-N4xZOl6m.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtOM1jbe.js → index-Tcf8b4Dr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bskk24yp.js → index-VkeJvcgx.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5r9kCHD.js → index-ZbZ9FKyc.js} +1 -1
- package/src/assets/web-panel/assets/{index-wdXKonKr.js → index-gOIec6-U.js} +1 -1
- package/src/assets/web-panel/assets/{index-5Mph6K8H.js → index-p5OuYAoC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DV5W06OY.js → index-tW1RyKbL.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-4uTrrqjb.js → initDefaultProps-BSjGxzmn.js} +1 -1
- package/src/assets/web-panel/assets/{motion-gkRvxGHW.js → motion-B5_Cszlm.js} +1 -1
- package/src/assets/web-panel/assets/{move-CIgnaWNe.js → move-Zi4ZApRv.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DB-Ux8Gg.js → omit-BNHsjZx4.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-ree-Itb0.js → pickAttrs-GzJRqTuY.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-EdAP6bCf.js → placementArrow-CXfzbh2d.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-B1DqSgFh.js → responsiveObserve-lhENMtmd.js} +1 -1
- package/src/assets/web-panel/assets/{slide-D_9NP_KE.js → slide-BaUzfEHc.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CAytzvoR.js → statusUtils-ZYc611Fn.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-zb3gob1_.js → styleChecker-CaymUbb1.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BQWuGrL0.js → useFlexGapSupport-9EAy_UPY.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-QMEsYkGX.js → useFs-CUqinWaA.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-Ccrz7-au.js → usePersonalDataHub-eOXrcae-.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DZVY4QM-.js → vnode-gBQTchDv.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Dii_0V_X.js → zoom-CCC5-LNi.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +6 -0
- package/src/commands/hub.js +6 -2
- package/src/commands/mcp.js +42 -10
- package/src/commands/pdh.js +4 -1
- package/src/constants.js +2 -2
- package/src/gateways/ws/video-protocol.js +7 -0
- package/src/harness/feature-flags.js +19 -6
- package/src/harness/prompt-compressor.js +1 -0
- package/src/lib/agent-core.js +2 -0
- package/src/lib/config-manager.js +42 -1
- package/src/lib/git-integration.js +1 -2
- package/src/lib/learning/learning-hooks.js +6 -0
- package/src/lib/llm-providers.js +2 -0
- package/src/lib/mcp-auth-target.js +53 -0
- package/src/lib/shell-approval.js +7 -12
- package/src/lib/skill-loader.js +6 -2
- package/src/lib/suggest-name.js +91 -0
- package/src/lib/task-model-selector.js +5 -5
- package/src/repl/agent-repl.js +9 -1
- package/src/repl/slash-macro.js +7 -1
- package/src/runtime/agent-core.js +62 -3
- package/src/runtime/coding-agent-contract-shared.cjs +11 -1
- package/src/runtime/headless-runner.js +57 -4
- package/src/runtime/headless-stream.js +50 -2
- package/src/session/index.js +6 -2
- package/src/assets/web-panel/assets/EmailListRenderer-smcpeq18.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-BmrU5B5U.js +0 -7
- package/src/assets/web-panel/assets/Terminal-BGgZIwb0.js +0 -3
- package/src/assets/web-panel/assets/devWarning-vwZx8E0a.js +0 -1
- package/src/assets/web-panel/assets/index-Dhn6EWMQ.js +0 -1
- package/src/assets/web-panel/assets/index-dGvDljur.js +0 -1
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
* Route keys use dot-case: video.assets.list, video.deconstruct, etc.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { createRequire } from "node:module";
|
|
12
|
+
// ESM module — `require` is not a global here. createRequire gives a working
|
|
13
|
+
// require() for the lazy loads below (VideoPipeline + node core modules, kept
|
|
14
|
+
// lazy so importing this protocol module doesn't pull the video stack on
|
|
15
|
+
// every `cc` startup). Node >=22.12 (our engines floor) supports require(esm).
|
|
16
|
+
const require = createRequire(import.meta.url);
|
|
17
|
+
|
|
11
18
|
function ok(data = {}) {
|
|
12
19
|
return { ok: true, ...data };
|
|
13
20
|
}
|
|
@@ -8,7 +8,13 @@
|
|
|
8
8
|
* - object { enabled, variant, description }
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
loadConfig,
|
|
13
|
+
getConfigValue,
|
|
14
|
+
saveConfig,
|
|
15
|
+
} from "../lib/config-manager.js";
|
|
16
|
+
import { withFileLock } from "../lib/with-file-lock.js";
|
|
17
|
+
import { getConfigPath } from "../lib/paths.js";
|
|
12
18
|
import { createHash } from "node:crypto";
|
|
13
19
|
import { hostname } from "node:os";
|
|
14
20
|
|
|
@@ -51,7 +57,8 @@ export function feature(name) {
|
|
|
51
57
|
if (value && typeof value === "object") return Boolean(value.enabled);
|
|
52
58
|
const defaultValue = _getDefault(name);
|
|
53
59
|
if (typeof defaultValue === "boolean") return defaultValue;
|
|
54
|
-
if (typeof defaultValue === "number")
|
|
60
|
+
if (typeof defaultValue === "number")
|
|
61
|
+
return _percentageCheck(name, defaultValue);
|
|
55
62
|
if (defaultValue && typeof defaultValue === "object") {
|
|
56
63
|
return Boolean(defaultValue.enabled);
|
|
57
64
|
}
|
|
@@ -106,10 +113,16 @@ export function listFeatures() {
|
|
|
106
113
|
}
|
|
107
114
|
|
|
108
115
|
export function setFeature(name, value) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
// Serialize the read-modify-write across processes so two concurrent
|
|
117
|
+
// `cc config features enable/disable` invocations don't clobber each other
|
|
118
|
+
// (and to space out writes so the atomic rename doesn't collide). Mirrors
|
|
119
|
+
// setConfigValue; the lock is best-effort and never hangs the CLI.
|
|
120
|
+
withFileLock(getConfigPath(), () => {
|
|
121
|
+
const config = loadConfig();
|
|
122
|
+
if (!config.features) config.features = {};
|
|
123
|
+
config.features[name] = value;
|
|
124
|
+
saveConfig(config);
|
|
125
|
+
});
|
|
113
126
|
}
|
|
114
127
|
|
|
115
128
|
export function getFlagInfo(name) {
|
package/src/lib/agent-core.js
CHANGED
|
@@ -37,7 +37,7 @@ export function saveConfig(config) {
|
|
|
37
37
|
const tmp = `${configPath}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
38
38
|
try {
|
|
39
39
|
writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
40
|
-
|
|
40
|
+
renameWithRetry(tmp, configPath);
|
|
41
41
|
} catch (err) {
|
|
42
42
|
try {
|
|
43
43
|
if (existsSync(tmp)) unlinkSync(tmp);
|
|
@@ -48,6 +48,47 @@ export function saveConfig(config) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
// On Windows, `rename` over a target that another process currently has open
|
|
52
|
+
// (e.g. a concurrent `loadConfig` reader) fails with EPERM/EACCES/EBUSY instead
|
|
53
|
+
// of POSIX's silent atomic replace. Under bursts of concurrent `cc config`/`cc
|
|
54
|
+
// config features` invocations this surfaced as a flaky exit-1. Retry the rename
|
|
55
|
+
// a few times with a short synchronous backoff so the transient lock clears;
|
|
56
|
+
// the temp file is already fully written, so this stays crash-safe.
|
|
57
|
+
export function renameWithRetry(tmp, target, opts = {}) {
|
|
58
|
+
const {
|
|
59
|
+
attempts = 8,
|
|
60
|
+
baseDelayMs = 15,
|
|
61
|
+
_rename = renameSync,
|
|
62
|
+
_sleep = sleepSync,
|
|
63
|
+
} = opts;
|
|
64
|
+
for (let i = 0; ; i++) {
|
|
65
|
+
try {
|
|
66
|
+
_rename(tmp, target);
|
|
67
|
+
return;
|
|
68
|
+
} catch (err) {
|
|
69
|
+
const transient =
|
|
70
|
+
err &&
|
|
71
|
+
(err.code === "EPERM" ||
|
|
72
|
+
err.code === "EACCES" ||
|
|
73
|
+
err.code === "EBUSY" ||
|
|
74
|
+
err.code === "EEXIST");
|
|
75
|
+
if (!transient || i >= attempts - 1) throw err;
|
|
76
|
+
_sleep(baseDelayMs * (i + 1));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function sleepSync(ms) {
|
|
82
|
+
try {
|
|
83
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
84
|
+
} catch {
|
|
85
|
+
const end = Date.now() + ms; // SharedArrayBuffer unavailable — bounded spin
|
|
86
|
+
while (Date.now() < end) {
|
|
87
|
+
/* spin */
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
51
92
|
export function getConfigValue(key) {
|
|
52
93
|
const config = loadConfig();
|
|
53
94
|
return getNestedValue(config, key);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { execSync } from "child_process";
|
|
6
|
-
import { existsSync } from "fs";
|
|
6
|
+
import { existsSync, writeFileSync, chmodSync } from "fs";
|
|
7
7
|
import { join } from "path";
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -236,7 +236,6 @@ export function installHooks(dir) {
|
|
|
236
236
|
echo "ChainlessChain: pre-commit hook running"
|
|
237
237
|
`;
|
|
238
238
|
|
|
239
|
-
const { writeFileSync, chmodSync } = require("fs");
|
|
240
239
|
writeFileSync(hookPath, hookContent, "utf-8");
|
|
241
240
|
try {
|
|
242
241
|
chmodSync(hookPath, 0o755);
|
|
@@ -16,6 +16,12 @@
|
|
|
16
16
|
* - Stateless — the trajectory ID is tracked on learningCtx
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { createRequire } from "node:module";
|
|
20
|
+
// ESM module — `require` is not a global here. createRequire gives a working
|
|
21
|
+
// require() for the lazy TrajectoryStore load below (kept lazy to avoid a
|
|
22
|
+
// circular import). Node >=22.12 (our engines floor) supports require(esm).
|
|
23
|
+
const require = createRequire(import.meta.url);
|
|
24
|
+
|
|
19
25
|
/**
|
|
20
26
|
* @typedef {object} LearningContext
|
|
21
27
|
* @property {import("./trajectory-store.js").TrajectoryStore} trajectoryStore
|
package/src/lib/llm-providers.js
CHANGED
|
@@ -84,6 +84,8 @@ export const BUILT_IN_PROVIDERS = {
|
|
|
84
84
|
baseUrl: "https://ark.cn-beijing.volces.com/api/v3",
|
|
85
85
|
apiKeyEnv: "VOLCENGINE_API_KEY",
|
|
86
86
|
models: [
|
|
87
|
+
"doubao-seed-2-1-pro-260628",
|
|
88
|
+
"doubao-seed-2-0-lite-260215",
|
|
87
89
|
"doubao-seed-1-6-251015",
|
|
88
90
|
"doubao-seed-1-6-flash-250828",
|
|
89
91
|
"doubao-seed-1-6-lite-251015",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mcp-auth-target — resolve a `cc mcp login|logout <target>` argument.
|
|
3
|
+
*
|
|
4
|
+
* Claude-Code 2.1.186 parity: `mcp login <name>` / `mcp logout <name>` accept
|
|
5
|
+
* the NAME of a configured MCP server, not only a raw URL. A `target` that
|
|
6
|
+
* already looks like a `scheme://…` URL is used verbatim (fast path — no DB
|
|
7
|
+
* needed); otherwise it is treated as a configured-server name and resolved to
|
|
8
|
+
* its stored url via the injected `lookupServer(name)` callback.
|
|
9
|
+
*
|
|
10
|
+
* Pure (no DB / IO of its own) so it is unit-testable; the caller supplies the
|
|
11
|
+
* `lookupServer` resolver (DB-backed in the command, a stub in tests).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** A `scheme://` URL (http, https, …) — not a bare host or a server name. */
|
|
15
|
+
export function isUrlLike(s) {
|
|
16
|
+
return /^[a-z][a-z0-9+.-]*:\/\//i.test(String(s || "").trim());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolve a login/logout target to a concrete server URL.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} target a URL or a configured MCP server name
|
|
23
|
+
* @param {(name: string) => ({ name?: string, url?: string|null,
|
|
24
|
+
* transport?: string }|null)} [lookupServer] name → server config (or null)
|
|
25
|
+
* @returns {{ url: string, name: string|null, source: "url"|"config" }}
|
|
26
|
+
* @throws {Error} when target is empty, names an unknown server, or names a
|
|
27
|
+
* server that has no http(s) url (stdio servers cannot do OAuth login)
|
|
28
|
+
*/
|
|
29
|
+
export function resolveMcpAuthTarget(target, lookupServer) {
|
|
30
|
+
const t = typeof target === "string" ? target.trim() : "";
|
|
31
|
+
if (!t) {
|
|
32
|
+
throw new Error("a server URL or configured server name is required");
|
|
33
|
+
}
|
|
34
|
+
if (isUrlLike(t)) {
|
|
35
|
+
return { url: t, name: null, source: "url" };
|
|
36
|
+
}
|
|
37
|
+
const server = typeof lookupServer === "function" ? lookupServer(t) : null;
|
|
38
|
+
if (!server) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`no configured MCP server named "${t}" (and it is not a URL) — ` +
|
|
41
|
+
`run "cc mcp servers" to list, or pass the full https:// URL`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
const url = server.url || null;
|
|
45
|
+
if (!url || !isUrlLike(url)) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`configured server "${t}" has no http(s) URL (transport: ` +
|
|
48
|
+
`${server.transport || "stdio"}); OAuth login only applies to remote ` +
|
|
49
|
+
`http/sse MCP servers`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return { url, name: server.name || t, source: "config" };
|
|
53
|
+
}
|
|
@@ -19,20 +19,20 @@
|
|
|
19
19
|
* riskLevel, policy }` so callers don't have to juggle two decision types.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
APPROVAL_RISK
|
|
25
|
-
APPROVAL_DECISION
|
|
26
|
-
}
|
|
22
|
+
import sharedShellPolicy from "../runtime/coding-agent-shell-policy.cjs";
|
|
23
|
+
import {
|
|
24
|
+
APPROVAL_RISK as RISK,
|
|
25
|
+
APPROVAL_DECISION as DECISION,
|
|
26
|
+
} from "@chainlesschain/session-core";
|
|
27
27
|
|
|
28
|
-
const SHELL_TO_RISK = {
|
|
28
|
+
export const SHELL_TO_RISK = {
|
|
29
29
|
allow: RISK.LOW,
|
|
30
30
|
warn: RISK.MEDIUM,
|
|
31
31
|
deny: RISK.HIGH,
|
|
32
32
|
reroute: RISK.HIGH,
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
async function evaluateShellCommandWithApproval({
|
|
35
|
+
export async function evaluateShellCommandWithApproval({
|
|
36
36
|
command,
|
|
37
37
|
sessionId = null,
|
|
38
38
|
approvalGate = null,
|
|
@@ -89,8 +89,3 @@ async function evaluateShellCommandWithApproval({
|
|
|
89
89
|
policy: gateResult.policy,
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
|
-
|
|
93
|
-
module.exports = {
|
|
94
|
-
evaluateShellCommandWithApproval,
|
|
95
|
-
SHELL_TO_RISK,
|
|
96
|
-
};
|
package/src/lib/skill-loader.js
CHANGED
|
@@ -129,8 +129,12 @@ export function parseSkillMd(content) {
|
|
|
129
129
|
const key = trimmed.slice(0, colonIndex).trim();
|
|
130
130
|
let value = trimmed.slice(colonIndex + 1).trim();
|
|
131
131
|
|
|
132
|
-
//
|
|
133
|
-
|
|
132
|
+
// Normalize frontmatter key case to camelCase. Claude-Code skills
|
|
133
|
+
// (auto-loaded from .claude/skills) may author keys in kebab-case,
|
|
134
|
+
// snake_case, or camelCase interchangeably (Claude Code 2.1.186), e.g.
|
|
135
|
+
// `display-name` / `display_name` / `displayName`. Treat all three the
|
|
136
|
+
// same so the field reads below (data.displayName, …) pick them up.
|
|
137
|
+
const camelKey = key.replace(/[-_]([a-z])/g, (_, c) => c.toUpperCase());
|
|
134
138
|
|
|
135
139
|
if (value === "") {
|
|
136
140
|
currentKey = camelKey;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* suggest-name — "did you mean?" suggestions for a mistyped identifier.
|
|
3
|
+
*
|
|
4
|
+
* Combines substring containment (a partial name) with bounded Levenshtein edit
|
|
5
|
+
* distance (single-char typos / transpositions) so `cc mcp remove cotnext7`
|
|
6
|
+
* suggests `context7`. Pure + dependency-free; reusable by any command that
|
|
7
|
+
* resolves a user-supplied name against a known set (MCP servers, skills, …).
|
|
8
|
+
*
|
|
9
|
+
* Claude-Code 2.1.186 parity: `mcp get/remove` typo suggestions.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Levenshtein edit distance between two strings (iterative two-row, O(n*m)). */
|
|
13
|
+
export function editDistance(a, b) {
|
|
14
|
+
a = String(a ?? "");
|
|
15
|
+
b = String(b ?? "");
|
|
16
|
+
if (a === b) return 0;
|
|
17
|
+
if (!a.length) return b.length;
|
|
18
|
+
if (!b.length) return a.length;
|
|
19
|
+
let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
20
|
+
let curr = new Array(b.length + 1);
|
|
21
|
+
for (let i = 1; i <= a.length; i++) {
|
|
22
|
+
curr[0] = i;
|
|
23
|
+
for (let j = 1; j <= b.length; j++) {
|
|
24
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
25
|
+
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
26
|
+
}
|
|
27
|
+
const tmp = prev;
|
|
28
|
+
prev = curr;
|
|
29
|
+
curr = tmp;
|
|
30
|
+
}
|
|
31
|
+
return prev[b.length];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Rank candidates by closeness to `target` and return up to `limit` names.
|
|
36
|
+
* A candidate qualifies if it contains `target` (or vice-versa) as a
|
|
37
|
+
* case-insensitive substring, OR is within `maxDistance` edits (default scales
|
|
38
|
+
* with target length, capped at 3). Exact matches are excluded (not a typo).
|
|
39
|
+
* Output is ordered: substring matches first, then by edit distance, then
|
|
40
|
+
* alphabetically (stable).
|
|
41
|
+
*
|
|
42
|
+
* @param {string} target
|
|
43
|
+
* @param {string[]} candidates
|
|
44
|
+
* @param {{ limit?: number, maxDistance?: number }} [opts]
|
|
45
|
+
* @returns {string[]}
|
|
46
|
+
*/
|
|
47
|
+
export function suggestNames(target, candidates, opts = {}) {
|
|
48
|
+
const t = String(target ?? "")
|
|
49
|
+
.trim()
|
|
50
|
+
.toLowerCase();
|
|
51
|
+
if (!t || !Array.isArray(candidates)) return [];
|
|
52
|
+
const limit = opts.limit ?? 3;
|
|
53
|
+
const maxDistance =
|
|
54
|
+
opts.maxDistance ?? Math.min(3, Math.max(1, Math.floor(t.length / 3)));
|
|
55
|
+
const scored = [];
|
|
56
|
+
for (const raw of candidates) {
|
|
57
|
+
const name = String(raw ?? "");
|
|
58
|
+
if (!name) continue;
|
|
59
|
+
const lc = name.toLowerCase();
|
|
60
|
+
if (lc === t) continue; // exact match isn't a "did you mean"
|
|
61
|
+
const contains = lc.includes(t) || t.includes(lc);
|
|
62
|
+
const dist = editDistance(t, lc);
|
|
63
|
+
if (contains || dist <= maxDistance) {
|
|
64
|
+
scored.push({ name, contains: contains ? 0 : 1, dist });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
scored.sort((a, b) => {
|
|
68
|
+
if (a.contains !== b.contains) return a.contains - b.contains;
|
|
69
|
+
if (a.dist !== b.dist) return a.dist - b.dist;
|
|
70
|
+
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
71
|
+
});
|
|
72
|
+
return scored.slice(0, limit).map((s) => s.name);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Build a `<noun> "<target>" not found` message, appending a `— did you mean
|
|
77
|
+
* "x", "y"?` hint when there are close candidates. Returns just the base when
|
|
78
|
+
* there are none.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} target
|
|
81
|
+
* @param {string[]} candidates
|
|
82
|
+
* @param {{ noun?: string, limit?: number, maxDistance?: number }} [opts]
|
|
83
|
+
* @returns {string}
|
|
84
|
+
*/
|
|
85
|
+
export function notFoundWithSuggestion(target, candidates, opts = {}) {
|
|
86
|
+
const noun = opts.noun || "item";
|
|
87
|
+
const base = `${noun} "${target}" not found`;
|
|
88
|
+
const hints = suggestNames(target, candidates, opts);
|
|
89
|
+
if (!hints.length) return base;
|
|
90
|
+
return `${base} — did you mean ${hints.map((h) => `"${h}"`).join(", ")}?`;
|
|
91
|
+
}
|
|
@@ -24,7 +24,7 @@ export const TaskType = {
|
|
|
24
24
|
*/
|
|
25
25
|
const TASK_MODEL_MAP = {
|
|
26
26
|
[TaskType.CHAT]: {
|
|
27
|
-
volcengine: "doubao-seed-1-
|
|
27
|
+
volcengine: "doubao-seed-2-1-pro-260628",
|
|
28
28
|
openai: "gpt-4o-mini",
|
|
29
29
|
anthropic: "claude-sonnet-4-6",
|
|
30
30
|
deepseek: "deepseek-chat",
|
|
@@ -36,7 +36,7 @@ const TASK_MODEL_MAP = {
|
|
|
36
36
|
ollama: "qwen2.5:7b",
|
|
37
37
|
},
|
|
38
38
|
[TaskType.CODE]: {
|
|
39
|
-
volcengine: "doubao-seed-1-
|
|
39
|
+
volcengine: "doubao-seed-2-1-pro-260628",
|
|
40
40
|
openai: "gpt-4o",
|
|
41
41
|
anthropic: "claude-sonnet-4-6",
|
|
42
42
|
deepseek: "deepseek-coder",
|
|
@@ -48,7 +48,7 @@ const TASK_MODEL_MAP = {
|
|
|
48
48
|
ollama: "qwen2.5-coder:14b",
|
|
49
49
|
},
|
|
50
50
|
[TaskType.REASONING]: {
|
|
51
|
-
volcengine: "doubao-seed-1-
|
|
51
|
+
volcengine: "doubao-seed-2-1-pro-260628",
|
|
52
52
|
openai: "o1",
|
|
53
53
|
anthropic: "claude-opus-4-6",
|
|
54
54
|
deepseek: "deepseek-reasoner",
|
|
@@ -72,7 +72,7 @@ const TASK_MODEL_MAP = {
|
|
|
72
72
|
ollama: "qwen2:7b",
|
|
73
73
|
},
|
|
74
74
|
[TaskType.TRANSLATE]: {
|
|
75
|
-
volcengine: "doubao-seed-1-
|
|
75
|
+
volcengine: "doubao-seed-2-1-pro-260628",
|
|
76
76
|
openai: "gpt-4o",
|
|
77
77
|
anthropic: "claude-sonnet-4-6",
|
|
78
78
|
deepseek: "deepseek-chat",
|
|
@@ -84,7 +84,7 @@ const TASK_MODEL_MAP = {
|
|
|
84
84
|
ollama: "qwen2:7b",
|
|
85
85
|
},
|
|
86
86
|
[TaskType.CREATIVE]: {
|
|
87
|
-
volcengine: "doubao-seed-1-
|
|
87
|
+
volcengine: "doubao-seed-2-1-pro-260628",
|
|
88
88
|
openai: "gpt-4o",
|
|
89
89
|
anthropic: "claude-opus-4-6",
|
|
90
90
|
deepseek: "deepseek-chat",
|
package/src/repl/agent-repl.js
CHANGED
|
@@ -1317,7 +1317,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
1317
1317
|
logger.log(
|
|
1318
1318
|
` ${chalk.cyan("… \\")} End a line with \\ to continue input onto the next line`,
|
|
1319
1319
|
);
|
|
1320
|
-
logger.log(
|
|
1320
|
+
logger.log(
|
|
1321
|
+
` ${chalk.cyan("/exit")} Exit the agent (alias: /quit)`,
|
|
1322
|
+
);
|
|
1321
1323
|
logger.log(
|
|
1322
1324
|
` ${chalk.cyan("/model")} Show/change model (/model <name>)`,
|
|
1323
1325
|
);
|
|
@@ -1338,6 +1340,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
1338
1340
|
logger.log(
|
|
1339
1341
|
` ${chalk.cyan("/theme")} Color theme (/theme <auto|dark|light|mono>; mono = no color)`,
|
|
1340
1342
|
);
|
|
1343
|
+
logger.log(
|
|
1344
|
+
` ${chalk.cyan("/output-style")} Response persona (/output-style <name|list>; explanatory/learning built-in)`,
|
|
1345
|
+
);
|
|
1341
1346
|
logger.log(
|
|
1342
1347
|
` ${chalk.cyan("/config")} Show config; ${chalk.cyan("/config <key>")} read, ${chalk.cyan("/config <key>=<val>")} set, ${chalk.cyan("/config --help")} keys`,
|
|
1343
1348
|
);
|
|
@@ -1423,6 +1428,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
1423
1428
|
logger.log(
|
|
1424
1429
|
` ${chalk.cyan("/ide")} IDE bridge status (connected editor, tools, or why not)`,
|
|
1425
1430
|
);
|
|
1431
|
+
logger.log(
|
|
1432
|
+
` ${chalk.cyan("/mcp")} MCP server status + tools (/mcp <name> for one server)`,
|
|
1433
|
+
);
|
|
1426
1434
|
logger.log(chalk.bold("\nCapabilities:"));
|
|
1427
1435
|
logger.log(" Read, write, and edit files");
|
|
1428
1436
|
logger.log(" Run shell commands (git, npm, etc.)");
|
package/src/repl/slash-macro.js
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
* @param {string} input raw REPL line
|
|
16
16
|
* @param {object} [opts] { cwd, deps:{ getCommand, expandCommand } }
|
|
17
17
|
* @returns {Promise<{matched:boolean, promptText:string, warnings:string[],
|
|
18
|
-
* name?:string, scope?:string
|
|
18
|
+
* name?:string, scope?:string, model?:string|null,
|
|
19
|
+
* allowedTools?:string|null}>}
|
|
20
|
+
* `model` / `allowedTools` are the matched command's frontmatter (or null),
|
|
21
|
+
* so a caller running the macro as a whole turn can scope the run the same
|
|
22
|
+
* way `cc command run` does.
|
|
19
23
|
*/
|
|
20
24
|
export async function resolveSlashMacro(input, opts = {}) {
|
|
21
25
|
const cwd = opts.cwd || process.cwd();
|
|
@@ -41,5 +45,7 @@ export async function resolveSlashMacro(input, opts = {}) {
|
|
|
41
45
|
warnings,
|
|
42
46
|
name: macro.name,
|
|
43
47
|
scope: macro.scope,
|
|
48
|
+
model: macro.model || null,
|
|
49
|
+
allowedTools: macro.allowedTools || null,
|
|
44
50
|
};
|
|
45
51
|
}
|
|
@@ -1546,17 +1546,43 @@ async function executeToolInner(
|
|
|
1546
1546
|
}
|
|
1547
1547
|
// Hashline mode: prefix each line with a 6-char content hash tag
|
|
1548
1548
|
// so downstream edit_file_hashed calls can anchor by hash.
|
|
1549
|
-
|
|
1549
|
+
let rendered = args.hashed === true ? annotateLines(content) : content;
|
|
1550
|
+
|
|
1551
|
+
// Line-range slice (Claude-Code Read offset/limit parity): `offset` is the
|
|
1552
|
+
// 1-based first line, `limit` the max line count — so a file larger than
|
|
1553
|
+
// the size cap can be paged through instead of being stuck at its head.
|
|
1554
|
+
// Coerces numeric strings the model may emit ("10" → 10).
|
|
1555
|
+
const toPos = (v) => {
|
|
1556
|
+
const n = typeof v === "number" ? v : parseInt(v, 10);
|
|
1557
|
+
return Number.isInteger(n) && n > 0 ? n : null;
|
|
1558
|
+
};
|
|
1559
|
+
const offset = toPos(args.offset);
|
|
1560
|
+
const limit = toPos(args.limit);
|
|
1561
|
+
let range = null;
|
|
1562
|
+
if (offset || limit) {
|
|
1563
|
+
const lines = rendered.split("\n");
|
|
1564
|
+
const start = offset ? offset - 1 : 0;
|
|
1565
|
+
const end = limit != null ? start + limit : lines.length;
|
|
1566
|
+
rendered = lines.slice(start, end).join("\n");
|
|
1567
|
+
range = {
|
|
1568
|
+
startLine: Math.min(start + 1, lines.length),
|
|
1569
|
+
endLine: Math.min(end, lines.length),
|
|
1570
|
+
totalLines: lines.length,
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1550
1574
|
if (rendered.length > 50000) {
|
|
1551
1575
|
return attachDescriptor({
|
|
1552
1576
|
content: rendered.substring(0, 50000) + "\n...(truncated)",
|
|
1553
1577
|
size: rendered.length,
|
|
1554
1578
|
hashed: args.hashed === true,
|
|
1579
|
+
...(range ? { range } : {}),
|
|
1555
1580
|
});
|
|
1556
1581
|
}
|
|
1557
1582
|
return attachDescriptor({
|
|
1558
1583
|
content: rendered,
|
|
1559
1584
|
hashed: args.hashed === true,
|
|
1585
|
+
...(range ? { range } : {}),
|
|
1560
1586
|
});
|
|
1561
1587
|
}
|
|
1562
1588
|
|
|
@@ -2633,6 +2659,36 @@ async function _executeRunCode(args, cwd) {
|
|
|
2633
2659
|
*/
|
|
2634
2660
|
export const MAX_SUB_AGENT_DEPTH = 5;
|
|
2635
2661
|
|
|
2662
|
+
/**
|
|
2663
|
+
* Per-tool-result character cap fed back to the model. One giant tool output (a
|
|
2664
|
+
* huge file, a verbose command, an MCP blob) must not blow the context window —
|
|
2665
|
+
* but the model is TOLD when output is cut, instead of the old silent
|
|
2666
|
+
* `substring(0, 5000)` that sliced mid-content with no marker and undercut even
|
|
2667
|
+
* read_file's own 50k self-limit. Tools that self-limit (read_file, notebook
|
|
2668
|
+
* render) stay below this; it is the final safety net for the ones that don't
|
|
2669
|
+
* (run_shell, search_files, run_code, MCP). Override with CC_MAX_TOOL_RESULT_CHARS.
|
|
2670
|
+
*/
|
|
2671
|
+
export const MAX_TOOL_RESULT_CHARS = (() => {
|
|
2672
|
+
const n = Number(process.env.CC_MAX_TOOL_RESULT_CHARS);
|
|
2673
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : 50000;
|
|
2674
|
+
})();
|
|
2675
|
+
|
|
2676
|
+
/**
|
|
2677
|
+
* Cap a serialized tool result to `max` chars, appending a visible truncation
|
|
2678
|
+
* marker (with the original length + how to get the rest) when it overflows —
|
|
2679
|
+
* so the model never silently receives a mid-content slice. Pure; exported for
|
|
2680
|
+
* tests.
|
|
2681
|
+
*/
|
|
2682
|
+
export function capToolResultString(serialized, max = MAX_TOOL_RESULT_CHARS) {
|
|
2683
|
+
const s = String(serialized ?? "");
|
|
2684
|
+
if (s.length <= max) return s;
|
|
2685
|
+
return (
|
|
2686
|
+
s.slice(0, max) +
|
|
2687
|
+
`\n…[tool output truncated: showing the first ${max} of ${s.length} chars` +
|
|
2688
|
+
` — narrow the request (read a line range, grep, or paginate) to see the rest]`
|
|
2689
|
+
);
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2636
2692
|
/**
|
|
2637
2693
|
* Execute a spawn_sub_agent tool call.
|
|
2638
2694
|
* Creates an isolated SubAgentContext, runs it, and returns only the summary.
|
|
@@ -3112,7 +3168,7 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
3112
3168
|
dashscope: "qwen-turbo",
|
|
3113
3169
|
mistral: "mistral-large-latest",
|
|
3114
3170
|
gemini: "gemini-2.0-flash",
|
|
3115
|
-
volcengine: "doubao-seed-1-
|
|
3171
|
+
volcengine: "doubao-seed-2-1-pro-260628",
|
|
3116
3172
|
};
|
|
3117
3173
|
|
|
3118
3174
|
// Real token streaming (--include-partial-messages) for every OpenAI-compatible
|
|
@@ -4586,7 +4642,10 @@ export async function* agentLoop(messages, options) {
|
|
|
4586
4642
|
|
|
4587
4643
|
// Append budget warning to tool result so the LLM sees it
|
|
4588
4644
|
const warningMsg = budget.toWarningMessage();
|
|
4589
|
-
|
|
4645
|
+
// Cap an individual tool result so one giant output can't blow the
|
|
4646
|
+
// context — but tell the model when we cut it (no more silent
|
|
4647
|
+
// mid-content slice). See MAX_TOOL_RESULT_CHARS / capToolResultString.
|
|
4648
|
+
const resultStr = capToolResultString(JSON.stringify(toolResult));
|
|
4590
4649
|
const toolContent = warningMsg
|
|
4591
4650
|
? `${resultStr}\n\n${warningMsg}`
|
|
4592
4651
|
: resultStr;
|
|
@@ -28,11 +28,21 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
|
|
|
28
28
|
kind: "filesystem",
|
|
29
29
|
tier: "mvp",
|
|
30
30
|
description:
|
|
31
|
-
"Read a file's content. Jupyter notebooks (.ipynb) are rendered as a compact cell listing (index/id/type/source, outputs hidden) for use with notebook_edit — pass raw:true for the underlying JSON.",
|
|
31
|
+
"Read a file's content. For a large file, page through it with offset+limit (line range) instead of re-reading the head. Jupyter notebooks (.ipynb) are rendered as a compact cell listing (index/id/type/source, outputs hidden) for use with notebook_edit — pass raw:true for the underlying JSON.",
|
|
32
32
|
inputSchema: {
|
|
33
33
|
type: "object",
|
|
34
34
|
properties: {
|
|
35
35
|
path: { type: "string", description: "File path to read" },
|
|
36
|
+
offset: {
|
|
37
|
+
type: "integer",
|
|
38
|
+
description:
|
|
39
|
+
"1-based line number to start reading from (omit to read from the top). Use with limit to page a large file.",
|
|
40
|
+
},
|
|
41
|
+
limit: {
|
|
42
|
+
type: "integer",
|
|
43
|
+
description:
|
|
44
|
+
"Maximum number of lines to return starting at offset (omit to read to end, subject to the size cap).",
|
|
45
|
+
},
|
|
36
46
|
hashed: {
|
|
37
47
|
type: "boolean",
|
|
38
48
|
description:
|