@cjhyy/code-shell-core 0.8.8 → 0.8.9
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/engine/engine.js +36 -21
- package/dist/engine/run-finalize.js +8 -6
- package/dist/engine/run-session-open.js +1 -1
- package/dist/engine/run-setup.d.ts +4 -0
- package/dist/engine/run-setup.js +5 -1
- package/dist/engine/run-tooling.js +7 -1
- package/dist/engine/run-types.d.ts +24 -0
- package/dist/engine/run-types.js +21 -0
- package/dist/engine/run-workspace.js +11 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/panel-apps/manifest.d.ts +11 -11
- package/dist/panel-apps/manifest.js +3 -1
- package/dist/prompt/composer.d.ts +8 -0
- package/dist/prompt/composer.js +8 -0
- package/dist/protocol/chat-session-manager.js +9 -0
- package/dist/protocol/server.js +37 -0
- package/dist/protocol/types.d.ts +12 -0
- package/dist/session/session-manager.d.ts +3 -1
- package/dist/session/session-manager.js +42 -26
- package/dist/tool-system/builtin/index.js +4 -1
- package/dist/tool-system/path-policy.js +25 -33
- package/package.json +1 -1
package/dist/engine/engine.js
CHANGED
|
@@ -44,7 +44,7 @@ import { detectProviderFromApiKey, buildModelPool } from "../onboarding.js";
|
|
|
44
44
|
import { detectPastedNoise } from "../utils/task-sanitizer.js";
|
|
45
45
|
import { PromptCacheDiagnosticRecorder, promptCacheDropHint, } from "./prompt-cache-diagnostics.js";
|
|
46
46
|
import { buildRunUserMessageContent, prepareRunImageInput } from "./run-image-input.js";
|
|
47
|
-
import { QUICK_CHAT_RESTRICTED_PROFILE, } from "./run-types.js";
|
|
47
|
+
import { ISOLATED_TASK_PROFILE, QUICK_CHAT_RESTRICTED_PROFILE, } from "./run-types.js";
|
|
48
48
|
import { createSubAgentSpawner } from "./subagent-spawner.js";
|
|
49
49
|
import { AuxiliaryPipeline, sameLlmIdentity } from "./auxiliary-pipeline.js";
|
|
50
50
|
import { PermissionController } from "./permission-controller.js";
|
|
@@ -413,6 +413,7 @@ export class Engine {
|
|
|
413
413
|
// extension modules — later registrations override earlier ones by id.
|
|
414
414
|
this.behaviorProfiles = new Map([
|
|
415
415
|
QUICK_CHAT_RESTRICTED_PROFILE,
|
|
416
|
+
ISOLATED_TASK_PROFILE,
|
|
416
417
|
...(config.behaviorProfiles ?? []),
|
|
417
418
|
...(config.extensionModules ?? []).flatMap((module) => module.behaviorProfiles ?? []),
|
|
418
419
|
].map((profile) => [profile.id, profile]));
|
|
@@ -1099,16 +1100,18 @@ export class Engine {
|
|
|
1099
1100
|
session = openedResult.opened.session;
|
|
1100
1101
|
this.stampRunToolContext(toolCtx, session, options);
|
|
1101
1102
|
const sessionRun = runWithSid(session.state.sessionId, async () => {
|
|
1102
|
-
const hookMessages =
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1103
|
+
const hookMessages = profile?.disableHooks
|
|
1104
|
+
? []
|
|
1105
|
+
: await this.runSessionStartHooks({
|
|
1106
|
+
session,
|
|
1107
|
+
task,
|
|
1108
|
+
cwd,
|
|
1109
|
+
runPermissionMode,
|
|
1110
|
+
resumedFromDisk,
|
|
1111
|
+
options,
|
|
1112
|
+
taskText,
|
|
1113
|
+
messages,
|
|
1114
|
+
});
|
|
1112
1115
|
const sid = session.state.sessionId;
|
|
1113
1116
|
const { contextManager, llmClientPromise, toolExecutor } = this.wireRunContextAndPermission({
|
|
1114
1117
|
session,
|
|
@@ -1939,17 +1942,23 @@ export class Engine {
|
|
|
1939
1942
|
// A profile whose tool allowlist excludes the Skill tool can never invoke
|
|
1940
1943
|
// a skill, so the full skills listing would be dead context for every one
|
|
1941
1944
|
// of its turns (e.g. the Pet manager) — inject none via an empty allowlist.
|
|
1942
|
-
const
|
|
1945
|
+
const runAllowedToolNames = toolCtx.allowedToolNames;
|
|
1946
|
+
const profileCanUseSkills = !runAllowedToolNames || runAllowedToolNames.has(skillToolDef.name);
|
|
1943
1947
|
const promptComposer = new PromptComposer(buildPromptComposerConfig({
|
|
1944
1948
|
cwd,
|
|
1945
1949
|
model: this.config.llm.model,
|
|
1946
1950
|
preset: this.preset,
|
|
1947
|
-
customSystemPrompt:
|
|
1948
|
-
|
|
1951
|
+
customSystemPrompt: profile?.disableInstructions
|
|
1952
|
+
? undefined
|
|
1953
|
+
: this.config.customSystemPrompt,
|
|
1954
|
+
appendSystemPrompt: [
|
|
1955
|
+
profile?.disableInstructions ? undefined : this.config.appendSystemPrompt,
|
|
1956
|
+
profile?.systemPromptAppend,
|
|
1957
|
+
]
|
|
1949
1958
|
.filter(Boolean)
|
|
1950
1959
|
.join("\n\n") || undefined,
|
|
1951
1960
|
responseLanguage: this.config.responseLanguage,
|
|
1952
|
-
userProfile: this.config.userProfile,
|
|
1961
|
+
userProfile: profile?.disableInstructions ? undefined : this.config.userProfile,
|
|
1953
1962
|
workspaceProfile: runWorkspaceProfile,
|
|
1954
1963
|
// Read from the Session's own persisted state, so every turn — not just
|
|
1955
1964
|
// the first — carries the standing brief.
|
|
@@ -1959,15 +1968,21 @@ export class Engine {
|
|
|
1959
1968
|
instructionBoundaryFinder: (scanCwd) => resolveInstructionBoundary(scanCwd, this.capabilities),
|
|
1960
1969
|
disabledSkills,
|
|
1961
1970
|
disabledPlugins,
|
|
1962
|
-
skillAllowlist: profileCanUseSkills ?
|
|
1971
|
+
skillAllowlist: profileCanUseSkills ? toolCtx.skillAllowlist : [],
|
|
1963
1972
|
memoriesMaxAgeDays: this.readMemoriesConfig()?.maxAge,
|
|
1964
1973
|
memoryCurrentProjectOnly: profile?.memoryCurrentProjectOnly,
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1974
|
+
disableInstructions: profile?.disableInstructions,
|
|
1975
|
+
disableMemoryContext: profile?.disableMemoryContext,
|
|
1976
|
+
disableCapabilityContext: profile?.disableCapabilityContext,
|
|
1977
|
+
disableSourcesContext: profile?.disableSourcesContext,
|
|
1978
|
+
goalToolState: !runAllowedToolNames ||
|
|
1979
|
+
runAllowedToolNames.has("complete_goal") ||
|
|
1980
|
+
runAllowedToolNames.has("cancel_goal")
|
|
1968
1981
|
? { hasGoal: hasRunnableGoal }
|
|
1969
1982
|
: undefined,
|
|
1970
|
-
capabilityPromptSections:
|
|
1983
|
+
capabilityPromptSections: profile?.disableCapabilityContext
|
|
1984
|
+
? {}
|
|
1985
|
+
: this.capabilityPromptSections,
|
|
1971
1986
|
dynamicContextProviders: this.capabilityDynamicContextProviders,
|
|
1972
1987
|
getSettingsManager: () => this.getSettingsManager(),
|
|
1973
1988
|
toolCatalog: this.toolCatalog,
|
|
@@ -2024,7 +2039,7 @@ export class Engine {
|
|
|
2024
2039
|
toolRewriters: this.toolRewriters,
|
|
2025
2040
|
toolFeatureFlags: TOOL_FEATURE_FLAGS,
|
|
2026
2041
|
applyBuiltinOverrideVisibility,
|
|
2027
|
-
profileAllowedToolNames:
|
|
2042
|
+
profileAllowedToolNames: runAllowedToolNames,
|
|
2028
2043
|
runPlanMode,
|
|
2029
2044
|
});
|
|
2030
2045
|
return { promptComposer, toolDefs };
|
|
@@ -88,11 +88,13 @@ export async function finalizeRunSuccess(args) {
|
|
|
88
88
|
// the turn loop has resolved (completion, error, or abort). Handlers
|
|
89
89
|
// are notify-only — any returned messages are dropped because the run
|
|
90
90
|
// is already over and there's no next turn to inject into.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
if (profile?.disableHooks !== true) {
|
|
92
|
+
await args.emitHook("on_session_end", {
|
|
93
|
+
sessionId: session.state.sessionId,
|
|
94
|
+
reason: result.reason,
|
|
95
|
+
turnCount,
|
|
96
|
+
}, options?.signal);
|
|
97
|
+
}
|
|
96
98
|
// Ephemeral side chats must never leak into durable memory, even after
|
|
97
99
|
// the user explicitly elevates tool permissions for a turn. Lifecycle
|
|
98
100
|
// isolation is independent of the run-scoped behavior/permission mode.
|
|
@@ -106,7 +108,7 @@ export async function finalizeRunSuccess(args) {
|
|
|
106
108
|
// Reuses the already-resolved auxSummaryClient (aux model, cheap). Best-
|
|
107
109
|
// effort: failures never touch the run result. The renderer writes the
|
|
108
110
|
// title into the sidebar on receipt of the session_title stream event.
|
|
109
|
-
{
|
|
111
|
+
if (profile?.disableSessionTitle !== true) {
|
|
110
112
|
const messageEvents = session.transcript.getEvents("message");
|
|
111
113
|
const userMsgEvents = messageEvents.filter((e) => e.data.role === "user");
|
|
112
114
|
const userMsgCount = userMsgEvents.length;
|
|
@@ -113,7 +113,7 @@ export function openRunSession(args) {
|
|
|
113
113
|
else {
|
|
114
114
|
// Cold start: shape (2) reuses the host-supplied sid; shape (3)
|
|
115
115
|
// lets sessionManager generate one with nanoid.
|
|
116
|
-
session = args.sessionManager.create(args.cwd, args.llmModel, args.llmProvider, options?.sessionId, args.isSubAgent ? getCurrentSid() : undefined, args.isSubAgent ? "subagent" : args.origin, args.sessionKind);
|
|
116
|
+
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);
|
|
117
117
|
const userMsg = { role: "user", content: args.userMessageContent };
|
|
118
118
|
claimClientMessageId(session, options?.clientMessageId, "submit");
|
|
119
119
|
if (args.parsedTask.hasImages)
|
|
@@ -33,6 +33,10 @@ export interface RunPromptComposerConfigInput {
|
|
|
33
33
|
skillAllowlist: ComposerOptions["skillAllowlist"];
|
|
34
34
|
memoriesMaxAgeDays: ComposerOptions["memoriesMaxAgeDays"];
|
|
35
35
|
memoryCurrentProjectOnly?: ComposerOptions["memoryCurrentProjectOnly"];
|
|
36
|
+
disableInstructions?: ComposerOptions["disableInstructions"];
|
|
37
|
+
disableMemoryContext?: ComposerOptions["disableMemoryContext"];
|
|
38
|
+
disableCapabilityContext?: ComposerOptions["disableCapabilityContext"];
|
|
39
|
+
disableSourcesContext?: ComposerOptions["disableSourcesContext"];
|
|
36
40
|
goalToolState: ComposerOptions["goalToolState"];
|
|
37
41
|
capabilityPromptSections: ComposerOptions["capabilityPromptSections"];
|
|
38
42
|
dynamicContextProviders: ComposerOptions["dynamicContextProviders"];
|
package/dist/engine/run-setup.js
CHANGED
|
@@ -23,7 +23,7 @@ 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, goalToolState, capabilityPromptSections, dynamicContextProviders, getSettingsManager, toolCatalog, } = 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;
|
|
27
27
|
return {
|
|
28
28
|
cwd,
|
|
29
29
|
model,
|
|
@@ -50,6 +50,10 @@ export function buildPromptComposerConfig(args) {
|
|
|
50
50
|
skillAllowlist,
|
|
51
51
|
memoriesMaxAgeDays,
|
|
52
52
|
memoryCurrentProjectOnly,
|
|
53
|
+
disableInstructions,
|
|
54
|
+
disableMemoryContext,
|
|
55
|
+
disableCapabilityContext,
|
|
56
|
+
disableSourcesContext,
|
|
53
57
|
goalToolState,
|
|
54
58
|
capabilityPromptSections,
|
|
55
59
|
dynamicContextProviders,
|
|
@@ -51,8 +51,14 @@ export function buildRunToolContext(args) {
|
|
|
51
51
|
return reason;
|
|
52
52
|
},
|
|
53
53
|
},
|
|
54
|
+
skillAllowlist: options?.skillAllowlist !== undefined
|
|
55
|
+
? [...options.skillAllowlist]
|
|
56
|
+
: args.base.skillAllowlist,
|
|
54
57
|
};
|
|
55
|
-
if (
|
|
58
|
+
if (options?.toolAllowlist !== undefined) {
|
|
59
|
+
toolCtx.allowedToolNames = new Set(options.toolAllowlist);
|
|
60
|
+
}
|
|
61
|
+
else if (profile?.allowedToolNames) {
|
|
56
62
|
toolCtx.allowedToolNames = profile.allowedToolNames;
|
|
57
63
|
}
|
|
58
64
|
if (profile?.createRunServices) {
|
|
@@ -32,6 +32,20 @@ export interface RunBehaviorProfile {
|
|
|
32
32
|
disablePlanMode?: boolean;
|
|
33
33
|
/** When true, MCP servers are neither connected nor exposed for the run. */
|
|
34
34
|
disableMcp?: boolean;
|
|
35
|
+
/** Skip repository/user instruction discovery for this run. */
|
|
36
|
+
disableInstructions?: boolean;
|
|
37
|
+
/** Skip persistent-memory injection for this run. */
|
|
38
|
+
disableMemoryContext?: boolean;
|
|
39
|
+
/** Skip capability-owned volatile context providers for this run. */
|
|
40
|
+
disableCapabilityContext?: boolean;
|
|
41
|
+
/** Skip bound-source metadata injection for this run. */
|
|
42
|
+
disableSourcesContext?: boolean;
|
|
43
|
+
/** Ignore the project's default digital-human profile for this run. */
|
|
44
|
+
disableWorkspaceProfile?: boolean;
|
|
45
|
+
/** Skip SessionStart/UserPromptSubmit/SessionEnd hooks for this run. */
|
|
46
|
+
disableHooks?: boolean;
|
|
47
|
+
/** Skip auxiliary title generation for this run. */
|
|
48
|
+
disableSessionTitle?: boolean;
|
|
35
49
|
/**
|
|
36
50
|
* When true, the injected persistent-memory index is trimmed to this run's
|
|
37
51
|
* project: global-layer project-type / dream-scope records tied to other
|
|
@@ -67,6 +81,10 @@ export interface RunBehaviorProfile {
|
|
|
67
81
|
export declare const QUICK_CHAT_RESTRICTED_SYSTEM_PROMPT = "# Side Conversation Boundary\n\nThis is a side conversation, not the main-thread task execution environment.\n- Treat all content before this boundary as reference history only. Do not proactively continue any earlier plan, task, or modification.\n- Default to answering the user's question directly. Use lightweight read-only exploration only when needed.\n- Do not modify files, git state, configuration, or permissions unless the user explicitly asks after this boundary (for example, \"Allow you to modify files, please help me...\" or \"Please directly edit...\"). When explicitly requested, use the normally available tools subject to the current permission and approval mode.\n- Sub-agents are disabled for this side conversation. Do not create or invoke sub-agents.";
|
|
68
82
|
/** The side-conversation restriction expressed as a generic behavior profile. */
|
|
69
83
|
export declare const QUICK_CHAT_RESTRICTED_PROFILE: RunBehaviorProfile;
|
|
84
|
+
export declare const ISOLATED_TASK_BEHAVIOR_MODE: "isolatedTask";
|
|
85
|
+
export declare const ISOLATED_TASK_SYSTEM_PROMPT = "# Isolated Task Boundary\n\nThis is a bounded Task, not a continuation of any user conversation.\n- Use only the explicit Task input, the visible tool surface, and the selected Skill when one is available.\n- Do not infer or search for prior Session history, persistent memory, unrelated Skills, or other projects.\n- Do not create sub-agents or start unrelated work.\n- Finish the requested outcome, then return a concise result suitable for the owning application.";
|
|
86
|
+
/** Minimal, process-local run profile used by host-owned Tasks. */
|
|
87
|
+
export declare const ISOLATED_TASK_PROFILE: RunBehaviorProfile;
|
|
70
88
|
export interface EngineRunOptions {
|
|
71
89
|
cwd?: string;
|
|
72
90
|
onStream?: StreamCallback;
|
|
@@ -83,6 +101,12 @@ export interface EngineRunOptions {
|
|
|
83
101
|
attachments?: InputAttachmentMeta[];
|
|
84
102
|
/** Named per-run behavior profile supplied by interactive product surfaces. */
|
|
85
103
|
behaviorMode?: RunBehaviorMode;
|
|
104
|
+
/** Per-run hard tool allowlist. An empty list exposes no tools. */
|
|
105
|
+
toolAllowlist?: readonly string[];
|
|
106
|
+
/** Per-run hard Skill allowlist. An empty list exposes no Skills. */
|
|
107
|
+
skillAllowlist?: readonly string[];
|
|
108
|
+
/** Keep a fresh Session in process memory only and omit it from Session pickers. */
|
|
109
|
+
ephemeral?: boolean;
|
|
86
110
|
/**
|
|
87
111
|
* Generic per-run parameters consumed by the active behavior profile
|
|
88
112
|
* (createRunServices / buildVisibilityMeta / runtime-context injection).
|
package/dist/engine/run-types.js
CHANGED
|
@@ -10,3 +10,24 @@ export const QUICK_CHAT_RESTRICTED_PROFILE = {
|
|
|
10
10
|
id: "quickChatRestricted",
|
|
11
11
|
systemPromptAppend: QUICK_CHAT_RESTRICTED_SYSTEM_PROMPT,
|
|
12
12
|
};
|
|
13
|
+
export const ISOLATED_TASK_BEHAVIOR_MODE = "isolatedTask";
|
|
14
|
+
export const ISOLATED_TASK_SYSTEM_PROMPT = `# Isolated Task Boundary
|
|
15
|
+
|
|
16
|
+
This is a bounded Task, not a continuation of any user conversation.
|
|
17
|
+
- Use only the explicit Task input, the visible tool surface, and the selected Skill when one is available.
|
|
18
|
+
- Do not infer or search for prior Session history, persistent memory, unrelated Skills, or other projects.
|
|
19
|
+
- Do not create sub-agents or start unrelated work.
|
|
20
|
+
- Finish the requested outcome, then return a concise result suitable for the owning application.`;
|
|
21
|
+
/** Minimal, process-local run profile used by host-owned Tasks. */
|
|
22
|
+
export const ISOLATED_TASK_PROFILE = {
|
|
23
|
+
id: ISOLATED_TASK_BEHAVIOR_MODE,
|
|
24
|
+
systemPromptAppend: ISOLATED_TASK_SYSTEM_PROMPT,
|
|
25
|
+
disableMcp: true,
|
|
26
|
+
disableInstructions: true,
|
|
27
|
+
disableMemoryContext: true,
|
|
28
|
+
disableCapabilityContext: true,
|
|
29
|
+
disableSourcesContext: true,
|
|
30
|
+
disableWorkspaceProfile: true,
|
|
31
|
+
disableHooks: true,
|
|
32
|
+
disableSessionTitle: true,
|
|
33
|
+
};
|
|
@@ -93,11 +93,17 @@ export async function resolveRunWorkspace(args) {
|
|
|
93
93
|
configCwd: args.configCwd,
|
|
94
94
|
processCwd: args.processCwd,
|
|
95
95
|
});
|
|
96
|
-
const profileState =
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
const profileState = profile?.disableWorkspaceProfile
|
|
97
|
+
? {
|
|
98
|
+
workspaceProfile: undefined,
|
|
99
|
+
sessionProfileOverrides: undefined,
|
|
100
|
+
profileMemoryDir: undefined,
|
|
101
|
+
}
|
|
102
|
+
: resolveRunProfileState({
|
|
103
|
+
sessionWorkspaceProfile,
|
|
104
|
+
cwd,
|
|
105
|
+
settings: args.settings,
|
|
106
|
+
});
|
|
101
107
|
return {
|
|
102
108
|
ok: true,
|
|
103
109
|
resolution: {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Public API exports.
|
|
5
5
|
*/
|
|
6
|
-
export declare const VERSION = "0.8.
|
|
6
|
+
export declare const VERSION = "0.8.9";
|
|
7
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";
|
|
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, SandboxUnavailableError, } from "./exceptions.js";
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Public API exports.
|
|
5
5
|
*/
|
|
6
|
-
export const VERSION = "0.8.
|
|
6
|
+
export const VERSION = "0.8.9";
|
|
7
7
|
// ─── Exceptions ──────────────────────────────────────────────────
|
|
8
8
|
export { FrameworkError, LLMError, LLMRateLimitError, ContextLimitError, ToolError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, PermissionDeniedError, SessionError, TranscriptError, ConfigError, SandboxUnavailableError, } from "./exceptions.js";
|
|
9
9
|
// ─── Engine (primary API) ────────────────────────────────────────
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare const PANEL_APP_MANIFEST_FILE = ".codeshell-panel/panel.json";
|
|
3
|
-
export declare const PANEL_APP_PERMISSIONS: readonly ["context.session", "context.workspace", "storage", "external.open", "agent.submitPrompt", "workspace.info", "workspace.read", "workspace.write", "notifications.send", "audio.transcribe", "credentials.cookies", "automations.manage"];
|
|
3
|
+
export declare const PANEL_APP_PERMISSIONS: readonly ["context.session", "context.workspace", "storage", "external.open", "agent.submitPrompt", "agent.task", "workspace.info", "workspace.read", "workspace.write", "notifications.send", "audio.transcribe", "credentials.cookies", "automations.manage", "process"];
|
|
4
4
|
export declare const PANEL_APP_ICONS: readonly ["panel", "activity", "bar-chart-3", "chart", "file-text", "globe", "image", "layout-dashboard", "line-chart", "palette", "pie-chart", "table", "terminal"];
|
|
5
5
|
export declare const PanelAppAgentTool: z.ZodEffects<z.ZodObject<{
|
|
6
6
|
name: z.ZodString;
|
|
@@ -116,12 +116,12 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
|
|
|
116
116
|
icon: z.ZodDefault<z.ZodEnum<["panel", "activity", "bar-chart-3", "chart", "file-text", "globe", "image", "layout-dashboard", "line-chart", "palette", "pie-chart", "table", "terminal"]>>;
|
|
117
117
|
placement: z.ZodDefault<z.ZodLiteral<"right-dock">>;
|
|
118
118
|
singleton: z.ZodDefault<z.ZodBoolean>;
|
|
119
|
-
permissions: z.ZodDefault<z.ZodArray<z.ZodEnum<["context.session", "context.workspace", "storage", "external.open", "agent.submitPrompt", "workspace.info", "workspace.read", "workspace.write", "notifications.send", "audio.transcribe", "credentials.cookies", "automations.manage"]>, "many">>;
|
|
119
|
+
permissions: z.ZodDefault<z.ZodArray<z.ZodEnum<["context.session", "context.workspace", "storage", "external.open", "agent.submitPrompt", "agent.task", "workspace.info", "workspace.read", "workspace.write", "notifications.send", "audio.transcribe", "credentials.cookies", "automations.manage", "process"]>, "many">>;
|
|
120
120
|
schemaVersion: z.ZodLiteral<1>;
|
|
121
121
|
}, "strict", z.ZodTypeAny, {
|
|
122
122
|
id: string;
|
|
123
123
|
version: string;
|
|
124
|
-
permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage")[];
|
|
124
|
+
permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "agent.task" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage" | "process")[];
|
|
125
125
|
entry: string;
|
|
126
126
|
title: {
|
|
127
127
|
default: string;
|
|
@@ -144,7 +144,7 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
|
|
|
144
144
|
};
|
|
145
145
|
schemaVersion: 1;
|
|
146
146
|
description?: string | undefined;
|
|
147
|
-
permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage")[] | undefined;
|
|
147
|
+
permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "agent.task" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage" | "process")[] | undefined;
|
|
148
148
|
icon?: "terminal" | "image" | "panel" | "activity" | "bar-chart-3" | "chart" | "file-text" | "globe" | "layout-dashboard" | "line-chart" | "palette" | "pie-chart" | "table" | undefined;
|
|
149
149
|
placement?: "right-dock" | undefined;
|
|
150
150
|
singleton?: boolean | undefined;
|
|
@@ -230,12 +230,12 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
|
|
|
230
230
|
icon: z.ZodDefault<z.ZodEnum<["panel", "activity", "bar-chart-3", "chart", "file-text", "globe", "image", "layout-dashboard", "line-chart", "palette", "pie-chart", "table", "terminal"]>>;
|
|
231
231
|
placement: z.ZodDefault<z.ZodLiteral<"right-dock">>;
|
|
232
232
|
singleton: z.ZodDefault<z.ZodBoolean>;
|
|
233
|
-
permissions: z.ZodDefault<z.ZodArray<z.ZodEnum<["context.session", "context.workspace", "storage", "external.open", "agent.submitPrompt", "workspace.info", "workspace.read", "workspace.write", "notifications.send", "audio.transcribe", "credentials.cookies", "automations.manage"]>, "many">>;
|
|
233
|
+
permissions: z.ZodDefault<z.ZodArray<z.ZodEnum<["context.session", "context.workspace", "storage", "external.open", "agent.submitPrompt", "agent.task", "workspace.info", "workspace.read", "workspace.write", "notifications.send", "audio.transcribe", "credentials.cookies", "automations.manage", "process"]>, "many">>;
|
|
234
234
|
schemaVersion: z.ZodLiteral<2>;
|
|
235
235
|
}, "strict", z.ZodTypeAny, {
|
|
236
236
|
id: string;
|
|
237
237
|
version: string;
|
|
238
|
-
permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage")[];
|
|
238
|
+
permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "agent.task" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage" | "process")[];
|
|
239
239
|
entry: string;
|
|
240
240
|
title: {
|
|
241
241
|
default: string;
|
|
@@ -276,14 +276,14 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
|
|
|
276
276
|
}[] | undefined;
|
|
277
277
|
} | undefined;
|
|
278
278
|
description?: string | undefined;
|
|
279
|
-
permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage")[] | undefined;
|
|
279
|
+
permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "agent.task" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage" | "process")[] | undefined;
|
|
280
280
|
icon?: "terminal" | "image" | "panel" | "activity" | "bar-chart-3" | "chart" | "file-text" | "globe" | "layout-dashboard" | "line-chart" | "palette" | "pie-chart" | "table" | undefined;
|
|
281
281
|
placement?: "right-dock" | undefined;
|
|
282
282
|
singleton?: boolean | undefined;
|
|
283
283
|
}>]>, {
|
|
284
284
|
id: string;
|
|
285
285
|
version: string;
|
|
286
|
-
permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage")[];
|
|
286
|
+
permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "agent.task" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage" | "process")[];
|
|
287
287
|
entry: string;
|
|
288
288
|
title: {
|
|
289
289
|
default: string;
|
|
@@ -298,7 +298,7 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
|
|
|
298
298
|
} | {
|
|
299
299
|
id: string;
|
|
300
300
|
version: string;
|
|
301
|
-
permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage")[];
|
|
301
|
+
permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "agent.task" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage" | "process")[];
|
|
302
302
|
entry: string;
|
|
303
303
|
title: {
|
|
304
304
|
default: string;
|
|
@@ -330,7 +330,7 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
|
|
|
330
330
|
};
|
|
331
331
|
schemaVersion: 1;
|
|
332
332
|
description?: string | undefined;
|
|
333
|
-
permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage")[] | undefined;
|
|
333
|
+
permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "agent.task" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage" | "process")[] | undefined;
|
|
334
334
|
icon?: "terminal" | "image" | "panel" | "activity" | "bar-chart-3" | "chart" | "file-text" | "globe" | "layout-dashboard" | "line-chart" | "palette" | "pie-chart" | "table" | undefined;
|
|
335
335
|
placement?: "right-dock" | undefined;
|
|
336
336
|
singleton?: boolean | undefined;
|
|
@@ -354,7 +354,7 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
|
|
|
354
354
|
}[] | undefined;
|
|
355
355
|
} | undefined;
|
|
356
356
|
description?: string | undefined;
|
|
357
|
-
permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage")[] | undefined;
|
|
357
|
+
permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "agent.task" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "audio.transcribe" | "credentials.cookies" | "automations.manage" | "process")[] | undefined;
|
|
358
358
|
icon?: "terminal" | "image" | "panel" | "activity" | "bar-chart-3" | "chart" | "file-text" | "globe" | "layout-dashboard" | "line-chart" | "palette" | "pie-chart" | "table" | undefined;
|
|
359
359
|
placement?: "right-dock" | undefined;
|
|
360
360
|
singleton?: boolean | undefined;
|
|
@@ -7,6 +7,7 @@ export const PANEL_APP_PERMISSIONS = [
|
|
|
7
7
|
"storage",
|
|
8
8
|
"external.open",
|
|
9
9
|
"agent.submitPrompt",
|
|
10
|
+
"agent.task",
|
|
10
11
|
"workspace.info",
|
|
11
12
|
"workspace.read",
|
|
12
13
|
"workspace.write",
|
|
@@ -14,6 +15,7 @@ export const PANEL_APP_PERMISSIONS = [
|
|
|
14
15
|
"audio.transcribe",
|
|
15
16
|
"credentials.cookies",
|
|
16
17
|
"automations.manage",
|
|
18
|
+
"process",
|
|
17
19
|
];
|
|
18
20
|
export const PANEL_APP_ICONS = [
|
|
19
21
|
"panel",
|
|
@@ -139,7 +141,7 @@ const PanelAppManifestFields = {
|
|
|
139
141
|
icon: z.enum(PANEL_APP_ICONS).default("panel"),
|
|
140
142
|
placement: z.literal("right-dock").default("right-dock"),
|
|
141
143
|
singleton: z.boolean().default(true),
|
|
142
|
-
permissions: z.array(z.enum(PANEL_APP_PERMISSIONS)).max(
|
|
144
|
+
permissions: z.array(z.enum(PANEL_APP_PERMISSIONS)).max(16).default([]),
|
|
143
145
|
};
|
|
144
146
|
/**
|
|
145
147
|
* Schema v2 keeps one installable Panel App identity while allowing it to
|
|
@@ -96,6 +96,14 @@ export interface ComposerOptions {
|
|
|
96
96
|
* See MemoryManager.buildInjectionIndex(currentProjectOnly).
|
|
97
97
|
*/
|
|
98
98
|
memoryCurrentProjectOnly?: boolean;
|
|
99
|
+
/** Isolated runs can omit repository/user instruction discovery entirely. */
|
|
100
|
+
disableInstructions?: boolean;
|
|
101
|
+
/** Isolated runs can omit persistent-memory context entirely. */
|
|
102
|
+
disableMemoryContext?: boolean;
|
|
103
|
+
/** Isolated runs can omit capability-owned volatile context providers. */
|
|
104
|
+
disableCapabilityContext?: boolean;
|
|
105
|
+
/** Isolated runs can omit bound-source metadata. */
|
|
106
|
+
disableSourcesContext?: boolean;
|
|
99
107
|
}
|
|
100
108
|
export declare class PromptComposer {
|
|
101
109
|
private readonly options;
|
package/dist/prompt/composer.js
CHANGED
|
@@ -30,6 +30,8 @@ export class PromptComposer {
|
|
|
30
30
|
* Build the userContext prefix message (CLAUDE.md content as <system-reminder>).
|
|
31
31
|
*/
|
|
32
32
|
buildUserContextMessage() {
|
|
33
|
+
if (this.options.disableInstructions === true)
|
|
34
|
+
return null;
|
|
33
35
|
const instructions = this.getInstructions();
|
|
34
36
|
// NOTE: memory is intentionally NOT here. It used to be, but memory mutates
|
|
35
37
|
// constantly (extraction, recall usage++/lastUsed, approve/demote), and this
|
|
@@ -51,6 +53,8 @@ export class PromptComposer {
|
|
|
51
53
|
}
|
|
52
54
|
/** Build volatile context contributed by installed capability modules. */
|
|
53
55
|
async buildSystemContext() {
|
|
56
|
+
if (this.options.disableCapabilityContext === true)
|
|
57
|
+
return "";
|
|
54
58
|
const preset = this.options.preset ?? resolveAgentPreset();
|
|
55
59
|
const parts = await Promise.all((this.options.dynamicContextProviders ?? []).map(async (provider) => {
|
|
56
60
|
try {
|
|
@@ -107,6 +111,8 @@ export class PromptComposer {
|
|
|
107
111
|
const [capabilityContext, sourcesContext] = await Promise.all([
|
|
108
112
|
this.buildSystemContext(),
|
|
109
113
|
(async () => {
|
|
114
|
+
if (this.options.disableSourcesContext === true)
|
|
115
|
+
return "";
|
|
110
116
|
try {
|
|
111
117
|
return (await this.options.sourcesContextProvider?.()) ?? "";
|
|
112
118
|
}
|
|
@@ -265,6 +271,8 @@ export class PromptComposer {
|
|
|
265
271
|
return this.cachedInstructions;
|
|
266
272
|
}
|
|
267
273
|
getMemoryContext() {
|
|
274
|
+
if (this.options.disableMemoryContext === true)
|
|
275
|
+
return "";
|
|
268
276
|
try {
|
|
269
277
|
// Three-layer injection (用户拍板): a compact index merging GLOBAL +
|
|
270
278
|
// DIGITAL-HUMAN + PROJECT memories. Global memories are surfaced every
|
|
@@ -168,6 +168,9 @@ export class ChatSessionManager {
|
|
|
168
168
|
getLiveSessionSnapshot() {
|
|
169
169
|
const sessions = [];
|
|
170
170
|
this.forEachSession((session) => {
|
|
171
|
+
const sessionManager = this.engineSessionManager(session.engine);
|
|
172
|
+
if (sessionManager?.isEphemeralSession?.(session.id))
|
|
173
|
+
return;
|
|
171
174
|
sessions.push({
|
|
172
175
|
sessionId: session.id,
|
|
173
176
|
busy: session.isBusy(),
|
|
@@ -280,6 +283,12 @@ export class ChatSessionManager {
|
|
|
280
283
|
sweepIdle() {
|
|
281
284
|
const cutoff = Date.now() - this.idleTtlMs;
|
|
282
285
|
for (const [id, s] of [...this.sessions]) {
|
|
286
|
+
// Quick Chat transcripts are process-local by design. Closing one from
|
|
287
|
+
// the generic idle sweeper destroys its inherited context while the
|
|
288
|
+
// renderer still owns (and displays) the panel. Its explicit claim/
|
|
289
|
+
// panel lifecycle is the sole authority that may close it.
|
|
290
|
+
if (id.startsWith("qchat-"))
|
|
291
|
+
continue;
|
|
283
292
|
if (s.lastActivityAt >= cutoff)
|
|
284
293
|
continue;
|
|
285
294
|
if (s.isBusy())
|
package/dist/protocol/server.js
CHANGED
|
@@ -58,10 +58,39 @@ function runInputError(params) {
|
|
|
58
58
|
if (params.injected !== undefined && typeof params.injected !== "boolean") {
|
|
59
59
|
return "injected must be a boolean";
|
|
60
60
|
}
|
|
61
|
+
if (params.quickChatClaimId !== undefined &&
|
|
62
|
+
(typeof params.quickChatClaimId !== "string" || params.quickChatClaimId.length === 0)) {
|
|
63
|
+
return "quickChatClaimId must be a non-empty string";
|
|
64
|
+
}
|
|
61
65
|
if (params.behaviorMode !== undefined &&
|
|
62
66
|
(typeof params.behaviorMode !== "string" || params.behaviorMode.length === 0)) {
|
|
63
67
|
return `invalid behavior mode: ${String(params.behaviorMode)}`;
|
|
64
68
|
}
|
|
69
|
+
for (const [field, value, maxItems] of [
|
|
70
|
+
["toolAllowlist", params.toolAllowlist, 32],
|
|
71
|
+
["skillAllowlist", params.skillAllowlist, 8],
|
|
72
|
+
]) {
|
|
73
|
+
if (value === undefined)
|
|
74
|
+
continue;
|
|
75
|
+
if (!Array.isArray(value) ||
|
|
76
|
+
value.length > maxItems ||
|
|
77
|
+
value.some((name) => typeof name !== "string" || name.length === 0 || name.length > 128 || /[\r\n]/.test(name))) {
|
|
78
|
+
return `${field} must be an array of at most ${maxItems} bounded names`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (params.ephemeral !== undefined && typeof params.ephemeral !== "boolean") {
|
|
82
|
+
return "ephemeral must be a boolean";
|
|
83
|
+
}
|
|
84
|
+
if (params.maxTurns !== undefined &&
|
|
85
|
+
(!Number.isInteger(params.maxTurns) || params.maxTurns < 1 || params.maxTurns > 30)) {
|
|
86
|
+
return "maxTurns must be an integer from 1 to 30";
|
|
87
|
+
}
|
|
88
|
+
if (params.maxContextTokens !== undefined &&
|
|
89
|
+
(!Number.isInteger(params.maxContextTokens) ||
|
|
90
|
+
params.maxContextTokens < 4_096 ||
|
|
91
|
+
params.maxContextTokens > 131_072)) {
|
|
92
|
+
return "maxContextTokens must be an integer from 4096 to 131072";
|
|
93
|
+
}
|
|
65
94
|
if (params.kind !== undefined && (typeof params.kind !== "string" || params.kind.length === 0)) {
|
|
66
95
|
return `invalid session kind: ${String(params.kind)}`;
|
|
67
96
|
}
|
|
@@ -1080,6 +1109,8 @@ export class AgentServer {
|
|
|
1080
1109
|
const sessionConfig = {
|
|
1081
1110
|
cwd: params.cwd,
|
|
1082
1111
|
projectTrusted: params.projectTrusted,
|
|
1112
|
+
maxTurns: params.maxTurns,
|
|
1113
|
+
maxContextTokens: params.maxContextTokens,
|
|
1083
1114
|
goal: typeof params.goal === "string" || (params.goal != null && typeof params.goal === "object")
|
|
1084
1115
|
? params.goal
|
|
1085
1116
|
: undefined,
|
|
@@ -1163,6 +1194,9 @@ export class AgentServer {
|
|
|
1163
1194
|
permissionMode: params.permissionMode,
|
|
1164
1195
|
planMode: params.planMode,
|
|
1165
1196
|
behaviorMode: params.behaviorMode,
|
|
1197
|
+
toolAllowlist: params.toolAllowlist,
|
|
1198
|
+
skillAllowlist: params.skillAllowlist,
|
|
1199
|
+
ephemeral: params.ephemeral,
|
|
1166
1200
|
profileParams: params.profileParams,
|
|
1167
1201
|
workspaceProfile: params.workspaceProfile,
|
|
1168
1202
|
sessionBrief: params.sessionBrief,
|
|
@@ -1290,6 +1324,9 @@ export class AgentServer {
|
|
|
1290
1324
|
permissionMode: params.permissionMode,
|
|
1291
1325
|
planMode: params.planMode,
|
|
1292
1326
|
behaviorMode: params.behaviorMode,
|
|
1327
|
+
toolAllowlist: params.toolAllowlist,
|
|
1328
|
+
skillAllowlist: params.skillAllowlist,
|
|
1329
|
+
ephemeral: params.ephemeral,
|
|
1293
1330
|
profileParams: params.profileParams,
|
|
1294
1331
|
workspaceProfile: params.workspaceProfile,
|
|
1295
1332
|
sessionBrief: params.sessionBrief,
|
package/dist/protocol/types.d.ts
CHANGED
|
@@ -80,6 +80,8 @@ export interface RunParams {
|
|
|
80
80
|
attachments?: InputAttachmentMeta[];
|
|
81
81
|
/** Stable id for the user's submit intent; duplicate ids are idempotent. */
|
|
82
82
|
clientMessageId?: string;
|
|
83
|
+
/** Desktop host ownership generation for process-local Quick Chat runs. */
|
|
84
|
+
quickChatClaimId?: string;
|
|
83
85
|
/**
|
|
84
86
|
* Host-generated system reminder. Persisted with an injected marker so disk
|
|
85
87
|
* transcript readers hide it as a user bubble while retaining the assistant reply.
|
|
@@ -116,6 +118,16 @@ export interface RunParams {
|
|
|
116
118
|
planMode?: boolean;
|
|
117
119
|
/** Named behavior profile for a product-specific per-run interaction mode. */
|
|
118
120
|
behaviorMode?: RunBehaviorMode;
|
|
121
|
+
/** Host-authorized per-run hard tool allowlist. Empty means no tools. */
|
|
122
|
+
toolAllowlist?: string[];
|
|
123
|
+
/** Host-authorized per-run hard Skill allowlist. Empty means no Skills. */
|
|
124
|
+
skillAllowlist?: string[];
|
|
125
|
+
/** Keep a fresh run Session process-local and out of normal Session pickers. */
|
|
126
|
+
ephemeral?: boolean;
|
|
127
|
+
/** Optional fresh-session turn ceiling used by bounded host Tasks. */
|
|
128
|
+
maxTurns?: number;
|
|
129
|
+
/** Optional fresh-session context ceiling used by bounded host Tasks. */
|
|
130
|
+
maxContextTokens?: number;
|
|
119
131
|
/**
|
|
120
132
|
* Generic per-run parameters consumed by the active behavior profile
|
|
121
133
|
* (delivered as non-durable run context; never appended to the task or
|
|
@@ -108,7 +108,7 @@ export declare class SessionManager {
|
|
|
108
108
|
* Create a session. `qchat-` sessions stay process-local; ordinary sessions
|
|
109
109
|
* materialize state.json + transcript.jsonl before return.
|
|
110
110
|
*/
|
|
111
|
-
create(cwd: string, model: string, provider: string, explicitSessionId?: string, parentSessionId?: string | null, origin?: import("../types.js").SessionOrigin, kind?: SessionKind): SessionBundle;
|
|
111
|
+
create(cwd: string, model: string, provider: string, explicitSessionId?: string, parentSessionId?: string | null, origin?: import("../types.js").SessionOrigin, kind?: SessionKind, ephemeral?: boolean): SessionBundle;
|
|
112
112
|
/** Whether a persisted or process-local session exists. */
|
|
113
113
|
exists(sessionId: string): boolean;
|
|
114
114
|
/**
|
|
@@ -236,6 +236,8 @@ export declare class SessionManager {
|
|
|
236
236
|
* workspace/profile metadata.
|
|
237
237
|
*/
|
|
238
238
|
readSessionState(sessionId: string): SessionState | undefined;
|
|
239
|
+
/** Whether a live or persisted Session is explicitly process-local. */
|
|
240
|
+
isEphemeralSession(sessionId: string): boolean;
|
|
239
241
|
/**
|
|
240
242
|
* Merge a field-level state update into the latest persisted snapshot.
|
|
241
243
|
*
|
|
@@ -148,26 +148,34 @@ function adoptCompatibilityGoalMutation(state) {
|
|
|
148
148
|
// disk. The storage root remains part of the key to preserve identity/data-root
|
|
149
149
|
// isolation.
|
|
150
150
|
const processLocalSessionBundles = new Map();
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
"
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
"
|
|
164
|
-
"
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
"
|
|
168
|
-
"
|
|
169
|
-
"
|
|
170
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Every known transcript event must make an explicit fork decision. The
|
|
153
|
+
* `satisfies` constraint turns new TranscriptEventType additions into a
|
|
154
|
+
* compile-time error instead of a quick-chat failure discovered at runtime.
|
|
155
|
+
* The runtime fallback below still rejects unknown events from newer or
|
|
156
|
+
* malformed persisted transcripts.
|
|
157
|
+
*/
|
|
158
|
+
const FORK_EVENT_POLICY = {
|
|
159
|
+
message: "copy",
|
|
160
|
+
tool_use: "copy",
|
|
161
|
+
tool_result: "copy",
|
|
162
|
+
summary: "copy",
|
|
163
|
+
context_transfer: "copy",
|
|
164
|
+
range_archive: "copy",
|
|
165
|
+
content_replace: "copy",
|
|
166
|
+
file_history: "skip",
|
|
167
|
+
plan_operation: "skip",
|
|
168
|
+
session_meta: "skip",
|
|
169
|
+
subagent: "copy",
|
|
170
|
+
external_file_changes: "copy",
|
|
171
|
+
turn_boundary: "copy",
|
|
172
|
+
// Idempotency receipts belong to the source session and never contribute to
|
|
173
|
+
// model context. Copying them could replay a source response in the child.
|
|
174
|
+
run_result: "skip",
|
|
175
|
+
goal_progress: "copy",
|
|
176
|
+
turn_stopped: "copy",
|
|
177
|
+
error: "copy",
|
|
178
|
+
};
|
|
171
179
|
const FORK_STAGING_NAME = /^\.pending-fork-[A-Za-z0-9_.-]+-[A-Za-z0-9_-]{8}$/;
|
|
172
180
|
const FORK_STAGING_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
173
181
|
const FORK_STAGING_CLEANUP_LIMIT = 32;
|
|
@@ -366,7 +374,7 @@ export class SessionManager {
|
|
|
366
374
|
* Create a session. `qchat-` sessions stay process-local; ordinary sessions
|
|
367
375
|
* materialize state.json + transcript.jsonl before return.
|
|
368
376
|
*/
|
|
369
|
-
create(cwd, model, provider, explicitSessionId, parentSessionId, origin, kind = "work") {
|
|
377
|
+
create(cwd, model, provider, explicitSessionId, parentSessionId, origin, kind = "work", ephemeral = false) {
|
|
370
378
|
// External callers may pass any string; nanoid output is trusted. Either
|
|
371
379
|
// way the ID gets joined into a filesystem path, so the public entry
|
|
372
380
|
// point validates before that join.
|
|
@@ -394,7 +402,7 @@ export class SessionManager {
|
|
|
394
402
|
// new top-level session (key present, null) apart from a legacy session
|
|
395
403
|
// (key absent) and from a sub-agent (key present, non-empty string).
|
|
396
404
|
parentSessionId: parentSessionId ?? null,
|
|
397
|
-
...(sessionId.startsWith("qchat-") ? { ephemeral: true } : {}),
|
|
405
|
+
...(ephemeral || sessionId.startsWith("qchat-") ? { ephemeral: true } : {}),
|
|
398
406
|
...(origin ? { origin } : {}),
|
|
399
407
|
};
|
|
400
408
|
if (isEphemeralSessionState(state)) {
|
|
@@ -1049,6 +1057,10 @@ export class SessionManager {
|
|
|
1049
1057
|
return undefined;
|
|
1050
1058
|
}
|
|
1051
1059
|
}
|
|
1060
|
+
/** Whether a live or persisted Session is explicitly process-local. */
|
|
1061
|
+
isEphemeralSession(sessionId) {
|
|
1062
|
+
return this.readSessionState(sessionId)?.ephemeral === true;
|
|
1063
|
+
}
|
|
1052
1064
|
/**
|
|
1053
1065
|
* Merge a field-level state update into the latest persisted snapshot.
|
|
1054
1066
|
*
|
|
@@ -1569,7 +1581,10 @@ export class SessionManager {
|
|
|
1569
1581
|
return this.freezeForkSnapshot(sourceSessionId, sourceState, parsed.events, throughEventId, snapshotMode);
|
|
1570
1582
|
}
|
|
1571
1583
|
freezeForkSnapshot(sourceSessionId, sourceState, events, throughEventId, snapshotMode) {
|
|
1572
|
-
|
|
1584
|
+
// Both process-local and disk readers provide a snapshot array. Fork
|
|
1585
|
+
// selection is synchronous, so keep event references here and clone once
|
|
1586
|
+
// when the independently-owned target transcript is constructed below.
|
|
1587
|
+
const sourceEvents = [...events];
|
|
1573
1588
|
let frozen = sourceEvents;
|
|
1574
1589
|
const effectiveCursor = snapshotMode === "completed" ? sourceState.completedThroughEventId : throughEventId;
|
|
1575
1590
|
if (snapshotMode === "completed" && effectiveCursor === undefined) {
|
|
@@ -1600,12 +1615,13 @@ export class SessionManager {
|
|
|
1600
1615
|
}
|
|
1601
1616
|
const copiedEvents = [];
|
|
1602
1617
|
for (const event of frozen) {
|
|
1603
|
-
|
|
1618
|
+
const policy = FORK_EVENT_POLICY[event.type];
|
|
1619
|
+
if (policy === "skip")
|
|
1604
1620
|
continue;
|
|
1605
|
-
if (
|
|
1621
|
+
if (policy !== "copy") {
|
|
1606
1622
|
throw new SessionError(`Unsupported transcript event in fork: ${String(event.type)}`);
|
|
1607
1623
|
}
|
|
1608
|
-
copiedEvents.push(
|
|
1624
|
+
copiedEvents.push(event);
|
|
1609
1625
|
}
|
|
1610
1626
|
validateForkToolPairs(copiedEvents);
|
|
1611
1627
|
return { sourceState: structuredClone(sourceState), copiedEvents };
|
|
@@ -310,7 +310,10 @@ const BUILTIN_CONTRIBUTIONS = [
|
|
|
310
310
|
timeoutMs: 1_800_000, // 30min — sub-agent runs may execute many tool calls
|
|
311
311
|
},
|
|
312
312
|
execute: agentTool,
|
|
313
|
-
exposure: expose(HARNESS_TAGS, {
|
|
313
|
+
exposure: expose(HARNESS_TAGS, {
|
|
314
|
+
defaultPermissionRules: allow(agentToolDef.name),
|
|
315
|
+
availability: (ctx) => ctx.behaviorProfile !== "quickChatRestricted",
|
|
316
|
+
}),
|
|
314
317
|
},
|
|
315
318
|
{
|
|
316
319
|
definition: {
|
|
@@ -449,8 +449,6 @@ function isRegisteredSkillResourceRead(resolved) {
|
|
|
449
449
|
return false;
|
|
450
450
|
if (isSkillTreeResource(resolved, join(codeShellRoot, "skills")))
|
|
451
451
|
return true;
|
|
452
|
-
if (isInstalledPanelAppSkillResourceRead(resolved, codeShellRoot))
|
|
453
|
-
return true;
|
|
454
452
|
let cacheRoot;
|
|
455
453
|
try {
|
|
456
454
|
cacheRoot = realpathSync(join(codeShellRoot, "plugins", "cache"));
|
|
@@ -479,20 +477,26 @@ function isRegisteredSkillResourceRead(resolved) {
|
|
|
479
477
|
return false;
|
|
480
478
|
}
|
|
481
479
|
/**
|
|
482
|
-
* Panel
|
|
483
|
-
* `~/.code-shell/panel-apps` tree.
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
480
|
+
* Installed Panel Apps live under the otherwise-sensitive
|
|
481
|
+
* `~/.code-shell/panel-apps` tree. Their package contents are reviewed and
|
|
482
|
+
* copied by the installer, so ordinary source, manifests, assets and declared
|
|
483
|
+
* Agent resources should not require a second approval merely because their
|
|
484
|
+
* parent directory is `~/.code-shell`.
|
|
487
485
|
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
486
|
+
* Keep the exception read-only at the call site and require a valid installed
|
|
487
|
+
* registry entry plus a matching V2 manifest. Registry, app root, manifest and
|
|
488
|
+
* target are all realpathed and containment-checked. Credential-shaped files
|
|
489
|
+
* are still caught by `SENSITIVE_FILE_PATTERNS` before this exception, and a
|
|
490
|
+
* symlink escaping the installed package never inherits its read authority.
|
|
491
491
|
*/
|
|
492
|
-
function
|
|
492
|
+
function isInstalledPanelAppResourceRead(resolved) {
|
|
493
|
+
let codeShellRoot;
|
|
493
494
|
let appsRoot;
|
|
494
495
|
let registryPath;
|
|
495
496
|
try {
|
|
497
|
+
codeShellRoot = realpathSync(join(configuredUserHome(), ".code-shell"));
|
|
498
|
+
if (!isInsideDir(resolved, codeShellRoot))
|
|
499
|
+
return false;
|
|
496
500
|
appsRoot = realpathSync(join(codeShellRoot, "panel-apps"));
|
|
497
501
|
if (!isInsideDir(appsRoot, codeShellRoot))
|
|
498
502
|
return false;
|
|
@@ -526,29 +530,10 @@ function isInstalledPanelAppSkillResourceRead(resolved, codeShellRoot) {
|
|
|
526
530
|
if (!isInsideDir(manifestPath, appRoot) || !statSync(manifestPath).isFile())
|
|
527
531
|
continue;
|
|
528
532
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
529
|
-
if (manifest.schemaVersion !== 2 ||
|
|
530
|
-
manifest.id !== id ||
|
|
531
|
-
!Array.isArray(manifest.agent?.skills)) {
|
|
533
|
+
if (manifest.schemaVersion !== 2 || manifest.id !== id)
|
|
532
534
|
continue;
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (typeof skillEntry !== "string")
|
|
536
|
-
continue;
|
|
537
|
-
const segments = skillEntry.split("/");
|
|
538
|
-
if (segments.length !== 4 ||
|
|
539
|
-
segments[0] !== "agent" ||
|
|
540
|
-
segments[1] !== "skills" ||
|
|
541
|
-
!/^[a-z][a-z0-9-]{0,63}$/.test(segments[2] ?? "") ||
|
|
542
|
-
segments[3] !== "SKILL.md") {
|
|
543
|
-
continue;
|
|
544
|
-
}
|
|
545
|
-
const skillManifest = realpathSync(join(appRoot, ...segments));
|
|
546
|
-
if (!isInsideDir(skillManifest, appRoot) || !statSync(skillManifest).isFile())
|
|
547
|
-
continue;
|
|
548
|
-
const skillRoot = dirname(skillManifest);
|
|
549
|
-
if (isInsideDir(resolved, skillRoot))
|
|
550
|
-
return true;
|
|
551
|
-
}
|
|
535
|
+
if (isInsideDir(resolved, appRoot))
|
|
536
|
+
return true;
|
|
552
537
|
}
|
|
553
538
|
catch {
|
|
554
539
|
// A stale, malformed, or tampered Panel App entry grants no read access.
|
|
@@ -652,6 +637,13 @@ export function classifyPath(rawPath, opts) {
|
|
|
652
637
|
resolvedPath: resolved,
|
|
653
638
|
};
|
|
654
639
|
}
|
|
640
|
+
if (opts.operation === "read" && !sensitiveFile && isInstalledPanelAppResourceRead(resolved)) {
|
|
641
|
+
return {
|
|
642
|
+
decision: "allow",
|
|
643
|
+
reason: "installed Panel App resource read",
|
|
644
|
+
resolvedPath: resolved,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
655
647
|
// Sensitive: write is always denied, read always asks. Workspace placement
|
|
656
648
|
// doesn't soften the rule — an `.env` in the project still asks on read.
|
|
657
649
|
if (sensitiveLabel) {
|
package/package.json
CHANGED