kimi-plugin-cc 2.0.7

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 (190) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.claude-plugin/marketplace.json +16 -0
  3. package/.claude-plugin/plugin.json +32 -0
  4. package/CHANGELOG.md +1096 -0
  5. package/LICENSE +201 -0
  6. package/README.md +772 -0
  7. package/agents/kimi-ask.md +82 -0
  8. package/agents/kimi-challenge.md +82 -0
  9. package/agents/kimi-pursue.md +91 -0
  10. package/agents/kimi-rescue.md +85 -0
  11. package/agents/kimi-review.md +82 -0
  12. package/agents/kimi-swarm-write.md +92 -0
  13. package/agents/kimi-swarm.md +83 -0
  14. package/commands/README.md +16 -0
  15. package/commands/ask.md +32 -0
  16. package/commands/cancel.md +17 -0
  17. package/commands/challenge.md +29 -0
  18. package/commands/pursue.md +37 -0
  19. package/commands/replay.md +13 -0
  20. package/commands/rescue.md +32 -0
  21. package/commands/result.md +13 -0
  22. package/commands/review.md +29 -0
  23. package/commands/setup.md +23 -0
  24. package/commands/status.md +13 -0
  25. package/commands/swarm.md +38 -0
  26. package/dist/background-spawn.js +224 -0
  27. package/dist/cli-cancellation.js +43 -0
  28. package/dist/cli-client.js +845 -0
  29. package/dist/commands/ask.js +327 -0
  30. package/dist/commands/cancel.js +178 -0
  31. package/dist/commands/cli-helpers.js +112 -0
  32. package/dist/commands/models.js +140 -0
  33. package/dist/commands/not-implemented.js +4 -0
  34. package/dist/commands/pursue.js +311 -0
  35. package/dist/commands/registry.js +87 -0
  36. package/dist/commands/repair-sessions.js +159 -0
  37. package/dist/commands/replay.js +123 -0
  38. package/dist/commands/rescue.js +333 -0
  39. package/dist/commands/result.js +58 -0
  40. package/dist/commands/review-gate.js +393 -0
  41. package/dist/commands/review.js +218 -0
  42. package/dist/commands/setup.js +1132 -0
  43. package/dist/commands/status.js +27 -0
  44. package/dist/commands/swarm.js +576 -0
  45. package/dist/commands/worker.js +31 -0
  46. package/dist/companion.js +114 -0
  47. package/dist/config.js +22 -0
  48. package/dist/errors.js +40 -0
  49. package/dist/git.js +215 -0
  50. package/dist/hooks/approval-hook.js +112 -0
  51. package/dist/hooks/approval-policy.js +254 -0
  52. package/dist/hooks/config-safety.js +637 -0
  53. package/dist/hooks/install-paths.js +504 -0
  54. package/dist/hooks/install.js +281 -0
  55. package/dist/hooks/managed-block.js +745 -0
  56. package/dist/hooks/review-gate-stop.js +19 -0
  57. package/dist/job-store.js +559 -0
  58. package/dist/jobs.js +202 -0
  59. package/dist/kimi-command.js +72 -0
  60. package/dist/kimi-engine.js +624 -0
  61. package/dist/kimi-errors.js +220 -0
  62. package/dist/kimi-home.js +9 -0
  63. package/dist/kimi-timeouts.js +68 -0
  64. package/dist/kimi-version-probe.js +1410 -0
  65. package/dist/logging.js +22 -0
  66. package/dist/native-v2-preflight.js +417 -0
  67. package/dist/package.json +1 -0
  68. package/dist/parsing.js +553 -0
  69. package/dist/paths.js +27 -0
  70. package/dist/plugin-data.js +98 -0
  71. package/dist/process-tree.js +448 -0
  72. package/dist/render.js +159 -0
  73. package/dist/rescue-approval.js +951 -0
  74. package/dist/schemas/review-gate-output.js +43 -0
  75. package/dist/session-title.js +293 -0
  76. package/dist/stream-json.js +498 -0
  77. package/dist/types.js +1 -0
  78. package/dist/vendor/shell-quote/LICENSE +24 -0
  79. package/dist/vendor/shell-quote/index.js +1 -0
  80. package/dist/vendor/shell-quote/parse.js +229 -0
  81. package/dist/vendor/smol-toml/LICENSE +24 -0
  82. package/dist/vendor/smol-toml/date.js +127 -0
  83. package/dist/vendor/smol-toml/error.js +63 -0
  84. package/dist/vendor/smol-toml/extract.js +100 -0
  85. package/dist/vendor/smol-toml/parse.js +148 -0
  86. package/dist/vendor/smol-toml/primitive.js +179 -0
  87. package/dist/vendor/smol-toml/struct.js +184 -0
  88. package/dist/vendor/smol-toml/util.js +113 -0
  89. package/dist/version.js +7 -0
  90. package/docs/migration.md +215 -0
  91. package/docs/models.md +82 -0
  92. package/docs/registry-releases.md +70 -0
  93. package/docs/safety.md +318 -0
  94. package/docs/session-visibility.md +32 -0
  95. package/hooks/README.md +9 -0
  96. package/hooks/hooks.json +16 -0
  97. package/package.json +67 -0
  98. package/plugins/kimi-codex/.codex-plugin/plugin.json +39 -0
  99. package/plugins/kimi-codex/dist/background-spawn.js +224 -0
  100. package/plugins/kimi-codex/dist/cli-cancellation.js +43 -0
  101. package/plugins/kimi-codex/dist/cli-client.js +845 -0
  102. package/plugins/kimi-codex/dist/commands/ask.js +327 -0
  103. package/plugins/kimi-codex/dist/commands/cancel.js +178 -0
  104. package/plugins/kimi-codex/dist/commands/cli-helpers.js +112 -0
  105. package/plugins/kimi-codex/dist/commands/models.js +140 -0
  106. package/plugins/kimi-codex/dist/commands/not-implemented.js +4 -0
  107. package/plugins/kimi-codex/dist/commands/pursue.js +311 -0
  108. package/plugins/kimi-codex/dist/commands/registry.js +87 -0
  109. package/plugins/kimi-codex/dist/commands/repair-sessions.js +159 -0
  110. package/plugins/kimi-codex/dist/commands/replay.js +123 -0
  111. package/plugins/kimi-codex/dist/commands/rescue.js +333 -0
  112. package/plugins/kimi-codex/dist/commands/result.js +58 -0
  113. package/plugins/kimi-codex/dist/commands/review-gate.js +393 -0
  114. package/plugins/kimi-codex/dist/commands/review.js +218 -0
  115. package/plugins/kimi-codex/dist/commands/setup.js +1132 -0
  116. package/plugins/kimi-codex/dist/commands/status.js +27 -0
  117. package/plugins/kimi-codex/dist/commands/swarm.js +576 -0
  118. package/plugins/kimi-codex/dist/commands/worker.js +31 -0
  119. package/plugins/kimi-codex/dist/companion.js +114 -0
  120. package/plugins/kimi-codex/dist/config.js +22 -0
  121. package/plugins/kimi-codex/dist/errors.js +40 -0
  122. package/plugins/kimi-codex/dist/git.js +215 -0
  123. package/plugins/kimi-codex/dist/hooks/approval-hook.js +112 -0
  124. package/plugins/kimi-codex/dist/hooks/approval-policy.js +254 -0
  125. package/plugins/kimi-codex/dist/hooks/config-safety.js +637 -0
  126. package/plugins/kimi-codex/dist/hooks/install-paths.js +504 -0
  127. package/plugins/kimi-codex/dist/hooks/install.js +281 -0
  128. package/plugins/kimi-codex/dist/hooks/managed-block.js +745 -0
  129. package/plugins/kimi-codex/dist/hooks/review-gate-stop.js +19 -0
  130. package/plugins/kimi-codex/dist/job-store.js +559 -0
  131. package/plugins/kimi-codex/dist/jobs.js +202 -0
  132. package/plugins/kimi-codex/dist/kimi-command.js +72 -0
  133. package/plugins/kimi-codex/dist/kimi-engine.js +624 -0
  134. package/plugins/kimi-codex/dist/kimi-errors.js +220 -0
  135. package/plugins/kimi-codex/dist/kimi-home.js +9 -0
  136. package/plugins/kimi-codex/dist/kimi-timeouts.js +68 -0
  137. package/plugins/kimi-codex/dist/kimi-version-probe.js +1410 -0
  138. package/plugins/kimi-codex/dist/logging.js +22 -0
  139. package/plugins/kimi-codex/dist/native-v2-preflight.js +417 -0
  140. package/plugins/kimi-codex/dist/package.json +1 -0
  141. package/plugins/kimi-codex/dist/parsing.js +553 -0
  142. package/plugins/kimi-codex/dist/paths.js +27 -0
  143. package/plugins/kimi-codex/dist/plugin-data.js +98 -0
  144. package/plugins/kimi-codex/dist/process-tree.js +448 -0
  145. package/plugins/kimi-codex/dist/render.js +159 -0
  146. package/plugins/kimi-codex/dist/rescue-approval.js +951 -0
  147. package/plugins/kimi-codex/dist/schemas/review-gate-output.js +43 -0
  148. package/plugins/kimi-codex/dist/session-title.js +293 -0
  149. package/plugins/kimi-codex/dist/stream-json.js +498 -0
  150. package/plugins/kimi-codex/dist/types.js +1 -0
  151. package/plugins/kimi-codex/dist/vendor/shell-quote/LICENSE +24 -0
  152. package/plugins/kimi-codex/dist/vendor/shell-quote/index.js +1 -0
  153. package/plugins/kimi-codex/dist/vendor/shell-quote/parse.js +229 -0
  154. package/plugins/kimi-codex/dist/vendor/smol-toml/LICENSE +24 -0
  155. package/plugins/kimi-codex/dist/vendor/smol-toml/date.js +127 -0
  156. package/plugins/kimi-codex/dist/vendor/smol-toml/error.js +63 -0
  157. package/plugins/kimi-codex/dist/vendor/smol-toml/extract.js +100 -0
  158. package/plugins/kimi-codex/dist/vendor/smol-toml/parse.js +148 -0
  159. package/plugins/kimi-codex/dist/vendor/smol-toml/primitive.js +179 -0
  160. package/plugins/kimi-codex/dist/vendor/smol-toml/struct.js +184 -0
  161. package/plugins/kimi-codex/dist/vendor/smol-toml/util.js +113 -0
  162. package/plugins/kimi-codex/dist/version.js +7 -0
  163. package/plugins/kimi-codex/scripts/companion.sh +37 -0
  164. package/plugins/kimi-codex/scripts/review-gate-hook.sh +32 -0
  165. package/plugins/kimi-codex/skills/kimi-ask/SKILL.md +31 -0
  166. package/plugins/kimi-codex/skills/kimi-ask/agents/openai.yaml +6 -0
  167. package/plugins/kimi-codex/skills/kimi-cancel/SKILL.md +25 -0
  168. package/plugins/kimi-codex/skills/kimi-cancel/agents/openai.yaml +6 -0
  169. package/plugins/kimi-codex/skills/kimi-challenge/SKILL.md +31 -0
  170. package/plugins/kimi-codex/skills/kimi-challenge/agents/openai.yaml +6 -0
  171. package/plugins/kimi-codex/skills/kimi-pursue/SKILL.md +30 -0
  172. package/plugins/kimi-codex/skills/kimi-pursue/agents/openai.yaml +6 -0
  173. package/plugins/kimi-codex/skills/kimi-replay/SKILL.md +25 -0
  174. package/plugins/kimi-codex/skills/kimi-replay/agents/openai.yaml +6 -0
  175. package/plugins/kimi-codex/skills/kimi-rescue/SKILL.md +30 -0
  176. package/plugins/kimi-codex/skills/kimi-rescue/agents/openai.yaml +6 -0
  177. package/plugins/kimi-codex/skills/kimi-result/SKILL.md +25 -0
  178. package/plugins/kimi-codex/skills/kimi-result/agents/openai.yaml +6 -0
  179. package/plugins/kimi-codex/skills/kimi-review/SKILL.md +31 -0
  180. package/plugins/kimi-codex/skills/kimi-review/agents/openai.yaml +6 -0
  181. package/plugins/kimi-codex/skills/kimi-setup/SKILL.md +32 -0
  182. package/plugins/kimi-codex/skills/kimi-setup/agents/openai.yaml +6 -0
  183. package/plugins/kimi-codex/skills/kimi-status/SKILL.md +25 -0
  184. package/plugins/kimi-codex/skills/kimi-status/agents/openai.yaml +6 -0
  185. package/plugins/kimi-codex/skills/kimi-swarm/SKILL.md +31 -0
  186. package/plugins/kimi-codex/skills/kimi-swarm/agents/openai.yaml +6 -0
  187. package/plugins/kimi-codex/skills/kimi-swarm-write/SKILL.md +32 -0
  188. package/plugins/kimi-codex/skills/kimi-swarm-write/agents/openai.yaml +6 -0
  189. package/scripts/companion.sh +37 -0
  190. package/scripts/review-gate-hook.sh +32 -0
