@cjhyy/code-shell-core 0.8.20 → 0.9.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/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 +4 -1
- package/dist/engine/engine-workspace-authority.d.ts +50 -0
- package/dist/engine/engine-workspace-authority.js +167 -0
- package/dist/engine/engine.d.ts +7 -10
- package/dist/engine/engine.js +87 -101
- 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 +6 -0
- 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 +2 -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/types.d.ts +2 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.extension.d.ts +1 -1
- package/dist/index.internal.d.ts +2 -2
- package/dist/index.internal.js +1 -0
- package/dist/index.js +1 -1
- package/dist/plugins/pluginAutomationTemplates.d.ts +2 -0
- package/dist/plugins/pluginAutomationTemplates.js +4 -0
- package/dist/prompt/composer.d.ts +4 -1
- package/dist/prompt/composer.js +11 -3
- package/dist/protocol/background-result-wakeup.d.ts +18 -0
- package/dist/protocol/background-result-wakeup.js +101 -0
- 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 +9 -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 +5 -6
- package/dist/protocol/server.js +62 -128
- 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 +45 -1
- package/dist/protocol/types.js +4 -0
- package/dist/session/session-manager.d.ts +18 -0
- package/dist/session/session-manager.js +87 -0
- package/dist/settings/manager.d.ts +7 -0
- package/dist/settings/manager.js +64 -3
- package/dist/tool-system/builtin/agent-notifications.d.ts +8 -0
- package/dist/tool-system/builtin/agent-notifications.js +19 -0
- package/dist/tool-system/builtin/config.d.ts +11 -0
- package/dist/tool-system/builtin/config.js +55 -10
- 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.js +5 -3
- package/dist/tool-system/builtin/view-image.js +8 -2
- package/dist/tool-system/builtin/write.js +5 -3
- package/dist/tool-system/context.d.ts +4 -1
- 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 +6 -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 +35 -0
- package/dist/workspace/workspace-context.js +128 -0
- package/package.json +1 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { resolveRunProfileState } from "./run-setup.js";
|
|
2
|
+
import { canonicalKey } from "../workspace/canonical-key.js";
|
|
3
|
+
import { legacySingleRootWorkspace, validateWorkspaceContext, workspacePrimaryRoot, } from "../workspace/workspace-context.js";
|
|
2
4
|
/**
|
|
3
5
|
* Resolve the working directory for a run. Precedence for legacy sessions:
|
|
4
6
|
* options.cwd > resumed session's state.cwd > config.cwd > process.cwd()
|
|
@@ -93,6 +95,37 @@ export async function resolveRunWorkspace(args) {
|
|
|
93
95
|
configCwd: args.configCwd,
|
|
94
96
|
processCwd: args.processCwd,
|
|
95
97
|
});
|
|
98
|
+
const rawWorkspaceContext = options?.workspaceContext ?? args.configWorkspaceContext;
|
|
99
|
+
let workspaceContext;
|
|
100
|
+
try {
|
|
101
|
+
workspaceContext = rawWorkspaceContext
|
|
102
|
+
? validateWorkspaceContext(rawWorkspaceContext)
|
|
103
|
+
: legacySingleRootWorkspace(cwd);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
return workspaceError(options?.sessionId, error instanceof Error ? error.message : String(error));
|
|
107
|
+
}
|
|
108
|
+
const authoritativeWorkspaceContext = rawWorkspaceContext !== undefined;
|
|
109
|
+
const primary = workspacePrimaryRoot(workspaceContext);
|
|
110
|
+
if (canonicalKey(primary.path) !== canonicalKey(cwd)) {
|
|
111
|
+
return workspaceError(options?.sessionId, `WorkspaceContext primary does not match effective cwd: ${primary.path} != ${cwd}`);
|
|
112
|
+
}
|
|
113
|
+
if (workspaceResume?.ok &&
|
|
114
|
+
workspaceResume.reason !== "legacy" &&
|
|
115
|
+
canonicalKey(workspaceResume.workspace.root) !== canonicalKey(primary.path)) {
|
|
116
|
+
return workspaceError(options?.sessionId, "WorkspaceContext primary does not match SessionWorkspace");
|
|
117
|
+
}
|
|
118
|
+
const binding = options?.sessionId
|
|
119
|
+
? args.sessionManager.readSessionProjectBinding(options.sessionId)
|
|
120
|
+
: undefined;
|
|
121
|
+
if (binding && !authoritativeWorkspaceContext) {
|
|
122
|
+
return workspaceError(options?.sessionId, "bound Session requires an authoritative WorkspaceContext");
|
|
123
|
+
}
|
|
124
|
+
if (binding &&
|
|
125
|
+
(binding.projectId !== workspaceContext.projectId ||
|
|
126
|
+
binding.mainRootId !== workspaceContext.sessionMainRootId)) {
|
|
127
|
+
return workspaceError(options?.sessionId, "WorkspaceContext does not match persisted project binding");
|
|
128
|
+
}
|
|
96
129
|
const profileState = profile?.disableWorkspaceProfile
|
|
97
130
|
? {
|
|
98
131
|
workspaceProfile: undefined,
|
|
@@ -114,7 +147,21 @@ export async function resolveRunWorkspace(args) {
|
|
|
114
147
|
runPermissionMode,
|
|
115
148
|
runPlanMode,
|
|
116
149
|
cwd,
|
|
150
|
+
workspaceContext,
|
|
151
|
+
authoritativeWorkspaceContext,
|
|
117
152
|
profileState,
|
|
118
153
|
},
|
|
119
154
|
};
|
|
120
155
|
}
|
|
156
|
+
function workspaceError(sessionId, message) {
|
|
157
|
+
return {
|
|
158
|
+
ok: false,
|
|
159
|
+
result: {
|
|
160
|
+
text: `ERROR: ${message}`,
|
|
161
|
+
reason: "completed",
|
|
162
|
+
sessionId: sessionId ?? "workspace-invalid",
|
|
163
|
+
turnCount: 0,
|
|
164
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
@@ -21,6 +21,7 @@ export interface CreateSubAgentSpawnerDeps {
|
|
|
21
21
|
parentSandbox: SandboxConfig;
|
|
22
22
|
presetName: AgentPresetName;
|
|
23
23
|
cwd: string;
|
|
24
|
+
workspaceContext?: import("../workspace/workspace-context.js").WorkspaceContext;
|
|
24
25
|
permissionMode: NonNullable<EngineConfig["permissionMode"]>;
|
|
25
26
|
modelPool?: ModelPool;
|
|
26
27
|
appendParentSubagent: (agentId: string, description: string) => void;
|
|
@@ -233,6 +233,7 @@ export function createSubAgentSpawner(deps) {
|
|
|
233
233
|
retryMaxAttempts: 2,
|
|
234
234
|
},
|
|
235
235
|
cwd: deps.cwd,
|
|
236
|
+
workspaceContext: deps.workspaceContext ?? deps.parentConfig.workspaceContext,
|
|
236
237
|
permissionMode: deps.permissionMode,
|
|
237
238
|
preset: deps.presetName,
|
|
238
239
|
enabledBuiltinTools: scope.enabled,
|
package/dist/engine/types.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export interface EngineConfig {
|
|
|
33
33
|
*/
|
|
34
34
|
clientDefaults?: ClientDefaults;
|
|
35
35
|
cwd?: string;
|
|
36
|
+
/** Trusted host-provided root authorization for runs created by this Engine. */
|
|
37
|
+
workspaceContext?: import("../workspace/workspace-context.js").WorkspaceContext;
|
|
36
38
|
maxTurns?: number;
|
|
37
39
|
/**
|
|
38
40
|
* Override the goal-mode consecutive-stop-block cap (TODO 3.1). Falls back to
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Public API exports.
|
|
5
5
|
*/
|
|
6
|
-
export declare const VERSION = "0.
|
|
7
|
-
export type { Message, ContentBlock, ToolDefinition, ToolCall, ToolResult, RegisteredTool, TranscriptEvent, TranscriptEventType, SessionState, SessionKind, SessionWorkspace, SessionForkLineage, ContextUsageAnchor, SessionStatus, TokenUsage, CompiledInput, PermissionDecision, PermissionMode, PermissionRule, TurnPhase, TurnResult, TerminalReason, TurnCompletionKind, StreamEvent, StreamCallback, LLMConfig, ClientDefaults, LLMResponse, Settings, MCPServerConfig, } from "./types.js";
|
|
6
|
+
export declare const VERSION = "0.9.1";
|
|
7
|
+
export type { Message, ContentBlock, ToolDefinition, ToolCall, ToolResult, RegisteredTool, TranscriptEvent, TranscriptEventType, SessionState, SessionKind, SessionWorkspace, SessionProjectBinding, SessionForkLineage, ContextUsageAnchor, SessionStatus, TokenUsage, CompiledInput, PermissionDecision, PermissionMode, PermissionRule, TurnPhase, TurnResult, TerminalReason, TurnCompletionKind, StreamEvent, StreamCallback, LLMConfig, ClientDefaults, LLMResponse, Settings, MCPServerConfig, } from "./types.js";
|
|
8
8
|
export type { GoalConfig, GoalLifecycleConfig, GoalLifecyclePhase, GoalLifecycleTerminalReason, GoalLifecycleV1, } from "./goal/lifecycle.js";
|
|
9
9
|
export { FrameworkError, LLMError, LLMRateLimitError, ContextLimitError, ToolError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, PermissionDeniedError, SessionError, TranscriptError, ConfigError, CompositionError, SandboxUnavailableError, } from "./exceptions.js";
|
|
10
10
|
export { compileComposition, toCompositionSnapshot, computeCompositionDigest, compositionToolCatalog, resolvePresetFromComposition, } from "./composition/index.js";
|
|
@@ -62,7 +62,7 @@ export { AgentClient, type BackgroundAgentCompletedHandler } from "./protocol/cl
|
|
|
62
62
|
export { createInProcessTransport, StdioTransport, type Transport } from "./protocol/transport.js";
|
|
63
63
|
export { SocketTransport, listenTcp, type TcpListenResult } from "./protocol/tcp-transport.js";
|
|
64
64
|
export { createServer, createClient, type CreateServerOptions, type CreateClientOptions, type ServerHandle, } from "./protocol/factories.js";
|
|
65
|
-
export { Methods, ErrorCodes, type RpcMessage, type RunResult, type ForkSessionParams, type ForkSessionResult as ProtocolForkSessionResult, } from "./protocol/types.js";
|
|
65
|
+
export { Methods, ErrorCodes, type RpcMessage, type RunResult, type ForkSessionParams, type ForkSessionResult as ProtocolForkSessionResult, type MigrateSessionMainRootResult, } from "./protocol/types.js";
|
|
66
66
|
export type * from "./protocol/mobile-remote-types.js";
|
|
67
67
|
export { Transcript } from "./session/transcript.js";
|
|
68
68
|
export { SessionManager, sessionMainRoot, codeShellHome, sessionsRoot, buildForkState, buildForkTranscript, type ForkSessionOptions, type ForkSessionResult, } from "./session/session-manager.js";
|
|
@@ -26,7 +26,7 @@ export type { ExposureRationale } from "./tool-system/external-tool-exposure.js"
|
|
|
26
26
|
export { webSearchTool } from "./tool-system/builtin/web-search.js";
|
|
27
27
|
export { webFetchTool } from "./tool-system/builtin/web-fetch.js";
|
|
28
28
|
export { extractJSON, extractJSONArray } from "./utils/json.js";
|
|
29
|
-
export type { ExtensionQueryHandler, ExtensionTool
|
|
29
|
+
export type { ExtensionQueryHandler, ExtensionTool } from "./tool-system/capability-module.js";
|
|
30
30
|
export type { SessionWorkspace } from "./types.js";
|
|
31
31
|
export type { CapabilityArtifactDetector, CapabilityDynamicContextProvider, CapabilityToolServiceHost, } from "./capabilities/index.js";
|
|
32
32
|
export type { AgentModule, AgentEngineContributions, AgentProtocolContributions, AgentModuleToolContribution, ResolvedComposition, } from "./composition/types.js";
|
package/dist/index.internal.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { getGraphemeSegmenter, firstGrapheme, lastGrapheme, getWordSegmenter, getRelativeTimeFormat, getTimeZone, getSystemLocaleLanguage, } from "./utils/intl.js";
|
|
9
9
|
export { env } from "./utils/env.js";
|
|
10
|
+
export { canonicalKey, canonicalPath, computeWorkspaceRootsDigest, createWorkspaceContext, legacySingleRootWorkspace, validateWorkspaceContext, workspacePrimaryRoot, type ProjectId, type ProjectRootContext, type ProjectRootId, type WorkspaceContext, type WorkspaceContextInput, } from "./workspace/workspace-context.js";
|
|
10
11
|
export { default as sliceAnsi } from "./utils/sliceAnsi.js";
|
|
11
12
|
export { execFileNoThrow } from "./utils/execFileNoThrow.js";
|
|
12
13
|
export { findExecutable, resolveExecutable, setGitPathOverride, resolveGit, isGitAvailable, resolveGitPath, } from "./utils/exec.js";
|
|
@@ -27,7 +28,6 @@ export { getInteractiveApprovalBackend } from "./tool-system/permission.js";
|
|
|
27
28
|
export { defaultSandboxConfig, type SandboxConfig } from "./tool-system/sandbox/index.js";
|
|
28
29
|
export { buildNotificationMessage, buildNotificationSummary, notificationQueue, agentNotificationBus, notificationItemToStreamEvent, type NotificationItem, } from "./tool-system/builtin/agent-notifications.js";
|
|
29
30
|
export { backgroundJobRegistry } from "./tool-system/builtin/background-jobs.js";
|
|
30
|
-
export type { BackgroundAgentCompletedEvent } from "./types.js";
|
|
31
31
|
export { startAutomation, type StartAutomationDeps, type AutomationHandle, CronScheduler, cronScheduler, type CronExecutionOutcome, type CronJob, type CronJobLifecycleEvent, type CronPermissionLevel, type CronTemplateSource, type CreateJobOptions, type UpdateJobPatch, CronStore, defaultCronStorePath, bindCronToEngine, bindCronToRunManager, type CronRunner, type CronRunRequest, type CronRunResult, type RunSubmitter, isCronExpression, parseCronExpression, nextCronTime, validateSchedule, type ParsedCron, resolveWritePolicy, wrapUntrustedInput, type WritePolicy, runWriteJobInWorktree, type WriteJobGitOps, type RunWriteJobInput, type RunWriteJobResult, } from "./automation/index.js";
|
|
32
32
|
export { asyncAgentRegistry, type AsyncAgentEntry } from "./tool-system/builtin/agent-registry.js";
|
|
33
33
|
export { backgroundShellManager, BackgroundShellManager, type BgShell, type BgShellStatus, } from "./runtime/background-shell.js";
|
|
@@ -46,7 +46,7 @@ export { capabilitiesFor, type Capability } from "./llm/capabilities/index.js";
|
|
|
46
46
|
export { reasoningControlFor, type ReasoningControl, } from "./llm/capabilities/reasoning-control.js";
|
|
47
47
|
export { REASONING_EFFORTS, type ReasoningSetting } from "./llm/reasoning-setting.js";
|
|
48
48
|
export { type ProviderConfig } from "./llm/provider-catalog.js";
|
|
49
|
-
export type { ApprovalRequest, ApprovalResult, ApprovalScope, TaskInfo } from "./types.js";
|
|
49
|
+
export type { ApprovalRequest, ApprovalResult, ApprovalScope, BackgroundAgentCompletedEvent, TaskInfo, } from "./types.js";
|
|
50
50
|
export { fileCache } from "./tool-system/builtin/file-cache.js";
|
|
51
51
|
export { validateToolArgs } from "./tool-system/validation.js";
|
|
52
52
|
export { createOffBackend } from "./tool-system/sandbox/off.js";
|
package/dist/index.internal.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// ─── Utils (shared primitives used by TUI) ───────────────────────
|
|
9
9
|
export { getGraphemeSegmenter, firstGrapheme, lastGrapheme, getWordSegmenter, getRelativeTimeFormat, getTimeZone, getSystemLocaleLanguage, } from "./utils/intl.js";
|
|
10
10
|
export { env } from "./utils/env.js";
|
|
11
|
+
export { canonicalKey, canonicalPath, computeWorkspaceRootsDigest, createWorkspaceContext, legacySingleRootWorkspace, validateWorkspaceContext, workspacePrimaryRoot, } from "./workspace/workspace-context.js";
|
|
11
12
|
export { default as sliceAnsi } from "./utils/sliceAnsi.js";
|
|
12
13
|
export { execFileNoThrow } from "./utils/execFileNoThrow.js";
|
|
13
14
|
export { findExecutable, resolveExecutable, setGitPathOverride, resolveGit, isGitAvailable, resolveGitPath, } from "./utils/exec.js";
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Public API exports.
|
|
5
5
|
*/
|
|
6
|
-
export const VERSION = "0.
|
|
6
|
+
export const VERSION = "0.9.1";
|
|
7
7
|
// ─── Exceptions ──────────────────────────────────────────────────
|
|
8
8
|
export { FrameworkError, LLMError, LLMRateLimitError, ContextLimitError, ToolError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, PermissionDeniedError, SessionError, TranscriptError, ConfigError, CompositionError, SandboxUnavailableError, } from "./exceptions.js";
|
|
9
9
|
// ─── Composition (AgentModule / ResolvedComposition) ─────────────
|
|
@@ -6,6 +6,8 @@ export interface InstantiatePluginAutomationTemplateOptions {
|
|
|
6
6
|
templateId: string;
|
|
7
7
|
expectedRevision: string;
|
|
8
8
|
cwd?: string;
|
|
9
|
+
projectId?: string;
|
|
10
|
+
rootId?: string;
|
|
9
11
|
/** Effective project/global disable list computed by the host for this cwd. */
|
|
10
12
|
disabledPluginNames: ReadonlySet<string>;
|
|
11
13
|
maxJobs?: number;
|
|
@@ -38,6 +38,10 @@ export function instantiatePluginAutomationTemplate(options) {
|
|
|
38
38
|
const template = contribution.template;
|
|
39
39
|
return options.scheduler.create(template.title.default, template.schedule, template.prompt, {
|
|
40
40
|
...(template.workspace === "current" && options.cwd ? { cwd: options.cwd } : {}),
|
|
41
|
+
...(template.workspace === "current" && options.projectId
|
|
42
|
+
? { projectId: options.projectId }
|
|
43
|
+
: {}),
|
|
44
|
+
...(template.workspace === "current" && options.rootId ? { rootId: options.rootId } : {}),
|
|
41
45
|
...(template.timezone ? { timezone: template.timezone } : {}),
|
|
42
46
|
permissionLevel: template.permissionLevel,
|
|
43
47
|
templateSource: {
|
|
@@ -10,8 +10,11 @@ import { type ScanOptions } from "./instruction-scanner.js";
|
|
|
10
10
|
import { type AgentPreset } from "../preset/index.js";
|
|
11
11
|
import type { BuiltinTool } from "../tool-system/builtin/index.js";
|
|
12
12
|
import type { CapabilityDynamicContextProvider } from "../capabilities/index.js";
|
|
13
|
+
import { type WorkspaceContext } from "../workspace/workspace-context.js";
|
|
13
14
|
export interface ComposerOptions {
|
|
14
15
|
cwd: string;
|
|
16
|
+
/** Run workspace; omitted by legacy callers that only provide cwd. */
|
|
17
|
+
workspace?: WorkspaceContext;
|
|
15
18
|
model: string;
|
|
16
19
|
instructionOptions?: ScanOptions;
|
|
17
20
|
/** Resolved preset — used to load section-based prompt. */
|
|
@@ -106,9 +109,9 @@ export interface ComposerOptions {
|
|
|
106
109
|
disableSourcesContext?: boolean;
|
|
107
110
|
}
|
|
108
111
|
export declare class PromptComposer {
|
|
109
|
-
private readonly options;
|
|
110
112
|
private sectionCache;
|
|
111
113
|
private cachedInstructions;
|
|
114
|
+
private readonly options;
|
|
112
115
|
constructor(options: ComposerOptions);
|
|
113
116
|
/**
|
|
114
117
|
* Build the system prompt from sections.
|
package/dist/prompt/composer.js
CHANGED
|
@@ -11,12 +11,18 @@ import { MemoryManager } from "../session/memory.js";
|
|
|
11
11
|
import { scanSkills } from "../skills/index.js";
|
|
12
12
|
import { buildSkillListing } from "../tool-system/builtin/skill-prompt.js";
|
|
13
13
|
import { resolveAgentPreset, buildPresetSystemPrompt } from "../preset/index.js";
|
|
14
|
+
import { legacySingleRootWorkspace, validateWorkspaceContext, } from "../workspace/workspace-context.js";
|
|
14
15
|
export class PromptComposer {
|
|
15
|
-
options;
|
|
16
16
|
sectionCache = new SectionCache();
|
|
17
17
|
cachedInstructions = null;
|
|
18
|
+
options;
|
|
18
19
|
constructor(options) {
|
|
19
|
-
this.options =
|
|
20
|
+
this.options = {
|
|
21
|
+
...options,
|
|
22
|
+
workspace: options.workspace === undefined
|
|
23
|
+
? legacySingleRootWorkspace(options.cwd)
|
|
24
|
+
: validateWorkspaceContext(options.workspace),
|
|
25
|
+
};
|
|
20
26
|
}
|
|
21
27
|
/**
|
|
22
28
|
* Build the system prompt from sections.
|
|
@@ -58,7 +64,7 @@ export class PromptComposer {
|
|
|
58
64
|
const preset = this.options.preset ?? resolveAgentPreset();
|
|
59
65
|
const parts = await Promise.all((this.options.dynamicContextProviders ?? []).map(async (provider) => {
|
|
60
66
|
try {
|
|
61
|
-
return await provider({ cwd: this.options.cwd, preset });
|
|
67
|
+
return await provider({ cwd: this.options.cwd, workspace: this.options.workspace, preset });
|
|
62
68
|
}
|
|
63
69
|
catch {
|
|
64
70
|
// A capability's optional context must not make a turn fail.
|
|
@@ -164,6 +170,8 @@ export class PromptComposer {
|
|
|
164
170
|
const lines = [
|
|
165
171
|
`You are an AI agent powered by ${this.options.model}.`,
|
|
166
172
|
`Working directory: ${this.options.cwd}`,
|
|
173
|
+
`Workspace roots (${this.options.workspace.roots.length}):`,
|
|
174
|
+
...this.options.workspace.roots.map((root) => `- [${root.role}] ${root.path} (rootId: ${root.id})`),
|
|
167
175
|
`Platform: ${process.platform}`,
|
|
168
176
|
`Shell: ${process.env.SHELL ?? "unknown"}`,
|
|
169
177
|
];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { StreamEvent } from "../types.js";
|
|
2
|
+
import type { ApprovalRouter } from "../tool-system/permission.js";
|
|
3
|
+
import type { ChatSession } from "./chat-session.js";
|
|
4
|
+
import type { ChatSessionManager } from "./chat-session-manager.js";
|
|
5
|
+
interface BackgroundResultWakeOptions {
|
|
6
|
+
sessionId: string;
|
|
7
|
+
manager: ChatSessionManager | null;
|
|
8
|
+
rehydrate(sessionId: string): Promise<ChatSession | null>;
|
|
9
|
+
approvalRouter: ApprovalRouter;
|
|
10
|
+
onStream(event: StreamEvent): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Drain pending background results into exactly one synthetic continuation.
|
|
14
|
+
* Busy sessions are awaited so a completion cannot fall into the gap between
|
|
15
|
+
* the notification bus callback and the interactive run-boundary re-check.
|
|
16
|
+
*/
|
|
17
|
+
export declare function wakeSessionForBackgroundResults({ sessionId, manager, rehydrate, approvalRouter, onStream, }: BackgroundResultWakeOptions): Promise<boolean>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { logger } from "../logging/logger.js";
|
|
2
|
+
import { buildNotificationMessage, notificationQueue, } from "../tool-system/builtin/agent-notifications.js";
|
|
3
|
+
/**
|
|
4
|
+
* Drain pending background results into exactly one synthetic continuation.
|
|
5
|
+
* Busy sessions are awaited so a completion cannot fall into the gap between
|
|
6
|
+
* the notification bus callback and the interactive run-boundary re-check.
|
|
7
|
+
*/
|
|
8
|
+
export async function wakeSessionForBackgroundResults({ sessionId, manager, rehydrate, approvalRouter, onStream, }) {
|
|
9
|
+
if (!manager) {
|
|
10
|
+
logger.debug("bg_wakeup.skipped", { sessionId, reason: "no_chat_manager" });
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
if (manager.isUnavailable(sessionId)) {
|
|
14
|
+
logger.debug("bg_wakeup.skipped", { sessionId, reason: "session_unavailable" });
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
let session = manager.get(sessionId) ?? (await rehydrate(sessionId));
|
|
18
|
+
if (!session) {
|
|
19
|
+
logger.debug("bg_wakeup.skipped", { sessionId, reason: "session_missing" });
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
while (session.isBusy()) {
|
|
23
|
+
logger.debug("bg_wakeup.waiting_for_idle", {
|
|
24
|
+
sessionId,
|
|
25
|
+
pendingCount: notificationQueue.getSnapshot(sessionId).length,
|
|
26
|
+
});
|
|
27
|
+
await session.settled;
|
|
28
|
+
if (manager.isUnavailable(sessionId)) {
|
|
29
|
+
logger.debug("bg_wakeup.skipped", {
|
|
30
|
+
sessionId,
|
|
31
|
+
reason: "session_became_unavailable",
|
|
32
|
+
});
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const current = manager.get(sessionId);
|
|
36
|
+
if (!current) {
|
|
37
|
+
logger.debug("bg_wakeup.skipped", { sessionId, reason: "session_evicted_after_settle" });
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
session = current;
|
|
41
|
+
}
|
|
42
|
+
// Headless/automation runs are one-shot and have no continuation consumer.
|
|
43
|
+
if (session.engine.isHeadless()) {
|
|
44
|
+
logger.debug("bg_wakeup.skipped", { sessionId, reason: "headless" });
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
// A user Stop must win over a later background completion.
|
|
48
|
+
if (session.wasCancelledSinceLastTurn()) {
|
|
49
|
+
logger.debug("bg_wakeup.skipped", { sessionId, reason: "cancelled_since_last_turn" });
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
let runWorkspace = {};
|
|
53
|
+
try {
|
|
54
|
+
const resolver = session.engine.resolveSessionRunWorkspace;
|
|
55
|
+
runWorkspace = resolver?.call(session.engine, sessionId) ?? {};
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
// Resolve before draining so an unavailable authoritative worktree context
|
|
59
|
+
// leaves the completion recoverable by a later user run.
|
|
60
|
+
logger.warn("bg_wakeup.workspace_unavailable", {
|
|
61
|
+
sessionId,
|
|
62
|
+
error: error instanceof Error ? error.message : String(error),
|
|
63
|
+
});
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const pending = notificationQueue.drainAll(sessionId);
|
|
67
|
+
if (pending.length === 0) {
|
|
68
|
+
logger.debug("bg_wakeup.skipped", { sessionId, reason: "no_pending_results" });
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const task = `<system-reminder>\n${buildNotificationMessage(pending)}\n</system-reminder>`;
|
|
72
|
+
try {
|
|
73
|
+
const result = await session.enqueueTurn(task, {
|
|
74
|
+
injected: true,
|
|
75
|
+
...runWorkspace,
|
|
76
|
+
onStream,
|
|
77
|
+
approvalRouter,
|
|
78
|
+
});
|
|
79
|
+
if (result.turnCount === 0) {
|
|
80
|
+
const restored = notificationQueue.restoreResults(sessionId, pending);
|
|
81
|
+
logger.warn("bg_wakeup.turn_not_started", {
|
|
82
|
+
sessionId,
|
|
83
|
+
restored,
|
|
84
|
+
reason: result.reason,
|
|
85
|
+
text: result.text.slice(0, 500),
|
|
86
|
+
});
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
92
|
+
const restored = notificationQueue.restoreResults(sessionId, pending);
|
|
93
|
+
logger.warn("bg_wakeup.turn_failed", { sessionId, error: message, restored });
|
|
94
|
+
// A setup failure can occur before the turn loop emits its own terminal
|
|
95
|
+
// event. Emit an error so every renderer clears its busy state, but keep
|
|
96
|
+
// the result queued so a later user turn can consume it safely.
|
|
97
|
+
onStream({ type: "error", error: message || "background wakeup failed" });
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { ChatSession } from "./chat-session.js";
|
|
2
2
|
import type { SessionKind } from "../types.js";
|
|
3
|
+
import type { SessionProjectBinding, SessionWorkspace } from "../types.js";
|
|
3
4
|
import type { Engine } from "../engine/engine.js";
|
|
4
5
|
import type { EngineRuntime } from "../engine/runtime.js";
|
|
5
6
|
import type { EngineConfig } from "../engine/types.js";
|
|
6
|
-
export type EngineConfigSlice = Pick<EngineConfig, "permissionMode" | "preset" | "customSystemPrompt" | "appendSystemPrompt" | "goal" | "maxTurns" | "maxContextTokens" | "cwd" | "projectTrusted" | "sessionStorageDir">;
|
|
7
|
+
export type EngineConfigSlice = Pick<EngineConfig, "permissionMode" | "preset" | "customSystemPrompt" | "appendSystemPrompt" | "goal" | "maxTurns" | "maxContextTokens" | "cwd" | "workspaceContext" | "projectTrusted" | "sessionStorageDir">;
|
|
7
8
|
/** Identity scope every ChatSessionManager belongs to when none is injected. */
|
|
8
9
|
export declare const LOCAL_CHAT_IDENTITY = "local";
|
|
9
10
|
export interface ChatSessionManagerOptions {
|
|
@@ -48,11 +49,30 @@ export interface LiveChatSessionSnapshot {
|
|
|
48
49
|
}>;
|
|
49
50
|
}
|
|
50
51
|
export declare const CLOSED_CHAT_SESSION_TOMBSTONE_LIMIT = 4096;
|
|
52
|
+
export interface ResidentSessionMainRootMigration {
|
|
53
|
+
project: SessionProjectBinding;
|
|
54
|
+
mainRoot: string;
|
|
55
|
+
workspaceContext: import("../workspace/workspace-context.js").WorkspaceContext;
|
|
56
|
+
projectTrusted: boolean;
|
|
57
|
+
}
|
|
58
|
+
export type SessionMigrationOwnership = {
|
|
59
|
+
status: "resident";
|
|
60
|
+
session: ChatSession;
|
|
61
|
+
} | {
|
|
62
|
+
status: "not-resident";
|
|
63
|
+
ownershipToken: string;
|
|
64
|
+
} | {
|
|
65
|
+
status: "failed";
|
|
66
|
+
error: string;
|
|
67
|
+
};
|
|
51
68
|
export declare class ChatSessionManager {
|
|
52
69
|
private readonly sessions;
|
|
53
70
|
private readonly closingSessions;
|
|
54
71
|
private readonly closedSessions;
|
|
55
72
|
private readonly sessionGeneration;
|
|
73
|
+
private readonly sessionSlices;
|
|
74
|
+
private readonly migrationClaims;
|
|
75
|
+
private readonly residentMigrations;
|
|
56
76
|
readonly runtime: EngineRuntime;
|
|
57
77
|
/** Identity scope this manager serves ("local" unless injected). */
|
|
58
78
|
readonly identity: string;
|
|
@@ -76,6 +96,26 @@ export declare class ChatSessionManager {
|
|
|
76
96
|
*/
|
|
77
97
|
forIdentity(identity: string): ChatSessionManager;
|
|
78
98
|
getOrCreate(sessionId: string, slice: EngineConfigSlice): Promise<ChatSession>;
|
|
99
|
+
/**
|
|
100
|
+
* Prove whether this worker currently owns a resident Engine, or fence the
|
|
101
|
+
* Session so Main can perform one durable migration without a re-resume race.
|
|
102
|
+
*/
|
|
103
|
+
beginSessionMigration(sessionId: string, ownershipToken: string): SessionMigrationOwnership;
|
|
104
|
+
/** Release only the exact claim minted for this Session. */
|
|
105
|
+
completeSessionMigration(sessionId: string, ownershipToken: string): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Rebuild an idle resident Engine against a new authoritative main root.
|
|
108
|
+
*
|
|
109
|
+
* Transaction order is intentional:
|
|
110
|
+
* 1. construct and restore the candidate while durable state still points at
|
|
111
|
+
* the old owner (factory/restore failure is therefore a no-op),
|
|
112
|
+
* 2. atomically commit durable state through the old owning Engine,
|
|
113
|
+
* 3. synchronously swap the Engine inside the existing ChatSession,
|
|
114
|
+
* 4. dispose the unreachable old Engine before reporting success.
|
|
115
|
+
*
|
|
116
|
+
* No await occurs between the idle check, durable commit and owner swap.
|
|
117
|
+
*/
|
|
118
|
+
migrateResidentSessionMainRoot(sessionId: string, target: ResidentSessionMainRootMigration): Promise<SessionWorkspace>;
|
|
79
119
|
/**
|
|
80
120
|
* Cold-resume a persisted session using its own cwd. The first detached
|
|
81
121
|
* Engine is only a storage probe; all actual work runs on the second Engine
|
|
@@ -121,6 +161,7 @@ export declare class ChatSessionManager {
|
|
|
121
161
|
startIdleSweeper(intervalMs?: number): void;
|
|
122
162
|
stopIdleSweeper(): void;
|
|
123
163
|
private unregisterMcpOwner;
|
|
164
|
+
private disposeEngine;
|
|
124
165
|
private engineSessionManager;
|
|
125
166
|
}
|
|
126
167
|
/**
|