chainlesschain 0.162.89 → 0.162.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/package.json +11 -2
  2. package/src/assets/web-panel/assets/{AIOps-DDKuDDBe.js → AIOps-CUU1CN5v.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-D_oEGk6a.js → ActionButton-D7obDp1D.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-1zi90Zqp.js → Analytics-DoHxIThS.js} +2 -2
  5. package/src/assets/web-panel/assets/{AppLayout-D5TSGV7Y.js → AppLayout-DoGNh61D.js} +5 -5
  6. package/src/assets/web-panel/assets/{Audit-CAdM6ZWq.js → Audit-Blsu-XVl.js} +1 -1
  7. package/src/assets/web-panel/assets/{Backup-Bbmq82UE.js → Backup-De-K8Mf6.js} +1 -1
  8. package/src/assets/web-panel/assets/{BaseInput-DdGZcAGo.js → BaseInput-0DBGC-Qy.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-3p4ROvmy.js → Chat-IEJXGp4A.js} +3 -3
  10. package/src/assets/web-panel/assets/ChatBubbleRenderer-BrdUSKJs.js +1 -0
  11. package/src/assets/web-panel/assets/{Checkbox-Bo5o4anX.js → Checkbox-Dgly7Wb4.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-BQNL8l9R.js → Codegen-BADA7pRy.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col-Cw_UMO2L.js → Col-DEOhJE0I.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-BVyhXx_x.js → Community-0RdnMWlk.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-ldL9fyLc.js → Compact-i9eT8LcF.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-4TeKhiTd.js → Compliance-CSRNgXGk.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-Ch6QoB18.js → Cowork-P7z8tygk.js} +2 -2
  18. package/src/assets/web-panel/assets/{Cron-LEjDutAK.js → Cron-C49W98ih.js} +2 -2
  19. package/src/assets/web-panel/assets/{Crosschain-bSr0mGyW.js → Crosschain-yMAal2Ah.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-DVdHJJm3.js → DID-Dl2Ad_VR.js} +2 -2
  21. package/src/assets/web-panel/assets/{Dashboard-xQ4mYwac.js → Dashboard-CYjuGfVa.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dropdown-CI2lo1UC.js → Dropdown-CleQMach.js} +1 -1
  23. package/src/assets/web-panel/assets/{EmailListRenderer-2U85M3PG.js → EmailListRenderer-BDDIGtuF.js} +1 -1
  24. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DTvedZ4R.js → FamilyGuardDashboard-B1ebvoEU.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-_WEmATdN.js → Federation-BqzJlYyH.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-CoJEED8P.js → FormItemContext-DaZ2IcKx.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-8pTqC5Pa.js → GenericCardRenderer-D1QgXKYz.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-DEl-Ya7C.js → Git-C_hcF3Us.js} +2 -2
  29. package/src/assets/web-panel/assets/{Governance-BFhRzz-i.js → Governance-D9dA-d8s.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-Bwtf10Lp.js → Inference--RrBmPXD.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-DYbKYjzW.js → KnowledgeGraph-DL8pYycV.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-Dbvs5Jqr.js → Logs-BaNV0yhO.js} +2 -2
  33. package/src/assets/web-panel/assets/{Marketplace-C8Hwf7Nt.js → Marketplace-DFwjpKpf.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-Dc06yx1O.js → McpTools-CBB749Ot.js} +4 -4
  35. package/src/assets/web-panel/assets/{Memory-CjtfAYMK.js → Memory--ByzjhfO.js} +2 -2
  36. package/src/assets/web-panel/assets/{MobileBridge-Bwg6Uv0-.js → MobileBridge-D95LlJsv.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileProjects-D_Yayger.js → MobileProjects-DMj3obZF.js} +1 -1
  38. package/src/assets/web-panel/assets/{Mtc-DimSkHYd.js → Mtc-B9KxzUdp.js} +2 -2
  39. package/src/assets/web-panel/assets/{MtcAudit-D7g1pn11.js → MtcAudit-BnsYAJtX.js} +2 -2
  40. package/src/assets/web-panel/assets/{Multisig-Be-fsQin.js → Multisig-xXiUo7j4.js} +3 -3
  41. package/src/assets/web-panel/assets/{NLProgramming-DcNcXu3l.js → NLProgramming-ZwP9uTMW.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-pv6Ot-V-.js → Notes-Ds9c8DsR.js} +2 -2
  43. package/src/assets/web-panel/assets/{NotificationSettings-CEi4Stfz.js → NotificationSettings-hmVl6u3C.js} +1 -1
  44. package/src/assets/web-panel/assets/{OrderTableRenderer-CN8i3BFY.js → OrderTableRenderer-0zwbhl09.js} +1 -1
  45. package/src/assets/web-panel/assets/{Organization-DtU9aIk3.js → Organization-DxAlPoAj.js} +2 -2
  46. package/src/assets/web-panel/assets/{Overflow-DhpkM6SB.js → Overflow-B8ZTSn_q.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-CUBG6AI4.js → P2P-DAtgVM3K.js} +2 -2
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-qN193Lvq.js → PdhVaultBrowser-UIVFUBGx.js} +4 -4
  49. package/src/assets/web-panel/assets/{Permissions-Co0mmuhn.js → Permissions-pnyWeMrU.js} +4 -4
  50. package/src/assets/web-panel/assets/{PersonalDataHub-CTBvxlRG.js → PersonalDataHub-BjugBPb1.js} +2 -2
  51. package/src/assets/web-panel/assets/{Pipeline-KxsyMqPq.js → Pipeline-CwouGOua.js} +1 -1
  52. package/src/assets/web-panel/assets/{Privacy-B5jrzQJA.js → Privacy-dzqF0gQv.js} +1 -1
  53. package/src/assets/web-panel/assets/{ProjectInit-B7OcgjxJ.js → ProjectInit-6ccqykoJ.js} +2 -2
  54. package/src/assets/web-panel/assets/{ProjectSettings-Ci4PDOdj.js → ProjectSettings-umC-Vug3.js} +2 -2
  55. package/src/assets/web-panel/assets/{Projects-BJKdEvTl.js → Projects-BeOnWXDB.js} +1 -1
  56. package/src/assets/web-panel/assets/{Providers-yVfFgGST.js → Providers-D8ccNwmj.js} +1 -1
  57. package/src/assets/web-panel/assets/{QuickAsk-Bc79xdK2.js → QuickAsk-C2xYcNtw.js} +1 -1
  58. package/src/assets/web-panel/assets/{Recommend-CkTvhsKQ.js → Recommend-tfWUU-s_.js} +1 -1
  59. package/src/assets/web-panel/assets/{Reputation-ijWjqEZm.js → Reputation-rFXftu7T.js} +1 -1
  60. package/src/assets/web-panel/assets/{Row-oV-drKth.js → Row-GSEmzZbO.js} +1 -1
  61. package/src/assets/web-panel/assets/{RssFeed-B1hkOEjr.js → RssFeed-DRc1SVBF.js} +2 -2
  62. package/src/assets/web-panel/assets/{Search-bjiydP4_.js → Search-zM4n0qTF.js} +1 -1
  63. package/src/assets/web-panel/assets/{Security-D07XF2NJ.js → Security-CAS0P5mv.js} +2 -2
  64. package/src/assets/web-panel/assets/{Services-D1XZW1VI.js → Services-m6T91kxF.js} +2 -2
  65. package/src/assets/web-panel/assets/{Skeleton-bWZHM36K.js → Skeleton-DeJP7jXC.js} +1 -1
  66. package/src/assets/web-panel/assets/{Skills-DprqZ58V.js → Skills-aoIPXqbU.js} +1 -1
  67. package/src/assets/web-panel/assets/{Sla-CiaKZ3op.js → Sla-CY8SyFjq.js} +1 -1
  68. package/src/assets/web-panel/assets/{SpeechSettings-AFDTRPq8.js → SpeechSettings-BU2wVhiT.js} +1 -1
  69. package/src/assets/web-panel/assets/{SyncSettings-lVnKrdHt.js → SyncSettings-UC8A4ZOp.js} +2 -2
  70. package/src/assets/web-panel/assets/{Tasks-DnvbnNTF.js → Tasks-D_wUVzBB.js} +1 -1
  71. package/src/assets/web-panel/assets/{Templates-CSvO-rac.js → Templates-C1Rryvmd.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tenant-CTS6CF8D.js → Tenant-C7i1YsZR.js} +1 -1
  73. package/src/assets/web-panel/assets/{Terminal-FhG6jIdV.js → Terminal-CNJPa6QX.js} +2 -2
  74. package/src/assets/web-panel/assets/{TimelineRenderer-CqwbzI2B.js → TimelineRenderer-CEMTBMX-.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tokens-CmXW3LL1.js → Tokens-CL2ZxjR6.js} +1 -1
  76. package/src/assets/web-panel/assets/{Trigger-DwoNiFAw.js → Trigger-CBmecSgf.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trust-D5rW48PN.js → Trust-DVEEJI8Q.js} +1 -1
  78. package/src/assets/web-panel/assets/{UkeySign-CrvoDfjI.js → UkeySign-Bumw9-l-.js} +1 -1
  79. package/src/assets/web-panel/assets/{VideoEditing-DTvhmxtn.js → VideoEditing-CxAn3OnO.js} +1 -1
  80. package/src/assets/web-panel/assets/{Wallet-R-NDs-nS.js → Wallet-DiaIIXpX.js} +4 -4
  81. package/src/assets/web-panel/assets/{WebAuthn-Bc6TtMHJ.js → WebAuthn-Cu77ilyR.js} +5 -5
  82. package/src/assets/web-panel/assets/{WorkflowEditor-TyS8hVP3.js → WorkflowEditor-CEVtkNGh.js} +1 -1
  83. package/src/assets/web-panel/assets/{chat-De1UwIe9.js → chat-DCIbNcWp.js} +1 -1
  84. package/src/assets/web-panel/assets/{colors-YLnxJgpV.js → colors-CFAsOWWc.js} +1 -1
  85. package/src/assets/web-panel/assets/{compact-item-DBW4zsaa.js → compact-item-BtcWzkn9.js} +1 -1
  86. package/src/assets/web-panel/assets/{createContext-D-UkFz2M.js → createContext-DM02coVP.js} +1 -1
  87. package/src/assets/web-panel/assets/devWarning-lHwojmXr.js +1 -0
  88. package/src/assets/web-panel/assets/{hasIn-DV4XA-9Q.js → hasIn-BDZ9Ekx9.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-Clq1H8f_.js → index--uVAPmGR.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-DMH0Yf1N.js → index-1rncs92o.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-DcUjc_dL.js → index-3bP_3pQF.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-DnETiQYX.js → index-7wFrzA4Z.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-BLnhTvTW.js → index-B-sCou25.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-wEmgmG9r.js → index-B2RA_MJb.js} +3 -3
  95. package/src/assets/web-panel/assets/{index-Bie6wUJF.js → index-B7k4idtg.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-RXPhl0sY.js → index-BKoKIDcM.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DeN7De7q.js → index-BLqJHRDI.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-L-uPkzzS.js → index-BMoQKqYy.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BdWZ8SkV.js → index-BXo_QE0d.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CHRF04G3.js → index-BcghAQsx.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CRhQZ2Qm.js → index-BtxcSBXC.js} +1 -1
  102. package/src/assets/web-panel/assets/index-BwswivuZ.js +1 -0
  103. package/src/assets/web-panel/assets/{index-asF__VSG.js → index-Bx6loBFk.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BG8q2ruP.js → index-C7Eci2-q.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-siuu7iQC.js → index-C7lzFVWX.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-jsH5BzXV.js → index-C7zC4EVL.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-D8wpSmf5.js → index-CBmXtXp6.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CS6TRyFe.js → index-CEEhhH_D.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-PrmHq4aD.js → index-CJTn1VjP.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DjOtMRdu.js → index-CM_qO-Wi.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BRzvcnNa.js → index-Cph9nP-9.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-B7vJw85L.js → index-CsaFqGsp.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BVoAVyxu.js → index-Cw6H_GmX.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-D8AhGIum.js → index-Cwl7qqJX.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DkMRQlNy.js → index-D4X_P7fC.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Drhspi9z.js → index-DDQ1bvfV.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-ClZBGDhE.js → index-DN5pX0pj.js} +1 -1
  118. package/src/assets/web-panel/assets/index-DT2mCN-q.js +1 -0
  119. package/src/assets/web-panel/assets/{index-ULxnYhN-.js → index-Dh1wBDli.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-KgCbeEHM.js → index-DkWVA1-9.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CYOu0-kh.js → index-HCl7zhMl.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-OxVLKI4_.js → index-HuAwgOyl.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CpEfuVwR.js → index-N0f9HUpv.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-r7CI1rPG.js → index-_b9l_C4D.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-B8lFTKvB.js → index-g529Tmyi.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D6wDswfi.js → index-uM8aQYlG.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CWdRRtPj.js → index-uUDj49aJ.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-BcboSW0u.js → initDefaultProps-BUUm7blZ.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-CJFBvrIr.js → motion-CInc71Ft.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-Cn1g7xYn.js → move-Cx55hmPo.js} +1 -1
  131. package/src/assets/web-panel/assets/{omit-BfJzbWsb.js → omit-JARbRj1V.js} +1 -1
  132. package/src/assets/web-panel/assets/{pickAttrs-CmfqYIiL.js → pickAttrs-BYLAq9Na.js} +1 -1
  133. package/src/assets/web-panel/assets/{placementArrow-Cta7hqTC.js → placementArrow-HMMFCRqE.js} +1 -1
  134. package/src/assets/web-panel/assets/{responsiveObserve-DaEkOCb-.js → responsiveObserve-oWVLeE2s.js} +1 -1
  135. package/src/assets/web-panel/assets/{slide-CHTJZhYO.js → slide-FQzTsWDz.js} +1 -1
  136. package/src/assets/web-panel/assets/{statusUtils-1p-VPpRr.js → statusUtils-CjP_iGFp.js} +1 -1
  137. package/src/assets/web-panel/assets/{styleChecker-ZSwUScrp.js → styleChecker-BRMI8mF3.js} +1 -1
  138. package/src/assets/web-panel/assets/{useFlexGapSupport-Viy5DmaP.js → useFlexGapSupport-xcJuPJdm.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFs-OkD_oGXm.js → useFs-BAiSiSMd.js} +1 -1
  140. package/src/assets/web-panel/assets/{usePersonalDataHub-CRAxqDR6.js → usePersonalDataHub-BeD-UCwh.js} +1 -1
  141. package/src/assets/web-panel/assets/{vnode-TfIXtx5S.js → vnode-Ld46zJcQ.js} +1 -1
  142. package/src/assets/web-panel/assets/{zoom-Cyvmz3v6.js → zoom-C9VRfvTW.js} +1 -1
  143. package/src/assets/web-panel/index.html +1 -1
  144. package/src/commands/config.js +32 -0
  145. package/src/lib/config-keys.js +140 -0
  146. package/src/repl/agent-repl.js +19 -14
  147. package/src/repl/empty-turn-notice.js +27 -0
  148. package/src/repl/permission-prompt.js +26 -0
  149. package/src/runtime/coding-agent-policy.cjs +18 -3
  150. package/src/runtime/coding-agent-shell-policy.cjs +36 -1
  151. package/src/assets/web-panel/assets/ChatBubbleRenderer-BORlWmtB.js +0 -1
  152. package/src/assets/web-panel/assets/devWarning-DD8LWYUk.js +0 -1
  153. package/src/assets/web-panel/assets/index-Cnx1SQa9.js +0 -1
  154. package/src/assets/web-panel/assets/index-vTEl5Ekh.js +0 -1
