@cjhyy/code-shell-core 0.6.0-rc.9 → 0.7.0-beta.1

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 (168) hide show
  1. package/THIRD_PARTY_NOTICES.md +206 -0
  2. package/dist/automation/scheduler.d.ts +13 -7
  3. package/dist/automation/scheduler.js +116 -37
  4. package/dist/capability-control/service.d.ts +2 -0
  5. package/dist/capability-control/service.js +4 -2
  6. package/dist/cc-orchestrator/agent-adapter.d.ts +2 -0
  7. package/dist/cc-orchestrator/agent-adapter.js +7 -1
  8. package/dist/cc-orchestrator/codex-session-history.js +1 -1
  9. package/dist/cc-orchestrator/cwd-normalize.d.ts +2 -0
  10. package/dist/cc-orchestrator/cwd-normalize.js +19 -0
  11. package/dist/cc-orchestrator/external-agent-bindings.d.ts +27 -0
  12. package/dist/cc-orchestrator/external-agent-bindings.js +150 -0
  13. package/dist/cc-orchestrator/external-agent-driver.d.ts +1 -0
  14. package/dist/cc-orchestrator/external-agent-driver.js +102 -51
  15. package/dist/cc-orchestrator/external-agent-session-store.d.ts +23 -0
  16. package/dist/cc-orchestrator/external-agent-session-store.js +146 -0
  17. package/dist/cc-orchestrator/session-history.js +2 -2
  18. package/dist/cli/agent-server-stdio.js +9 -2
  19. package/dist/context/compaction.d.ts +8 -1
  20. package/dist/context/compaction.js +49 -4
  21. package/dist/context/manager.d.ts +16 -2
  22. package/dist/context/manager.js +103 -19
  23. package/dist/credentials/access.d.ts +56 -0
  24. package/dist/credentials/access.js +183 -0
  25. package/dist/credentials/index.d.ts +1 -0
  26. package/dist/credentials/index.js +1 -0
  27. package/dist/credentials/inject-credential-tool.d.ts +3 -1
  28. package/dist/credentials/inject-credential-tool.js +30 -11
  29. package/dist/credentials/types.d.ts +2 -2
  30. package/dist/credentials/use-credential-tool.d.ts +9 -1
  31. package/dist/credentials/use-credential-tool.js +58 -45
  32. package/dist/engine/engine.d.ts +17 -1
  33. package/dist/engine/engine.js +247 -89
  34. package/dist/engine/image-policy.d.ts +6 -0
  35. package/dist/engine/image-policy.js +17 -6
  36. package/dist/engine/input-attachments.d.ts +13 -0
  37. package/dist/engine/input-attachments.js +255 -0
  38. package/dist/engine/model-facade.d.ts +5 -2
  39. package/dist/engine/model-facade.js +4 -4
  40. package/dist/engine/parse-task.d.ts +10 -0
  41. package/dist/engine/parse-task.js +5 -0
  42. package/dist/engine/streaming-tool-queue.d.ts +11 -7
  43. package/dist/engine/streaming-tool-queue.js +11 -7
  44. package/dist/engine/turn-loop.d.ts +7 -1
  45. package/dist/engine/turn-loop.js +117 -27
  46. package/dist/engine/types.d.ts +8 -0
  47. package/dist/git/worktree/crud.d.ts +69 -0
  48. package/dist/git/worktree/crud.js +206 -0
  49. package/dist/git/worktree/diff.d.ts +14 -0
  50. package/dist/git/worktree/diff.js +82 -0
  51. package/dist/git/worktree/git-exec.d.ts +7 -0
  52. package/dist/git/worktree/git-exec.js +51 -0
  53. package/dist/git/worktree/index.d.ts +5 -0
  54. package/dist/git/worktree/index.js +5 -0
  55. package/dist/git/worktree/query.d.ts +42 -0
  56. package/dist/git/worktree/query.js +121 -0
  57. package/dist/git/worktree/slug.d.ts +11 -0
  58. package/dist/git/worktree/slug.js +58 -0
  59. package/dist/git/worktree.d.ts +1 -84
  60. package/dist/git/worktree.js +5 -230
  61. package/dist/index.d.ts +31 -28
  62. package/dist/index.js +27 -25
  63. package/dist/logging/logger.js +6 -6
  64. package/dist/logging/sanitize-messages.d.ts +10 -2
  65. package/dist/logging/sanitize-messages.js +21 -6
  66. package/dist/plugins/installer/checkUpdate.d.ts +4 -1
  67. package/dist/plugins/installer/checkUpdate.js +4 -2
  68. package/dist/plugins/installer/install.js +2 -0
  69. package/dist/plugins/installer/installFromSource.js +9 -1
  70. package/dist/plugins/installer/parseSource.d.ts +4 -1
  71. package/dist/plugins/installer/parseSource.js +28 -10
  72. package/dist/plugins/installer/sourcePath.d.ts +9 -0
  73. package/dist/plugins/installer/sourcePath.js +50 -0
  74. package/dist/plugins/installer/update.d.ts +4 -1
  75. package/dist/plugins/installer/update.js +5 -3
  76. package/dist/plugins/parseMarketplaceInput.d.ts +4 -1
  77. package/dist/plugins/parseMarketplaceInput.js +4 -3
  78. package/dist/plugins/pluginInstaller.js +24 -22
  79. package/dist/preset/index.d.ts +1 -0
  80. package/dist/preset/index.js +16 -9
  81. package/dist/prompt/sections/base.md +1 -1
  82. package/dist/protocol/chat-session-manager.d.ts +2 -0
  83. package/dist/protocol/chat-session-manager.js +38 -2
  84. package/dist/protocol/chat-session.d.ts +3 -0
  85. package/dist/protocol/chat-session.js +1 -0
  86. package/dist/protocol/client.d.ts +9 -4
  87. package/dist/protocol/client.js +18 -1
  88. package/dist/protocol/server.d.ts +30 -0
  89. package/dist/protocol/server.js +246 -46
  90. package/dist/protocol/types.d.ts +47 -0
  91. package/dist/protocol/types.js +6 -0
  92. package/dist/run/FileRunStore.js +10 -1
  93. package/dist/run/Heartbeat.js +12 -0
  94. package/dist/run/RunApprovalBackend.d.ts +3 -0
  95. package/dist/run/RunApprovalBackend.js +41 -6
  96. package/dist/run/RunLock.js +2 -0
  97. package/dist/run/RunManager.d.ts +2 -0
  98. package/dist/run/RunManager.js +64 -24
  99. package/dist/run/ids.d.ts +2 -0
  100. package/dist/run/ids.js +23 -0
  101. package/dist/runtime/background-shell.d.ts +1 -0
  102. package/dist/runtime/background-shell.js +23 -13
  103. package/dist/runtime/spawn-common.js +10 -0
  104. package/dist/services/auto-dream.d.ts +15 -4
  105. package/dist/services/auto-dream.js +20 -20
  106. package/dist/services/dream-consolidation.d.ts +2 -3
  107. package/dist/services/dream-consolidation.js +65 -15
  108. package/dist/services/extract-memories.d.ts +14 -5
  109. package/dist/services/extract-memories.js +20 -3
  110. package/dist/services/global-dream-promotion.d.ts +23 -0
  111. package/dist/services/global-dream-promotion.js +112 -0
  112. package/dist/services/memory-orchestrator.js +347 -34
  113. package/dist/session/memory.d.ts +61 -18
  114. package/dist/session/memory.js +342 -79
  115. package/dist/session/session-manager.d.ts +35 -1
  116. package/dist/session/session-manager.js +190 -3
  117. package/dist/session/transcript.d.ts +1 -1
  118. package/dist/session/transcript.js +17 -5
  119. package/dist/settings/manager.d.ts +1 -0
  120. package/dist/settings/manager.js +87 -37
  121. package/dist/settings/schema-export.d.ts +2 -3
  122. package/dist/settings/schema-export.js +2 -3
  123. package/dist/settings/schema.d.ts +21 -0
  124. package/dist/settings/schema.js +12 -0
  125. package/dist/skills/scanner.d.ts +3 -2
  126. package/dist/skills/scanner.js +12 -9
  127. package/dist/tool-system/builtin/background-jobs.d.ts +10 -1
  128. package/dist/tool-system/builtin/background-jobs.js +12 -1
  129. package/dist/tool-system/builtin/background-work.d.ts +17 -18
  130. package/dist/tool-system/builtin/background-work.js +51 -5
  131. package/dist/tool-system/builtin/config.d.ts +2 -1
  132. package/dist/tool-system/builtin/config.js +16 -11
  133. package/dist/tool-system/builtin/drive-claude-code.d.ts +16 -2
  134. package/dist/tool-system/builtin/drive-claude-code.js +301 -47
  135. package/dist/tool-system/builtin/edit.js +5 -2
  136. package/dist/tool-system/builtin/generate-video.d.ts +1 -0
  137. package/dist/tool-system/builtin/generate-video.js +13 -4
  138. package/dist/tool-system/builtin/index.d.ts +8 -3
  139. package/dist/tool-system/builtin/index.js +37 -21
  140. package/dist/tool-system/builtin/lsp.d.ts +2 -1
  141. package/dist/tool-system/builtin/lsp.js +6 -3
  142. package/dist/tool-system/builtin/memory.js +40 -8
  143. package/dist/tool-system/builtin/notebook-edit.js +5 -2
  144. package/dist/tool-system/builtin/powershell.d.ts +5 -2
  145. package/dist/tool-system/builtin/powershell.js +11 -7
  146. package/dist/tool-system/builtin/read.js +118 -6
  147. package/dist/tool-system/builtin/view-image.d.ts +2 -2
  148. package/dist/tool-system/builtin/view-image.js +109 -19
  149. package/dist/tool-system/builtin/worktree.d.ts +4 -4
  150. package/dist/tool-system/builtin/worktree.js +297 -74
  151. package/dist/tool-system/builtin/write.js +5 -3
  152. package/dist/tool-system/context.d.ts +23 -1
  153. package/dist/tool-system/executor.d.ts +1 -5
  154. package/dist/tool-system/executor.js +94 -115
  155. package/dist/tool-system/mcp-manager.d.ts +18 -5
  156. package/dist/tool-system/mcp-manager.js +140 -74
  157. package/dist/tool-system/path-policy.d.ts +2 -0
  158. package/dist/tool-system/path-policy.js +41 -12
  159. package/dist/tool-system/permission.d.ts +28 -7
  160. package/dist/tool-system/permission.js +130 -49
  161. package/dist/tool-system/registry.js +11 -4
  162. package/dist/tool-system/tool-result-redaction.d.ts +7 -0
  163. package/dist/tool-system/tool-result-redaction.js +48 -0
  164. package/dist/tool-system/workspace-bridge.d.ts +11 -0
  165. package/dist/tool-system/workspace-bridge.js +1 -0
  166. package/dist/types.d.ts +55 -4
  167. package/dist/utils/toolDisplay.js +1 -1
  168. package/package.json +4 -3
