@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.
- package/THIRD_PARTY_NOTICES.md +206 -0
- package/dist/automation/scheduler.d.ts +13 -7
- package/dist/automation/scheduler.js +116 -37
- package/dist/capability-control/service.d.ts +2 -0
- package/dist/capability-control/service.js +4 -2
- package/dist/cc-orchestrator/agent-adapter.d.ts +2 -0
- package/dist/cc-orchestrator/agent-adapter.js +7 -1
- package/dist/cc-orchestrator/codex-session-history.js +1 -1
- package/dist/cc-orchestrator/cwd-normalize.d.ts +2 -0
- package/dist/cc-orchestrator/cwd-normalize.js +19 -0
- package/dist/cc-orchestrator/external-agent-bindings.d.ts +27 -0
- package/dist/cc-orchestrator/external-agent-bindings.js +150 -0
- package/dist/cc-orchestrator/external-agent-driver.d.ts +1 -0
- package/dist/cc-orchestrator/external-agent-driver.js +102 -51
- package/dist/cc-orchestrator/external-agent-session-store.d.ts +23 -0
- package/dist/cc-orchestrator/external-agent-session-store.js +146 -0
- package/dist/cc-orchestrator/session-history.js +2 -2
- package/dist/cli/agent-server-stdio.js +9 -2
- package/dist/context/compaction.d.ts +8 -1
- package/dist/context/compaction.js +49 -4
- package/dist/context/manager.d.ts +16 -2
- package/dist/context/manager.js +103 -19
- package/dist/credentials/access.d.ts +56 -0
- package/dist/credentials/access.js +183 -0
- package/dist/credentials/index.d.ts +1 -0
- package/dist/credentials/index.js +1 -0
- package/dist/credentials/inject-credential-tool.d.ts +3 -1
- package/dist/credentials/inject-credential-tool.js +30 -11
- package/dist/credentials/types.d.ts +2 -2
- package/dist/credentials/use-credential-tool.d.ts +9 -1
- package/dist/credentials/use-credential-tool.js +58 -45
- package/dist/engine/engine.d.ts +17 -1
- package/dist/engine/engine.js +247 -89
- package/dist/engine/image-policy.d.ts +6 -0
- package/dist/engine/image-policy.js +17 -6
- package/dist/engine/input-attachments.d.ts +13 -0
- package/dist/engine/input-attachments.js +255 -0
- package/dist/engine/model-facade.d.ts +5 -2
- package/dist/engine/model-facade.js +4 -4
- package/dist/engine/parse-task.d.ts +10 -0
- package/dist/engine/parse-task.js +5 -0
- package/dist/engine/streaming-tool-queue.d.ts +11 -7
- package/dist/engine/streaming-tool-queue.js +11 -7
- package/dist/engine/turn-loop.d.ts +7 -1
- package/dist/engine/turn-loop.js +117 -27
- package/dist/engine/types.d.ts +8 -0
- package/dist/git/worktree/crud.d.ts +69 -0
- package/dist/git/worktree/crud.js +206 -0
- package/dist/git/worktree/diff.d.ts +14 -0
- package/dist/git/worktree/diff.js +82 -0
- package/dist/git/worktree/git-exec.d.ts +7 -0
- package/dist/git/worktree/git-exec.js +51 -0
- package/dist/git/worktree/index.d.ts +5 -0
- package/dist/git/worktree/index.js +5 -0
- package/dist/git/worktree/query.d.ts +42 -0
- package/dist/git/worktree/query.js +121 -0
- package/dist/git/worktree/slug.d.ts +11 -0
- package/dist/git/worktree/slug.js +58 -0
- package/dist/git/worktree.d.ts +1 -84
- package/dist/git/worktree.js +5 -230
- package/dist/index.d.ts +31 -28
- package/dist/index.js +27 -25
- package/dist/logging/logger.js +6 -6
- package/dist/logging/sanitize-messages.d.ts +10 -2
- package/dist/logging/sanitize-messages.js +21 -6
- package/dist/plugins/installer/checkUpdate.d.ts +4 -1
- package/dist/plugins/installer/checkUpdate.js +4 -2
- package/dist/plugins/installer/install.js +2 -0
- package/dist/plugins/installer/installFromSource.js +9 -1
- package/dist/plugins/installer/parseSource.d.ts +4 -1
- package/dist/plugins/installer/parseSource.js +28 -10
- package/dist/plugins/installer/sourcePath.d.ts +9 -0
- package/dist/plugins/installer/sourcePath.js +50 -0
- package/dist/plugins/installer/update.d.ts +4 -1
- package/dist/plugins/installer/update.js +5 -3
- package/dist/plugins/parseMarketplaceInput.d.ts +4 -1
- package/dist/plugins/parseMarketplaceInput.js +4 -3
- package/dist/plugins/pluginInstaller.js +24 -22
- package/dist/preset/index.d.ts +1 -0
- package/dist/preset/index.js +16 -9
- package/dist/prompt/sections/base.md +1 -1
- package/dist/protocol/chat-session-manager.d.ts +2 -0
- package/dist/protocol/chat-session-manager.js +38 -2
- package/dist/protocol/chat-session.d.ts +3 -0
- package/dist/protocol/chat-session.js +1 -0
- package/dist/protocol/client.d.ts +9 -4
- package/dist/protocol/client.js +18 -1
- package/dist/protocol/server.d.ts +30 -0
- package/dist/protocol/server.js +246 -46
- package/dist/protocol/types.d.ts +47 -0
- package/dist/protocol/types.js +6 -0
- package/dist/run/FileRunStore.js +10 -1
- package/dist/run/Heartbeat.js +12 -0
- package/dist/run/RunApprovalBackend.d.ts +3 -0
- package/dist/run/RunApprovalBackend.js +41 -6
- package/dist/run/RunLock.js +2 -0
- package/dist/run/RunManager.d.ts +2 -0
- package/dist/run/RunManager.js +64 -24
- package/dist/run/ids.d.ts +2 -0
- package/dist/run/ids.js +23 -0
- package/dist/runtime/background-shell.d.ts +1 -0
- package/dist/runtime/background-shell.js +23 -13
- package/dist/runtime/spawn-common.js +10 -0
- package/dist/services/auto-dream.d.ts +15 -4
- package/dist/services/auto-dream.js +20 -20
- package/dist/services/dream-consolidation.d.ts +2 -3
- package/dist/services/dream-consolidation.js +65 -15
- package/dist/services/extract-memories.d.ts +14 -5
- package/dist/services/extract-memories.js +20 -3
- package/dist/services/global-dream-promotion.d.ts +23 -0
- package/dist/services/global-dream-promotion.js +112 -0
- package/dist/services/memory-orchestrator.js +347 -34
- package/dist/session/memory.d.ts +61 -18
- package/dist/session/memory.js +342 -79
- package/dist/session/session-manager.d.ts +35 -1
- package/dist/session/session-manager.js +190 -3
- package/dist/session/transcript.d.ts +1 -1
- package/dist/session/transcript.js +17 -5
- package/dist/settings/manager.d.ts +1 -0
- package/dist/settings/manager.js +87 -37
- package/dist/settings/schema-export.d.ts +2 -3
- package/dist/settings/schema-export.js +2 -3
- package/dist/settings/schema.d.ts +21 -0
- package/dist/settings/schema.js +12 -0
- package/dist/skills/scanner.d.ts +3 -2
- package/dist/skills/scanner.js +12 -9
- package/dist/tool-system/builtin/background-jobs.d.ts +10 -1
- package/dist/tool-system/builtin/background-jobs.js +12 -1
- package/dist/tool-system/builtin/background-work.d.ts +17 -18
- package/dist/tool-system/builtin/background-work.js +51 -5
- package/dist/tool-system/builtin/config.d.ts +2 -1
- package/dist/tool-system/builtin/config.js +16 -11
- package/dist/tool-system/builtin/drive-claude-code.d.ts +16 -2
- package/dist/tool-system/builtin/drive-claude-code.js +301 -47
- package/dist/tool-system/builtin/edit.js +5 -2
- package/dist/tool-system/builtin/generate-video.d.ts +1 -0
- package/dist/tool-system/builtin/generate-video.js +13 -4
- package/dist/tool-system/builtin/index.d.ts +8 -3
- package/dist/tool-system/builtin/index.js +37 -21
- package/dist/tool-system/builtin/lsp.d.ts +2 -1
- package/dist/tool-system/builtin/lsp.js +6 -3
- package/dist/tool-system/builtin/memory.js +40 -8
- package/dist/tool-system/builtin/notebook-edit.js +5 -2
- package/dist/tool-system/builtin/powershell.d.ts +5 -2
- package/dist/tool-system/builtin/powershell.js +11 -7
- package/dist/tool-system/builtin/read.js +118 -6
- package/dist/tool-system/builtin/view-image.d.ts +2 -2
- package/dist/tool-system/builtin/view-image.js +109 -19
- package/dist/tool-system/builtin/worktree.d.ts +4 -4
- package/dist/tool-system/builtin/worktree.js +297 -74
- package/dist/tool-system/builtin/write.js +5 -3
- package/dist/tool-system/context.d.ts +23 -1
- package/dist/tool-system/executor.d.ts +1 -5
- package/dist/tool-system/executor.js +94 -115
- package/dist/tool-system/mcp-manager.d.ts +18 -5
- package/dist/tool-system/mcp-manager.js +140 -74
- package/dist/tool-system/path-policy.d.ts +2 -0
- package/dist/tool-system/path-policy.js +41 -12
- package/dist/tool-system/permission.d.ts +28 -7
- package/dist/tool-system/permission.js +130 -49
- package/dist/tool-system/registry.js +11 -4
- package/dist/tool-system/tool-result-redaction.d.ts +7 -0
- package/dist/tool-system/tool-result-redaction.js +48 -0
- package/dist/tool-system/workspace-bridge.d.ts +11 -0
- package/dist/tool-system/workspace-bridge.js +1 -0
- package/dist/types.d.ts +55 -4
- package/dist/utils/toolDisplay.js +1 -1
- 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
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
//
|
|
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
|
|
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 &&
|
|
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", {
|
|
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
|
|
322
|
-
//
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
-
|
|
329
|
-
|
|
355
|
+
from: classifierDecision,
|
|
356
|
+
to: decision,
|
|
330
357
|
});
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
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
|
|
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 :
|
|
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:
|
|
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:
|
|
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<
|
|
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 {};
|