@cjhyy/code-shell-core 0.8.13 → 0.9.0
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/dist/automation/desktop-authority-client.d.ts +9 -0
- package/dist/automation/desktop-authority-client.js +47 -0
- package/dist/automation/scheduler.d.ts +8 -0
- package/dist/automation/scheduler.js +37 -1
- package/dist/automation/store.js +14 -1
- package/dist/capabilities/index.d.ts +1 -0
- package/dist/cli/agent-server-stdio.js +8 -4
- package/dist/engine/engine-workspace-authority.d.ts +35 -0
- package/dist/engine/engine-workspace-authority.js +136 -0
- package/dist/engine/engine.d.ts +5 -10
- package/dist/engine/engine.js +108 -112
- package/dist/engine/input-attachments.d.ts +1 -0
- package/dist/engine/input-attachments.js +6 -2
- package/dist/engine/run-environment.d.ts +8 -3
- package/dist/engine/run-environment.js +33 -8
- package/dist/engine/run-image-input.d.ts +1 -0
- package/dist/engine/run-image-input.js +1 -0
- package/dist/engine/run-session-open.d.ts +2 -1
- package/dist/engine/run-session-open.js +7 -1
- package/dist/engine/run-setup.d.ts +1 -0
- package/dist/engine/run-setup.js +2 -1
- package/dist/engine/run-tooling.d.ts +2 -0
- package/dist/engine/run-tooling.js +3 -0
- package/dist/engine/run-types.d.ts +4 -0
- package/dist/engine/run-workspace.d.ts +6 -1
- package/dist/engine/run-workspace.js +47 -0
- package/dist/engine/subagent-spawner.d.ts +1 -0
- package/dist/engine/subagent-spawner.js +1 -0
- package/dist/engine/turn-loop.js +17 -0
- package/dist/engine/types.d.ts +2 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.extension.d.ts +1 -1
- package/dist/index.internal.d.ts +3 -2
- package/dist/index.internal.js +2 -0
- package/dist/index.js +2 -2
- package/dist/llm/client-base.d.ts +2 -1
- package/dist/llm/client-base.js +3 -1
- package/dist/llm/providers/anthropic.js +24 -25
- package/dist/llm/providers/openai.d.ts +4 -1
- package/dist/llm/providers/openai.js +76 -13
- package/dist/panel-apps/index.d.ts +1 -1
- package/dist/panel-apps/index.js +1 -1
- package/dist/panel-apps/installer.d.ts +29 -0
- package/dist/panel-apps/installer.js +124 -15
- package/dist/plugins/pluginAutomationTemplates.d.ts +2 -0
- package/dist/plugins/pluginAutomationTemplates.js +4 -0
- package/dist/plugins/pluginCatalog.d.ts +6 -0
- package/dist/plugins/pluginCatalog.js +7 -2
- package/dist/plugins/pluginContent.d.ts +1 -1
- package/dist/plugins/pluginContent.js +2 -10
- package/dist/prompt/composer.d.ts +4 -1
- package/dist/prompt/composer.js +11 -3
- package/dist/protocol/chat-session-manager.d.ts +42 -1
- package/dist/protocol/chat-session-manager.js +167 -4
- package/dist/protocol/chat-session.d.ts +11 -1
- package/dist/protocol/chat-session.js +20 -2
- package/dist/protocol/mobile-remote-types.d.ts +15 -0
- package/dist/protocol/server.d.ts +1 -2
- package/dist/protocol/server.js +28 -51
- package/dist/protocol/session-workspace-rpc.d.ts +23 -0
- package/dist/protocol/session-workspace-rpc.js +171 -0
- package/dist/protocol/types.d.ts +47 -1
- package/dist/protocol/types.js +4 -0
- package/dist/session/session-manager.d.ts +25 -0
- package/dist/session/session-manager.js +106 -6
- package/dist/session/transcript.d.ts +9 -0
- package/dist/session/transcript.js +81 -0
- package/dist/settings/manager.d.ts +12 -0
- package/dist/settings/manager.js +31 -0
- package/dist/tool-system/builtin/configure-model-connection.d.ts +36 -0
- package/dist/tool-system/builtin/configure-model-connection.js +396 -0
- package/dist/tool-system/builtin/cron.d.ts +11 -0
- package/dist/tool-system/builtin/cron.js +27 -2
- package/dist/tool-system/builtin/edit-model-catalog.d.ts +4 -6
- package/dist/tool-system/builtin/edit-model-catalog.js +5 -8
- package/dist/tool-system/builtin/edit.js +5 -3
- package/dist/tool-system/builtin/index.js +14 -0
- package/dist/tool-system/builtin/install-capability.js +22 -8
- package/dist/tool-system/builtin/settings-changed.d.ts +9 -0
- package/dist/tool-system/builtin/settings-changed.js +18 -0
- package/dist/tool-system/builtin/write.js +5 -3
- package/dist/tool-system/context.d.ts +12 -3
- package/dist/tool-system/executor.js +1 -1
- package/dist/tool-system/path-policy.d.ts +11 -3
- package/dist/tool-system/path-policy.js +56 -36
- package/dist/types.d.ts +11 -0
- package/dist/workspace/canonical-key.d.ts +7 -0
- package/dist/workspace/canonical-key.js +39 -0
- package/dist/workspace/workspace-context.d.ts +26 -0
- package/dist/workspace/workspace-context.js +112 -0
- package/package.json +1 -1
package/dist/engine/engine.js
CHANGED
|
@@ -9,6 +9,7 @@ import { modelEntriesFromConnections } from "./model-connections-pool.js";
|
|
|
9
9
|
import { cumulativeCacheHitRate, foldRunUsage, normalizeCumulativeUsageCounters, } from "../session/usage.js";
|
|
10
10
|
import { enqueueSteerItem, consumeSteerItems, removeSteerItem, } from "./steer-queue.js";
|
|
11
11
|
import { RunEnvironmentResolver } from "./run-environment.js";
|
|
12
|
+
import { legacySingleRootWorkspace } from "../workspace/workspace-context.js";
|
|
12
13
|
import { asyncAgentRegistry } from "../tool-system/builtin/agent-registry.js";
|
|
13
14
|
import { skillToolDef } from "../tool-system/builtin/skill.js";
|
|
14
15
|
import { backgroundShellManager } from "../runtime/background-shell.js";
|
|
@@ -21,7 +22,8 @@ import { runShellHook, shellHookMatches } from "../hooks/shell-runner.js";
|
|
|
21
22
|
import { ContextManager } from "../context/manager.js";
|
|
22
23
|
import { CONTEXT_PACKAGE_MAX_OUTPUT_TOKENS, buildAnchoredSummaryMessage, buildContextPackagePromptFromSerialized, estimateTokens, groupMessagesByApiRound, serializeContextPackageMessages, clampContextRatios as clampContextRatiosImpl, } from "../context/compaction.js";
|
|
23
24
|
import { PromptComposer } from "../prompt/composer.js";
|
|
24
|
-
import { SessionManager,
|
|
25
|
+
import { SessionManager, isEphemeralSessionState, sessionsRoot, } from "../session/session-manager.js";
|
|
26
|
+
import { createAuthorizedSessionMessageService, migrateOwnedSessionMainRoot, releaseOwnedSessionWorkspace, SessionWorkspaceAuthorityTracker, setOwnedSessionWorkspace, } from "./engine-workspace-authority.js";
|
|
25
27
|
import { createRunUsageAccounting, wireRunModelFacade } from "./run-accounting.js";
|
|
26
28
|
import { logger, runWithSid } from "../logging/logger.js";
|
|
27
29
|
import { recordSessionStart } from "../logging/session-recorder.js";
|
|
@@ -198,6 +200,7 @@ export class Engine {
|
|
|
198
200
|
lastMessages;
|
|
199
201
|
lastSessionId;
|
|
200
202
|
compactedMessagesBySession = new Map();
|
|
203
|
+
workspaceAuthorities = new SessionWorkspaceAuthorityTracker();
|
|
201
204
|
/**
|
|
202
205
|
* SIDs whose ctx-bar seed we've already emitted in this process. The seed
|
|
203
206
|
* is a rough char/4 estimate; only useful before the first real
|
|
@@ -260,6 +263,7 @@ export class Engine {
|
|
|
260
263
|
* writers are additionally fenced by SessionManager's persisted revision CAS.
|
|
261
264
|
*/
|
|
262
265
|
runInProgress = false;
|
|
266
|
+
disposed = false;
|
|
263
267
|
agentControlStateListener;
|
|
264
268
|
agentDirectionsDeliveredListener;
|
|
265
269
|
/** Permission update requested while runInProgress. Applied in run() finally. */
|
|
@@ -404,8 +408,7 @@ export class Engine {
|
|
|
404
408
|
throw new ConfigError("EngineConfig.composition is mutually exclusive with modules");
|
|
405
409
|
}
|
|
406
410
|
// Single composition fact source for every module contribution.
|
|
407
|
-
this.composition =
|
|
408
|
-
config.composition ?? compileComposition({ modules: config.modules ?? [] });
|
|
411
|
+
this.composition = config.composition ?? compileComposition({ modules: config.modules ?? [] });
|
|
409
412
|
this.toolCatalog = compositionToolCatalog(this.composition);
|
|
410
413
|
this.toolGuards = new Map(this.toolCatalog.flatMap((tool) => tool.exposure.availability
|
|
411
414
|
? [[tool.definition.name, tool.exposure.availability]]
|
|
@@ -941,6 +944,8 @@ export class Engine {
|
|
|
941
944
|
* policy (for example ChatSession's FIFO queue).
|
|
942
945
|
*/
|
|
943
946
|
async run(task, options) {
|
|
947
|
+
if (this.disposed)
|
|
948
|
+
throw new Error("Engine has been disposed");
|
|
944
949
|
if (this.runInProgress) {
|
|
945
950
|
throw new Error("Engine.run() cannot start while another run is in progress");
|
|
946
951
|
}
|
|
@@ -987,12 +992,16 @@ export class Engine {
|
|
|
987
992
|
resolveBehaviorProfile: (kind, mode) => this.resolveBehaviorProfile(kind, mode),
|
|
988
993
|
configPermissionMode: this.config.permissionMode,
|
|
989
994
|
configCwd: this.config.cwd,
|
|
995
|
+
configWorkspaceContext: this.config.workspaceContext,
|
|
990
996
|
settings: this.getSettingsManager(),
|
|
991
997
|
processCwd: process.cwd(),
|
|
992
998
|
});
|
|
993
999
|
if (!workspaceResolved.ok)
|
|
994
1000
|
return workspaceResolved.result;
|
|
995
|
-
const { sessionKind, sessionWorkspaceProfile, profile, profileParams, runPermissionMode, runPlanMode, cwd, profileState: { workspaceProfile: runWorkspaceProfile, sessionProfileOverrides, profileMemoryDir, }, } = workspaceResolved.resolution;
|
|
1001
|
+
const { sessionKind, sessionWorkspaceProfile, profile, profileParams, runPermissionMode, runPlanMode, cwd, workspaceContext, authoritativeWorkspaceContext, profileState: { workspaceProfile: runWorkspaceProfile, sessionProfileOverrides, profileMemoryDir, }, } = workspaceResolved.resolution;
|
|
1002
|
+
if (options?.sessionId) {
|
|
1003
|
+
this.workspaceAuthorities.remember(options.sessionId, workspaceContext);
|
|
1004
|
+
}
|
|
996
1005
|
/** Structured results the profile's run services report; keyed per profile contract. */
|
|
997
1006
|
let profileReportedResults;
|
|
998
1007
|
// Wrap the caller's onStream so we can intercept `task_update`
|
|
@@ -1018,6 +1027,7 @@ export class Engine {
|
|
|
1018
1027
|
llm: this.config.llm,
|
|
1019
1028
|
sessionId: options?.sessionId,
|
|
1020
1029
|
attachments: options?.attachments,
|
|
1030
|
+
workspaceContext,
|
|
1021
1031
|
});
|
|
1022
1032
|
if (!imageInput.ok)
|
|
1023
1033
|
return imageInput.result;
|
|
@@ -1039,6 +1049,7 @@ export class Engine {
|
|
|
1039
1049
|
const toolCtx = await this.wireRunSandboxToolContext({
|
|
1040
1050
|
options,
|
|
1041
1051
|
cwd,
|
|
1052
|
+
workspaceContext,
|
|
1042
1053
|
runPermissionMode,
|
|
1043
1054
|
runPlanMode,
|
|
1044
1055
|
profile,
|
|
@@ -1080,6 +1091,12 @@ export class Engine {
|
|
|
1080
1091
|
cwd,
|
|
1081
1092
|
sessionKind,
|
|
1082
1093
|
sessionWorkspaceProfile,
|
|
1094
|
+
projectBinding: authoritativeWorkspaceContext
|
|
1095
|
+
? {
|
|
1096
|
+
projectId: workspaceContext.projectId,
|
|
1097
|
+
mainRootId: workspaceContext.sessionMainRootId,
|
|
1098
|
+
}
|
|
1099
|
+
: undefined,
|
|
1083
1100
|
...(options?.sessionBrief ? { sessionBrief: options.sessionBrief } : {}),
|
|
1084
1101
|
llmModel: this.config.llm.model,
|
|
1085
1102
|
llmProvider: this.config.llm.provider,
|
|
@@ -1104,7 +1121,7 @@ export class Engine {
|
|
|
1104
1121
|
// the entire old topic. Fail open to full history if summarization fails.
|
|
1105
1122
|
if (options?.archiveBeforeCurrentTurn && options.clientMessageId) {
|
|
1106
1123
|
try {
|
|
1107
|
-
const
|
|
1124
|
+
const anchors = {
|
|
1108
1125
|
toClientMessageId: options.clientMessageId,
|
|
1109
1126
|
...(options.archiveBeforeCurrentTurn.fromClientMessageId
|
|
1110
1127
|
? {
|
|
@@ -1114,7 +1131,24 @@ export class Engine {
|
|
|
1114
1131
|
...(options.archiveBeforeCurrentTurn.segmentId
|
|
1115
1132
|
? { segmentId: options.archiveBeforeCurrentTurn.segmentId }
|
|
1116
1133
|
: {}),
|
|
1117
|
-
}
|
|
1134
|
+
};
|
|
1135
|
+
const before = estimateTokens(messages);
|
|
1136
|
+
if (options.archiveBeforeCurrentTurn.summary) {
|
|
1137
|
+
await this.appendArchiveMarker(session.state.sessionId, {
|
|
1138
|
+
...anchors,
|
|
1139
|
+
summary: options.archiveBeforeCurrentTurn.summary,
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
else {
|
|
1143
|
+
await this.archiveTurnRange(session.state.sessionId, { start: 0, end: 0 }, anchors);
|
|
1144
|
+
}
|
|
1145
|
+
// openRunSession captured the pre-marker replay. Rebuild from the
|
|
1146
|
+
// persisted marker so this very first post-boundary model call gets
|
|
1147
|
+
// the archived view rather than waiting until the following turn.
|
|
1148
|
+
messages =
|
|
1149
|
+
this.compactedMessagesBySession.get(session.state.sessionId) ??
|
|
1150
|
+
this.sessionManager.resumeForRun(session.state.sessionId).transcript.toMessages();
|
|
1151
|
+
const archived = { before, after: estimateTokens(messages) };
|
|
1118
1152
|
if (archived.before > archived.after) {
|
|
1119
1153
|
options.onStream?.({
|
|
1120
1154
|
type: "context_compact",
|
|
@@ -1123,12 +1157,6 @@ export class Engine {
|
|
|
1123
1157
|
after: archived.after,
|
|
1124
1158
|
});
|
|
1125
1159
|
}
|
|
1126
|
-
// openRunSession captured the pre-marker replay. Rebuild from the
|
|
1127
|
-
// persisted marker so this very first post-boundary model call gets
|
|
1128
|
-
// the archived view rather than waiting until the following turn.
|
|
1129
|
-
messages =
|
|
1130
|
-
this.compactedMessagesBySession.get(session.state.sessionId) ??
|
|
1131
|
-
this.sessionManager.resume(session.state.sessionId).transcript.toMessages();
|
|
1132
1160
|
}
|
|
1133
1161
|
catch (error) {
|
|
1134
1162
|
logger.warn("engine.pre_run_archive.failed", {
|
|
@@ -1170,6 +1198,7 @@ export class Engine {
|
|
|
1170
1198
|
options,
|
|
1171
1199
|
session,
|
|
1172
1200
|
cwd,
|
|
1201
|
+
workspaceContext,
|
|
1173
1202
|
toolCtx,
|
|
1174
1203
|
profile,
|
|
1175
1204
|
profileParams,
|
|
@@ -1452,66 +1481,12 @@ export class Engine {
|
|
|
1452
1481
|
}
|
|
1453
1482
|
/** Attach a closed-set, host-routed Session message sender to this run. */
|
|
1454
1483
|
attachSessionMessageService(toolCtx, session, options) {
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
const catalog = [];
|
|
1462
|
-
const seen = new Set();
|
|
1463
|
-
const rawTargets = Array.isArray(options?.sessionMessageTargets)
|
|
1464
|
-
? [...options.sessionMessageTargets]
|
|
1465
|
-
: [];
|
|
1466
|
-
for (const raw of rawTargets.slice(0, 100)) {
|
|
1467
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
1468
|
-
continue;
|
|
1469
|
-
const candidate = raw;
|
|
1470
|
-
const sessionId = typeof candidate.sessionId === "string" ? candidate.sessionId : "";
|
|
1471
|
-
try {
|
|
1472
|
-
assertSafeSessionId(sessionId);
|
|
1473
|
-
}
|
|
1474
|
-
catch {
|
|
1475
|
-
continue;
|
|
1476
|
-
}
|
|
1477
|
-
if (seen.has(sessionId))
|
|
1478
|
-
continue;
|
|
1479
|
-
if (candidate.workspaceRoot !== sourceRoot)
|
|
1480
|
-
continue;
|
|
1481
|
-
const title = typeof candidate.title === "string" ? candidate.title.trim() : "";
|
|
1482
|
-
if (!title || title.length > 512)
|
|
1483
|
-
continue;
|
|
1484
|
-
const workspaceProfile = typeof candidate.workspaceProfile === "string"
|
|
1485
|
-
? candidate.workspaceProfile.trim().slice(0, 256)
|
|
1486
|
-
: "";
|
|
1487
|
-
seen.add(sessionId);
|
|
1488
|
-
catalog.push({
|
|
1489
|
-
sessionId,
|
|
1490
|
-
title,
|
|
1491
|
-
workspaceRoot: sourceRoot,
|
|
1492
|
-
...(workspaceProfile ? { workspaceProfile } : {}),
|
|
1493
|
-
});
|
|
1494
|
-
}
|
|
1495
|
-
const targets = catalog.filter((target) => target.sessionId !== sourceSessionId);
|
|
1496
|
-
toolCtx.sessionMessages = {
|
|
1497
|
-
targets,
|
|
1498
|
-
send: async ({ targetSessionId, message }) => {
|
|
1499
|
-
const target = targets.find((candidate) => candidate.sessionId === targetSessionId);
|
|
1500
|
-
if (!target)
|
|
1501
|
-
throw new Error("target Session is not in the host-authorized project list");
|
|
1502
|
-
if (!message.trim())
|
|
1503
|
-
throw new Error("message is required");
|
|
1504
|
-
if (message.length > 48_000)
|
|
1505
|
-
throw new Error("message exceeds 48000 characters");
|
|
1506
|
-
await this.sessionMessageRouter({
|
|
1507
|
-
sourceSessionId,
|
|
1508
|
-
target,
|
|
1509
|
-
message,
|
|
1510
|
-
catalog,
|
|
1511
|
-
});
|
|
1512
|
-
return target;
|
|
1513
|
-
},
|
|
1514
|
-
};
|
|
1484
|
+
toolCtx.sessionMessages = createAuthorizedSessionMessageService({
|
|
1485
|
+
sessionManager: this.sessionManager,
|
|
1486
|
+
router: this.sessionMessageRouter,
|
|
1487
|
+
sourceSessionId: session.state.sessionId,
|
|
1488
|
+
rawTargets: options?.sessionMessageTargets,
|
|
1489
|
+
});
|
|
1515
1490
|
}
|
|
1516
1491
|
/**
|
|
1517
1492
|
* Await the parallel prompt/context assembly (LLM client handshake, system
|
|
@@ -1645,12 +1620,13 @@ export class Engine {
|
|
|
1645
1620
|
* lazily via `getSession` because it is opened after this wiring runs.
|
|
1646
1621
|
*/
|
|
1647
1622
|
async wireRunSandboxToolContext(args) {
|
|
1648
|
-
const { options, cwd, runPermissionMode, runPlanMode, profile, profileParams, sessionProfileOverrides, profileMemoryDir, getSession, reportResult, } = args;
|
|
1623
|
+
const { options, cwd, workspaceContext, runPermissionMode, runPlanMode, profile, profileParams, sessionProfileOverrides, profileMemoryDir, getSession, reportResult, } = args;
|
|
1649
1624
|
// Resolve before constructing the child spawner: a parent sandbox may come
|
|
1650
1625
|
// solely from project/user settings rather than config.sandbox, while a
|
|
1651
1626
|
// child intentionally skips project settings. Passing the complete
|
|
1652
1627
|
// effective config is what makes undefined role sandbox mean inherit.
|
|
1653
|
-
const
|
|
1628
|
+
const sandboxRun = { cwd, workspaceContext };
|
|
1629
|
+
const sandboxConfig = this.runEnvironmentResolver.resolveSandboxConfig(sandboxRun);
|
|
1654
1630
|
// Build the per-Engine ToolContext that will be threaded through every
|
|
1655
1631
|
// tool call. Replaces the old module-level singleton setters used by
|
|
1656
1632
|
// built-ins and product capabilities.
|
|
@@ -1659,6 +1635,7 @@ export class Engine {
|
|
|
1659
1635
|
parentSandbox: sandboxConfig,
|
|
1660
1636
|
presetName: this.preset.name,
|
|
1661
1637
|
cwd,
|
|
1638
|
+
workspaceContext,
|
|
1662
1639
|
permissionMode: runPermissionMode,
|
|
1663
1640
|
modelPool: this.modelPool,
|
|
1664
1641
|
parentStream: options?.onStream,
|
|
@@ -1685,7 +1662,7 @@ export class Engine {
|
|
|
1685
1662
|
//
|
|
1686
1663
|
// Backend is cached per runtime/engine so the capability probe runs once
|
|
1687
1664
|
// per (mode, cwd) instead of every turn.
|
|
1688
|
-
const sandboxBackend = await this.runEnvironmentResolver.resolveSandbox(
|
|
1665
|
+
const sandboxBackend = await this.runEnvironmentResolver.resolveSandbox(sandboxRun);
|
|
1689
1666
|
// Observability: surface what sandbox actually applied this run — the
|
|
1690
1667
|
// configured mode vs the resolved backend (auto may downgrade to off when
|
|
1691
1668
|
// no OS backend is available) + the network policy. Without this you can't
|
|
@@ -1709,6 +1686,7 @@ export class Engine {
|
|
|
1709
1686
|
? sandboxBackend
|
|
1710
1687
|
: { ...sandboxBackend, network: sandboxConfig.network },
|
|
1711
1688
|
cwd,
|
|
1689
|
+
workspace: workspaceContext,
|
|
1712
1690
|
shellEnv: this.runEnvironmentResolver.readShellEnv(cwd),
|
|
1713
1691
|
profile,
|
|
1714
1692
|
profileParams,
|
|
@@ -1965,7 +1943,7 @@ export class Engine {
|
|
|
1965
1943
|
* this method — only the composer and tool defs cross back out.
|
|
1966
1944
|
*/
|
|
1967
1945
|
async wireRunTooling(args) {
|
|
1968
|
-
const { options, session, cwd, toolCtx, profile, profileParams, runWorkspaceProfile, profileMemoryDir, sessionProfileOverrides, runPlanMode, } = args;
|
|
1946
|
+
const { options, session, cwd, workspaceContext, toolCtx, profile, profileParams, runWorkspaceProfile, profileMemoryDir, sessionProfileOverrides, runPlanMode, } = args;
|
|
1969
1947
|
const visibilityExplicitGoal = normalizeGoal(options?.goal);
|
|
1970
1948
|
const visibilityLifecycle = session.state.goalLifecycle;
|
|
1971
1949
|
const visibilityStoredGoal = visibilityLifecycle && isGoalLifecycleCurrent(visibilityLifecycle)
|
|
@@ -1984,6 +1962,7 @@ export class Engine {
|
|
|
1984
1962
|
const profileCanUseSkills = !runAllowedToolNames || runAllowedToolNames.has(skillToolDef.name);
|
|
1985
1963
|
const promptComposer = new PromptComposer(buildPromptComposerConfig({
|
|
1986
1964
|
cwd,
|
|
1965
|
+
workspace: workspaceContext,
|
|
1987
1966
|
model: this.config.llm.model,
|
|
1988
1967
|
preset: this.preset,
|
|
1989
1968
|
customSystemPrompt: profile?.disableInstructions
|
|
@@ -2018,9 +1997,11 @@ export class Engine {
|
|
|
2018
1997
|
runAllowedToolNames.has("cancel_goal")
|
|
2019
1998
|
? { hasGoal: hasRunnableGoal }
|
|
2020
1999
|
: undefined,
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2000
|
+
// Static sections named by the active preset are part of that preset's
|
|
2001
|
+
// contract. `disableCapabilityContext` suppresses ambient/dynamic
|
|
2002
|
+
// capability context, but removing these definitions leaves presets
|
|
2003
|
+
// with dangling section names (for example terminal-coding → coding).
|
|
2004
|
+
capabilityPromptSections: this.capabilityPromptSections,
|
|
2024
2005
|
dynamicContextProviders: this.capabilityDynamicContextProviders,
|
|
2025
2006
|
getSettingsManager: () => this.getSettingsManager(),
|
|
2026
2007
|
toolCatalog: this.toolCatalog,
|
|
@@ -2144,6 +2125,7 @@ export class Engine {
|
|
|
2144
2125
|
llm: this.config.llm,
|
|
2145
2126
|
sessionId: sid,
|
|
2146
2127
|
attachments: item.attachments,
|
|
2128
|
+
workspaceContext: toolCtx.workspace,
|
|
2147
2129
|
});
|
|
2148
2130
|
if (!steerImageInput.ok) {
|
|
2149
2131
|
throw new Error(steerImageInput.result.text);
|
|
@@ -2513,6 +2495,27 @@ export class Engine {
|
|
|
2513
2495
|
getConfig() {
|
|
2514
2496
|
return this.config;
|
|
2515
2497
|
}
|
|
2498
|
+
/** Release Engine-local registrations after its owning ChatSession is replaced. */
|
|
2499
|
+
async dispose() {
|
|
2500
|
+
if (this.disposed)
|
|
2501
|
+
return;
|
|
2502
|
+
if (this.runInProgress)
|
|
2503
|
+
throw new Error("Engine cannot be disposed while a run is in progress");
|
|
2504
|
+
this.disposed = true;
|
|
2505
|
+
try {
|
|
2506
|
+
const mcp = this.runtime?.mcpPool ?? this.mcpManager;
|
|
2507
|
+
await mcp?.unregisterOwner(this);
|
|
2508
|
+
}
|
|
2509
|
+
finally {
|
|
2510
|
+
this.hooks.clear();
|
|
2511
|
+
this.settingsHookHandles = [];
|
|
2512
|
+
this.workspaceAuthorities.clear();
|
|
2513
|
+
this.steerQueueBySid.clear();
|
|
2514
|
+
this.compactedMessagesBySession.clear();
|
|
2515
|
+
this.agentDefsCache = undefined;
|
|
2516
|
+
this.lastContextManager = undefined;
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2516
2519
|
/**
|
|
2517
2520
|
* Config hot-reload "layer 2": merge a disk-default config patch into this
|
|
2518
2521
|
* ALREADY-RUNNING session's `this.config`, reload settings hooks, and
|
|
@@ -2784,40 +2787,30 @@ export class Engine {
|
|
|
2784
2787
|
}
|
|
2785
2788
|
return had;
|
|
2786
2789
|
}
|
|
2787
|
-
/**
|
|
2788
|
-
* Persist a workspace pointer through the Engine that owns the live bundle.
|
|
2789
|
-
* Host-side workspace actions use this RPC-facing seam so advancing the disk
|
|
2790
|
-
* revision also rebases the active run before its next progress write.
|
|
2791
|
-
*/
|
|
2792
2790
|
setSessionWorkspace(sessionId, workspace) {
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2791
|
+
return setOwnedSessionWorkspace({
|
|
2792
|
+
sessionManager: this.sessionManager,
|
|
2793
|
+
activeState: this.activeRunSession?.state,
|
|
2794
|
+
sessionId,
|
|
2795
|
+
workspace,
|
|
2796
|
+
});
|
|
2797
|
+
}
|
|
2798
|
+
migrateSessionMainRoot(sessionId, project, mainRoot) {
|
|
2799
|
+
return migrateOwnedSessionMainRoot({
|
|
2800
|
+
sessionManager: this.sessionManager,
|
|
2801
|
+
activeState: this.activeRunSession?.state,
|
|
2802
|
+
authorities: this.workspaceAuthorities,
|
|
2803
|
+
sessionId,
|
|
2804
|
+
project,
|
|
2805
|
+
mainRoot,
|
|
2806
|
+
});
|
|
2805
2807
|
}
|
|
2806
|
-
/**
|
|
2807
|
-
* Reset a session's workspace pointer back to its main root. If the session is
|
|
2808
|
-
* actively running, mutate that live SessionBundle first so the run's next
|
|
2809
|
-
* saveState cannot resurrect a stale worktree pointer.
|
|
2810
|
-
*/
|
|
2811
2808
|
releaseSessionWorkspace(sessionId) {
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
: undefined);
|
|
2818
|
-
if (!mainRoot)
|
|
2819
|
-
return null;
|
|
2820
|
-
return this.setSessionWorkspace(sessionId, { root: mainRoot, kind: "main" });
|
|
2809
|
+
return releaseOwnedSessionWorkspace({
|
|
2810
|
+
sessionManager: this.sessionManager,
|
|
2811
|
+
activeState: this.activeRunSession?.state,
|
|
2812
|
+
sessionId,
|
|
2813
|
+
});
|
|
2821
2814
|
}
|
|
2822
2815
|
injectContext(sessionId, content) {
|
|
2823
2816
|
const session = this.sessionManager.resume(sessionId);
|
|
@@ -3282,7 +3275,10 @@ export class Engine {
|
|
|
3282
3275
|
const service = value({
|
|
3283
3276
|
isSubAgent: this.config.isSubAgent === true,
|
|
3284
3277
|
settings: this.getSettingsManager(),
|
|
3285
|
-
resolveSandbox: (cwd) => this.runEnvironmentResolver.resolveSandbox(
|
|
3278
|
+
resolveSandbox: (cwd) => this.runEnvironmentResolver.resolveSandbox({
|
|
3279
|
+
cwd,
|
|
3280
|
+
workspaceContext: legacySingleRootWorkspace(cwd),
|
|
3281
|
+
}),
|
|
3286
3282
|
readShellEnv: (cwd) => this.runEnvironmentResolver.readShellEnv(cwd),
|
|
3287
3283
|
getSessionManager: () => this.sessionManager,
|
|
3288
3284
|
});
|
|
@@ -3298,7 +3294,7 @@ export class Engine {
|
|
|
3298
3294
|
capabilityServices,
|
|
3299
3295
|
askUser: this.config.askUser,
|
|
3300
3296
|
browser: this.config.browserBridge,
|
|
3301
|
-
|
|
3297
|
+
workspaceBridge: this.config.workspaceBridge,
|
|
3302
3298
|
panels: this.config.panelBridge,
|
|
3303
3299
|
injectCredentialToBrowser: this.config.injectCredentialToBrowser,
|
|
3304
3300
|
isSubAgent: this.config.isSubAgent === true,
|
|
@@ -9,5 +9,6 @@ export interface InputAttachmentContext {
|
|
|
9
9
|
export interface BuildInputAttachmentContextOptions {
|
|
10
10
|
includeImageBytes?: boolean;
|
|
11
11
|
expectedSessionId?: string;
|
|
12
|
+
workspaceRoots?: readonly string[];
|
|
12
13
|
}
|
|
13
14
|
export declare function buildInputAttachmentContext(attachments: readonly InputAttachmentMeta[] | undefined, cwd: string, options?: BuildInputAttachmentContextOptions): Promise<InputAttachmentContext>;
|
|
@@ -113,7 +113,11 @@ export async function buildInputAttachmentContext(attachments, cwd, options = {}
|
|
|
113
113
|
});
|
|
114
114
|
continue;
|
|
115
115
|
}
|
|
116
|
-
const policy = classifyPath(resolved, {
|
|
116
|
+
const policy = classifyPath(resolved, {
|
|
117
|
+
workspaceRoot: cwdReal,
|
|
118
|
+
workspaceRoots: options.workspaceRoots,
|
|
119
|
+
operation: "read",
|
|
120
|
+
});
|
|
117
121
|
if (policy.decision !== "allow") {
|
|
118
122
|
errors.push(`attachment ${id === "(unknown)" ? displayPath : id} blocked by path policy: ${policy.reason}`);
|
|
119
123
|
diagnostics.push({
|
|
@@ -141,7 +145,7 @@ export async function buildInputAttachmentContext(attachments, cwd, options = {}
|
|
|
141
145
|
continue;
|
|
142
146
|
}
|
|
143
147
|
if (attachment.kind === "directory" || info.isDirectory()) {
|
|
144
|
-
const tree = await directoryTree(realPath, cwdReal).catch((err) => ({
|
|
148
|
+
const tree = await directoryTree(realPath, policy.matchedRoot ?? cwdReal).catch((err) => ({
|
|
145
149
|
lines: [`(directory tree unavailable: ${err.message})`],
|
|
146
150
|
truncated: true,
|
|
147
151
|
entryCount: 0,
|
|
@@ -2,6 +2,7 @@ import type { CredentialAccess } from "../credentials/access.js";
|
|
|
2
2
|
import { type SandboxBackend, type SandboxConfig } from "../tool-system/sandbox/index.js";
|
|
3
3
|
import type { EngineRuntime } from "./runtime.js";
|
|
4
4
|
import type { EngineConfig } from "./types.js";
|
|
5
|
+
import type { WorkspaceContext } from "../workspace/workspace-context.js";
|
|
5
6
|
type EnvironmentSettings = {
|
|
6
7
|
get(): unknown;
|
|
7
8
|
getForScope(scope: "user" | "project", cwd?: string): unknown;
|
|
@@ -13,14 +14,18 @@ export interface RunEnvironmentResolverDeps {
|
|
|
13
14
|
runtime?: Pick<EngineRuntime, "resolveSandbox">;
|
|
14
15
|
resolveBackend?: (config: SandboxConfig, cwd: string) => Promise<SandboxBackend>;
|
|
15
16
|
}
|
|
17
|
+
export interface RunEnvironmentInput {
|
|
18
|
+
cwd: string;
|
|
19
|
+
workspaceContext: WorkspaceContext;
|
|
20
|
+
}
|
|
16
21
|
export declare class RunEnvironmentResolver {
|
|
17
22
|
private readonly deps;
|
|
18
23
|
private readonly sandboxCache;
|
|
19
24
|
private readonly resolveBackend;
|
|
20
25
|
constructor(deps: RunEnvironmentResolverDeps);
|
|
21
|
-
resolveSandboxConfig(
|
|
22
|
-
resolveSandbox(
|
|
23
|
-
resolve(
|
|
26
|
+
resolveSandboxConfig(run: RunEnvironmentInput): SandboxConfig;
|
|
27
|
+
resolveSandbox(run: RunEnvironmentInput): Promise<SandboxBackend>;
|
|
28
|
+
resolve(run: RunEnvironmentInput): Promise<{
|
|
24
29
|
sandbox: SandboxBackend;
|
|
25
30
|
sandboxConfig: SandboxConfig;
|
|
26
31
|
shellEnv?: Record<string, string>;
|
|
@@ -1,6 +1,28 @@
|
|
|
1
|
-
import { resolveSandboxBackend, } from "../tool-system/sandbox/index.js";
|
|
1
|
+
import { expandPath, resolveSandboxBackend, } from "../tool-system/sandbox/index.js";
|
|
2
2
|
import { resolveSandboxConfig } from "./sandbox-config.js";
|
|
3
3
|
import { sandboxCacheKey } from "./sandbox-cache-key.js";
|
|
4
|
+
import { canonicalKey, canonicalPath } from "../workspace/canonical-key.js";
|
|
5
|
+
function appendWorkspaceRoots(config, run) {
|
|
6
|
+
const writableRoots = [];
|
|
7
|
+
const seen = new Set();
|
|
8
|
+
// Key configured roots after placeholder expansion so `${workspace}` wins
|
|
9
|
+
// over the equivalent primary path. Keep the first spelling and order.
|
|
10
|
+
for (const root of config.writableRoots) {
|
|
11
|
+
const key = canonicalKey(expandPath(root, run.cwd));
|
|
12
|
+
if (seen.has(key))
|
|
13
|
+
continue;
|
|
14
|
+
seen.add(key);
|
|
15
|
+
writableRoots.push(root);
|
|
16
|
+
}
|
|
17
|
+
for (const root of run.workspaceContext.roots) {
|
|
18
|
+
const key = canonicalKey(root.path);
|
|
19
|
+
if (seen.has(key))
|
|
20
|
+
continue;
|
|
21
|
+
seen.add(key);
|
|
22
|
+
writableRoots.push(canonicalPath(root.path));
|
|
23
|
+
}
|
|
24
|
+
return { ...config, writableRoots };
|
|
25
|
+
}
|
|
4
26
|
export class RunEnvironmentResolver {
|
|
5
27
|
deps;
|
|
6
28
|
sandboxCache = new Map();
|
|
@@ -9,7 +31,8 @@ export class RunEnvironmentResolver {
|
|
|
9
31
|
this.deps = deps;
|
|
10
32
|
this.resolveBackend = deps.resolveBackend ?? resolveSandboxBackend;
|
|
11
33
|
}
|
|
12
|
-
resolveSandboxConfig(
|
|
34
|
+
resolveSandboxConfig(run) {
|
|
35
|
+
const { cwd } = run;
|
|
13
36
|
const config = this.deps.config();
|
|
14
37
|
let projectSandbox;
|
|
15
38
|
let globalSandbox;
|
|
@@ -24,10 +47,11 @@ export class RunEnvironmentResolver {
|
|
|
24
47
|
catch {
|
|
25
48
|
// Missing settings fall through to the run default.
|
|
26
49
|
}
|
|
27
|
-
return resolveSandboxConfig(config.sandbox, projectSandbox, globalSandbox, config.headless === true);
|
|
50
|
+
return appendWorkspaceRoots(resolveSandboxConfig(config.sandbox, projectSandbox, globalSandbox, config.headless === true), run);
|
|
28
51
|
}
|
|
29
|
-
resolveSandbox(
|
|
30
|
-
const
|
|
52
|
+
resolveSandbox(run) {
|
|
53
|
+
const { cwd } = run;
|
|
54
|
+
const config = this.resolveSandboxConfig(run);
|
|
31
55
|
if (this.deps.runtime)
|
|
32
56
|
return this.deps.runtime.resolveSandbox(config, cwd);
|
|
33
57
|
const key = sandboxCacheKey(config, cwd);
|
|
@@ -42,9 +66,10 @@ export class RunEnvironmentResolver {
|
|
|
42
66
|
}
|
|
43
67
|
return cached;
|
|
44
68
|
}
|
|
45
|
-
async resolve(
|
|
46
|
-
const
|
|
47
|
-
const
|
|
69
|
+
async resolve(run) {
|
|
70
|
+
const { cwd } = run;
|
|
71
|
+
const sandboxConfig = this.resolveSandboxConfig(run);
|
|
72
|
+
const backend = await this.resolveSandbox(run);
|
|
48
73
|
return {
|
|
49
74
|
sandbox: backend.name === "off" ? backend : { ...backend, network: sandboxConfig.network },
|
|
50
75
|
sandboxConfig,
|
|
@@ -18,5 +18,6 @@ export declare function prepareRunImageInput(args: {
|
|
|
18
18
|
llm: Pick<LLMConfig, "provider" | "providerKind" | "model">;
|
|
19
19
|
sessionId?: string;
|
|
20
20
|
attachments?: readonly InputAttachmentMeta[];
|
|
21
|
+
workspaceContext?: import("../workspace/workspace-context.js").WorkspaceContext;
|
|
21
22
|
}): Promise<PrepareRunImageInputResult>;
|
|
22
23
|
export declare function buildRunUserMessageContent(parsedTask: ParsedTask, cwd: string, taskText: string): string | ContentBlock[];
|
|
@@ -38,6 +38,7 @@ export async function prepareRunImageInput(args) {
|
|
|
38
38
|
attachmentContext = await buildInputAttachmentContext(args.attachments, cwd, {
|
|
39
39
|
includeImageBytes: cap.supportsVision,
|
|
40
40
|
expectedSessionId: sessionId,
|
|
41
|
+
workspaceRoots: args.workspaceContext?.roots.map((root) => root.path),
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
44
|
catch (err) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Message, SessionKind } from "../types.js";
|
|
1
|
+
import type { Message, SessionKind, SessionProjectBinding } from "../types.js";
|
|
2
2
|
import type { SessionBundle, SessionManager } from "../session/session-manager.js";
|
|
3
3
|
import type { ParsedTask } from "./parse-task.js";
|
|
4
4
|
import type { buildRunUserMessageContent } from "./run-image-input.js";
|
|
@@ -13,6 +13,7 @@ export interface OpenRunSessionArgs {
|
|
|
13
13
|
cwd: string;
|
|
14
14
|
sessionKind: SessionKind;
|
|
15
15
|
sessionWorkspaceProfile: string | undefined;
|
|
16
|
+
projectBinding?: SessionProjectBinding;
|
|
16
17
|
/** Standing brief resent by the host each turn until the Session persists it. */
|
|
17
18
|
sessionBrief?: string;
|
|
18
19
|
llmModel: string;
|
|
@@ -29,7 +29,10 @@ export function openRunSession(args) {
|
|
|
29
29
|
};
|
|
30
30
|
if (options?.sessionId && args.sessionManager.exists(options.sessionId)) {
|
|
31
31
|
resumedFromDisk = true;
|
|
32
|
-
session = args.sessionManager.
|
|
32
|
+
session = args.sessionManager.resumeForRun(options.sessionId);
|
|
33
|
+
if (!session.state.project && args.projectBinding) {
|
|
34
|
+
args.sessionManager.saveStateOrUpdateFields(session.state, { project: args.projectBinding });
|
|
35
|
+
}
|
|
33
36
|
const cachedCompacted = args.cachedCompactedMessages;
|
|
34
37
|
messages = cachedCompacted ? [...cachedCompacted] : session.transcript.toMessages();
|
|
35
38
|
// If the previous run was Ctrl+C'd or crashed between an assistant
|
|
@@ -114,6 +117,9 @@ export function openRunSession(args) {
|
|
|
114
117
|
// Cold start: shape (2) reuses the host-supplied sid; shape (3)
|
|
115
118
|
// lets sessionManager generate one with nanoid.
|
|
116
119
|
session = args.sessionManager.create(args.cwd, args.llmModel, args.llmProvider, options?.sessionId, args.isSubAgent ? getCurrentSid() : undefined, args.isSubAgent ? "subagent" : args.origin, args.sessionKind, options?.ephemeral === true);
|
|
120
|
+
if (args.projectBinding) {
|
|
121
|
+
args.sessionManager.saveStateOrUpdateFields(session.state, { project: args.projectBinding });
|
|
122
|
+
}
|
|
117
123
|
const userMsg = { role: "user", content: args.userMessageContent };
|
|
118
124
|
claimClientMessageId(session, options?.clientMessageId, "submit");
|
|
119
125
|
if (args.parsedTask.hasImages)
|
|
@@ -16,6 +16,7 @@ export declare function resolveRunProfileState(args: {
|
|
|
16
16
|
}): RunProfileState;
|
|
17
17
|
export interface RunPromptComposerConfigInput {
|
|
18
18
|
cwd: ComposerOptions["cwd"];
|
|
19
|
+
workspace: ComposerOptions["workspace"];
|
|
19
20
|
/** Standing brief persisted on the Session (see SessionState.sessionBrief). */
|
|
20
21
|
sessionBrief?: ComposerOptions["sessionBrief"];
|
|
21
22
|
model: ComposerOptions["model"];
|
package/dist/engine/run-setup.js
CHANGED
|
@@ -23,9 +23,10 @@ export function resolveRunProfileState(args) {
|
|
|
23
23
|
}
|
|
24
24
|
/** Build the prompt-composer options for a run without capturing the Engine facade. */
|
|
25
25
|
export function buildPromptComposerConfig(args) {
|
|
26
|
-
const { cwd, model, preset, customSystemPrompt, appendSystemPrompt, responseLanguage, userProfile, workspaceProfile, sessionBrief, profileMemoryDir, instructionCompatFileNames, instructionBoundaryFinder, disabledSkills, disabledPlugins, skillAllowlist, memoriesMaxAgeDays, memoryCurrentProjectOnly, disableInstructions, disableMemoryContext, disableCapabilityContext, disableSourcesContext, goalToolState, capabilityPromptSections, dynamicContextProviders, getSettingsManager, toolCatalog, } = args;
|
|
26
|
+
const { cwd, workspace, model, preset, customSystemPrompt, appendSystemPrompt, responseLanguage, userProfile, workspaceProfile, sessionBrief, profileMemoryDir, instructionCompatFileNames, instructionBoundaryFinder, disabledSkills, disabledPlugins, skillAllowlist, memoriesMaxAgeDays, memoryCurrentProjectOnly, disableInstructions, disableMemoryContext, disableCapabilityContext, disableSourcesContext, goalToolState, capabilityPromptSections, dynamicContextProviders, getSettingsManager, toolCatalog, } = args;
|
|
27
27
|
return {
|
|
28
28
|
cwd,
|
|
29
|
+
workspace,
|
|
29
30
|
model,
|
|
30
31
|
preset,
|
|
31
32
|
customSystemPrompt,
|
|
@@ -29,6 +29,7 @@ export declare function buildRunToolContext(args: {
|
|
|
29
29
|
agentDefinitions: ToolContext["agentDefinitions"];
|
|
30
30
|
sandbox: ToolContext["sandbox"];
|
|
31
31
|
cwd: string;
|
|
32
|
+
workspace: ToolContext["workspace"];
|
|
32
33
|
shellEnv: ToolContext["shellEnv"];
|
|
33
34
|
profile: RunBehaviorProfile | undefined;
|
|
34
35
|
profileParams: Readonly<Record<string, unknown>>;
|
|
@@ -73,6 +74,7 @@ export declare function connectRunMcp(args: {
|
|
|
73
74
|
*/
|
|
74
75
|
export interface ToolVisibilityInputs {
|
|
75
76
|
cwd: string;
|
|
77
|
+
workspace?: ToolContext["workspace"];
|
|
76
78
|
hasGoal: boolean;
|
|
77
79
|
sessionId?: string;
|
|
78
80
|
settingsScope?: SettingsScope;
|