chainlesschain 0.162.143 → 0.162.147
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 +2 -2
- package/src/assets/web-panel/assets/{AIOps-Bq1t-B4V.js → AIOps-DywJ7xTv.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BHb3KMc3.js → ActionButton-BTlqcY-q.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DtJrE99O.js → Analytics-FdG1Mvwy.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-HXt309zh.js → AppLayout-CVsnbvN1.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-B-0384hX.js → Audit-ITdpJ7vR.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BXNwUJ7y.js → Backup-BfXqnXo1.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CYdSBe24.js → BaseInput-C09ip3_E.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-C5hSt-bf.js → Chat-qdkhB42l.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BJQ6SG49.js → ChatBubbleRenderer-D2KfETZD.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DZSpGJs8.js → Checkbox-j6WZCuff.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BKfMmpq3.js → Codegen-CGZ-K3uK.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DgsxR9Gb.js → Col-CIlPOAu6.js} +1 -1
- package/src/assets/web-panel/assets/{Community-B3GhKOp_.js → Community-sY-i-hic.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-Dtk1xHSY.js → Compact-DcoEfyL8.js} +1 -1
- package/src/assets/web-panel/assets/Compliance-T8EtTecW.js +1 -0
- package/src/assets/web-panel/assets/{Cowork-CB_Tby_i.js → Cowork-DLY_fkLv.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-BLQFn3UU.js → Cron-D049pZBC.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-LCVSqtiL.js → Crosschain-DCXdE8M9.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Cv3xor-m.js → DID-JCvPZtjW.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DHAEYeUk.js → Dashboard-Cg2xu3iE.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-FAp99VHO.js → Dropdown-DjJLjbLh.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Cl4enKIT.js → EmailListRenderer-DugNcSLR.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CgbuK_Rv.js → FamilyGuardDashboard-J6Nb-E30.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-C2I5-34o.js → Federation-CQ3Ttpbl.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-4RNlhf3D.js → FormItemContext-Cz0NKag4.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DmaKYzyo.js → GenericCardRenderer-NYcMP6iz.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BMrCOEGV.js → Git-B4hYN18N.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BLwFR0Vd.js → Governance-DOQPCcC0.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Cdlx3Y4t.js → Inference-HtLF746W.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CiwGb2h3.js → KnowledgeGraph-BXrk8rMm.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-C6A4r33C.js → Logs-DS0JnD3-.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-DdybH2YZ.js → Marketplace-Cqnjihrz.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-pgwwEzwg.js → McpTools-Cx3Psk-U.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-BQ31MLPN.js → Memory-BjyysPtj.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CnRXuSP5.js → MobileBridge-CYaITZ7w.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CmnCTggB.js → MobileProjects-Xwf-fdOQ.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-3K6RfaUB.js → Mtc-Djkql5m5.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-BN9hXvXq.js → MtcAudit-D2BAZlu0.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-CRxIT2RV.js → Multisig-ByqP1ZzH.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CdPvh_dA.js → NLProgramming-C_kn0nE2.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DzQSZ0ps.js → Notes-7l7eXHPq.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DkqRVvfb.js → NotificationSettings-BrCDoitw.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-ClTA64ji.js → OrderTableRenderer-CmtqIdwr.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-B-Pndhhn.js → Organization-DdoNAxsM.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Dt56UM9b.js → Overflow-Bw7R6dE3.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-JDGQmED0.js → P2P-DJNtBugA.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-Dwhd5_ue.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-Dnqe1sny.js → Permissions-BYoSfhPR.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-mTkYbIR-.js → PersonalDataHub-aYCYJbnH.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-BG6J9UZO.js → Pipeline-DJp6CprF.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BdEydH6w.js → Privacy-Cq9HZweR.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-QPxCoUzs.js → ProjectInit-CmF0HsSt.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-hU_phubM.js → ProjectSettings-P8V5Aea2.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BBzAEGk4.js → Projects-DPd5-lNS.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DJ_bIDRG.js → Providers-D589v0q2.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BNmX7Jgh.js → QuickAsk-lvRV2osB.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bo1I5nAG.js → Recommend-sb84MTpY.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-Bc9mfsh7.js → Reputation-d9Y3vy-W.js} +1 -1
- package/src/assets/web-panel/assets/{Row-D12ZZx5m.js → Row-DMdhLUTp.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CXSlmgHl.js → RssFeed-OL-SMDkz.js} +2 -2
- package/src/assets/web-panel/assets/{Search-bNXObahl.js → Search-BjStLKq1.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Bt7yBkJc.js → Security-MeI411qr.js} +2 -2
- package/src/assets/web-panel/assets/{Services-ECQDydWI.js → Services-UajosuhT.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BcgzLbTM.js → Skeleton-CGpG-QJ1.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-py_xMG8b.js → Skills-C6P2RPY-.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CzNvjwaW.js → Sla-B_fPprgw.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-S3zMMqbB.js → SpeechSettings-CqxLjSlQ.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BpDfhFv6.js → SyncSettings-DixSfFOQ.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CUn36tpM.js → Tasks-PavzPBxc.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-yzCxc3lI.js → Templates-BYdGRnfX.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BTMrYsHv.js → Tenant-BQLgnarD.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-BzOHpq-B.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-BwJ4nL9S.js → TimelineRenderer-Bv7uZRM7.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-B1OwTaT_.js → Tokens-DxhgszRJ.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BDxi9fib.js → Trigger-nQYHayuc.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-B_5hXqzI.js → Trust-CvqZDuZ4.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BpIMO611.js → UkeySign-DaR8GV2I.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Ds-9lxOW.js → VideoEditing-C6Mpsokw.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CNfFp8if.js → Wallet-B57VRrWc.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DbCj1-qn.js → WebAuthn-BbtEEtpq.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-DjEeb957.js → WorkflowEditor-C5LwvKmH.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BX-27gI4.js → chat-MzuPR5jh.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CfM5FmdA.js → colors-HyW8mi_y.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Cb0Fm64g.js → compact-item-DvUSzWAp.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-D99MZ4gz.js → createContext-4MppZl7X.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-6YJJMjSH.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CW7wPkfb.js → hasIn-D9q3CJ-u.js} +1 -1
- package/src/assets/web-panel/assets/{index-kRlIAKNw.js → index--Bm6OqmU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BqOy4TS5.js → index-B5E4DhVc.js} +1 -1
- package/src/assets/web-panel/assets/{index--Y8IzVi2.js → index-B994UQfE.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dt-h8idq.js → index-BF2JsbiX.js} +1 -1
- package/src/assets/web-panel/assets/{index-ChGWrUQv.js → index-BFGfC5RS.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAIk483F.js → index-BGQtFso0.js} +1 -1
- package/src/assets/web-panel/assets/{index-CxG5S21O.js → index-BcunsBIx.js} +1 -1
- package/src/assets/web-panel/assets/{index-rIKv98lY.js → index-Bl-E4W4q.js} +1 -1
- package/src/assets/web-panel/assets/{index-CxFhtfoo.js → index-BmCo2Xdp.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUENEaPU.js → index-Btz8pU68.js} +1 -1
- package/src/assets/web-panel/assets/{index-D8U1HFcm.js → index-C0DiL97c.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFRrP3LL.js → index-C9I5MuJ0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DcRbrdqR.js → index-CLAZs1Vd.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcCSEGzu.js → index-Caqtu6Et.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXrNkD5j.js → index-CgIfXOD9.js} +1 -1
- package/src/assets/web-panel/assets/{index-DwhDhfZ8.js → index-Ci6adKh8.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzwoBX8m.js → index-CifKUNtV.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8tI4Hg_.js → index-Cs-RKRUM.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bf7G5mYl.js → index-CucRo4Vz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvFAgMxx.js → index-CvAqCAo9.js} +1 -1
- package/src/assets/web-panel/assets/{index-7cahuPKG.js → index-D2jrnaq3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cavk4P2D.js → index-D7TDMMfu.js} +1 -1
- package/src/assets/web-panel/assets/{index-BoCp5SY7.js → index-DDZ6fP5Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPXhpO02.js → index-DFqvWeGc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNhZanpM.js → index-DNYqw0MO.js} +1 -1
- package/src/assets/web-panel/assets/index-D_oeGLr6.js +1 -0
- package/src/assets/web-panel/assets/{index-BTlOIaQj.js → index-DcMkjb92.js} +1 -1
- package/src/assets/web-panel/assets/{index-3TDpYBKK.js → index-DmZ6-suL.js} +1 -1
- package/src/assets/web-panel/assets/{index-ClSExghA.js → index-Dv7ffI2g.js} +1 -1
- package/src/assets/web-panel/assets/{index-ab4T5cPr.js → index-Dvg5xYuP.js} +1 -1
- package/src/assets/web-panel/assets/index-Qa57YG19.js +1 -0
- package/src/assets/web-panel/assets/{index-yBiqcb9Z.js → index-SDblbKj6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDGBasj6.js → index-UUMCA2Sq.js} +3 -3
- package/src/assets/web-panel/assets/{index-kt0S8nXp.js → index-Zvtru2oE.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaPluhcX.js → index-eOQO6KnV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZjN-U7A.js → index-m0b6Fj9q.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUxnWbi6.js → index-ryFLxB1p.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPmtR1Wr.js → index-xvUVuFnA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAd31-6W.js → index-zLwYpvX0.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-mzQV01we.js → initDefaultProps-CC--PMsC.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DgWAtKps.js → motion-DdrBjRF1.js} +1 -1
- package/src/assets/web-panel/assets/{move-Dm9NMjIw.js → move-D712Gwl2.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BpuQIoKH.js → mtc-parser-39y-keKO.js} +1 -1
- package/src/assets/web-panel/assets/{omit-C507FdFH.js → omit-6Y51gDB9.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BL5eykDR.js → pickAttrs-Cy6EHbq9.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-uSE1U6yg.js → placementArrow-BYxdAm0p.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BVhSz_U1.js → responsiveObserve-C9R6q_cr.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CWCp5XZc.js → slide-CXUJlilB.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-B_rS9tlX.js → statusUtils-DOtrOeql.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DRo4TNq4.js → styleChecker-D5r39o92.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-QsPzwpvi.js → useFlexGapSupport-DfAD5U4Z.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DhsLsCr_.js → useFs-BkrT-Zbc.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BXI-FLwS.js → usePersonalDataHub-BwnnnZjU.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CYE0CVkI.js → vnode-SU-N4pum.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-B2-MtXVF.js → zoom-DHScfpTP.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/auth/npm-auth.js +65 -0
- package/src/commands/agent.js +92 -0
- package/src/commands/agents.js +68 -0
- package/src/commands/permissions.js +66 -10
- package/src/commands/plugin.js +50 -3
- package/src/lib/agent-sandbox.js +72 -0
- package/src/lib/background-agent-supervisor.js +197 -0
- package/src/lib/code-review.js +72 -0
- package/src/lib/plugin-security.js +128 -0
- package/src/lib/pr-create.js +108 -0
- package/src/lib/publish-workspace.js +107 -0
- package/src/lib/resume-session.js +111 -0
- package/src/lib/settings-hooks.cjs +68 -25
- package/src/lib/settings-loader.cjs +115 -2
- package/src/lib/task-list.js +53 -0
- package/src/repl/agent-repl.js +83 -2
- package/src/repl/agents-status.js +8 -2
- package/src/repl/btw-command.js +56 -0
- package/src/repl/chat-repl.js +52 -1
- package/src/repl/hooks-status.js +1 -2
- package/src/repl/slash-command-registry.js +197 -0
- package/src/runtime/agent-core.js +137 -1
- package/src/runtime/coding-agent-contract-shared.cjs +37 -0
- package/src/runtime/coding-agent-policy.cjs +14 -0
- package/src/runtime/headless-runner.js +26 -6
- package/src/runtime/headless-stream.js +25 -6
- package/src/runtime/mcp-config.js +46 -2
- package/src/runtime/policies/agent-policy.js +1 -0
- package/src/workers/background-agent-worker.js +60 -0
- package/src/assets/web-panel/assets/Compliance-DmkkUDze.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-C56Oi811.js +0 -7
- package/src/assets/web-panel/assets/Terminal-BjZry1Jj.js +0 -3
- package/src/assets/web-panel/assets/devWarning-CDh1y3xt.js +0 -1
- package/src/assets/web-panel/assets/index-ACeVMo7x.js +0 -1
- package/src/assets/web-panel/assets/index-gYKDzWA9.js +0 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session resume utilities (ESM)
|
|
3
|
+
* Mirrors Claude Code /resume behavior
|
|
4
|
+
* @module lib/resume-session
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import os from "os";
|
|
10
|
+
import { logger } from "./logger.js";
|
|
11
|
+
|
|
12
|
+
const SESSION_DIR = path.join(os.homedir(), ".chainlesschain", "sessions");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* List recent chat sessions
|
|
16
|
+
* @param {number} limit Maximum sessions to return
|
|
17
|
+
* @returns {Array<Object>} list of sessions
|
|
18
|
+
*/
|
|
19
|
+
export function listSessions(limit = 10) {
|
|
20
|
+
try {
|
|
21
|
+
if (!fs.existsSync(SESSION_DIR)) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const files = fs
|
|
26
|
+
.readdirSync(SESSION_DIR)
|
|
27
|
+
.filter((f) => f.endsWith(".json"))
|
|
28
|
+
.map((f) => {
|
|
29
|
+
const filePath = path.join(SESSION_DIR, f);
|
|
30
|
+
const stat = fs.statSync(filePath);
|
|
31
|
+
let summary = "";
|
|
32
|
+
try {
|
|
33
|
+
const data = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
34
|
+
summary = data.summary || data.title || f.replace(".json", "");
|
|
35
|
+
} catch {
|
|
36
|
+
summary = f.replace(".json", "");
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
id: f.replace(".json", ""),
|
|
40
|
+
file: filePath,
|
|
41
|
+
mtime: stat.mtimeMs,
|
|
42
|
+
summary,
|
|
43
|
+
};
|
|
44
|
+
})
|
|
45
|
+
.sort((a, b) => b.mtime - a.mtime)
|
|
46
|
+
.slice(0, limit);
|
|
47
|
+
|
|
48
|
+
return files;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
logger.debug(`Error listing sessions: ${err.message}`);
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Load a session by ID
|
|
57
|
+
* @param {string} sessionId
|
|
58
|
+
* @returns {Object|null} session data
|
|
59
|
+
*/
|
|
60
|
+
export function loadSession(sessionId) {
|
|
61
|
+
try {
|
|
62
|
+
const filePath = path.join(SESSION_DIR, `${sessionId}.json`);
|
|
63
|
+
if (!fs.existsSync(filePath)) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
67
|
+
} catch (err) {
|
|
68
|
+
logger.error(`Error loading session ${sessionId}: ${err.message}`);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get the most recent session
|
|
75
|
+
* @returns {Object|null}
|
|
76
|
+
*/
|
|
77
|
+
export function getLatestSession() {
|
|
78
|
+
const sessions = listSessions(1);
|
|
79
|
+
return sessions.length > 0 ? loadSession(sessions[0].id) : null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Resume a chat session
|
|
84
|
+
* @param {string} [sessionId] Optional session ID (resumes latest if not provided)
|
|
85
|
+
* @returns {Promise<Object>} resume result
|
|
86
|
+
*/
|
|
87
|
+
export async function resumeSession(sessionId) {
|
|
88
|
+
let session;
|
|
89
|
+
if (sessionId) {
|
|
90
|
+
session = loadSession(sessionId);
|
|
91
|
+
if (!session) {
|
|
92
|
+
return { success: false, message: `Session ${sessionId} not found` };
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
session = getLatestSession();
|
|
96
|
+
if (!session) {
|
|
97
|
+
return { success: false, message: "No previous sessions found" };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
success: true,
|
|
103
|
+
sessionId: session.id || sessionId,
|
|
104
|
+
messageCount: session.messages?.length || 0,
|
|
105
|
+
summary: session.summary,
|
|
106
|
+
session,
|
|
107
|
+
message: "Session loaded. Continue chatting to resume the conversation.",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export default { listSessions, loadSession, getLatestSession, resumeSession };
|
|
@@ -82,20 +82,66 @@ function readJson(file, onWarn) {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
function managedSettingsFile(env = process.env) {
|
|
86
|
+
if (env.CC_MANAGED_SETTINGS) return path.resolve(env.CC_MANAGED_SETTINGS);
|
|
87
|
+
if (process.platform === "win32") {
|
|
88
|
+
const base = env.ProgramData || env.PROGRAMDATA || "C:\\ProgramData";
|
|
89
|
+
return path.join(base, "ChainlessChain", "managed-settings.json");
|
|
90
|
+
}
|
|
91
|
+
return "/etc/chainlesschain/managed-settings.json";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function appendHookBlock(merged, data) {
|
|
95
|
+
const block =
|
|
96
|
+
data && data.hooks && typeof data.hooks === "object" ? data.hooks : null;
|
|
97
|
+
if (!block) return false;
|
|
98
|
+
let contributed = false;
|
|
99
|
+
for (const [event, groups] of Object.entries(block)) {
|
|
100
|
+
if (!Array.isArray(groups)) continue;
|
|
101
|
+
for (const g of groups) {
|
|
102
|
+
if (!g || typeof g !== "object" || !Array.isArray(g.hooks)) continue;
|
|
103
|
+
const cmds = g.hooks.filter(
|
|
104
|
+
(h) => h && h.type === "command" && h.command,
|
|
105
|
+
);
|
|
106
|
+
if (cmds.length === 0) continue;
|
|
107
|
+
if (!merged[event]) merged[event] = [];
|
|
108
|
+
merged[event].push({ matcher: g.matcher ?? null, hooks: cmds });
|
|
109
|
+
contributed = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return contributed;
|
|
113
|
+
}
|
|
114
|
+
|
|
85
115
|
/**
|
|
86
116
|
* Load + concatenate the `hooks` blocks across the hierarchy.
|
|
87
117
|
* @returns {{ hooks: Record<string, Array<{matcher:string|null, hooks:Array<{type,command,timeout}>}>>, files:string[] }}
|
|
88
118
|
*/
|
|
89
|
-
function loadHooks({
|
|
119
|
+
function loadHooks({
|
|
120
|
+
cwd = process.cwd(),
|
|
121
|
+
settingsFile,
|
|
122
|
+
onWarn,
|
|
123
|
+
env = process.env,
|
|
124
|
+
} = {}) {
|
|
90
125
|
const merged = {};
|
|
91
126
|
const files = [];
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (
|
|
95
|
-
|
|
127
|
+
const managedFile = managedSettingsFile(env);
|
|
128
|
+
let managed = null;
|
|
129
|
+
if (_deps.fs.existsSync(managedFile)) {
|
|
130
|
+
managed = readJson(managedFile, onWarn);
|
|
131
|
+
if (!managed) {
|
|
132
|
+
const error = new Error(
|
|
133
|
+
`managed settings are unreadable or malformed: ${managedFile}`,
|
|
134
|
+
);
|
|
135
|
+
error.code = "CC_MANAGED_SETTINGS_INVALID";
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
96
138
|
}
|
|
139
|
+
// Safe-mode kill switch (`cc agent --safe-mode` sets CC_SETTINGS_HOOKS=0):
|
|
140
|
+
// user/project hooks are disabled, but administrator hooks remain active.
|
|
141
|
+
const managedOnly = managed?.allowManagedHooksOnly === true;
|
|
142
|
+
const skipUnmanaged = env.CC_SETTINGS_HOOKS === "0" || managedOnly;
|
|
97
143
|
const seenPaths = new Set();
|
|
98
|
-
for (const file of settingsFiles(cwd, settingsFile)) {
|
|
144
|
+
for (const file of skipUnmanaged ? [] : settingsFiles(cwd, settingsFile)) {
|
|
99
145
|
// Dedup by RESOLVED path so the SAME physical file is never read twice — e.g.
|
|
100
146
|
// `--settings .claude/settings.json` from the project root aliases the
|
|
101
147
|
// auto-loaded cwd file, which would otherwise CONCATENATE its hook groups a
|
|
@@ -106,26 +152,16 @@ function loadHooks({ cwd = process.cwd(), settingsFile, onWarn } = {}) {
|
|
|
106
152
|
if (seenPaths.has(resolved)) continue;
|
|
107
153
|
seenPaths.add(resolved);
|
|
108
154
|
const data = readJson(file, onWarn);
|
|
109
|
-
const
|
|
110
|
-
data && data.hooks && typeof data.hooks === "object" ? data.hooks : null;
|
|
111
|
-
if (!block) continue;
|
|
112
|
-
let contributed = false;
|
|
113
|
-
for (const [event, groups] of Object.entries(block)) {
|
|
114
|
-
if (!Array.isArray(groups)) continue;
|
|
115
|
-
for (const g of groups) {
|
|
116
|
-
if (!g || typeof g !== "object" || !Array.isArray(g.hooks)) continue;
|
|
117
|
-
const cmds = g.hooks.filter(
|
|
118
|
-
(h) => h && h.type === "command" && h.command,
|
|
119
|
-
);
|
|
120
|
-
if (cmds.length === 0) continue;
|
|
121
|
-
if (!merged[event]) merged[event] = [];
|
|
122
|
-
merged[event].push({ matcher: g.matcher ?? null, hooks: cmds });
|
|
123
|
-
contributed = true;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
155
|
+
const contributed = appendHookBlock(merged, data);
|
|
126
156
|
if (contributed) files.push(file);
|
|
127
157
|
}
|
|
128
|
-
|
|
158
|
+
if (managed && appendHookBlock(merged, managed)) files.push(managedFile);
|
|
159
|
+
const result = { hooks: merged, files };
|
|
160
|
+
if (managed) {
|
|
161
|
+
result.managed = managed;
|
|
162
|
+
result.managedFile = managedFile;
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
129
165
|
}
|
|
130
166
|
|
|
131
167
|
/**
|
|
@@ -184,7 +220,8 @@ function collectHooks(hooksBlock, event, toolName) {
|
|
|
184
220
|
for (const g of groups) {
|
|
185
221
|
const fn = compileMatcher(g.matcher);
|
|
186
222
|
if (fn(umbrella) || (raw && fn(raw))) {
|
|
187
|
-
for (const h of g.hooks)
|
|
223
|
+
for (const h of g.hooks)
|
|
224
|
+
out.push({ command: h.command, timeout: h.timeout });
|
|
188
225
|
}
|
|
189
226
|
}
|
|
190
227
|
return out;
|
|
@@ -265,6 +302,11 @@ function writeHookTrustStore(store) {
|
|
|
265
302
|
function projectHookTrustNotice({ cwd = process.cwd(), settingsFile } = {}) {
|
|
266
303
|
if (process.env.CC_HOOK_TRUST_NOTICE === "0") return null;
|
|
267
304
|
if (process.env.CC_SETTINGS_HOOKS === "0") return null; // hooks won't run anyway
|
|
305
|
+
const managedFile = managedSettingsFile(process.env);
|
|
306
|
+
if (_deps.fs.existsSync(managedFile)) {
|
|
307
|
+
const managed = readJson(managedFile);
|
|
308
|
+
if (managed?.allowManagedHooksOnly === true) return null;
|
|
309
|
+
}
|
|
268
310
|
const home = path.join(_deps.homedir(), ".claude", "settings.json");
|
|
269
311
|
const explicit = settingsFile ? path.resolve(cwd, settingsFile) : null;
|
|
270
312
|
const trusted = new Set([home, explicit].filter(Boolean));
|
|
@@ -328,6 +370,7 @@ module.exports = {
|
|
|
328
370
|
compileMatcher,
|
|
329
371
|
umbrellaFor,
|
|
330
372
|
settingsFiles,
|
|
373
|
+
managedSettingsFile,
|
|
331
374
|
HOOK_EVENTS,
|
|
332
375
|
_deps,
|
|
333
376
|
};
|
|
@@ -88,6 +88,69 @@ function settingsPaths(cwd, explicitFile) {
|
|
|
88
88
|
return list;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
/** Organization-controlled settings file. This layer is always highest. */
|
|
92
|
+
function managedSettingsPath(opts = {}) {
|
|
93
|
+
if (opts.managedSettingsFile) return path.resolve(opts.managedSettingsFile);
|
|
94
|
+
const env = opts.env || process.env;
|
|
95
|
+
if (env.CC_MANAGED_SETTINGS) return path.resolve(env.CC_MANAGED_SETTINGS);
|
|
96
|
+
if (process.platform === "win32") {
|
|
97
|
+
const base = env.ProgramData || env.PROGRAMDATA || "C:\\ProgramData";
|
|
98
|
+
return path.join(base, "ChainlessChain", "managed-settings.json");
|
|
99
|
+
}
|
|
100
|
+
return "/etc/chainlesschain/managed-settings.json";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function loadManagedSettings(opts = {}) {
|
|
104
|
+
const file = managedSettingsPath(opts);
|
|
105
|
+
if (!_deps.fs.existsSync(file)) return { file, settings: null };
|
|
106
|
+
const data = readSettingsFile(file, { onWarn: opts.onWarn });
|
|
107
|
+
if (!data) {
|
|
108
|
+
const error = new Error(
|
|
109
|
+
`managed settings are unreadable or malformed: ${file}`,
|
|
110
|
+
);
|
|
111
|
+
error.code = "CC_MANAGED_SETTINGS_INVALID";
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
return { file, settings: data };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function emptyRules() {
|
|
118
|
+
return { allow: [], ask: [], deny: [] };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Apply the non-bypassable permission portion of managed settings. */
|
|
122
|
+
function applyManagedPermissionPolicy(baseRules, managed, sources = {}) {
|
|
123
|
+
const policy = managed && typeof managed === "object" ? managed : {};
|
|
124
|
+
const rules = policy.allowManagedPermissionRulesOnly
|
|
125
|
+
? emptyRules()
|
|
126
|
+
: {
|
|
127
|
+
allow: [...(baseRules?.allow || [])],
|
|
128
|
+
ask: [...(baseRules?.ask || [])],
|
|
129
|
+
deny: [...(baseRules?.deny || [])],
|
|
130
|
+
};
|
|
131
|
+
const perms =
|
|
132
|
+
policy.permissions && typeof policy.permissions === "object"
|
|
133
|
+
? policy.permissions
|
|
134
|
+
: {};
|
|
135
|
+
for (const kind of KINDS) {
|
|
136
|
+
accrete(rules[kind], sources, perms[kind], "<managed>", kind);
|
|
137
|
+
}
|
|
138
|
+
return rules;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function assertManagedPermissionMode(mode, managed) {
|
|
142
|
+
if (
|
|
143
|
+
mode === "bypassPermissions" &&
|
|
144
|
+
(managed?.disableBypassPermissionsMode === true ||
|
|
145
|
+
managed?.disableBypassPermissionsMode === "disable")
|
|
146
|
+
) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
'permission mode "bypassPermissions" is disabled by managed settings',
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return mode;
|
|
152
|
+
}
|
|
153
|
+
|
|
91
154
|
/** Parse a comma/space separated env override into a string[]. */
|
|
92
155
|
function parseEnvList(value) {
|
|
93
156
|
if (!value) return [];
|
|
@@ -116,7 +179,7 @@ function loadSettings(opts = {}) {
|
|
|
116
179
|
const env = opts.env || process.env;
|
|
117
180
|
const onWarn = opts.onWarn;
|
|
118
181
|
|
|
119
|
-
|
|
182
|
+
let rules = emptyRules();
|
|
120
183
|
const sources = {};
|
|
121
184
|
const files = [];
|
|
122
185
|
|
|
@@ -150,7 +213,27 @@ function loadSettings(opts = {}) {
|
|
|
150
213
|
}
|
|
151
214
|
if (envContributed) files.push("<env>");
|
|
152
215
|
|
|
153
|
-
|
|
216
|
+
const managedLoaded = loadManagedSettings({
|
|
217
|
+
env,
|
|
218
|
+
managedSettingsFile: opts.managedSettingsFile,
|
|
219
|
+
onWarn,
|
|
220
|
+
});
|
|
221
|
+
if (managedLoaded.settings) {
|
|
222
|
+
rules = applyManagedPermissionPolicy(
|
|
223
|
+
rules,
|
|
224
|
+
managedLoaded.settings,
|
|
225
|
+
sources,
|
|
226
|
+
);
|
|
227
|
+
files.push(managedLoaded.file);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
rules,
|
|
232
|
+
sources,
|
|
233
|
+
files,
|
|
234
|
+
managed: managedLoaded.settings,
|
|
235
|
+
managedFile: managedLoaded.settings ? managedLoaded.file : null,
|
|
236
|
+
};
|
|
154
237
|
}
|
|
155
238
|
|
|
156
239
|
/** Look up the source file a matched `{ kind, rule }` came from. */
|
|
@@ -236,6 +319,24 @@ function loadSettingsConfig(opts = {}) {
|
|
|
236
319
|
}
|
|
237
320
|
if (contributed) files.push(file);
|
|
238
321
|
}
|
|
322
|
+
const managed = loadManagedSettings(opts);
|
|
323
|
+
if (managed.settings) {
|
|
324
|
+
const data = managed.settings;
|
|
325
|
+
let contributed = false;
|
|
326
|
+
if (typeof data.model === "string" && data.model.trim()) {
|
|
327
|
+
model = data.model.trim();
|
|
328
|
+
contributed = true;
|
|
329
|
+
}
|
|
330
|
+
if (data.env && typeof data.env === "object" && !Array.isArray(data.env)) {
|
|
331
|
+
for (const [k, v] of Object.entries(data.env)) {
|
|
332
|
+
if (typeof v === "string") {
|
|
333
|
+
env[k] = v;
|
|
334
|
+
contributed = true;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (contributed) files.push(managed.file);
|
|
339
|
+
}
|
|
239
340
|
return { model, env, files };
|
|
240
341
|
}
|
|
241
342
|
|
|
@@ -270,6 +371,14 @@ function readBooleanSetting(key, opts = {}) {
|
|
|
270
371
|
value = node; // last (closest) layer wins
|
|
271
372
|
}
|
|
272
373
|
}
|
|
374
|
+
const managed = loadManagedSettings(opts);
|
|
375
|
+
if (managed.settings) {
|
|
376
|
+
let node = managed.settings;
|
|
377
|
+
for (const p of parts) {
|
|
378
|
+
node = node && typeof node === "object" ? node[p] : undefined;
|
|
379
|
+
}
|
|
380
|
+
if (typeof node === "boolean") value = node;
|
|
381
|
+
}
|
|
273
382
|
return value;
|
|
274
383
|
}
|
|
275
384
|
|
|
@@ -279,6 +388,10 @@ module.exports = {
|
|
|
279
388
|
readSettingsFile,
|
|
280
389
|
readBooleanSetting,
|
|
281
390
|
settingsPaths,
|
|
391
|
+
managedSettingsPath,
|
|
392
|
+
loadManagedSettings,
|
|
393
|
+
applyManagedPermissionPolicy,
|
|
394
|
+
assertManagedPermissionMode,
|
|
282
395
|
parseEnvList,
|
|
283
396
|
ruleSource,
|
|
284
397
|
addRule,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background task listing utilities (ESM)
|
|
3
|
+
* Mirrors Claude Code /tasks behavior
|
|
4
|
+
* @module lib/task-list
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { listBackgroundShellTasks } from "../runtime/agent-core.js";
|
|
8
|
+
import { formatBackgroundTasks } from "../repl/tasks-status.js";
|
|
9
|
+
import { logger } from "./logger.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* List all background tasks
|
|
13
|
+
* @param {Object} options List options
|
|
14
|
+
* @returns {Promise<Object>} task list result
|
|
15
|
+
*/
|
|
16
|
+
export async function listTasks(options = {}) {
|
|
17
|
+
const tasks = listBackgroundShellTasks();
|
|
18
|
+
const active = tasks.filter((t) => t.status === "running");
|
|
19
|
+
const completed = tasks.filter((t) => t.status !== "running");
|
|
20
|
+
|
|
21
|
+
if (tasks.length === 0) {
|
|
22
|
+
logger.info("No background tasks");
|
|
23
|
+
return { count: 0, active: 0, completed: 0, tasks: [] };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!options.quiet) {
|
|
27
|
+
logger.info(formatBackgroundTasks(tasks));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
logger.info(
|
|
31
|
+
` ${active.length} active, ${completed.length} completed (total: ${tasks.length})`,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
count: tasks.length,
|
|
36
|
+
active: active.length,
|
|
37
|
+
completed: completed.length,
|
|
38
|
+
tasks,
|
|
39
|
+
formatted: formatBackgroundTasks(tasks),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get a specific task by ID
|
|
45
|
+
* @param {string} taskId
|
|
46
|
+
* @returns {Object|null}
|
|
47
|
+
*/
|
|
48
|
+
export function getTask(taskId) {
|
|
49
|
+
const tasks = listBackgroundShellTasks();
|
|
50
|
+
return tasks.find((t) => t.id === taskId) || null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default { listTasks, getTask };
|
package/src/repl/agent-repl.js
CHANGED
|
@@ -98,6 +98,7 @@ import { resolveSlashMacro } from "./slash-macro.js";
|
|
|
98
98
|
import { expandMcpPrompt, renderMcpSurface } from "./mcp-prompt.js";
|
|
99
99
|
import { newCostStore, addUsage } from "./session-cost.js";
|
|
100
100
|
import { parseThinkCommand, parseEffortCommand } from "./think-command.js";
|
|
101
|
+
import { parseBtwCommand, buildAsideBlock, applyAside } from "./btw-command.js";
|
|
101
102
|
import { shouldStreamLive } from "./stream-decision.js";
|
|
102
103
|
import { emptyTurnNotice } from "./empty-turn-notice.js";
|
|
103
104
|
import { buildPermissionPrompt } from "./permission-prompt.js";
|
|
@@ -117,6 +118,7 @@ let _approvalGate = null;
|
|
|
117
118
|
// confirmer for `ask` matches. Loaded once at REPL startup; null = no file.
|
|
118
119
|
let _permissionRules = null;
|
|
119
120
|
let _permissionConfirm = null;
|
|
121
|
+
let _managedPermissionRulesOnly = false;
|
|
120
122
|
// .claude/settings.json `hooks` block (decision-capable PreToolUse/PostToolUse).
|
|
121
123
|
let _settingsHooks = null;
|
|
122
124
|
// .claude/settings.json `respondToBashCommands` (Claude-Code 2.1.186): whether a
|
|
@@ -127,6 +129,7 @@ let _respondToBash;
|
|
|
127
129
|
// the built-in verification allowlist through the shell-policy classifier (→
|
|
128
130
|
// ApprovalGate confirm) instead of fast-pathing it. false = unset → off.
|
|
129
131
|
let _classifyAllShell = false;
|
|
132
|
+
let _sandbox = null;
|
|
130
133
|
// Bounded log of tool calls the agent was BLOCKED from running this session
|
|
131
134
|
// (shell-policy / ApprovalGate / settings rule / hook). Surfaced by
|
|
132
135
|
// `/permissions denials` (Claude-Code 2.1.193 "recent denials"). In-memory only.
|
|
@@ -161,6 +164,12 @@ async function _fireNotification(message) {
|
|
|
161
164
|
*/
|
|
162
165
|
async function _persistAlwaysAllow(tool, args) {
|
|
163
166
|
try {
|
|
167
|
+
if (_managedPermissionRulesOnly) {
|
|
168
|
+
process.stderr.write(
|
|
169
|
+
" always-allow is disabled by managed settings; ask your administrator to change the policy.\n",
|
|
170
|
+
);
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
164
173
|
const rulesMod = await import("../lib/permission-rules.cjs");
|
|
165
174
|
const { suggestAllowRule } = rulesMod.default || rulesMod;
|
|
166
175
|
const rule = suggestAllowRule(tool || "run_shell", args || {});
|
|
@@ -194,6 +203,7 @@ async function executeTool(name, args) {
|
|
|
194
203
|
permissionConfirm: _permissionConfirm,
|
|
195
204
|
settingsHooks: _settingsHooks,
|
|
196
205
|
classifyAllShell: _classifyAllShell,
|
|
206
|
+
sandbox: _sandbox,
|
|
197
207
|
});
|
|
198
208
|
}
|
|
199
209
|
|
|
@@ -371,6 +381,12 @@ export async function startAgentRepl(options = {}) {
|
|
|
371
381
|
// (--thinking-budget) is the companion legacy-model budget_tokens override.
|
|
372
382
|
let thinking = options.thinking || null;
|
|
373
383
|
const thinkingBudget = options.thinkingBudget || null;
|
|
384
|
+
// `/btw` one-shot asides: queued notes that ride ONLY the next turn (sent to
|
|
385
|
+
// the model, then stripped so they never persist or carry forward).
|
|
386
|
+
// `_btwRestore` holds the user message + its pre-aside content so we can undo
|
|
387
|
+
// the injection after the turn (or as a backstop at the next submit on throw).
|
|
388
|
+
let pendingBtw = [];
|
|
389
|
+
let _btwRestore = null;
|
|
374
390
|
// Current ApprovalGate session tier (strict|trusted|autopilot), mirrored here
|
|
375
391
|
// so Shift+Tab can cycle it and `/permissions <tier>` can set it. Kept in sync
|
|
376
392
|
// with _approvalGate.setSessionPolicy below.
|
|
@@ -402,6 +418,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
402
418
|
const additionalDirectories = Array.isArray(options.additionalDirectories)
|
|
403
419
|
? options.additionalDirectories
|
|
404
420
|
: [];
|
|
421
|
+
_sandbox = options.sandbox || null;
|
|
405
422
|
// Snapshot the work tree before each mutating tool (git engine) so the user
|
|
406
423
|
// can `cc checkpoint restore` to just before any tool call.
|
|
407
424
|
const autoCheckpoint = options.autoCheckpoint === true;
|
|
@@ -542,6 +559,8 @@ export async function startAgentRepl(options = {}) {
|
|
|
542
559
|
const { loadSettings, readBooleanSetting } =
|
|
543
560
|
await import("../lib/settings-loader.cjs");
|
|
544
561
|
const loaded = loadSettings({ cwd: process.cwd() });
|
|
562
|
+
_managedPermissionRulesOnly =
|
|
563
|
+
loaded.managed?.allowManagedPermissionRulesOnly === true;
|
|
545
564
|
// Claude-Code 2.1.186 respondToBashCommands (default OFF / opt-in when unset).
|
|
546
565
|
_respondToBash = readBooleanSetting("respondToBashCommands", {
|
|
547
566
|
cwd: process.cwd(),
|
|
@@ -578,8 +597,10 @@ export async function startAgentRepl(options = {}) {
|
|
|
578
597
|
return ans === "y" || ans === "yes";
|
|
579
598
|
};
|
|
580
599
|
} catch (_err) {
|
|
600
|
+
if (_err?.code === "CC_MANAGED_SETTINGS_INVALID") throw _err;
|
|
581
601
|
_permissionRules = null;
|
|
582
602
|
_permissionConfirm = null;
|
|
603
|
+
_managedPermissionRulesOnly = false;
|
|
583
604
|
}
|
|
584
605
|
|
|
585
606
|
// Load .claude/settings.json `hooks` block (decision-capable PreToolUse/
|
|
@@ -1040,6 +1061,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
1040
1061
|
"/add-dir",
|
|
1041
1062
|
"/agents",
|
|
1042
1063
|
"/auto",
|
|
1064
|
+
"/btw",
|
|
1043
1065
|
"/cd",
|
|
1044
1066
|
"/clear",
|
|
1045
1067
|
"/compact",
|
|
@@ -1518,6 +1540,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
1518
1540
|
logger.log(
|
|
1519
1541
|
` ${chalk.cyan("/effort")} Reasoning effort (/effort low|medium|high|xhigh; Anthropic)`,
|
|
1520
1542
|
);
|
|
1543
|
+
logger.log(
|
|
1544
|
+
` ${chalk.cyan("/btw")} Queue a one-off aside for your next message (not saved to history)`,
|
|
1545
|
+
);
|
|
1521
1546
|
logger.log(` ${chalk.cyan("/clear")} Clear conversation`);
|
|
1522
1547
|
logger.log(
|
|
1523
1548
|
` ${chalk.cyan("/vim")} Toggle vim-mode line editing (/vim [on|off]; Esc → NORMAL)`,
|
|
@@ -2030,6 +2055,27 @@ export async function startAgentRepl(options = {}) {
|
|
|
2030
2055
|
}
|
|
2031
2056
|
}
|
|
2032
2057
|
|
|
2058
|
+
// `/btw <note>` — queue a one-shot aside (Claude-Code parity). It rides the
|
|
2059
|
+
// NEXT message to the model, then is stripped so it never bloats history.
|
|
2060
|
+
{
|
|
2061
|
+
const btw = parseBtwCommand(trimmed);
|
|
2062
|
+
if (btw) {
|
|
2063
|
+
if (btw.error) {
|
|
2064
|
+
logger.info(btw.error);
|
|
2065
|
+
prompt();
|
|
2066
|
+
return;
|
|
2067
|
+
}
|
|
2068
|
+
pendingBtw.push(btw.text);
|
|
2069
|
+
logger.info(
|
|
2070
|
+
chalk.gray(
|
|
2071
|
+
`Aside noted (${pendingBtw.length}) — rides your next message only, not saved to history.`,
|
|
2072
|
+
),
|
|
2073
|
+
);
|
|
2074
|
+
prompt();
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2033
2079
|
if (trimmed === "/clear") {
|
|
2034
2080
|
// Stash the conversation first so `/rewind clear` can restore it
|
|
2035
2081
|
// (Claude-Code 2.1.191). A no-op /clear (nothing to stash) keeps any
|
|
@@ -3644,6 +3690,14 @@ export async function startAgentRepl(options = {}) {
|
|
|
3644
3690
|
logger.verbose(`[hook] prompt rewritten by UserPromptSubmit hook`);
|
|
3645
3691
|
}
|
|
3646
3692
|
|
|
3693
|
+
// Backstop: if the previous turn's agentLoop threw before the success-path
|
|
3694
|
+
// restore ran, undo its /btw aside now so it never leaks into this turn's
|
|
3695
|
+
// history/model call. (Normal turns clear _btwRestore right after agentLoop.)
|
|
3696
|
+
if (_btwRestore) {
|
|
3697
|
+
_btwRestore.msg.content = _btwRestore.content;
|
|
3698
|
+
_btwRestore = null;
|
|
3699
|
+
}
|
|
3700
|
+
|
|
3647
3701
|
// Expand @path file references into context blocks (Claude-Code parity),
|
|
3648
3702
|
// so `review @src/x.js` injects the file contents. Typo'd paths are warned
|
|
3649
3703
|
// about and left as-is.
|
|
@@ -3746,8 +3800,10 @@ export async function startAgentRepl(options = {}) {
|
|
|
3746
3800
|
}
|
|
3747
3801
|
}
|
|
3748
3802
|
|
|
3749
|
-
// Add user message
|
|
3750
|
-
|
|
3803
|
+
// Add user message (keep the object ref so a /btw aside can be injected for
|
|
3804
|
+
// this turn's model call and then stripped before persistence).
|
|
3805
|
+
const _userMsg = { role: "user", content: _userMessageContent };
|
|
3806
|
+
messages.push(_userMsg);
|
|
3751
3807
|
|
|
3752
3808
|
// Slot-filling: detect intent and fill missing parameters interactively
|
|
3753
3809
|
try {
|
|
@@ -3856,6 +3912,23 @@ export async function startAgentRepl(options = {}) {
|
|
|
3856
3912
|
}
|
|
3857
3913
|
: {};
|
|
3858
3914
|
if (_streamLive) process.stdout.write("\n");
|
|
3915
|
+
// Inject any queued /btw asides into THIS turn's user message just before
|
|
3916
|
+
// the model call. We remember the pre-aside content so it's restored right
|
|
3917
|
+
// after agentLoop returns — the aside steers this answer but is never
|
|
3918
|
+
// persisted (saveMessages/JSONL) or carried into later turns. Consumed on
|
|
3919
|
+
// send (cleared now) so a thrown turn doesn't re-inject next time; the
|
|
3920
|
+
// submit-start backstop restores if agentLoop throws before we get back.
|
|
3921
|
+
if (pendingBtw.length > 0) {
|
|
3922
|
+
const block = buildAsideBlock(pendingBtw);
|
|
3923
|
+
if (block) {
|
|
3924
|
+
_btwRestore = { msg: _userMsg, content: _userMsg.content };
|
|
3925
|
+
_userMsg.content = applyAside(_userMsg.content, block);
|
|
3926
|
+
logger.verbose(
|
|
3927
|
+
`[btw] applied ${pendingBtw.length} aside(s) to this turn`,
|
|
3928
|
+
);
|
|
3929
|
+
}
|
|
3930
|
+
pendingBtw = [];
|
|
3931
|
+
}
|
|
3859
3932
|
// Consume the one-shot resume-degeneracy flag for THIS turn so the role
|
|
3860
3933
|
// merge fires exactly once (2.1.187 parity; see _sanitizeRolesNextTurn).
|
|
3861
3934
|
const _mergeRolesThisTurn = _sanitizeRolesNextTurn;
|
|
@@ -3913,6 +3986,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
3913
3986
|
sessionId,
|
|
3914
3987
|
cwd: process.cwd(),
|
|
3915
3988
|
additionalDirectories,
|
|
3989
|
+
sandbox: _sandbox,
|
|
3916
3990
|
autoCheckpoint,
|
|
3917
3991
|
checkpointSession: sessionId,
|
|
3918
3992
|
checkpointMarks: _checkpointMarks,
|
|
@@ -3936,6 +4010,13 @@ export async function startAgentRepl(options = {}) {
|
|
|
3936
4010
|
});
|
|
3937
4011
|
_turnAbort = null;
|
|
3938
4012
|
|
|
4013
|
+
// Strip the one-shot /btw aside now the model has seen it — so it is never
|
|
4014
|
+
// persisted (DB saveMessages below) or carried into the next turn.
|
|
4015
|
+
if (_btwRestore) {
|
|
4016
|
+
_btwRestore.msg.content = _btwRestore.content;
|
|
4017
|
+
_btwRestore = null;
|
|
4018
|
+
}
|
|
4019
|
+
|
|
3939
4020
|
// Running spend for `/cost` (in-memory, works without persistence).
|
|
3940
4021
|
if (usageEvents?.length) addUsage(_costStore, usageEvents);
|
|
3941
4022
|
|
|
@@ -65,7 +65,13 @@ export function parseAgentsCommand(line) {
|
|
|
65
65
|
if (first === "help") return { action: "help" };
|
|
66
66
|
if (first === "show") return { action: "show", name: tokens[1] || null };
|
|
67
67
|
if (first === "new") {
|
|
68
|
-
return {
|
|
68
|
+
return {
|
|
69
|
+
action: "new",
|
|
70
|
+
name: tokens[1] || null,
|
|
71
|
+
description,
|
|
72
|
+
tools,
|
|
73
|
+
location,
|
|
74
|
+
};
|
|
69
75
|
}
|
|
70
76
|
// A bare token is treated as a name to show.
|
|
71
77
|
return { action: "show", name: first };
|
|
@@ -95,7 +101,7 @@ export function formatAgentsList(agents, profiles) {
|
|
|
95
101
|
}
|
|
96
102
|
lines.push(
|
|
97
103
|
"",
|
|
98
|
-
|
|
104
|
+
'Manage: /agents show <name> · /agents new <name> · run via `cc agents run <name> "<task>"`',
|
|
99
105
|
);
|
|
100
106
|
return lines.join("\n");
|
|
101
107
|
}
|