@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
package/dist/engine/engine.js
CHANGED
|
@@ -34,26 +34,27 @@ import { PLAN_MODE_ALLOWED_TOOLS } from "../tool-system/plan-mode-allowlist.js";
|
|
|
34
34
|
import { PromptComposer } from "../prompt/composer.js";
|
|
35
35
|
import { SessionManager } from "../session/session-manager.js";
|
|
36
36
|
import { ModelFacade } from "./model-facade.js";
|
|
37
|
-
import { logger,
|
|
37
|
+
import { logger, runWithSid, getCurrentSid } from "../logging/logger.js";
|
|
38
38
|
import { recordSessionStart, recordSessionEnd } from "../logging/session-recorder.js";
|
|
39
39
|
import { sanitizeContent, sanitizeTaskString } from "../logging/sanitize-messages.js";
|
|
40
40
|
import { TurnLoop } from "./turn-loop.js";
|
|
41
41
|
import { MCPManager } from "../tool-system/mcp-manager.js";
|
|
42
42
|
import { SettingsManager, userHome } from "../settings/manager.js";
|
|
43
|
-
import {
|
|
43
|
+
import { getCredentialAccess } from "../credentials/access.js";
|
|
44
44
|
import { isFeatureEnabled, resolveFeatureFlags, } from "../settings/feature-flags.js";
|
|
45
|
-
import { effectiveDisabledList, effectiveBuiltinLists
|
|
45
|
+
import { effectiveDisabledList, effectiveBuiltinLists } from "../capability-control/overlay.js";
|
|
46
46
|
import { computeEffectiveDisabledLists } from "../capability-control/disabled-lists.js";
|
|
47
47
|
import { FileHistory } from "../session/file-history.js";
|
|
48
48
|
import { patchBackupTargets } from "../tool-system/builtin/apply-patch/backup-targets.js";
|
|
49
49
|
import { resolveSandboxBackend, } from "../tool-system/sandbox/index.js";
|
|
50
|
-
import { resolveAgentPreset, resolveBuiltinToolNames
|
|
50
|
+
import { resolveAgentPreset, resolveBuiltinToolNames } from "../preset/index.js";
|
|
51
51
|
import { ModelPool } from "../llm/model-pool.js";
|
|
52
52
|
import { AgentDefinitionRegistry } from "../agent/agent-definition-registry.js";
|
|
53
53
|
import { defaultCacheDir } from "../llm/model-cache.js";
|
|
54
54
|
import { detectProviderFromApiKey, buildModelPool } from "../onboarding.js";
|
|
55
55
|
import { detectPastedNoise } from "../utils/task-sanitizer.js";
|
|
56
56
|
import { parseTaskWithImages } from "./parse-task.js";
|
|
57
|
+
import { buildInputAttachmentContext } from "./input-attachments.js";
|
|
57
58
|
import { enforceImagePolicy, byteLengthFromBase64, dropOversizedImages, collectAttachedImagePaths, } from "./image-policy.js";
|
|
58
59
|
import { tryCompressImages } from "./image-compression.js";
|
|
59
60
|
import { buildSessionTitle } from "./session-title.js";
|
|
@@ -129,7 +130,7 @@ export function resolveChildLlm(modelKey, pool, parentLlm) {
|
|
|
129
130
|
* Names in `disabledAgents` are filtered out so the LLM never sees them.
|
|
130
131
|
*/
|
|
131
132
|
/**
|
|
132
|
-
* Resolve the working directory for a run. Precedence:
|
|
133
|
+
* Resolve the working directory for a run. Precedence for legacy sessions:
|
|
133
134
|
* options.cwd > resumed session's state.cwd > config.cwd > process.cwd()
|
|
134
135
|
*
|
|
135
136
|
* The session-cwd tier is what stops a project-bound session from being
|
|
@@ -445,6 +446,7 @@ export class Engine {
|
|
|
445
446
|
new ToolRegistry({
|
|
446
447
|
builtinTools: resolveBuiltinToolNames({
|
|
447
448
|
preset: this.preset.name,
|
|
449
|
+
host: config.builtinToolHost,
|
|
448
450
|
enabledBuiltinTools: builtinLists.enabledBuiltinTools,
|
|
449
451
|
disabledBuiltinTools: builtinLists.disabledBuiltinTools,
|
|
450
452
|
}),
|
|
@@ -635,6 +637,10 @@ export class Engine {
|
|
|
635
637
|
setBrowserBridge(bridge) {
|
|
636
638
|
this.config.browserBridge = bridge;
|
|
637
639
|
}
|
|
640
|
+
/** Inject the host-backed workspace bridge after construction. */
|
|
641
|
+
setWorkspaceBridge(bridge) {
|
|
642
|
+
this.config.workspaceBridge = bridge;
|
|
643
|
+
}
|
|
638
644
|
/**
|
|
639
645
|
* Queue a user message to be spliced into the in-flight run for `sessionId`
|
|
640
646
|
* at the next turn-loop step boundary — the 不打断 steering path (vs cancel +
|
|
@@ -735,20 +741,45 @@ export class Engine {
|
|
|
735
741
|
* Run a task from start to finish.
|
|
736
742
|
*/
|
|
737
743
|
async run(task, options) {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
// loading its own agents/settings/memory even if the host's UI repo
|
|
741
|
-
// selection has drifted to null. Only probe on omission — an explicit cwd
|
|
742
|
-
// always wins, and a fresh session has nothing to recover.
|
|
743
|
-
const sessionCwd = options?.cwd === undefined && options?.sessionId
|
|
744
|
-
? this.sessionManager.readCwd(options.sessionId)
|
|
744
|
+
const workspaceResume = options?.sessionId && this.sessionManager.exists(options.sessionId)
|
|
745
|
+
? await this.sessionManager.resolveSessionWorkspaceForResume(options.sessionId)
|
|
745
746
|
: undefined;
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
747
|
+
if (workspaceResume && !workspaceResume.ok) {
|
|
748
|
+
return {
|
|
749
|
+
text: `ERROR: ${workspaceResume.message}`,
|
|
750
|
+
reason: "completed",
|
|
751
|
+
sessionId: options.sessionId,
|
|
752
|
+
turnCount: 0,
|
|
753
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
if (workspaceResume?.ok &&
|
|
757
|
+
workspaceResume.reason === "worktree_missing_branch_gone" &&
|
|
758
|
+
workspaceResume.message) {
|
|
759
|
+
return {
|
|
760
|
+
text: workspaceResume.message,
|
|
761
|
+
reason: "completed",
|
|
762
|
+
sessionId: options.sessionId,
|
|
763
|
+
turnCount: 0,
|
|
764
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
// Existing P1 sessions resolve cwd from SessionWorkspace, even if the host
|
|
768
|
+
// passes a stale cwd. Legacy sessions without workspace keep the historical
|
|
769
|
+
// explicit-cwd precedence for backward compatibility.
|
|
770
|
+
const workspaceCwd = workspaceResume?.ok && workspaceResume.reason !== "legacy" ? workspaceResume.cwd : undefined;
|
|
771
|
+
const sessionCwd = workspaceCwd === undefined && options?.cwd === undefined && options?.sessionId
|
|
772
|
+
? workspaceResume?.ok
|
|
773
|
+
? workspaceResume.cwd
|
|
774
|
+
: this.sessionManager.readCwd(options.sessionId)
|
|
775
|
+
: undefined;
|
|
776
|
+
const cwd = workspaceCwd ??
|
|
777
|
+
resolveRunCwd({
|
|
778
|
+
optionCwd: options?.cwd,
|
|
779
|
+
sessionCwd,
|
|
780
|
+
configCwd: this.config.cwd,
|
|
781
|
+
processCwd: process.cwd(),
|
|
782
|
+
});
|
|
752
783
|
// Wrap the caller's onStream so we can intercept `task_update`
|
|
753
784
|
// events emitted by TodoWrite and keep an in-engine snapshot.
|
|
754
785
|
// TaskGuard reads this snapshot at turn end to decide whether to
|
|
@@ -801,8 +832,32 @@ export class Engine {
|
|
|
801
832
|
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
802
833
|
};
|
|
803
834
|
}
|
|
835
|
+
const cap = capabilitiesFor((this.config.llm.providerKind ?? this.config.llm.provider), this.config.llm.model);
|
|
836
|
+
const attachmentContext = await buildInputAttachmentContext(options?.attachments, cwd, {
|
|
837
|
+
includeImageBytes: cap.supportsVision,
|
|
838
|
+
expectedSessionId: options?.sessionId,
|
|
839
|
+
});
|
|
840
|
+
if (attachmentContext.errors.length > 0) {
|
|
841
|
+
const detail = attachmentContext.errors.join("; ");
|
|
842
|
+
logger.warn("engine.run.input_attachment_failed", { error: detail });
|
|
843
|
+
return {
|
|
844
|
+
text: `ERROR: input attachment could not be read (${detail}). Re-attach it or choose a path inside the workspace.`,
|
|
845
|
+
reason: "image_error",
|
|
846
|
+
sessionId: options?.sessionId ?? "input-attachment-failed",
|
|
847
|
+
turnCount: 0,
|
|
848
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
if (attachmentContext.text || attachmentContext.hasStructuredImageAttachments) {
|
|
852
|
+
parsedTask = {
|
|
853
|
+
text: [parsedTask.text, attachmentContext.text].filter(Boolean).join("\n\n"),
|
|
854
|
+
images: [...parsedTask.images, ...attachmentContext.images],
|
|
855
|
+
hasImages: parsedTask.hasImages ||
|
|
856
|
+
attachmentContext.images.length > 0 ||
|
|
857
|
+
attachmentContext.hasStructuredImageAttachments,
|
|
858
|
+
};
|
|
859
|
+
}
|
|
804
860
|
if (parsedTask.hasImages) {
|
|
805
|
-
const cap = capabilitiesFor((this.config.llm.providerKind ?? this.config.llm.provider), this.config.llm.model);
|
|
806
861
|
if (!cap.supportsVision) {
|
|
807
862
|
logger.warn("engine.run.vision_not_supported", {
|
|
808
863
|
provider: this.config.llm.provider,
|
|
@@ -884,7 +939,7 @@ export class Engine {
|
|
|
884
939
|
// *text* portion only — base64 bytes count as "noise" by the heuristic
|
|
885
940
|
// and would also bloat the transcript by megabytes per image. Image
|
|
886
941
|
// bytes ride in parsedTask.images and re-enter the message tree below.
|
|
887
|
-
const taskText = parsedTask.
|
|
942
|
+
const taskText = parsedTask.text;
|
|
888
943
|
const noise = detectPastedNoise(taskText);
|
|
889
944
|
if (noise.isNoise) {
|
|
890
945
|
const hint = `Your input looks like pasted terminal output (${noise.reason}). ` +
|
|
@@ -946,6 +1001,7 @@ export class Engine {
|
|
|
946
1001
|
preset: this.preset.name,
|
|
947
1002
|
enabledBuiltinTools: childEnabled,
|
|
948
1003
|
disabledBuiltinTools: childDisabled,
|
|
1004
|
+
builtinToolHost: this.config.builtinToolHost,
|
|
949
1005
|
customSystemPrompt: this.config.customSystemPrompt,
|
|
950
1006
|
appendSystemPrompt: [this.config.appendSystemPrompt, req.appendSystemPrompt].filter(Boolean).join("\n\n") ||
|
|
951
1007
|
undefined,
|
|
@@ -1013,23 +1069,7 @@ export class Engine {
|
|
|
1013
1069
|
},
|
|
1014
1070
|
sessionExists: (sessionId) => this.sessionManager.exists(sessionId),
|
|
1015
1071
|
};
|
|
1016
|
-
|
|
1017
|
-
// default. Read UNMERGED per-scope (getForScope) so a project that wrote no
|
|
1018
|
-
// sandbox genuinely follows global, rather than inheriting global's mode and
|
|
1019
|
-
// looking like it set one. Fixes "项目级配了不生效" + the scope model.
|
|
1020
|
-
let projectSandbox;
|
|
1021
|
-
let globalSandbox;
|
|
1022
|
-
try {
|
|
1023
|
-
const sm = this.getSettingsManager();
|
|
1024
|
-
if (this.config.isSubAgent !== true) {
|
|
1025
|
-
projectSandbox = sm.getForScope("project", cwd).sandbox;
|
|
1026
|
-
}
|
|
1027
|
-
globalSandbox = sm.getForScope("user").sandbox;
|
|
1028
|
-
}
|
|
1029
|
-
catch {
|
|
1030
|
-
// settings unavailable → fall through to per-run default
|
|
1031
|
-
}
|
|
1032
|
-
const sandboxConfig = resolveSandboxConfig(this.config.sandbox, projectSandbox, globalSandbox, this.config.headless === true);
|
|
1072
|
+
const sandboxConfig = this.resolveSandboxConfigForCwd(cwd);
|
|
1033
1073
|
// A2: explicit sandbox modes (seatbelt, bwrap) must fail closed
|
|
1034
1074
|
// per standard §S4. resolveSandboxBackend throws when an explicit
|
|
1035
1075
|
// mode is unavailable on this host; we let it propagate. The
|
|
@@ -1070,12 +1110,16 @@ export class Engine {
|
|
|
1070
1110
|
? sandboxBackend
|
|
1071
1111
|
: { ...sandboxBackend, network: sandboxConfig.network },
|
|
1072
1112
|
cwd,
|
|
1113
|
+
shellEnv: this.readShellEnv(cwd),
|
|
1073
1114
|
// TodoWrite reads this to push task_update events independently
|
|
1074
1115
|
// of its return value, so the UI's pinned task panel refreshes
|
|
1075
1116
|
// immediately rather than after the LLM next surfaces the
|
|
1076
1117
|
// snapshot. wrappedOnStream snoops the same channel to keep
|
|
1077
1118
|
// latestTodos current for TaskGuard.
|
|
1078
1119
|
streamCallback: options?.onStream,
|
|
1120
|
+
setCwd(nextCwd) {
|
|
1121
|
+
toolCtx.cwd = nextCwd;
|
|
1122
|
+
},
|
|
1079
1123
|
};
|
|
1080
1124
|
logger.info("engine.run", {
|
|
1081
1125
|
task: taskText.slice(0, 200),
|
|
@@ -1134,6 +1178,7 @@ export class Engine {
|
|
|
1134
1178
|
let session;
|
|
1135
1179
|
let messages;
|
|
1136
1180
|
let freshImageMessage;
|
|
1181
|
+
let resumedFromDisk = false;
|
|
1137
1182
|
const claimedClientMessageIds = new Set();
|
|
1138
1183
|
const claimClientMessageId = (bundle, clientMessageId, source) => {
|
|
1139
1184
|
if (!clientMessageId)
|
|
@@ -1151,6 +1196,7 @@ export class Engine {
|
|
|
1151
1196
|
return true;
|
|
1152
1197
|
};
|
|
1153
1198
|
if (options?.sessionId && this.sessionManager.exists(options.sessionId)) {
|
|
1199
|
+
resumedFromDisk = true;
|
|
1154
1200
|
session = this.sessionManager.resume(options.sessionId);
|
|
1155
1201
|
const cachedCompacted = this.compactedMessagesBySession.get(options.sessionId);
|
|
1156
1202
|
messages = cachedCompacted ? [...cachedCompacted] : session.transcript.toMessages();
|
|
@@ -1233,21 +1279,6 @@ export class Engine {
|
|
|
1233
1279
|
// with this value so `/undo` reverts exactly this turn's file changes.
|
|
1234
1280
|
// (Both resume and cold-start paths converge here.)
|
|
1235
1281
|
session.state.turnSeq = (session.state.turnSeq ?? 0) + 1;
|
|
1236
|
-
// Stamp the resolved session id for downstream logging.
|
|
1237
|
-
//
|
|
1238
|
-
// `setCurrentSid` updates the module-level fallback so any code path
|
|
1239
|
-
// running outside an ALS scope (bootstrap, /sid before a run starts)
|
|
1240
|
-
// still sees the latest sid.
|
|
1241
|
-
//
|
|
1242
|
-
// The real isolation comes from wrapping the rest of `run` in
|
|
1243
|
-
// `runWithSid(sid, async () => { ... })`: every `getCurrentSid()`
|
|
1244
|
-
// call inside that closure — including those inside `await`ed child
|
|
1245
|
-
// Engine.run() calls — reads sid from this scope's
|
|
1246
|
-
// AsyncLocalStorage binding, not the module global. Sibling Engines
|
|
1247
|
-
// running concurrently each get their own scope; an `enterSid` inside
|
|
1248
|
-
// a child mutates that child's scope only and doesn't leak back to
|
|
1249
|
-
// the parent's chain after `await child.run(...)` returns.
|
|
1250
|
-
setCurrentSid(session.state.sessionId);
|
|
1251
1282
|
// B2 / Gate 1: stamp the resolved sid onto the tool context so
|
|
1252
1283
|
// session-scoped side effects (background-agent completion
|
|
1253
1284
|
// notifications) attribute to the right session. toolCtx is created
|
|
@@ -1255,6 +1286,9 @@ export class Engine {
|
|
|
1255
1286
|
// the first point we can set it. After this assignment treat the
|
|
1256
1287
|
// field as immutable for the rest of the run.
|
|
1257
1288
|
toolCtx.sessionId = session.state.sessionId;
|
|
1289
|
+
toolCtx.setSessionWorkspace = (workspace) => {
|
|
1290
|
+
session.state.workspace = workspace;
|
|
1291
|
+
};
|
|
1258
1292
|
return runWithSid(session.state.sessionId, async () => {
|
|
1259
1293
|
recordSessionStart(session.state.sessionId, {
|
|
1260
1294
|
// Strip <codeshell-image> base64 payloads before they reach
|
|
@@ -1265,7 +1299,7 @@ export class Engine {
|
|
|
1265
1299
|
model: this.config.llm.model,
|
|
1266
1300
|
provider: this.config.llm.provider,
|
|
1267
1301
|
permissionMode: this.config.permissionMode ?? "acceptEdits",
|
|
1268
|
-
resumed:
|
|
1302
|
+
resumed: resumedFromDisk,
|
|
1269
1303
|
});
|
|
1270
1304
|
// Session-level hook: fired once per Engine.run() entry, regardless of
|
|
1271
1305
|
// cold-start vs resume. Handlers can return `messages` to inject a
|
|
@@ -1275,7 +1309,8 @@ export class Engine {
|
|
|
1275
1309
|
const sessionStartHook = await this.emitHook("on_session_start", {
|
|
1276
1310
|
sessionId: session.state.sessionId,
|
|
1277
1311
|
cwd,
|
|
1278
|
-
resumed:
|
|
1312
|
+
resumed: resumedFromDisk,
|
|
1313
|
+
source: resumedFromDisk ? "resume" : "startup",
|
|
1279
1314
|
});
|
|
1280
1315
|
// Per-turn hook: fired every time a new user prompt enters the loop.
|
|
1281
1316
|
// Equivalent to CC's UserPromptSubmit. Handlers can inject lightweight
|
|
@@ -1289,7 +1324,7 @@ export class Engine {
|
|
|
1289
1324
|
// and silently leaking attachment bytes through hooks is the kind of
|
|
1290
1325
|
// exfiltration risk a curious user-installed shell hook shouldn't carry.
|
|
1291
1326
|
prompt: taskText,
|
|
1292
|
-
resumed:
|
|
1327
|
+
resumed: resumedFromDisk,
|
|
1293
1328
|
});
|
|
1294
1329
|
// updatedPrompt: handler rewrote the user's prompt text. Replace the
|
|
1295
1330
|
// last user message we just pushed (cold-start: line ~511; resume:
|
|
@@ -1307,22 +1342,49 @@ export class Engine {
|
|
|
1307
1342
|
messages[lastIdx] = { role: "user", content: promptSubmitHook.updatedPrompt };
|
|
1308
1343
|
}
|
|
1309
1344
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1345
|
+
const contextManager = new ContextManager({
|
|
1346
|
+
maxTokens: this.resolveMaxContextTokens(),
|
|
1347
|
+
// Drop undefined fields so they don't clobber ContextManager defaults
|
|
1348
|
+
// (spread of `{x: undefined}` would override the default with undefined).
|
|
1349
|
+
...Object.fromEntries(Object.entries(this.resolveContextRatios()).filter(([, v]) => v !== undefined)),
|
|
1350
|
+
});
|
|
1351
|
+
this.lastContextManager = contextManager;
|
|
1352
|
+
const persistedContextAnchor = session.state.contextUsageAnchor;
|
|
1353
|
+
const contextAnchorCompatible = persistedContextAnchor !== undefined &&
|
|
1354
|
+
(persistedContextAnchor.provider === undefined ||
|
|
1355
|
+
persistedContextAnchor.provider === this.config.llm.provider) &&
|
|
1356
|
+
(persistedContextAnchor.model === undefined ||
|
|
1357
|
+
persistedContextAnchor.model === this.config.llm.model) &&
|
|
1358
|
+
(persistedContextAnchor.messageCount <= messages.length ||
|
|
1359
|
+
persistedContextAnchor.estimateAtAnchor !== undefined);
|
|
1360
|
+
if (contextAnchorCompatible) {
|
|
1361
|
+
contextManager.seedActualUsage(persistedContextAnchor);
|
|
1362
|
+
}
|
|
1363
|
+
// Best-effort token estimate of the full prompt so the UI's ctx bar isn't
|
|
1364
|
+
// 0% before the first real usage_update arrives. The authoritative count
|
|
1312
1365
|
// comes from `usage.promptTokens` after the first LLM response — this is
|
|
1313
|
-
// just a display-friendly approximation for the first frame
|
|
1366
|
+
// just a display-friendly approximation for the first frame, annotated
|
|
1367
|
+
// with source/confidence so consumers don't treat heuristics as truth.
|
|
1314
1368
|
//
|
|
1315
1369
|
// Only seed once per (process, sid). On subsequent turns the UI already
|
|
1316
|
-
// shows the previous turn's accurate ctx; overwriting it with
|
|
1317
|
-
//
|
|
1370
|
+
// shows the previous turn's accurate ctx; overwriting it with a fresh
|
|
1371
|
+
// best-effort estimate would make the bar visibly drop on every submit.
|
|
1318
1372
|
const sid = session.state.sessionId;
|
|
1319
1373
|
const needsCtxSeed = !this.ctxSeedSent.has(sid);
|
|
1320
|
-
const
|
|
1321
|
-
?
|
|
1322
|
-
const
|
|
1323
|
-
return
|
|
1324
|
-
|
|
1325
|
-
|
|
1374
|
+
const ctxSeed = needsCtxSeed
|
|
1375
|
+
? (() => {
|
|
1376
|
+
const checked = contextManager.checkLimits(messages);
|
|
1377
|
+
return {
|
|
1378
|
+
tokens: checked.tokens,
|
|
1379
|
+
source: checked.promptTokensSource,
|
|
1380
|
+
confidence: checked.promptTokensConfidence,
|
|
1381
|
+
};
|
|
1382
|
+
})()
|
|
1383
|
+
: {
|
|
1384
|
+
tokens: 0,
|
|
1385
|
+
source: "heuristic_estimate",
|
|
1386
|
+
confidence: "low",
|
|
1387
|
+
};
|
|
1326
1388
|
if (needsCtxSeed)
|
|
1327
1389
|
this.ctxSeedSent.add(sid);
|
|
1328
1390
|
// Tell the client the sid *now* instead of waiting for run() to resolve.
|
|
@@ -1331,7 +1393,9 @@ export class Engine {
|
|
|
1331
1393
|
options?.onStream?.({
|
|
1332
1394
|
type: "session_started",
|
|
1333
1395
|
sessionId: sid,
|
|
1334
|
-
promptTokens:
|
|
1396
|
+
promptTokens: ctxSeed.tokens,
|
|
1397
|
+
promptTokensSource: ctxSeed.source,
|
|
1398
|
+
promptTokensConfidence: ctxSeed.confidence,
|
|
1335
1399
|
});
|
|
1336
1400
|
// Replay the last TodoWrite snapshot on resume so the UI's pinned
|
|
1337
1401
|
// task panel re-hydrates without the LLM needing to call TodoWrite
|
|
@@ -1358,12 +1422,14 @@ export class Engine {
|
|
|
1358
1422
|
// in this same session don't re-prompt. Headless/auto backends skip
|
|
1359
1423
|
// this — they don't prompt, so there are no project rules to persist.
|
|
1360
1424
|
if (approvalBackend instanceof InteractiveApprovalBackend) {
|
|
1361
|
-
approvalBackend.
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1425
|
+
approvalBackend.setSessionContext(session.state.sessionId, {
|
|
1426
|
+
cwd,
|
|
1427
|
+
onProjectRules: (rules) => {
|
|
1428
|
+
// Prepend the *full* accumulated list of session-saved project rules
|
|
1429
|
+
// so user approvals win over defaults and earlier approvals aren't
|
|
1430
|
+
// dropped when later ones come in.
|
|
1431
|
+
permission.reconfigure(mode, approvalBackend, [...rules, ...defaultRules]);
|
|
1432
|
+
},
|
|
1367
1433
|
});
|
|
1368
1434
|
}
|
|
1369
1435
|
const toolExecutor = new ToolExecutor(this.toolRegistry, permission, this.hooks);
|
|
@@ -1379,13 +1445,6 @@ export class Engine {
|
|
|
1379
1445
|
// Wire abort signal for cascading cancellation + per-Engine ToolContext
|
|
1380
1446
|
toolExecutor.setSignal(options?.signal);
|
|
1381
1447
|
toolExecutor.setContext(toolCtx);
|
|
1382
|
-
const contextManager = new ContextManager({
|
|
1383
|
-
maxTokens: this.resolveMaxContextTokens(),
|
|
1384
|
-
// Drop undefined fields so they don't clobber ContextManager defaults
|
|
1385
|
-
// (spread of `{x: undefined}` would override the default with undefined).
|
|
1386
|
-
...Object.fromEntries(Object.entries(this.resolveContextRatios()).filter(([, v]) => v !== undefined)),
|
|
1387
|
-
});
|
|
1388
|
-
this.lastContextManager = contextManager;
|
|
1389
1448
|
const { disabledSkills, disabledPlugins } = this.readDisabledLists();
|
|
1390
1449
|
const promptComposer = new PromptComposer({
|
|
1391
1450
|
cwd,
|
|
@@ -1447,6 +1506,7 @@ export class Engine {
|
|
|
1447
1506
|
(normalizeGoal(options?.goal) !== undefined ||
|
|
1448
1507
|
session.state.activeGoal !== undefined ||
|
|
1449
1508
|
normalizeGoal(this.config.goal) !== undefined),
|
|
1509
|
+
settingsScope: this.config.settingsScope ?? "project",
|
|
1450
1510
|
};
|
|
1451
1511
|
toolCtx.toolVisibility = toolVisibility;
|
|
1452
1512
|
// #7: per-turn project builtin override. The toolRegistry's builtin tool
|
|
@@ -1757,6 +1817,13 @@ export class Engine {
|
|
|
1757
1817
|
consumeSteer: (source) => this.consumeSteer(sid, source),
|
|
1758
1818
|
claimClientMessageId: (clientMessageId, source) => claimClientMessageId(session, clientMessageId, source),
|
|
1759
1819
|
recordCumulativeUsage,
|
|
1820
|
+
recordContextUsageAnchor: (anchor) => {
|
|
1821
|
+
session.state.contextUsageAnchor = {
|
|
1822
|
+
...anchor,
|
|
1823
|
+
provider: this.config.llm.provider,
|
|
1824
|
+
model: this.config.llm.model,
|
|
1825
|
+
};
|
|
1826
|
+
},
|
|
1760
1827
|
// Clear the persisted goal for a self-reported completion / confirmed
|
|
1761
1828
|
// cancel. Clears the in-RAM session's activeGoal (so THIS run's later
|
|
1762
1829
|
// turns don't re-arm) AND persists it, and drops the in-flight stop
|
|
@@ -1816,6 +1883,8 @@ export class Engine {
|
|
|
1816
1883
|
options?.onStream?.({
|
|
1817
1884
|
type: "usage_update",
|
|
1818
1885
|
promptTokens: cumulative.cumulativePromptTokens,
|
|
1886
|
+
promptTokensSource: "session_cumulative",
|
|
1887
|
+
promptTokensConfidence: "high",
|
|
1819
1888
|
cumulativePromptTokens: cumulative.cumulativePromptTokens,
|
|
1820
1889
|
cumulativeCacheReadTokens: cumulative.cumulativeCacheReadTokens,
|
|
1821
1890
|
cumulativeCacheCreationTokens: cumulative.cumulativeCacheCreationTokens,
|
|
@@ -2355,7 +2424,6 @@ export class Engine {
|
|
|
2355
2424
|
refreshRuntimeConfig(patch, version) {
|
|
2356
2425
|
if (version <= this.lastAppliedConfigVersion)
|
|
2357
2426
|
return;
|
|
2358
|
-
const prevServers = this.config.mcpServers ?? {};
|
|
2359
2427
|
const prevPresetName = this.preset.name;
|
|
2360
2428
|
this.config = { ...this.config, ...patch };
|
|
2361
2429
|
// #2: re-resolve the prompt-affecting preset so the next-turn PromptComposer
|
|
@@ -2366,11 +2434,17 @@ export class Engine {
|
|
|
2366
2434
|
// The builtin tool SET is ctor-frozen and may be shared via runtime — we
|
|
2367
2435
|
// do NOT rebuild it here. If the new preset implies a different builtin
|
|
2368
2436
|
// tool set, that part of the change only lands on session restart.
|
|
2369
|
-
const prevTools = resolveBuiltinToolNames({
|
|
2437
|
+
const prevTools = resolveBuiltinToolNames({
|
|
2438
|
+
preset: prevPresetName,
|
|
2439
|
+
host: this.config.builtinToolHost,
|
|
2440
|
+
})
|
|
2370
2441
|
.slice()
|
|
2371
2442
|
.sort()
|
|
2372
2443
|
.join(",");
|
|
2373
|
-
const nextTools = resolveBuiltinToolNames({
|
|
2444
|
+
const nextTools = resolveBuiltinToolNames({
|
|
2445
|
+
preset: nextPreset.name,
|
|
2446
|
+
host: this.config.builtinToolHost,
|
|
2447
|
+
})
|
|
2374
2448
|
.slice()
|
|
2375
2449
|
.sort()
|
|
2376
2450
|
.join(",");
|
|
@@ -2457,6 +2531,41 @@ export class Engine {
|
|
|
2457
2531
|
}
|
|
2458
2532
|
return had;
|
|
2459
2533
|
}
|
|
2534
|
+
/**
|
|
2535
|
+
* Reset a session's workspace pointer back to its main root. If the session is
|
|
2536
|
+
* actively running, mutate that live SessionBundle first so the run's next
|
|
2537
|
+
* saveState cannot resurrect a stale worktree pointer.
|
|
2538
|
+
*/
|
|
2539
|
+
releaseSessionWorkspace(sessionId) {
|
|
2540
|
+
if (!sessionId || !this.sessionManager.exists(sessionId))
|
|
2541
|
+
return null;
|
|
2542
|
+
const mainRoot = this.sessionManager.readCwd(sessionId) ??
|
|
2543
|
+
(this.activeRunSession?.state.sessionId === sessionId
|
|
2544
|
+
? this.activeRunSession.state.cwd
|
|
2545
|
+
: undefined);
|
|
2546
|
+
if (!mainRoot)
|
|
2547
|
+
return null;
|
|
2548
|
+
const workspace = { root: mainRoot, kind: "main" };
|
|
2549
|
+
if (this.activeRunSession?.state.sessionId === sessionId) {
|
|
2550
|
+
this.activeRunSession.state.workspace = workspace;
|
|
2551
|
+
}
|
|
2552
|
+
try {
|
|
2553
|
+
const bundle = this.activeRunSession?.state.sessionId === sessionId
|
|
2554
|
+
? this.activeRunSession
|
|
2555
|
+
: this.sessionManager.resume(sessionId);
|
|
2556
|
+
bundle.state.workspace = workspace;
|
|
2557
|
+
this.sessionManager.saveState(bundle.state);
|
|
2558
|
+
}
|
|
2559
|
+
catch {
|
|
2560
|
+
try {
|
|
2561
|
+
this.sessionManager.setSessionWorkspace(sessionId, workspace);
|
|
2562
|
+
}
|
|
2563
|
+
catch {
|
|
2564
|
+
return null;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
return workspace;
|
|
2568
|
+
}
|
|
2460
2569
|
injectContext(sessionId, content) {
|
|
2461
2570
|
const session = this.sessionManager.resume(sessionId);
|
|
2462
2571
|
session.transcript.appendMessage("assistant", content);
|
|
@@ -2575,10 +2684,10 @@ export class Engine {
|
|
|
2575
2684
|
buildPermissionConfig(mode, cwd) {
|
|
2576
2685
|
const rules = [...this.preset.defaultPermissionRules];
|
|
2577
2686
|
// Memory tools: dream scope is the LLM's own workspace, so save/delete
|
|
2578
|
-
// there go through without prompting. user-scope save/delete
|
|
2579
|
-
//
|
|
2580
|
-
//
|
|
2581
|
-
//
|
|
2687
|
+
// there go through without prompting. user-scope save/delete have no
|
|
2688
|
+
// explicit allow rule here, so default-mode classifier fallback asks the
|
|
2689
|
+
// user to confirm modifications. RegisteredTool.permissionDefault is only
|
|
2690
|
+
// UI/metadata and is not read by the classifier.
|
|
2582
2691
|
rules.push({
|
|
2583
2692
|
tool: "MemorySave",
|
|
2584
2693
|
argsPattern: { scope: "^dream$" },
|
|
@@ -2837,6 +2946,25 @@ export class Engine {
|
|
|
2837
2946
|
}
|
|
2838
2947
|
return cached;
|
|
2839
2948
|
}
|
|
2949
|
+
resolveSandboxConfigForCwd(cwd) {
|
|
2950
|
+
// Priority: config.sandbox → project settings.sandbox → global → per-run
|
|
2951
|
+
// default. Read UNMERGED per-scope (getForScope) so a project that wrote no
|
|
2952
|
+
// sandbox genuinely follows global, rather than inheriting global's mode and
|
|
2953
|
+
// looking like it set one. Fixes "项目级配了不生效" + the scope model.
|
|
2954
|
+
let projectSandbox;
|
|
2955
|
+
let globalSandbox;
|
|
2956
|
+
try {
|
|
2957
|
+
const sm = this.getSettingsManager();
|
|
2958
|
+
if (this.config.isSubAgent !== true) {
|
|
2959
|
+
projectSandbox = sm.getForScope("project", cwd).sandbox;
|
|
2960
|
+
}
|
|
2961
|
+
globalSandbox = sm.getForScope("user").sandbox;
|
|
2962
|
+
}
|
|
2963
|
+
catch {
|
|
2964
|
+
// settings unavailable → fall through to per-run default
|
|
2965
|
+
}
|
|
2966
|
+
return resolveSandboxConfig(this.config.sandbox, projectSandbox, globalSandbox, this.config.headless === true);
|
|
2967
|
+
}
|
|
2840
2968
|
/**
|
|
2841
2969
|
* Build the shell env layered onto the Bash tool / background shells (see
|
|
2842
2970
|
* mergeShellEnv). Three user-configured sources, merged lowest → highest:
|
|
@@ -2894,7 +3022,7 @@ export class Engine {
|
|
|
2894
3022
|
// the host user's credentials (same isolation contract as top-level env).
|
|
2895
3023
|
// Placed below settings.env so an explicit `env` entry can still override.
|
|
2896
3024
|
const credScope = (this.config.settingsScope ?? "project") === "full" ? "full" : "project";
|
|
2897
|
-
layer(
|
|
3025
|
+
layer(getCredentialAccess().envExposures(cwd, credScope));
|
|
2898
3026
|
layer(settings.env); // top-level env (global ⊕ project) wins
|
|
2899
3027
|
}
|
|
2900
3028
|
catch {
|
|
@@ -2931,9 +3059,34 @@ export class Engine {
|
|
|
2931
3059
|
return undefined;
|
|
2932
3060
|
}
|
|
2933
3061
|
}
|
|
3062
|
+
readWorktreeBranchPrefix(cwd) {
|
|
3063
|
+
if (this.config.isSubAgent === true || !cwd)
|
|
3064
|
+
return undefined;
|
|
3065
|
+
try {
|
|
3066
|
+
const settings = this.getSettingsManager().get();
|
|
3067
|
+
return settings.worktree?.branchPrefix;
|
|
3068
|
+
}
|
|
3069
|
+
catch {
|
|
3070
|
+
return undefined;
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
async resolveWorktreeSetupSandbox(cwd) {
|
|
3074
|
+
if (!cwd)
|
|
3075
|
+
return undefined;
|
|
3076
|
+
const sandboxConfig = this.resolveSandboxConfigForCwd(cwd);
|
|
3077
|
+
const sandboxBackend = this.runtime
|
|
3078
|
+
? await this.runtime.resolveSandbox(sandboxConfig, cwd)
|
|
3079
|
+
: await this.resolveSandboxWithoutRuntime(sandboxConfig, cwd);
|
|
3080
|
+
return sandboxBackend.name === "off"
|
|
3081
|
+
? sandboxBackend
|
|
3082
|
+
: { ...sandboxBackend, network: sandboxConfig.network };
|
|
3083
|
+
}
|
|
3084
|
+
readWorktreeSetupShellEnv(cwd) {
|
|
3085
|
+
return this.readShellEnv(cwd);
|
|
3086
|
+
}
|
|
2934
3087
|
buildToolContext() {
|
|
2935
3088
|
const { disabledSkills, disabledPlugins } = this.readDisabledLists();
|
|
2936
|
-
|
|
3089
|
+
const ctx = {
|
|
2937
3090
|
shellEnv: this.readShellEnv(this.config.cwd),
|
|
2938
3091
|
cwd: this.config.cwd ?? process.cwd(),
|
|
2939
3092
|
llmConfig: this.config.llm,
|
|
@@ -2941,6 +3094,7 @@ export class Engine {
|
|
|
2941
3094
|
toolRegistry: this.toolRegistry,
|
|
2942
3095
|
askUser: this.config.askUser,
|
|
2943
3096
|
browser: this.config.browserBridge,
|
|
3097
|
+
workspace: this.config.workspaceBridge,
|
|
2944
3098
|
injectCredentialToBrowser: this.config.injectCredentialToBrowser,
|
|
2945
3099
|
isSubAgent: this.config.isSubAgent === true,
|
|
2946
3100
|
// Credential tools narrow their disk reads to this scope: a project/
|
|
@@ -2961,6 +3115,10 @@ export class Engine {
|
|
|
2961
3115
|
// opts out via config. Otherwise allowed.
|
|
2962
3116
|
allowBackgroundShells: this.config.isSubAgent === true ? false : this.config.allowBackgroundShells !== false,
|
|
2963
3117
|
};
|
|
3118
|
+
ctx.setCwd = (cwd) => {
|
|
3119
|
+
ctx.cwd = cwd;
|
|
3120
|
+
};
|
|
3121
|
+
return ctx;
|
|
2964
3122
|
}
|
|
2965
3123
|
/**
|
|
2966
3124
|
* Read settings.disabledSkills + settings.disabledPlugins in a single
|
|
@@ -149,6 +149,12 @@ export interface DropOversizedResult {
|
|
|
149
149
|
droppedCount: number;
|
|
150
150
|
}
|
|
151
151
|
export declare function dropOversizedImages(images: readonly ParsedImage[]): DropOversizedResult;
|
|
152
|
+
export interface ImagePolicyByteInput {
|
|
153
|
+
name: string;
|
|
154
|
+
mime: string;
|
|
155
|
+
bytes: number;
|
|
156
|
+
}
|
|
157
|
+
export declare function enforceImageBytePolicy(images: readonly ImagePolicyByteInput[]): ImagePolicyVerdict;
|
|
152
158
|
export declare function enforceImagePolicy(images: readonly ParsedImage[]): ImagePolicyVerdict;
|
|
153
159
|
/**
|
|
154
160
|
* Collect the workspace file paths of any attached images that came from a
|