@@ -0,0 +1,254 @@
1
+ // Pure decision logic for the kimi-code PreToolUse hook.
2
+ //
3
+ // Why this module exists separately from the entry script:
4
+ //
5
+ // The hook script (approval-hook.ts) handles stdin / stdout / exit
6
+ // semantics. This module is a pure function — easy to unit-test, easy
7
+ // to import from the entry script and from PR 3's rescue dispatcher
8
+ // without forcing those callers to mock stdin.
9
+ //
10
+ // Why the hook is load-bearing for safety:
11
+ //
12
+ // kimi-code's `kimi -p` mode hard-codes `permission: 'auto'` and
13
+ // registers an auto-approve handler that allows every tool. The
14
+ // PreToolUse hook fires BEFORE permission rules (verified in
15
+ // agent-core/src/agent/turn/index.ts:436-458). For read-only commands
16
+ // (review/challenge/review_gate/ask), the hook is the only mechanism
17
+ // that overrides -p's auto-approve and enforces the safety contract
18
+ // advertised by /kimi:review, /kimi:challenge, etc.
19
+ //
20
+ // Fail-closed posture:
21
+ //
22
+ // Unknown command labels deny anything but Read/Grep/Glob. A
23
+ // KIMI_PLUGIN_CC_CMD env var someone forgot to set is the "out of
24
+ // plugin context" case (treated as allow); a label we don't recognize
25
+ // is the "stale config / misconfigured caller" case (treated as deny
26
+ // by default).
27
+ /**
28
+ * Tools considered read-only across review/challenge/review_gate/rescue.
29
+ *
30
+ * Includes kimi-code's own local-read defaults (`Read`, `Grep`, `Glob`)
31
+ * plus a few additional read-safe tools the runtime auto-allows
32
+ * (`ReadMediaFile`, `TaskList`, `TaskOutput`). Excludes network
33
+ * operations (`WebSearch`, `FetchURL`) since review-style commands
34
+ * should stay on the local repo — adding them would silently broaden
35
+ * the safety contract for what is documented as a local-only review.
36
+ */
37
+ export const READ_ONLY_TOOLS = new Set([
38
+ "Read",
39
+ "Grep",
40
+ "Glob",
41
+ "ReadMediaFile",
42
+ "TaskList",
43
+ "TaskOutput",
44
+ ]);
45
+ /**
46
+ * kimi-code's parallel fan-out tool (PR #424, 0.12.0). Its exact tool_name
47
+ * string is `AgentSwarm` (verified against
48
+ * packages/agent-core/src/tools/builtin/collaboration/agent-swarm.ts:87,
49
+ * `readonly name = 'AgentSwarm' as const`). The `/kimi:swarm` (read-only)
50
+ * label allowlists THIS tool so the parent agent can launch the swarm; every
51
+ * spawned subagent inherits the same KIMI_PLUGIN_CC_CMD label and fires THIS
52
+ * hook at permission policy index 0 (kimi-code
53
+ * createPermissionDecisionPolicies puts PreToolCallHookPermissionPolicy at
54
+ * index 0 for ALL agents — no sub-vs-main branch — so a subagent's write is
55
+ * denied exactly like a single-turn review's). The singular `Agent` tool is
56
+ * deliberately NOT allowlisted: swarm is specifically the fan-out surface.
57
+ */
58
+ const AGENT_SWARM_TOOL = "AgentSwarm";
59
+ /**
60
+ * agent-core-v2's ONLY chain-breaking final allow is the plan-file guard, and
61
+ * it fires only while plan mode is ACTIVE. Native v2 is therefore certified on
62
+ * the invariant that plan mode is never armed in a plugin-managed session:
63
+ * `default_plan_mode` is refused pre-spawn (runtime/native-v2-preflight.ts),
64
+ * plan-tainted sessions are never resumed, and the model-reachable entry tool
65
+ * is denied HERE for every label. ExitPlanMode is denied too so the deny set
66
+ * is explicit rather than a side effect of the read-only allowlists.
67
+ */
68
+ const PLAN_MODE_TOOLS = new Set(["EnterPlanMode", "ExitPlanMode"]);
69
+ /**
70
+ * Recognized command labels. Sole source of truth; keep in sync with
71
+ * the strings emitted by `runCliPrompt` callers in
72
+ * `runtime/commands/*.ts`.
73
+ */
74
+ const KNOWN_LABELS = new Set([
75
+ "ask",
76
+ "review",
77
+ "challenge",
78
+ "review_gate",
79
+ "rescue",
80
+ "swarm",
81
+ "swarm-write",
82
+ ]);
83
+ export async function decideHookOutcome(input, ctx) {
84
+ const label = typeof ctx.commandLabel === "string" ? ctx.commandLabel : undefined;
85
+ // Out-of-plugin context: any kimi process spawned without our env var
86
+ // is direct user usage. The hook MUST NOT restrict it.
87
+ if (label === undefined || label.length === 0) {
88
+ return { decision: "allow" };
89
+ }
90
+ const toolName = typeof input.tool_name === "string" ? input.tool_name : "";
91
+ if (PLAN_MODE_TOOLS.has(toolName)) {
92
+ return {
93
+ decision: "deny",
94
+ reason: `kimi-plugin-cc safety hook: tool "${toolName}" is denied for plugin-managed sessions because agent-core-v2 plan-file writes can bypass later external hooks; native plan mode is never armed in plugin-managed sessions.`,
95
+ };
96
+ }
97
+ if (["GetGoal", "UpdateGoal", "CreateGoal", "SetGoalBudget"].includes(toolName)) {
98
+ // Pursue keeps the rescue write boundary, but must be able to inspect and
99
+ // terminally settle its existing goal. Never allow creating/reactivating a
100
+ // goal or changing its budget through this metadata-only exception.
101
+ const args = input.tool_input;
102
+ const objectArgs = typeof args === "object" && args !== null && !Array.isArray(args) &&
103
+ (Object.getPrototypeOf(args) === Object.prototype || Object.getPrototypeOf(args) === null);
104
+ const keys = objectArgs ? Reflect.ownKeys(args) : [];
105
+ const validArgs = objectArgs && ((toolName === "GetGoal" && keys.length === 0) ||
106
+ (toolName === "UpdateGoal" && keys.length === 1 && keys[0] === "status" &&
107
+ (args.status === "complete" || args.status === "blocked")));
108
+ if (label === "rescue" && ctx.operationKind === "pursue" &&
109
+ typeof ctx.trustedWorkspaceRoot === "string" && ctx.trustedWorkspaceRoot.trim().length > 0 && validArgs) {
110
+ return { decision: "allow" };
111
+ }
112
+ return {
113
+ decision: "deny",
114
+ reason: `kimi-plugin-cc safety hook: tool "${toolName}" requires a pursue run with a trusted workspace root and exact terminal goal metadata arguments; goal creation, reactivation, and budget changes are denied.`,
115
+ };
116
+ }
117
+ switch (label) {
118
+ case "ask":
119
+ case "review":
120
+ case "challenge":
121
+ case "review_gate":
122
+ // /kimi:ask is documented and dispatched as a read-only narrative
123
+ // surface (see agents/kimi-ask.md: "do not implement anything Kimi
124
+ // describes"). PR 2 initially trusted kimi-code's `permission:auto`
125
+ // for ask on the assumption the user was watching every tool call,
126
+ // but `/kimi:ask` runs as a non-interactive subprocess via
127
+ // companion.sh — the user never sees individual tool prompts. PR 4
128
+ // reviewers flagged this contradiction; ask now shares the
129
+ // read-only allowlist with review/challenge/review_gate.
130
+ if (READ_ONLY_TOOLS.has(toolName)) {
131
+ return { decision: "allow" };
132
+ }
133
+ return {
134
+ decision: "deny",
135
+ reason: denyReadOnlyMessage(label, toolName),
136
+ };
137
+ case "swarm":
138
+ // /kimi:swarm — read-only PARALLEL fan-out. The parent agent must be
139
+ // allowed to call AgentSwarm (else the swarm never launches and the
140
+ // hook silently breaks the feature). Every spawned subagent inherits
141
+ // THIS "swarm" label and fires THIS hook at policy index 0, so a
142
+ // subagent's Write/Edit/Bash is denied exactly like a single-turn
143
+ // review's — read-only swarm opens ZERO new write surface. Allow only
144
+ // the read-only set plus AgentSwarm; deny everything else (including the
145
+ // singular `Agent` tool — swarm is the fan-out surface, not arbitrary
146
+ // delegation). Nested AgentSwarm calls from a subagent are allowed and
147
+ // bounded by the command's wall-clock budget (the only new risk is
148
+ // cost, not writes).
149
+ if (READ_ONLY_TOOLS.has(toolName) || toolName === AGENT_SWARM_TOOL) {
150
+ return { decision: "allow" };
151
+ }
152
+ return {
153
+ decision: "deny",
154
+ reason: denySwarmMessage(toolName),
155
+ };
156
+ case "swarm-write": {
157
+ // /kimi:swarm --write — WRITE-capable parallel fan-out (v1.4). The
158
+ // coordinator and `coder` subagents run in an EPHEMERAL THROWAWAY WORKTREE
159
+ // (off the user's HEAD). Allow AgentSwarm (else the swarm never launches)
160
+ // plus the read-only set; deny the singular Agent. Every write/edit/shell
161
+ // goes through the rescue allowlist — but scoped to the TRUSTED env-provided
162
+ // worktree root (ctx.trustedWorkspaceRoot), NOT the hook payload `cwd`.
163
+ // Rationale: the worktree path is exported as KIMI_PLUGIN_CC_WORKSPACE_ROOT
164
+ // by the same trusted plugin spawn that sets KIMI_PLUGIN_CC_CMD, so the
165
+ // model running inside kimi cannot forge it, and confinement does not
166
+ // depend on how upstream derives the payload cwd for an in-process
167
+ // subagent. Missing root ⇒ fail-closed (deny writes).
168
+ if (READ_ONLY_TOOLS.has(toolName) || toolName === AGENT_SWARM_TOOL) {
169
+ return { decision: "allow" };
170
+ }
171
+ const workspaceRoot = typeof ctx.trustedWorkspaceRoot === "string" ? ctx.trustedWorkspaceRoot : "";
172
+ if (workspaceRoot.length === 0) {
173
+ return {
174
+ decision: "deny",
175
+ reason: `kimi-plugin-cc safety hook: swarm-write received no trusted workspace root; tool "${toolName}" denied. ` +
176
+ "This is a plugin misconfiguration — writes are refused rather than risk the user's working tree.",
177
+ };
178
+ }
179
+ if (ctx.rescueEvaluator !== undefined) {
180
+ return await ctx.rescueEvaluator(workspaceRoot, toolName, input.tool_input);
181
+ }
182
+ // Stub for callers that didn't inject the evaluator (tests of the policy
183
+ // function alone). The production entry script always injects it.
184
+ if (READ_ONLY_TOOLS.has(toolName)) {
185
+ return { decision: "allow" };
186
+ }
187
+ return {
188
+ decision: "deny",
189
+ reason: `kimi-plugin-cc safety hook: swarm-write evaluator not configured; tool "${toolName}" denied as a safety default.`,
190
+ };
191
+ }
192
+ case "rescue": {
193
+ const workspaceRoot = typeof ctx.trustedWorkspaceRoot === "string" ? ctx.trustedWorkspaceRoot : "";
194
+ if (workspaceRoot.length === 0) {
195
+ if (READ_ONLY_TOOLS.has(toolName))
196
+ return { decision: "allow" };
197
+ return {
198
+ decision: "deny",
199
+ reason: `kimi-plugin-cc safety hook: rescue received no trusted workspace root; tool "${toolName}" denied. ` +
200
+ "This is a plugin misconfiguration — writes are refused rather than trusting hook payload cwd.",
201
+ };
202
+ }
203
+ if (ctx.rescueEvaluator !== undefined) {
204
+ return await ctx.rescueEvaluator(workspaceRoot, toolName, input.tool_input);
205
+ }
206
+ // Stub for callers that didn't inject the evaluator (e.g.,
207
+ // tests that exercise only the policy function). Allow
208
+ // Read/Grep/Glob and friends; deny everything else. The
209
+ // production entry script wires the real evaluator from
210
+ // runtime/rescue-approval.ts.
211
+ if (READ_ONLY_TOOLS.has(toolName)) {
212
+ return { decision: "allow" };
213
+ }
214
+ return {
215
+ decision: "deny",
216
+ reason: `kimi-plugin-cc safety hook: rescue evaluator not configured; tool "${toolName}" denied as a safety default.`,
217
+ };
218
+ }
219
+ default:
220
+ if (KNOWN_LABELS.has(label)) {
221
+ // Should be unreachable — every entry in KNOWN_LABELS has its
222
+ // own branch above. If you see this, add the case.
223
+ return {
224
+ decision: "deny",
225
+ reason: `kimi-plugin-cc safety hook: command label "${label}" has no policy branch.`,
226
+ };
227
+ }
228
+ // Unknown label. Be conservative; only allow Read/Grep/Glob.
229
+ if (READ_ONLY_TOOLS.has(toolName)) {
230
+ return { decision: "allow" };
231
+ }
232
+ return {
233
+ decision: "deny",
234
+ reason: `kimi-plugin-cc safety hook: unrecognized command label "${label}"; tool "${toolName}" denied as a safety default.`,
235
+ };
236
+ }
237
+ }
238
+ function denyReadOnlyMessage(label, toolName) {
239
+ const tool = toolName.length > 0 ? toolName : "<unspecified>";
240
+ return [
241
+ `kimi-plugin-cc safety hook: ${label} is read-only.`,
242
+ `Tool "${tool}" is denied — use Read, Grep, or Glob to inspect the workspace instead.`,
243
+ "If you need to mutate state, the user must invoke /kimi:rescue (write-capable) rather than this command.",
244
+ ].join(" ");
245
+ }
246
+ function denySwarmMessage(toolName) {
247
+ const tool = toolName.length > 0 ? toolName : "<unspecified>";
248
+ return [
249
+ "kimi-plugin-cc safety hook: swarm is a read-only parallel review.",
250
+ `Tool "${tool}" is denied — every subagent may use Read, Grep, or Glob to inspect the workspace,`,
251
+ "and the coordinator may use AgentSwarm to fan out, but no write, edit, or shell operations are permitted.",
252
+ "Consolidate the subagents' findings into a markdown report instead.",
253
+ ].join(" ");
254
+ }