@@ -18,6 +18,8 @@ import type { ToolRegistry } from "./registry.js";
18
18
  import type { AgentPresetName } from "../preset/index.js";
19
19
  import type { SandboxBackend } from "./sandbox/index.js";
20
20
  import type { HookRegistry } from "../hooks/registry.js";
21
+ import type { SessionManager } from "../session/session-manager.js";
22
+ import type { SessionWorkspace } from "../types.js";
21
23
  /**
22
24
  * Narrow view of the owning Engine that tools are allowed to call back into.
23
25
  * Defined here (in the low-level tool-system) rather than importing the
@@ -38,6 +40,14 @@ export interface ToolRuntimeHost {
38
40
  linux?: string;
39
41
  windows?: string;
40
42
  } | undefined;
43
+ /** Branch prefix used for CodeShell-managed worktree branches. */
44
+ readWorktreeBranchPrefix?(cwd?: string): string | undefined;
45
+ /** Resolve a setup-only sandbox for a newly-created worktree root. */
46
+ resolveWorktreeSetupSandbox?(cwd: string): Promise<SandboxBackend | undefined>;
47
+ /** Resolve setup-only shell env for a newly-created worktree root. */
48
+ readWorktreeSetupShellEnv?(cwd?: string): Record<string, string> | undefined;
49
+ /** Session state store used by session-scoped tools such as worktree switching. */
50
+ getSessionManager?(): SessionManager;
41
51
  }