@@ -25,6 +25,38 @@ export function registerConfigCommand(program) {
25
25
  logger.newline();
26
26
  });
27
27
 
28
+ cmd
29
+ .command("keys")
30
+ .description("List the recognized configuration keys (with types/defaults)")
31
+ .option("--json", "Output as JSON")
32
+ .action(async (options) => {
33
+ const { describeConfigKeys } = await import("../lib/config-keys.js");
34
+ const entries = describeConfigKeys();
35
+ if (options.json) {
36
+ console.log(JSON.stringify(entries, null, 2));
37
+ return;
38
+ }
39
+ logger.log(chalk.bold("\n Configuration keys\n"));
40
+ logger.log(
41
+ chalk.gray(
42
+ " Set with: cc config set <key> <value> Get: cc config get <key>\n",
43
+ ),
44
+ );
45
+ for (const e of entries) {
46
+ const cur =
47
+ e.current === undefined || e.current === null
48
+ ? chalk.gray("(unset)")
49
+ : typeof e.current === "object"
50
+ ? chalk.gray(JSON.stringify(e.current))
51
+ : chalk.green(String(e.current));
52
+ logger.log(
53
+ ` ${chalk.cyan(e.key)} ${chalk.gray(`<${e.type}>`)} = ${cur}`,
54
+ );
55
+ if (e.description) logger.log(` ${chalk.gray(e.description)}`);
56
+ }
57
+ logger.newline();
58
+ });
59
+
28
60
  cmd
