@cjhyy/code-shell-core 0.8.7 → 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.
@@ -93,11 +93,17 @@ export async function resolveRunWorkspace(args) {
93
93
  configCwd: args.configCwd,
94
94
  processCwd: args.processCwd,
95
95
  });
96
- const profileState = resolveRunProfileState({
97
- sessionWorkspaceProfile,
98
- cwd,
99
- settings: args.settings,
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: {
@@ -202,9 +202,6 @@ export declare class TurnLoop {
202
202
  * loop forces a stop so a stuck goal can't loop forever.
203
203
  */
204
204
  private stopBlockCount;
205
- /** Consecutive identical tool-call/result batches, ignoring provider call ids. */
206
- private repeatedToolBatchFingerprint;
207
- private repeatedToolBatchCount;
208
205
  /**
209
206
  * Run-scoped goal budget tracker (Goal mode). Hoisted to an instance field
210
207
  * (not a run() local) so extend() can bump its budgets mid-run. Null when no
@@ -4,7 +4,7 @@
4
4
  * Following Claude Code's po_() pattern:
5
5
  * pre_check → model_call → post_check → tool_exec → context_mgmt → hook_notify → next turn
6
6
  */
7
- import { createHash, randomUUID } from "node:crypto";
7
+ import { randomUUID } from "node:crypto";
8
8
  import { buildAgentDirectionMessage } from "../tool-system/builtin/agent-notifications.js";
9
9
  import { newTurnId } from "./turn-state.js";
10
10
  import { formatFriendlyError } from "./friendly-error.js";
@@ -40,39 +40,6 @@ export function toolResultToBlock(result) {
40
40
  block.is_error = true;
41
41
  return block;
42
42
  }
43
- const REPEATED_TOOL_BATCH_LIMIT = 3;
44
- function canonicalToolValue(value) {
45
- if (value === null)
46
- return "null";
47
- if (value === undefined)
48
- return "undefined";
49
- if (Array.isArray(value))
50
- return `[${value.map(canonicalToolValue).join(",")}]`;
51
- if (typeof value === "object") {
52
- return `{${Object.entries(value)
53
- .sort(([left], [right]) => left.localeCompare(right))
54
- .map(([key, entry]) => `${JSON.stringify(key)}:${canonicalToolValue(entry)}`)
55
- .join(",")}}`;
56
- }
57
- return JSON.stringify(value);
58
- }
59
- function repeatedToolBatchFingerprint(toolCalls, results) {
60
- // Hash immediately and never log the canonical source: tool results may
61
- // contain credentials or large media payloads. Call ids are deliberately
62
- // omitted because providers generate a fresh id for every identical retry.
63
- return createHash("sha256")
64
- .update(canonicalToolValue({
65
- calls: toolCalls.map((call) => ({ toolName: call.toolName, args: call.args })),
66
- results: results.map((result) => ({
67
- toolName: result.toolName,
68
- isError: result.isError === true || Boolean(result.error),
69
- error: result.error,
70
- result: result.result,
71
- contentBlocks: result.contentBlocks,
72
- })),
73
- }))
74
- .digest("hex");
75
- }
76
43
  export class TurnLoop {
77
44
  deps;
78
45
  config;
@@ -109,9 +76,6 @@ export class TurnLoop {
109
76
  * loop forces a stop so a stuck goal can't loop forever.
110
77
  */
111
78
  stopBlockCount = 0;
112
- /** Consecutive identical tool-call/result batches, ignoring provider call ids. */
113
- repeatedToolBatchFingerprint;
114
- repeatedToolBatchCount = 0;
115
79
  /**
116
80
  * Run-scoped goal budget tracker (Goal mode). Hoisted to an instance field
117
81
  * (not a run() local) so extend() can bump its budgets mid-run. Null when no
@@ -1445,49 +1409,6 @@ export class TurnLoop {
1445
1409
  tlog.info("guard.stale_task", { cat: "guard", turn: this.turnCount });
1446
1410
  }
1447
1411
  }
1448
- const toolBatchFingerprint = repeatedToolBatchFingerprint(toolCalls, results);
1449
- if (toolBatchFingerprint === this.repeatedToolBatchFingerprint) {
1450
- this.repeatedToolBatchCount++;
1451
- }
1452
- else {
1453
- this.repeatedToolBatchFingerprint = toolBatchFingerprint;
1454
- this.repeatedToolBatchCount = 1;
1455
- }
1456
- if (this.repeatedToolBatchCount >= REPEATED_TOOL_BATCH_LIMIT) {
1457
- tlog.warn("turn.repeated_tool_batch_stopped", {
1458
- cat: "turn",
1459
- repeatedCount: this.repeatedToolBatchCount,
1460
- tools: toolCalls.map((call) => call.toolName),
1461
- });
1462
- await this.emitHook("on_turn_end", {
1463
- turnNumber: this.turnCount,
1464
- hasToolUse: true,
1465
- toolCallCount: toolCalls.length,
1466
- });
1467
- finalText =
1468
- `检测到同一组工具调用及其结果连续重复 ${REPEATED_TOOL_BATCH_LIMIT} 次,` +
1469
- "已自动停止,避免继续空转。请调整请求或让 Session 获取新的上下文后再试。";
1470
- this.deps.transcript.appendMessage("assistant", finalText);
1471
- messages.push({ role: "assistant", content: finalText });
1472
- this.config.onStream?.({
1473
- type: "assistant_message",
1474
- messageId: assistantMessageId,
1475
- message: { role: "assistant", content: finalText },
1476
- });
1477
- this.finalizeModelTurn();
1478
- if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
1479
- this.repeatedToolBatchFingerprint = undefined;
1480
- this.repeatedToolBatchCount = 0;
1481
- continue;
1482
- }
1483
- messages = this.redactConsumedSensitiveToolResults(messages);
1484
- return {
1485
- text: finalText,
1486
- reason: "completed",
1487
- messages,
1488
- completionKind: "limit_stop",
1489
- };
1490
- }
1491
1412
  // Hook: turn end
1492
1413
  await this.emitHook("on_turn_end", {
1493
1414
  turnNumber: this.turnCount,
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.7";
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.7";
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", "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", "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" | "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" | "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", "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" | "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" | "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" | "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" | "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" | "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" | "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,12 +7,15 @@ 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",
13
14
  "notifications.send",
15
+ "audio.transcribe",
14
16
  "credentials.cookies",
15
17
  "automations.manage",
18
+ "process",
16
19
  ];
17
20
  export const PANEL_APP_ICONS = [
18
21
  "panel",
@@ -138,7 +141,7 @@ const PanelAppManifestFields = {
138
141
  icon: z.enum(PANEL_APP_ICONS).default("panel"),
139
142
  placement: z.literal("right-dock").default("right-dock"),
140
143
  singleton: z.boolean().default(true),
141
- permissions: z.array(z.enum(PANEL_APP_PERMISSIONS)).max(12).default([]),
144
+ permissions: z.array(z.enum(PANEL_APP_PERMISSIONS)).max(16).default([]),
142
145
  };
143
146
  /**
144
147
  * Schema v2 keeps one installable Panel App identity while allowing it to
@@ -180,12 +183,13 @@ export const PanelAppManifest = z
180
183
  });
181
184
  }
182
185
  if ((value.permissions.includes("workspace.read") ||
183
- value.permissions.includes("workspace.write")) &&
186
+ value.permissions.includes("workspace.write") ||
187
+ value.permissions.includes("audio.transcribe")) &&
184
188
  !value.permissions.includes("context.workspace")) {
185
189
  ctx.addIssue({
186
190
  code: z.ZodIssueCode.custom,
187
191
  path: ["permissions"],
188
- message: "workspace.read and workspace.write require context.workspace",
192
+ message: "workspace.read, workspace.write, and audio.transcribe require context.workspace",
189
193
  });
190
194
  }
191
195
  if (value.permissions.includes("automations.manage") &&
@@ -89,6 +89,21 @@ export interface ComposerOptions {
89
89
  * Undefined/0 → inject all.
90
90
  */
91
91
  memoriesMaxAgeDays?: number;
92
+ /**
93
+ * When true (set from the active behavior profile, e.g. the Pet manager),
94
+ * the injected memory index keeps only memories relevant to this cwd —
95
+ * global-layer project/dream records tied to other projects are dropped.
96
+ * See MemoryManager.buildInjectionIndex(currentProjectOnly).
97
+ */
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;
92
107
  }
93
108
  export declare class PromptComposer {
94
109
  private readonly options;
@@ -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 {
@@ -102,18 +106,26 @@ export class PromptComposer {
102
106
  });
103
107
  const skillsListing = buildSkillListing(skills);
104
108
  const declaredSkillGap = this.buildDeclaredSkillGap(skills);
105
- const capabilityContext = await this.buildSystemContext();
109
+ // Capability and sources context are independent I/O — resolve them
110
+ // concurrently instead of serially.
111
+ const [capabilityContext, sourcesContext] = await Promise.all([
112
+ this.buildSystemContext(),
113
+ (async () => {
114
+ if (this.options.disableSourcesContext === true)
115
+ return "";
116
+ try {
117
+ return (await this.options.sourcesContextProvider?.()) ?? "";
118
+ }
119
+ catch {
120
+ // Optional metadata context must not make a turn fail.
121
+ return "";
122
+ }
123
+ })(),
124
+ ]);
106
125
  // Memory rides here (tail, past the cache breakpoint) — not the system
107
126
  // prefix — so a memory change (extraction / recall usage++ / approve) never
108
127
  // re-bills the cached prefix. See buildUserContextMessage for the rationale.
109
128
  const memoryContext = this.getMemoryContext();
110
- let sourcesContext = "";
111
- try {
112
- sourcesContext = (await this.options.sourcesContextProvider?.()) ?? "";
113
- }
114
- catch {
115
- // Optional metadata context must not make a turn fail.
116
- }
117
129
  const goalToolContext = this.buildGoalToolContext();
118
130
  const parts = [
119
131
  skillsListing,
@@ -259,6 +271,8 @@ export class PromptComposer {
259
271
  return this.cachedInstructions;
260
272
  }
261
273
  getMemoryContext() {
274
+ if (this.options.disableMemoryContext === true)
275
+ return "";
262
276
  try {
263
277
  // Three-layer injection (用户拍板): a compact index merging GLOBAL +
264
278
  // DIGITAL-HUMAN + PROJECT memories. Global memories are surfaced every
@@ -268,6 +282,7 @@ export class PromptComposer {
268
282
  projectDir: this.options.cwd,
269
283
  profileDir: this.options.profileMemoryDir,
270
284
  maxAgeDays: this.options.memoriesMaxAgeDays,
285
+ currentProjectOnly: this.options.memoryCurrentProjectOnly,
271
286
  });
272
287
  }
273
288
  catch {
@@ -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())
@@ -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,
@@ -2240,7 +2277,17 @@ export class AgentServer {
2240
2277
  if (!archiveEngine)
2241
2278
  return;
2242
2279
  try {
2243
- const result = await archiveEngine.archiveTurnRange(archiveSessionId, { start, end });
2280
+ const toClientMessageId = typeof params.toClientMessageId === "string" ? params.toClientMessageId : undefined;
2281
+ const anchors = toClientMessageId
2282
+ ? {
2283
+ toClientMessageId,
2284
+ ...(typeof params.fromClientMessageId === "string"
2285
+ ? { fromClientMessageId: params.fromClientMessageId }
2286
+ : {}),
2287
+ ...(typeof params.segmentId === "string" ? { segmentId: params.segmentId } : {}),
2288
+ }
2289
+ : undefined;
2290
+ const result = await archiveEngine.archiveTurnRange(archiveSessionId, { start, end }, anchors);
2244
2291
  if (result.before > result.after) {
2245
2292
  const event = {
2246
2293
  type: "context_compact",
@@ -2265,6 +2312,35 @@ export class AgentServer {
2265
2312
  }
2266
2313
  break;
2267
2314
  }
2315
+ case "archive_marker": {
2316
+ const markerSessionId = typeof params.sessionId === "string" && params.sessionId.length > 0
2317
+ ? params.sessionId
2318
+ : undefined;
2319
+ const summary = typeof params.summary === "string" ? params.summary : undefined;
2320
+ const toClientMessageId = typeof params.toClientMessageId === "string" ? params.toClientMessageId : undefined;
2321
+ if (!markerSessionId || !summary || !toClientMessageId) {
2322
+ this.transport.send(createErrorResponse(req.id, ErrorCodes.InvalidParams, "archive_marker requires sessionId, summary and toClientMessageId"));
2323
+ return;
2324
+ }
2325
+ const markerEngine = await this.resolveEngineForSessionQuery(req, markerSessionId, engine, "archive_marker");
2326
+ if (!markerEngine)
2327
+ return;
2328
+ try {
2329
+ const appended = await markerEngine.appendArchiveMarker(markerSessionId, {
2330
+ summary,
2331
+ toClientMessageId,
2332
+ ...(typeof params.fromClientMessageId === "string"
2333
+ ? { fromClientMessageId: params.fromClientMessageId }
2334
+ : {}),
2335
+ ...(typeof params.segmentId === "string" ? { segmentId: params.segmentId } : {}),
2336
+ });
2337
+ this.transport.send(createResponse(req.id, { type: "archive_marker", data: { appended } }));
2338
+ }
2339
+ catch (err) {
2340
+ this.transport.send(createErrorResponse(req.id, ErrorCodes.InternalError, err.message));
2341
+ }
2342
+ break;
2343
+ }
2268
2344
  case "models": {
2269
2345
  if (!engine) {
2270
2346
  this.transport.send(createErrorResponse(req.id, ErrorCodes.InternalError, "No engine available for models query"));
@@ -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
@@ -342,6 +354,20 @@ export interface QueryParams {
342
354
  start?: unknown;
343
355
  /** Used by archive_range: half-open message-index window end (exclusive). */
344
356
  end?: unknown;
357
+ /**
358
+ * Used by archive_range (optional) and archive_marker (required): the
359
+ * clientMessageId the archived/marked span ends at. archive_range only
360
+ * persists a boundary when this anchor is supplied and resolves to a live
361
+ * message; without it, archive_range behaves exactly as before (in-memory
362
+ * compaction only, no persisted range_archive event).
363
+ */
364
+ toClientMessageId?: string;
365
+ /** Used by archive_range / archive_marker: optional start-of-span anchor. */
366
+ fromClientMessageId?: string;
367
+ /** Used by archive_range / archive_marker: optional idempotency key. */
368
+ segmentId?: string;
369
+ /** Used by archive_marker: the summary text to persist without a model call. */
370
+ summary?: string;
345
371
  }
346
372
  export interface QueryResult {
347
373
  type: string;
@@ -233,6 +233,16 @@ export declare class MemoryManager {
233
233
  baseDir?: string;
234
234
  maxAgeDays?: number;
235
235
  now?: number;
236
+ /**
237
+ * Trim the global layer to what is relevant to this run's project:
238
+ * project-type and dream-scope records are per-project experience, so
239
+ * they are kept only when their origin metadata ties them to
240
+ * `projectDir`. General knowledge (user/feedback/reference in user
241
+ * scope) always stays; the project layer is untouched because it is
242
+ * already this project's own store. Set by behavior profiles whose runs
243
+ * never work inside other repos (e.g. a manager/dispatcher session).
244
+ */
245
+ currentProjectOnly?: boolean;
236
246
  }): string;
237
247
  /**
238
248
  * Load every entry belonging to the given scope, without changing the
@@ -651,7 +651,13 @@ export class MemoryManager {
651
651
  const profile = opts.profileDir ? new MemoryManager({ baseDir: opts.profileDir }) : null;
652
652
  const pinnedFirst = (a, b) => Number(b.pinned ?? false) - Number(a.pinned ?? false);
653
653
  const collect = (mm) => filterByAge([...mm.loadScope("user"), ...mm.loadScope("dream")], opts.maxAgeDays, opts.now).sort(pinnedFirst);
654
- const globalEntries = collect(global);
654
+ let globalEntries = collect(global);
655
+ if (opts.currentProjectOnly) {
656
+ const tiedToProject = (e) => opts.projectDir !== undefined &&
657
+ (e.originProject === opts.projectDir ||
658
+ (e.originProjects?.includes(opts.projectDir) ?? false));
659
+ globalEntries = globalEntries.filter((e) => (e.type !== "project" && e.scope !== "dream") || tiedToProject(e));
660
+ }
655
661
  const profileEntries = profile ? collect(profile) : [];
656
662
  const projectEntries = project ? collect(project) : [];
657
663
  if (globalEntries.length === 0 && profileEntries.length === 0 && projectEntries.length === 0) {
@@ -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
  *