42
52
  /** One choice in a multiple-choice AskUserQuestion. */
43
53
  export interface AskUserChoice {
@@ -172,10 +182,15 @@ export interface SubAgentSpawner {
172
182
  export interface ToolVisibilityContext {
173
183
  cwd: string;
174
184
  hasGoal: boolean;
185
+ settingsScope?: import("../settings/manager.js").SettingsScope;
175
186
  }
176
187
  export interface ToolContext {
177
188
  /** Active working directory for this Engine. */
178
189
  cwd: string;
190
+ /** Mutate the owning live context cwd. Worktree switching intentionally does not use this. */
191
+ setCwd?(cwd: string): void;
192
+ /** Mutate the owning live session state after a session workspace switch. */
193
+ setSessionWorkspace?(workspace: SessionWorkspace): void;
179
194
  /** LLM credentials/endpoint for tools that need to make their own calls. */
180
195
  llmConfig: LLMConfig;
181
196
  /** Active model pool (Arena reads this to pick participants). */
@@ -336,6 +351,13 @@ export interface ToolContext {
336
351
  * docs/superpowers/specs/2026-06-16-browser-automation-mvp.md.
337
352
  */
338
353
  browser?: import("./browser-bridge.js").BrowserBridge;
354
+ /**
355
+ * Host-backed workspace switch bridge. Desktop wires this to Electron main so
356
+ * the model's SwitchSessionWorkspace tool goes through the same path as the UI
357
+ * switcher. Undefined outside hosts that can switch this conversation's
358
+ * session workspace.
359
+ */
360
+ workspace?: import("./workspace-bridge.js").WorkspaceBridge;
339
361
  /**
340
362
  * Inject a stored cookie credential into the built-in browser (restore its
341
363
  * login state so the AI can drive the page as that account). The host
@@ -347,7 +369,7 @@ export interface ToolContext {
347
369
  injectCredentialToBrowser?: InjectCredentialFn;
348
370
  }
349
371
  /** Inject a cookie credential into the built-in browser (host-implemented). */
350
- export type InjectCredentialFn = (credentialId: string) => Promise<{
372
+ export type InjectCredentialFn = (credentialId: string, credentialScope?: "full" | "project") => Promise<{
351
373
  ok: boolean;
352
374
  count?: number;
353
375
  error?: string;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Tool executor — orchestrates permission checks, hooks, and execution.
3
3
  */
4
- import type { ToolCall, ToolResult, Message } from "../types.js";
4
+ import type { ToolCall, ToolResult } from "../types.js";
5
5
  import type { HookRegistry } from "../hooks/registry.js";
6
6
  import { ToolRegistry } from "./registry.js";
7
7
  import { PermissionClassifier } from "./permission.js";
@@ -42,9 +42,5 @@ export declare class ToolExecutor {
42
42
  private resolvePolicyOperation;
43
43
  private enforceDeclaredPathPolicy;
44
44
  private resolvePathPolicyTargets;
45
- /**
46
- * Convert tool results into Message entries for the transcript.
47
- */
48
- resultsToMessages(toolCalls: ToolCall[], results: ToolResult[]): Message[];
49
45
  }
50
46
  export {};
@@ -14,22 +14,33 @@ import { parsePatch } from "./builtin/apply-patch/parser.js";
14
14
  import { BUILTIN_TOOL_GUARDS } from "./builtin/index.js";
15
15
  import { COMPLETE_GOAL_TOOL_NAME } from "./builtin/complete-goal.js";
16
16
  import { CANCEL_GOAL_TOOL_NAME } from "./builtin/cancel-goal.js";
17
- // A1 hardening: hooks must never promote a non-`allow` classifier
18
- // decision to `allow`. They may otherwise adjust the decision freely
19
- // (e.g. tighten `allow` to `deny`/`ask`, or relax `deny` to `ask` to
20
- // request interactive confirmation — both legitimate audit patterns).
21
- // The user remains the only source of `allow` when the classifier
22
- // said `ask`/`deny`.
23
- //
24
- // See standard §S4 and spec docs/superpowers/specs/2026-05-26-a1-permission-hardening-design.md.
17
+ import { toolResultDisplayText } from "./tool-result-redaction.js";
18
+ const PERMISSION_DECISION_RANK = {
19
+ allow: 0,
20
+ ask: 1,
21
+ deny: 2,
22
+ };
23
+ // Permission hooks may only keep or tighten the current decision. The merge
24
+ // order is deny > ask > allow, so classifier deny/rules stay hard-deny even if
25
+ // a hook asks the user for confirmation.
25
26
  function clampHookDecision(classifier, hook) {
26
27
  if (!hook)
27
28
  return { decision: classifier, rejectedUpgrade: false };
28
- if (hook === "allow" && classifier !== "allow") {
29
+ if (PERMISSION_DECISION_RANK[hook] < PERMISSION_DECISION_RANK[classifier]) {
29
30
  return { decision: classifier, rejectedUpgrade: true };
30
31
  }
31
32
  return { decision: hook, rejectedUpgrade: false };
32
33
  }
34
+ function permissionAskReason(preHookMessages, permissionHookMessages) {
35
+ const parts = [];
36
+ if (preHookMessages?.length) {
37
+ parts.push(`pre_tool_use:\n${preHookMessages.join("\n")}`);
38
+ }
39
+ if (permissionHookMessages?.length) {
40
+ parts.push(`on_permission_check:\n${permissionHookMessages.join("\n")}`);
41
+ }
42
+ return parts.length > 0 ? parts.join("\n\n") : undefined;
43
+ }
33
44
  export class ToolExecutor {
34
45
  registry;
35
46
  permission;
@@ -132,7 +143,9 @@ export class ToolExecutor {
132
143
  };
133
144
  }
134
145
  const visibilityGuard = BUILTIN_TOOL_GUARDS.get(call.toolName);
135
- if (visibilityGuard && this.toolCtx?.toolVisibility && !visibilityGuard(this.toolCtx.toolVisibility)) {
146
+ if (visibilityGuard &&
147
+ this.toolCtx?.toolVisibility &&
148
+ !visibilityGuard(this.toolCtx.toolVisibility)) {
136
149
  return {
137
150
  id: call.id,
138
151
  toolName: call.toolName,
@@ -276,41 +289,16 @@ export class ToolExecutor {
276
289
  isError: true,
277
290
  };
278
291
  }
279
- // A1 hardening: pre_tool_use can no longer pre-approve a tool via
280
- // `decision === "allow"`. Hooks may relax `allow` and may force
281
- // `ask`/`deny`, but they cannot promote a `deny`/`ask` decision to
282
- // `allow`. The only paths to `allow` are the classifier (rules,
283
- // safe-read, allowlist) and the user (interactive approval).
284
- if (hookResult.decision === "allow") {
285
- this.log.info("permission.hook_upgrade_rejected", {
286
- cat: "permission",
287
- tool: call.toolName,
288
- site: "pre_tool_use",
289
- });
290
- }
291
- // pre_tool_use can request interactive confirmation via
292
- // decision: "ask". We invoke the same handleAsk path the classifier
293
- // uses, but feed the hook's messages so the user sees the handler's
294
- // reasoning. If the user approves, we skip the classifier below —
295
- // the hook and the user together have decided.
296
- if (hookResult.decision === "ask") {
297
- const reason = hookResult.messages?.join("\n") ?? undefined;
298
- const approved = await this.permission.handleAsk(call.toolName, call.args, reason, { sessionId: this.toolCtx?.sessionId });
299
- if (!approved) {
300
- return {
301
- id: call.id,
302
- toolName: call.toolName,
303
- error: `Tool call denied by user (pre_tool_use ask).`,
304
- isError: true,
305
- };
306
- }
307
- }
308
292
  // 0.7. Investigation guard — block redundant reads and tag soft reminders
309
293
  // onto results. See investigation-guard.ts for the rules; they enforce the
310
294
  // soft prompt guidance in coding.md ("never re-read", "3-call budget").
311
295
  const guardDecision = this.guard?.preToolCheck(call);
312
296
  if (guardDecision?.block) {
313
- this.log.info("guard.block", { cat: "guard", tool: call.toolName, reason: guardDecision.block.slice(0, 200) });
297
+ this.log.info("guard.block", {
298
+ cat: "guard",
299
+ tool: call.toolName,
300
+ reason: guardDecision.block.slice(0, 200),
301
+ });
314
302
  return {
315
303
  id: call.id,
316
304
  toolName: call.toolName,
@@ -318,68 +306,77 @@ export class ToolExecutor {
318
306
  isError: true,
319
307
  };
320
308
  }
321
- // 1. Permission check skipped only when pre_tool_use issued an
322
- // `ask` that the user just approved (we don't double-prompt).
323
- if (hookResult.decision !== "ask") {
324
- const classifierDecision = this.permission.classify(call.toolName, call.args);
325
- this.log.info("permission.classify", {
309
+ // 1. Permission check. `pre_tool_use: ask` only adds a stricter ask
310
+ // requirement; user approval for that ask cannot bypass classifier deny
311
+ // rules. Final merge order is deny > ask > allow, and at most one prompt
312
+ // is shown for the merged ask decision.
313
+ const classifierDecision = this.permission.classify(call.toolName, call.args);
314
+ this.log.info("permission.classify", {
315
+ cat: "permission",
316
+ tool: call.toolName,
317
+ decision: classifierDecision,
318
+ mode: this.permission.getMode(),
319
+ });
320
+ // on_permission_check hook: lets handlers audit and tighten the decision
321
+ // (e.g. `allow → ask/deny`). Attempts to relax classifier/pre-hook deny or
322
+ // ask are rejected by clampHookDecision.
323
+ const permHook = await this.hooks.emit("on_permission_check", {
324
+ toolName: call.toolName,
325
+ args: call.args,
326
+ toolCallId: call.id,
327
+ classifierDecision,
328
+ });
329
+ const preClamped = clampHookDecision(classifierDecision, hookResult.decision);
330
+ let decision = preClamped.decision;
331
+ if (preClamped.rejectedUpgrade) {
332
+ this.log.info("permission.hook_upgrade_rejected", {
333
+ cat: "permission",
334
+ tool: call.toolName,
335
+ site: "pre_tool_use",
336
+ attempted: hookResult.decision,
337
+ classifier: classifierDecision,
338
+ });
339
+ }
340
+ const permClamped = clampHookDecision(decision, permHook.decision);
341
+ decision = permClamped.decision;
342
+ if (permClamped.rejectedUpgrade) {
343
+ this.log.info("permission.hook_upgrade_rejected", {
344
+ cat: "permission",
345
+ tool: call.toolName,
346
+ site: "on_permission_check",
347
+ attempted: permHook.decision,
348
+ classifier: classifierDecision,
349
+ });
350
+ }
351
+ if (decision !== classifierDecision) {
352
+ this.log.info("permission.hook_override", {
326
353
  cat: "permission",
327
354
  tool: call.toolName,
328
- decision: classifierDecision,
329
- mode: this.permission.getMode(),
355
+ from: classifierDecision,
356
+ to: decision,
330
357
  });
331
- // on_permission_check hook: lets handlers audit and *downgrade*
332
- // the classifier decision (e.g. `allow → ask/deny`, `deny →
333
- // ask`). Promotion to `allow` is rejected by clampHookDecision
334
- // — only the classifier and the user can grant `allow`.
335
- const permHook = await this.hooks.emit("on_permission_check", {
358
+ }
359
+ if (decision === "deny") {
360
+ return {
361
+ id: call.id,
336
362
  toolName: call.toolName,
337
- args: call.args,
338
- toolCallId: call.id,
339
- classifierDecision,
363
+ error: `Permission denied for tool: ${call.toolName}`,
364
+ isError: true,
365
+ };
366
+ }
367
+ if (decision === "ask") {
368
+ const reason = permissionAskReason(hookResult.decision === "ask" ? hookResult.messages : undefined, permHook.decision === "ask" ? permHook.messages : undefined);
369
+ const approved = await this.permission.handleAsk(call.toolName, call.args, reason, {
370
+ sessionId: this.toolCtx?.sessionId,
340
371
  });
341
- // A1 hardening: clamp hook decision to downgrades only.
342
- const clamped = clampHookDecision(classifierDecision, permHook.decision);
343
- const decision = clamped.decision;
344
- if (clamped.rejectedUpgrade) {
345
- this.log.info("permission.hook_upgrade_rejected", {
346
- cat: "permission",
347
- tool: call.toolName,
348
- site: "on_permission_check",
349
- attempted: permHook.decision,
350
- classifier: classifierDecision,
351
- });
352
- }
353
- if (decision !== classifierDecision) {
354
- this.log.info("permission.hook_override", {
355
- cat: "permission",
356
- tool: call.toolName,
357
- from: classifierDecision,
358
- to: decision,
359
- });
360
- }
361
- if (decision === "deny") {
372
+ if (!approved) {
362
373
  return {
363
374
  id: call.id,
364
375
  toolName: call.toolName,
365
- error: `Permission denied for tool: ${call.toolName}`,
376
+ error: `Permission denied by user for tool: ${call.toolName}`,
366
377
  isError: true,
367
378
  };
368
379
  }
369
- if (decision === "ask") {
370
- const reason = permHook.messages?.join("\n");
371
- const approved = await this.permission.handleAsk(call.toolName, call.args, reason, {
372
- sessionId: this.toolCtx?.sessionId,
373
- });
374
- if (!approved) {
375
- return {
376
- id: call.id,
377
- toolName: call.toolName,
378
- error: `Permission denied by user for tool: ${call.toolName}`,
379
- isError: true,
380
- };
381
- }
382
- }
383
380
  }
384
381
  // 2. Pre-tool hook
385
382
  await this.hooks.emit("on_tool_start", {
@@ -415,7 +412,7 @@ export class ToolExecutor {
415
412
  toolName: call.toolName,
416
413
  ok: false,
417
414
  durationMs: Date.now() - toolStartedAt,
418
- error: err instanceof Error ? err.stack ?? err.message : String(err),
415
+ error: err instanceof Error ? (err.stack ?? err.message) : String(err),
419
416
  });
420
417
  // A model calling a tool that isn't in the registry (hallucinated name,
421
418
  // or a builtin missing from the active preset's whitelist) must not kill
@@ -437,7 +434,8 @@ export class ToolExecutor {
437
434
  if (guardDecision?.prepend && !result.error && result.result) {
438
435
  result.result = `${guardDecision.prepend}\n${result.result}`;
439
436
  }
440
- const payload = result.result ?? result.error ?? "";
437
+ const observerResult = toolResultDisplayText(result);
438
+ const payload = observerResult ?? result.error ?? "";
441
439
  span.end({
442
440
  ok: !result.error,
443
441
  chars: payload.length,
@@ -451,21 +449,21 @@ export class ToolExecutor {
451
449
  toolName: call.toolName,
452
450
  ok: !result.error,
453
451
  durationMs: Date.now() - toolStartedAt,
454
- output: result.error ? undefined : result.result,
452
+ output: result.error ? undefined : observerResult,
455
453
  error: result.error,
456
454
  });
457
455
  // 4. Post-tool hook
458
456
  await this.hooks.emit("on_tool_end", {
459
457
  toolName: call.toolName,
460
458
  toolCallId: call.id,
461
- result: result.result,
459
+ result: observerResult,
462
460
  error: result.error,
463
461
  });
464
462
  // 5. Post-tool-use hook (after execution, can observe/modify result)
465
463
  const postHook = await this.hooks.emit("post_tool_use", {
466
464
  toolName: call.toolName,
467
465
  toolCallId: call.id,
468
- result: result.result,
466
+ result: observerResult,
469
467
  error: result.error,
470
468
  });
471
469
  // Append handler-supplied context (linter output, type-check result,
@@ -574,23 +572,4 @@ export class ToolExecutor {
574
572
  return `Error: ${err.message}`;
575
573
  }
576
574
  }
577
- /**
578
- * Convert tool results into Message entries for the transcript.
579
- */
580
- resultsToMessages(toolCalls, results) {
581
- const blocks = [];
582
- for (const result of results) {
583
- blocks.push({
584
- type: "tool_result",
585
- tool_use_id: result.id,
586
- content: result.error ? `Error: ${result.error}` : (result.result ?? "(no output)"),
587
- });
588
- }
589
- return [
590
- {
591
- role: "user",
592
- content: blocks,
593
- },
594
- ];
595
- }
596
575
  }
@@ -6,6 +6,13 @@
6
6
  import type { Tool as McpTool } from "@modelcontextprotocol/sdk/types.js";
7
7
  import type { MCPServerConfig, RegisteredTool } from "../types.js";
8
8
  import { ToolRegistry } from "./registry.js";
9
+ import { type CredentialAccess } from "../credentials/access.js";
10
+ interface MCPResourceInfo {
11
+ uri: string;
12
+ name: string;
13
+ description?: string;
14
+ serverName: string;
15
+ }
9
16
  /**
10
17
  * Read a required secret from `process.env` by NAME (Codex-style env-secret
11
18
  * handling — the value is never persisted in MCP config). A referenced env var
@@ -28,6 +35,7 @@ export declare function buildStdioEnv(serverName: string, config: MCPServerConfi
28
35
  * and win on conflict. Pure + exported for unit testing.
29
36
  */
30
37
  export declare function buildHttpHeaders(serverName: string, config: MCPServerConfig, resolveCredential?: (id: string) => string | undefined): Record<string, string>;
38
+ export declare function buildHttpHeadersWithCredentialAccess(serverName: string, config: MCPServerConfig, access?: Pick<CredentialAccess, "resolveValue">): Promise<Record<string, string>>;
31
39
  /**
32
40
  * Infer the transport when the config doesn't name one: a url-only entry is
33
41
  * HTTP, everything else stdio. This is the CC `.mcp.json` convention —
@@ -80,6 +88,14 @@ export declare class MCPManager {
80
88
  */
81
89
  connectAll(servers: Record<string, MCPServerConfig>, owner?: unknown): Promise<void>;
82
90
  reconcile(servers: Record<string, MCPServerConfig>, owner?: unknown): Promise<void>;
91
+ /**
92
+ * Remove one owner from the shared desired-server pool and disconnect servers
93
+ * that no remaining owner wants. This is called when a session-owned Engine
94
+ * is closed; without it project-scoped MCP servers can stay connected until
95
+ * worker shutdown.
96
+ */
97
+ unregisterOwner(owner: unknown): Promise<void>;
98
+ private enabledServerNames;
83
99
  /** Union of every registered owner's desired set; null when none registered. */
84
100
  private unionDesired;
85
101
  /**
@@ -117,13 +133,10 @@ export declare class MCPManager {
117
133
  /**
118
134
  * List resources from MCP servers.
119
135
  */
120
- listResources(serverName?: string, signal?: AbortSignal): Promise<Array<{
121
- uri: string;
122
- name: string;
123
- description?: string;
124
- }>>;
136
+ listResources(serverName?: string, signal?: AbortSignal): Promise<MCPResourceInfo[]>;
125
137
  /**
126
138
  * Read a resource from an MCP server.
127
139
  */
128
140
  readResource(serverName: string, uri: string, signal?: AbortSignal): Promise<string>;
129
141
  }
142
+ export {};