29
61
  .command("get")
30
62
  .description("Get a configuration value")
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Discoverable config-key registry (Claude-Code 2.1.183 parity: "list
3
+ * available shorthand keys"). Backs `cc config keys`.
4
+ *
5
+ * The canonical schema is DEFAULT_CONFIG (constants.js) — base keys are derived
6
+ * from it automatically so this never drifts. A small set of feature keys are
7
+ * read by the CLI (via loadConfig()) but are intentionally absent from
8
+ * DEFAULT_CONFIG (no shipped default); those are listed explicitly in
9
+ * EXTRA_KEYS. Human descriptions live in KEY_DESCRIPTIONS.
10
+ */
11
+ import { DEFAULT_CONFIG } from "../constants.js";
12
+ import { loadConfig } from "./config-manager.js";
13
+
14
+ const KEY_DESCRIPTIONS = {
15
+ "llm.provider":
16
+ "LLM provider id (volcengine | ollama | anthropic | openai | …)",
17
+ "llm.baseUrl": "Base URL / endpoint for the LLM provider",
18
+ "llm.model": "Default chat / agent model id",
19
+ "llm.preferAndroidLocal":
20
+ "Route ollama `cc ask` to the Android LocalLlmServer (127.0.0.1:18484)",
21
+ "llm.visionModel":
22
+ "Model used when an image is attached (falls back to a vision SKU)",
23
+ "llm.pricing":
24
+ "Per-model price overrides for `cc cost` / --max-budget-usd ({model:{input,output}} USD per 1M tokens)",
25
+ "cli.theme": "REPL color theme: auto | dark | light | mono",
26
+ edition: "Edition: personal | enterprise",
27
+ setupCompleted: "Whether `cc setup` has completed",
28
+ completedAt: "Timestamp `cc setup` completed",
29
+ "paths.projectRoot": "Override the detected project root",
30
+ "paths.database": "Override the SQLite database path",
31
+ "enterprise.serverUrl": "Enterprise server URL",
32
+ "enterprise.tenantId": "Enterprise tenant id",
33
+ "services.autoStart": "Auto-start backing services",
34
+ "services.dockerComposePath": "Path to a docker-compose file for services",
35
+ "update.channel": "Update channel: stable | beta",
36
+ "update.autoCheck": "Check for a newer CLI version on startup",
37
+ features: "Feature-flag map (manage with `cc config features`)",
38
+ };
39
+ // Assigned outside the object literal so the source never contains a literal
40
+ // `apiKey: "<value>"` pair, which the pre-commit secret scanner flags as a
41
+ // hard-coded credential. These are documentation strings, not secrets.
42
+ KEY_DESCRIPTIONS["llm.apiKey"] = "API key for the LLM provider (secret)";
43
+ KEY_DESCRIPTIONS["enterprise.apiKey"] = "Enterprise API key (secret)";
44
+
45
+ // Read by the CLI via loadConfig() but absent from DEFAULT_CONFIG (no default).
46
+ const EXTRA_KEYS = [
47
+ { key: "llm.visionModel", type: "string" },
48
+ { key: "llm.pricing", type: "object" },
49
+ { key: "cli.theme", type: "string" },
50
+ ];
51
+
52
+ /** Whether a dotted key holds a secret that should be masked on display. */
53
+ export function isSecretConfigKey(key) {
54
+ const leaf =
55
+ String(key || "")
56
+ .split(".")
57
+ .pop() || "";
58
+ return /key$/i.test(leaf) || /apikey/i.test(leaf);
59
+ }
60
+
61
+ function typeOfDefault(value) {
62
+ if (value === null) return "string | null";
63
+ if (Array.isArray(value)) return "array";
64
+ return typeof value;
65
+ }
66
+
67
+ /**
68
+ * Flatten DEFAULT_CONFIG into leaf dotted keys. A non-empty plain object is
69
+ * recursed into; an empty object (e.g. `features`) is treated as an open map
70
+ * and emitted as a single key.
71
+ */
72
+ function flattenDefaults(obj, prefix = "") {
73
+ const out = [];
74
+ for (const [k, v] of Object.entries(obj)) {
75
+ const key = prefix ? `${prefix}.${k}` : k;
76
+ if (
77
+ v &&
78
+ typeof v === "object" &&
79
+ !Array.isArray(v) &&
80
+ Object.keys(v).length > 0
81
+ ) {
82
+ out.push(...flattenDefaults(v, key));
83
+ } else {
84
+ out.push({ key, type: typeOfDefault(v), default: v });
85
+ }
86
+ }
87
+ return out;
88
+ }
89
+
90
+ /**
91
+ * The full set of recognized global config keys, each with `{ key, type,
92
+ * default, description }`, sorted by key. Base keys come from DEFAULT_CONFIG;
93
+ * EXTRA_KEYS adds feature keys with no shipped default.
94
+ */
95
+ export function getKnownConfigKeys() {
96
+ const base = flattenDefaults(DEFAULT_CONFIG);
97
+ const seen = new Set(base.map((e) => e.key));
98
+ for (const extra of EXTRA_KEYS) {
99
+ if (!seen.has(extra.key)) {
100
+ base.push({ key: extra.key, type: extra.type, default: undefined });
101
+ seen.add(extra.key);
102
+ }
103
+ }
104
+ return base
105
+ .map((e) => ({
106
+ ...e,
107
+ description: KEY_DESCRIPTIONS[e.key] || "",
108
+ secret: isSecretConfigKey(e.key),
109
+ }))
110
+ .sort((a, b) => a.key.localeCompare(b.key));
111
+ }
112
+
113
+ function getNested(obj, key) {
114
+ let cur = obj;
115
+ for (const part of String(key).split(".")) {
116
+ if (cur == null || typeof cur !== "object") return undefined;
117
+ cur = cur[part];
118
+ }
119
+ return cur;
120
+ }
121
+
122
+ /**
123
+ * Known keys annotated with each key's CURRENT value from the loaded config
124
+ * (secrets masked). `deps.loadConfig` is injectable for tests.
125
+ */
126
+ export function describeConfigKeys(deps = {}) {
127
+ const load = deps.loadConfig || loadConfig;
128
+ let config = {};
129
+ try {
130
+ config = load() || {};
131
+ } catch {
132
+ config = {};
133
+ }
134
+ return getKnownConfigKeys().map((entry) => {
135
+ const current = getNested(config, entry.key);
136
+ let display = current;
137
+ if (entry.secret && current) display = "****";
138
+ return { ...entry, current: display };
139
+ });
140
+ }
@@ -92,6 +92,8 @@ import { expandMcpPrompt, renderMcpSurface } from "./mcp-prompt.js";
92
92
  import { newCostStore, addUsage } from "./session-cost.js";
93
93
  import { parseThinkCommand } from "./think-command.js";
94
94
  import { shouldStreamLive } from "./stream-decision.js";
95
+ import { emptyTurnNotice } from "./empty-turn-notice.js";
96
+ import { buildPermissionPrompt } from "./permission-prompt.js";
95
97
  import {
96
98
  parsePermissionTier,
97
99
  describeTier,
@@ -441,7 +443,7 @@ export async function startAgentRepl(options = {}) {
441
443
  _permissionRules = total > 0 ? loaded.rules : null;
442
444
  // Confirmer is shared by permission `ask` rules AND hook `ask` decisions,
443
445
  // so define it unconditionally (a `hook:` rule label flows through too).
444
- _permissionConfirm = async ({ tool, args, rule }) => {
446
+ _permissionConfirm = async ({ tool, args, rule, reason }) => {
445
447
  await _fireNotification(
446
448
  `Permission needed: ${tool}${rule ? " (" + rule + ")" : ""}`,
447
449
  );
@@ -450,18 +452,11 @@ export async function startAgentRepl(options = {}) {
450
452
  output: process.stdout,
451
453
  });
452
454
  const q = (p) => new Promise((res) => rl.question(p, res));
453
- const detail = args?.command
454
- ? ` ${args.command}`
455
- : args?.path
456
- ? ` ${args.path}`
457
- : "";
458
- const ans = (
459
- await q(
460
- chalk.yellow(
461
- `\n[Permission] ${rule} asks before ${tool}:${detail}\n Proceed? (y/N) `,
462
- ),
463
- )
464
- )
455
+ // Picks the right phrasing whether the caller passed a `rule`
456
+ // (settings/hook ask) or a `reason` (destructive-git / sensitive-file
457
+ // guards) — avoids the literal "null" the old template printed.
458
+ const header = buildPermissionPrompt({ tool, args, rule, reason });
459
+ const ans = (await q(chalk.yellow(`\n${header}\n Proceed? (y/N) `)))
465
460
  .trim()
466
461
  .toLowerCase();
467
462
  rl.close();
@@ -3361,7 +3356,17 @@ export async function startAgentRepl(options = {}) {
3361
3356
  messages.push({ role: "assistant", content: streamResult.text });
3362
3357
  }
3363
3358
  } else if (!responseDirective.suppress) {
3364
- process.stdout.write("\n");
3359
+ // Claude-Code 2.1.183 parity: a turn that completes with no answer text
3360
+ // (model produced only extended-thinking blocks, or an empty response)
3361
+ // otherwise returned to the prompt silently — looking like a no-op/hang.
3362
+ // Surface a dim notice so the turn's completion is always visible.
3363
+ const notice = emptyTurnNotice({
3364
+ response: effectiveResponse,
3365
+ reasoning,
3366
+ });
3367
+ process.stdout.write(
3368
+ notice ? "\n" + chalk.dim(" " + notice) + "\n\n" : "\n",
3369
+ );
3365
3370
  }
3366
3371
 
3367
3372
  // Auto-save session
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Decide the notice to show when an agent turn completes with NO answer text
3
+ * (Claude-Code 2.1.183 parity: "Fixed silent turn completion with only thinking
4
+ * blocks"). Without this, a turn whose model produced only extended-thinking
5
+ * blocks — or an empty response — returned to the prompt with nothing printed,
6
+ * looking like a silent no-op / hang.
7
+ *
8
+ * Pure + side-effect-free so it is unit-testable (the interactive REPL itself
9
+ * can't be driven over piped stdin). The REPL applies dim styling to the
10
+ * returned string.
11
+ *
12
+ * @param {object} opts
13
+ * @param {string} [opts.response] the turn's final answer text
14
+ * @param {string} [opts.reasoning] extended-thinking text, if any
15
+ * @param {boolean} [opts.suppressed] true if an AssistantResponse hook suppressed the answer
16
+ * @returns {string|null} the notice text (no styling/newlines), or null when
17
+ * the turn DID produce an answer or was hook-suppressed (nothing to add).
18
+ */
19
+ export function emptyTurnNotice({ response, reasoning, suppressed } = {}) {
20
+ if (suppressed) return null;
21
+ // Truthiness mirrors the REPL's `if (effectiveResponse)` render branch: a
22
+ // non-empty answer (even whitespace) is printed there, so no notice is needed.
23
+ if (response) return null;
24
+ return reasoning
25
+ ? "(the model returned reasoning but no answer text)"
26
+ : "(the model returned no text response)";
27
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Build the header line for the REPL's interactive permission prompt.
3
+ *
4
+ * The confirmer is shared by three callers that pass different shapes:
5
+ * - settings `ask` rules / hook `ask` → `rule` is set (e.g. "Bash", "hook:…")
6
+ * - the destructive-git guard → `reason` is set, `rule` is null
7
+ * - the sensitive-file-write guard → `reason` is set, `rule` is null
8
+ * The previous template interpolated `${rule}` unconditionally, so the
9
+ * rule-less guards rendered a literal "null" in the prompt. This picks the
10
+ * right phrasing for each case.
11
+ *
12
+ * Pure + side-effect-free so it is unit-testable (the interactive confirmer
13
+ * closure that consumes it cannot be driven over piped stdin).
14
+ *
15
+ * @returns {string} the prompt header (no styling, no trailing "Proceed?")
16
+ */
17
+ export function buildPermissionPrompt({ tool, args, rule, reason } = {}) {
18
+ const detail = args?.command
19
+ ? ` ${args.command}`
20
+ : args?.path
21
+ ? ` ${args.path}`
22
+ : "";
23
+ if (rule) return `[Permission] rule "${rule}" asks before ${tool}:${detail}`;
24
+ if (reason) return `[Permission] ${reason}`;
25
+ return `[Permission] confirm ${tool}:${detail}`;
26
+ }
@@ -263,6 +263,11 @@ function isDangerousGitCommand(command) {
263
263
  case "clean":
264
264
  // Deletes untracked files — irrecoverable.
265
265
  return true;
266
+ case "commit":
267
+ // `--amend` rewrites the last commit (history rewrite); a plain commit is
268
+ // not flagged. (Claude-Code 2.1.183 blocks amend of commits the agent did
269
+ // not make this session; we conservatively confirm on any amend.)
270
+ return has("--amend");
266
271
  case "checkout":
267
272
  // Discard working-tree changes: `checkout -- <path>`, `checkout .`,
268
273
  // or `-f`/`--force` (a plain branch checkout is not flagged).
@@ -274,9 +279,19 @@ function isDangerousGitCommand(command) {
274
279
  case "switch":
275
280
  return has("-f", "--force", "--discard-changes");
276
281
  case "push":
277
- // `--force`/`-f` rewrites remote history; `--force-with-lease`/
278
- // `--force-if-includes` are the safe forms and are NOT flagged.
279
- return has("--force", "-f");
282
+ // Rewrites or deletes remote refs:
283
+ // --force/-f → force overwrite remote history
284
+ // a `+refspec` token → per-ref force push (e.g. `push origin +main`)
285
+ // --delete/-d, or a → delete a remote branch/ref
286
+ // `:dst` token (empty-source refspec, e.g. `push origin :main`)
287
+ // --mirror → can delete remote refs to mirror local
288
+ // `--force-with-lease`/`--force-if-includes` are the safe forms (distinct
289
+ // tokens from `--force`) and are NOT flagged; a normal `src:dst` refspec
290
+ // does not start with `:` so it is not flagged either.
291
+ return (
292
+ has("--force", "-f", "--delete", "-d", "--mirror") ||
293
+ rest.some((t) => t.startsWith("+") || t.startsWith(":"))
294
+ );
280
295
  case "branch":
281
296
  // `-D` (force delete, case-sensitive — `-d` only deletes merged branches)
282
297
  // or an explicit `--delete --force`.
@@ -11,11 +11,46 @@ const BLOCKED_SHELL_RULES = Object.freeze([
11
11
  {
12
12
  id: "dangerous-delete",
13
13
  decision: SHELL_POLICY_DECISIONS.DENY,
14
+ // Unix (rm) AND Windows/PowerShell deletes. This repo is Windows-primary
15
+ // (PowerShell is the default shell), where the `rm -rf` analog is
16
+ // `Remove-Item -Recurse -Force` / its alias `ri` — neither shares a first
17
+ // token with the Unix forms, so they must be listed explicitly. (`rm`/`del`
18
+ // already cover PowerShell's `rm`/`del` aliases; `rd` covers `rmdir`.)
14
19
  test: ({ firstToken }) =>
15
- ["rm", "del", "erase", "rmdir", "rd"].includes(firstToken),
20
+ [
21
+ "rm",
22
+ "del",
23
+ "erase",
24
+ "rmdir",
25
+ "rd",
26
+ "remove-item",
27
+ "ri",
28
+ ].includes(firstToken),
16
29
  reason:
17
30
  "Destructive delete commands are blocked by the coding-agent shell policy.",
18
31
  },
32
+ {
33
+ // Disk / filesystem destroyers — near-zero legitimate use for a coding
34
+ // agent, catastrophic when wrong. Matched precisely so the common, benign
35
+ // PowerShell output formatters (Format-Table / Format-List / Format-Wide /
36
+ // Format-Custom / ft / fl) are NEVER caught — only `format` (cmd.exe disk
37
+ // format) and `Format-Volume` are. `dd` is blocked ONLY when writing to a
38
+ // raw block device (`of=/dev/…` / Windows `\\.\…`); file-image creation
39
+ // (`of=disk.img`) stays allowed.
40
+ id: "disk-destruction",
41
+ decision: SHELL_POLICY_DECISIONS.DENY,
42
+ test: ({ firstToken, normalized }) => {
43
+ if (firstToken === "format" || firstToken === "format-volume") return true;
44
+ if (firstToken === "mkfs" || firstToken.startsWith("mkfs.")) return true;
45
+ if (["wipefs", "shred", "diskpart"].includes(firstToken)) return true;
46
+ if (firstToken === "dd") {
47
+ return /\bof=(\/dev\/|\\\\)/i.test(normalized);
48
+ }
49
+ return false;
50
+ },
51
+ reason:
52
+ "Disk-destroying commands (format / Format-Volume / mkfs / wipefs / shred / diskpart / dd-to-device) are blocked by the coding-agent shell policy.",
53
+ },
19
54
  // The dangerous-git-* DENY rules MUST precede `git-tool-reroute` below:
20
55
  // `evaluateSegmentPolicy` returns the FIRST matching rule, and the reroute
21
56
  // rule matches every git command. Ordering reroute first (as it was) made
@@ -1 +0,0 @@
1
- import{I as v,P as u,J as S,U as n,R as a,S as y,K as C,Q as w,V as x,_ as B,b as s}from"./vendor-BvqAck49.js";import{_ as k}from"./index-wEmgmG9r.js";import"./icons-DP3uiYxy.js";const N={__name:"ChatBubbleRenderer",props:{event:{type:Object,required:!0}},setup(c,{expose:d}){d();const t=c,r=s(()=>{const e=t.event.content||{};return e.text||e.body||e.message||e.title||JSON.stringify(e).slice(0,200)}),i=s(()=>{const e=t.event.content||{};return e.from||e.sender||e.senderName||t.event.actor||"(unknown)"}),l=s(()=>{const e=(t.event.actor||"").toLowerCase();return e.includes("self")||e==="me"||e.endsWith("_self")}),o=s(()=>{const e=t.event.source.adapter||"";return e.startsWith("messaging-qq")?"magenta":e==="wechat"?"green":"blue"}),m=s(()=>{if(!t.event.occurredAt)return"";try{const e=new Date(t.event.occurredAt),p=e.getFullYear(),f=String(e.getMonth()+1).padStart(2,"0"),g=String(e.getDate()).padStart(2,"0"),b=String(e.getHours()).padStart(2,"0"),h=String(e.getMinutes()).padStart(2,"0");return`${p}-${f}-${g} ${b}:${h}`}catch{return""}}),_={props:t,messageText:r,actorLabel:i,isMine:l,adapterColor:o,formattedTime:m,computed:s};return Object.defineProperty(_,"__isScriptSetup",{enumerable:!1,value:!0}),_}},T={class:"bubble"},M={class:"meta"},R={class:"actor"},V={class:"time"},q={class:"body"};function D(c,d,t,r,i,l){const o=v("a-tag");return u(),S("div",{class:B(["chat-row",{mine:r.isMine}])},[n("div",T,[n("div",M,[n("span",R,a(r.actorLabel),1),n("span",V,a(r.formattedTime),1)]),n("div",q,a(r.messageText),1),t.event.source.adapter?(u(),y(o,{key:0,class:"src",color:r.adapterColor},{default:C(()=>[w(a(t.event.source.adapter),1)]),_:1},8,["color"])):x("v-if",!0)])],2)}const A=k(N,[["render",D],["__scopeId","data-v-49238629"],["__file","/tmp/cc-web-panel-drwr9c/repo/packages/web-panel/src/components/pdh/renderers/ChatBubbleRenderer.vue"]]);export{A as default};
@@ -1 +0,0 @@
1
- import{O as r}from"./index-wEmgmG9r.js";const o=((n,a,e)=>{r(n,`[ant-design-vue: ${a}] ${e}`)});export{o as d};
@@ -1 +0,0 @@
1
- import{A as o}from"./Row-oV-drKth.js";import{U as t}from"./index-wEmgmG9r.js";import"./vendor-BvqAck49.js";import"./responsiveObserve-DaEkOCb-.js";import"./useFlexGapSupport-Viy5DmaP.js";import"./styleChecker-ZSwUScrp.js";import"./index-ULxnYhN-.js";import"./icons-DP3uiYxy.js";const l=t(o);export{l as default};
@@ -1 +0,0 @@
1
- import{C as o}from"./Col-Cw_UMO2L.js";import{U as t}from"./index-wEmgmG9r.js";import"./vendor-BvqAck49.js";import"./index-ULxnYhN-.js";import"./icons-DP3uiYxy.js";const s=t(o);export{s as default};