@cjhyy/code-shell-core 0.8.13 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/automation/desktop-authority-client.d.ts +9 -0
  2. package/dist/automation/desktop-authority-client.js +47 -0
  3. package/dist/automation/scheduler.d.ts +8 -0
  4. package/dist/automation/scheduler.js +37 -1
  5. package/dist/automation/store.js +14 -1
  6. package/dist/capabilities/index.d.ts +1 -0
  7. package/dist/cli/agent-server-stdio.js +8 -4
  8. package/dist/engine/engine-workspace-authority.d.ts +35 -0
  9. package/dist/engine/engine-workspace-authority.js +136 -0
  10. package/dist/engine/engine.d.ts +5 -10
  11. package/dist/engine/engine.js +108 -112
  12. package/dist/engine/input-attachments.d.ts +1 -0
  13. package/dist/engine/input-attachments.js +6 -2
  14. package/dist/engine/run-environment.d.ts +8 -3
  15. package/dist/engine/run-environment.js +33 -8
  16. package/dist/engine/run-image-input.d.ts +1 -0
  17. package/dist/engine/run-image-input.js +1 -0
  18. package/dist/engine/run-session-open.d.ts +2 -1
  19. package/dist/engine/run-session-open.js +7 -1
  20. package/dist/engine/run-setup.d.ts +1 -0
  21. package/dist/engine/run-setup.js +2 -1
  22. package/dist/engine/run-tooling.d.ts +2 -0
  23. package/dist/engine/run-tooling.js +3 -0
  24. package/dist/engine/run-types.d.ts +4 -0
  25. package/dist/engine/run-workspace.d.ts +6 -1
  26. package/dist/engine/run-workspace.js +47 -0
  27. package/dist/engine/subagent-spawner.d.ts +1 -0
  28. package/dist/engine/subagent-spawner.js +1 -0
  29. package/dist/engine/turn-loop.js +17 -0
  30. package/dist/engine/types.d.ts +2 -0
  31. package/dist/index.d.ts +4 -4
  32. package/dist/index.extension.d.ts +1 -1
  33. package/dist/index.internal.d.ts +3 -2
  34. package/dist/index.internal.js +2 -0
  35. package/dist/index.js +2 -2
  36. package/dist/llm/client-base.d.ts +2 -1
  37. package/dist/llm/client-base.js +3 -1
  38. package/dist/llm/providers/anthropic.js +24 -25
  39. package/dist/llm/providers/openai.d.ts +4 -1
  40. package/dist/llm/providers/openai.js +76 -13
  41. package/dist/panel-apps/index.d.ts +1 -1
  42. package/dist/panel-apps/index.js +1 -1
  43. package/dist/panel-apps/installer.d.ts +29 -0
  44. package/dist/panel-apps/installer.js +124 -15
  45. package/dist/plugins/pluginAutomationTemplates.d.ts +2 -0
  46. package/dist/plugins/pluginAutomationTemplates.js +4 -0
  47. package/dist/plugins/pluginCatalog.d.ts +6 -0
  48. package/dist/plugins/pluginCatalog.js +7 -2
  49. package/dist/plugins/pluginContent.d.ts +1 -1
  50. package/dist/plugins/pluginContent.js +2 -10
  51. package/dist/prompt/composer.d.ts +4 -1
  52. package/dist/prompt/composer.js +11 -3
  53. package/dist/protocol/chat-session-manager.d.ts +42 -1
  54. package/dist/protocol/chat-session-manager.js +167 -4
  55. package/dist/protocol/chat-session.d.ts +11 -1
  56. package/dist/protocol/chat-session.js +20 -2
  57. package/dist/protocol/mobile-remote-types.d.ts +15 -0
  58. package/dist/protocol/server.d.ts +1 -2
  59. package/dist/protocol/server.js +28 -51
  60. package/dist/protocol/session-workspace-rpc.d.ts +23 -0
  61. package/dist/protocol/session-workspace-rpc.js +171 -0
  62. package/dist/protocol/types.d.ts +47 -1
  63. package/dist/protocol/types.js +4 -0
  64. package/dist/session/session-manager.d.ts +25 -0
  65. package/dist/session/session-manager.js +106 -6
  66. package/dist/session/transcript.d.ts +9 -0
  67. package/dist/session/transcript.js +81 -0
  68. package/dist/settings/manager.d.ts +12 -0
  69. package/dist/settings/manager.js +31 -0
  70. package/dist/tool-system/builtin/configure-model-connection.d.ts +36 -0
  71. package/dist/tool-system/builtin/configure-model-connection.js +396 -0
  72. package/dist/tool-system/builtin/cron.d.ts +11 -0
  73. package/dist/tool-system/builtin/cron.js +27 -2
  74. package/dist/tool-system/builtin/edit-model-catalog.d.ts +4 -6
  75. package/dist/tool-system/builtin/edit-model-catalog.js +5 -8
  76. package/dist/tool-system/builtin/edit.js +5 -3
  77. package/dist/tool-system/builtin/index.js +14 -0
  78. package/dist/tool-system/builtin/install-capability.js +22 -8
  79. package/dist/tool-system/builtin/settings-changed.d.ts +9 -0
  80. package/dist/tool-system/builtin/settings-changed.js +18 -0
  81. package/dist/tool-system/builtin/write.js +5 -3
  82. package/dist/tool-system/context.d.ts +12 -3
  83. package/dist/tool-system/executor.js +1 -1
  84. package/dist/tool-system/path-policy.d.ts +11 -3
  85. package/dist/tool-system/path-policy.js +56 -36
  86. package/dist/types.d.ts +11 -0
  87. package/dist/workspace/canonical-key.d.ts +7 -0
  88. package/dist/workspace/canonical-key.js +39 -0
  89. package/dist/workspace/workspace-context.d.ts +26 -0
  90. package/dist/workspace/workspace-context.js +112 -0
  91. package/package.json +1 -1
@@ -0,0 +1,9 @@
1
+ import type { Transport } from "../protocol/transport.js";
2
+ import type { CronCreateAuthority } from "../tool-system/builtin/cron.js";
3
+ declare const DESKTOP_AUTOMATION_CREATE_METHOD = "desktop/automationCreate";
4
+ /**
5
+ * Worker-side request client. Desktop uses it to keep CronCreate out of the
6
+ * shared store until Main has resolved project/root and resume authority.
7
+ */
8
+ export declare function createDesktopAutomationAuthorityClient(transport: Pick<Transport, "send" | "onMessage">): CronCreateAuthority;
9
+ export { DESKTOP_AUTOMATION_CREATE_METHOD };
@@ -0,0 +1,47 @@
1
+ const DESKTOP_AUTOMATION_CREATE_METHOD = "desktop/automationCreate";
2
+ /**
3
+ * Worker-side request client. Desktop uses it to keep CronCreate out of the
4
+ * shared store until Main has resolved project/root and resume authority.
5
+ */
6
+ export function createDesktopAutomationAuthorityClient(transport) {
7
+ let nextId = 1;
8
+ const pending = new Map();
9
+ transport.onMessage((message) => {
10
+ if (!message || typeof message !== "object" || !("id" in message) || "method" in message) {
11
+ return;
12
+ }
13
+ const id = String(message.id);
14
+ const waiter = pending.get(id);
15
+ if (!waiter)
16
+ return;
17
+ pending.delete(id);
18
+ clearTimeout(waiter.timer);
19
+ if ("error" in message && message.error) {
20
+ waiter.reject(new Error(message.error.message));
21
+ return;
22
+ }
23
+ const job = message.result;
24
+ if (!job || typeof job.id !== "string" || typeof job.name !== "string") {
25
+ waiter.reject(new Error("Desktop Main returned an invalid automation job"));
26
+ return;
27
+ }
28
+ waiter.resolve(job);
29
+ });
30
+ return (input) => {
31
+ const id = `automation-authority-${nextId++}`;
32
+ return new Promise((resolve, reject) => {
33
+ const timer = setTimeout(() => {
34
+ pending.delete(id);
35
+ reject(new Error("Desktop Main automation authority timed out"));
36
+ }, 30_000);
37
+ pending.set(id, { resolve, reject, timer });
38
+ transport.send({
39
+ jsonrpc: "2.0",
40
+ id,
41
+ method: DESKTOP_AUTOMATION_CREATE_METHOD,
42
+ params: input,
43
+ });
44
+ });
45
+ };
46
+ }
47
+ export { DESKTOP_AUTOMATION_CREATE_METHOD };
@@ -32,6 +32,10 @@ export interface CronJob {
32
32
  createdAt: number;
33
33
  /** Working directory the job runs in (the project it monitors/edits). */
34
34
  cwd?: string;
35
+ /** Stable desktop project binding. Legacy jobs may omit both identifiers. */
36
+ projectId?: string;
37
+ /** Stable root within projectId. */
38
+ rootId?: string;
35
39
  /** IANA timezone for cron-expression schedules (e.g. "Asia/Shanghai"). Default "UTC". */
36
40
  timezone?: string;
37
41
  /** Permission tier; defaults to read-only when unset. */
@@ -80,6 +84,8 @@ export interface CronExecutionOutcome {
80
84
  /** Optional metadata accepted by create(). */
81
85
  export interface CreateJobOptions {
82
86
  cwd?: string;
87
+ projectId?: string;
88
+ rootId?: string;
83
89
  timezone?: string;
84
90
  permissionLevel?: CronPermissionLevel;
85
91
  once?: boolean;
@@ -93,6 +99,8 @@ export interface UpdateJobPatch {
93
99
  schedule?: string;
94
100
  timezone?: string;
95
101
  cwd?: string;
102
+ projectId?: string | null;
103
+ rootId?: string | null;
96
104
  permissionLevel?: CronPermissionLevel;
97
105
  }
98
106
  export declare class CronScheduler {
@@ -43,6 +43,7 @@ const MAX_JOB_SCHEDULE_CHARS = 512;
43
43
  const MAX_JOB_PROMPT_CHARS = 1024 * 1024;
44
44
  const MAX_JOB_CWD_CHARS = 32_768;
45
45
  const MAX_JOB_TIMEZONE_CHARS = 128;
46
+ const MAX_JOB_BINDING_ID_CHARS = 512;
46
47
  function validateJobFields(input) {
47
48
  if (input.name !== undefined &&
48
49
  (typeof input.name !== "string" ||
@@ -66,9 +67,24 @@ function validateJobFields(input) {
66
67
  throw new Error("automation prompt must be a bounded non-empty string");
67
68
  }
68
69
  if (input.cwd !== undefined &&
69
- (typeof input.cwd !== "string" || input.cwd.length > MAX_JOB_CWD_CHARS || input.cwd.includes("\0"))) {
70
+ (typeof input.cwd !== "string" ||
71
+ input.cwd.length > MAX_JOB_CWD_CHARS ||
72
+ input.cwd.includes("\0"))) {
70
73
  throw new Error("automation cwd must be a bounded string");
71
74
  }
75
+ for (const [field, value] of [
76
+ ["projectId", input.projectId],
77
+ ["rootId", input.rootId],
78
+ ]) {
79
+ if (value !== undefined &&
80
+ value !== null &&
81
+ (typeof value !== "string" ||
82
+ !value ||
83
+ value.length > MAX_JOB_BINDING_ID_CHARS ||
84
+ value.includes("\0"))) {
85
+ throw new Error(`automation ${field} must be a bounded non-empty string`);
86
+ }
87
+ }
72
88
  if (input.timezone !== undefined &&
73
89
  (typeof input.timezone !== "string" ||
74
90
  input.timezone.length > MAX_JOB_TIMEZONE_CHARS ||
@@ -361,6 +377,8 @@ export class CronScheduler {
361
377
  runCount: 0,
362
378
  createdAt: Date.now(),
363
379
  ...(opts?.cwd !== undefined ? { cwd: opts.cwd } : {}),
380
+ ...(opts?.projectId !== undefined ? { projectId: opts.projectId } : {}),
381
+ ...(opts?.rootId !== undefined ? { rootId: opts.rootId } : {}),
364
382
  ...(opts?.timezone !== undefined ? { timezone: opts.timezone } : {}),
365
383
  ...(opts?.permissionLevel !== undefined ? { permissionLevel: opts.permissionLevel } : {}),
366
384
  ...(opts?.once === true ? { once: true } : {}),
@@ -383,6 +401,8 @@ export class CronScheduler {
383
401
  runCount: 0,
384
402
  createdAt: Date.now(),
385
403
  ...(opts?.cwd !== undefined ? { cwd: opts.cwd } : {}),
404
+ ...(opts?.projectId !== undefined ? { projectId: opts.projectId } : {}),
405
+ ...(opts?.rootId !== undefined ? { rootId: opts.rootId } : {}),
386
406
  ...(opts?.timezone !== undefined ? { timezone: opts.timezone } : {}),
387
407
  ...(opts?.permissionLevel !== undefined ? { permissionLevel: opts.permissionLevel } : {}),
388
408
  ...(opts?.once === true ? { once: true } : {}),
@@ -532,6 +552,14 @@ export class CronScheduler {
532
552
  job.timezone = patch.timezone;
533
553
  if (patch.cwd !== undefined)
534
554
  job.cwd = patch.cwd;
555
+ if (patch.projectId === null)
556
+ delete job.projectId;
557
+ else if (patch.projectId !== undefined)
558
+ job.projectId = patch.projectId;
559
+ if (patch.rootId === null)
560
+ delete job.rootId;
561
+ else if (patch.rootId !== undefined)
562
+ job.rootId = patch.rootId;
535
563
  if (patch.permissionLevel !== undefined)
536
564
  job.permissionLevel = patch.permissionLevel;
537
565
  if (scheduleChanged)
@@ -565,6 +593,14 @@ export class CronScheduler {
565
593
  job.timezone = patch.timezone;
566
594
  if (patch.cwd !== undefined)
567
595
  job.cwd = patch.cwd;
596
+ if (patch.projectId === null)
597
+ delete job.projectId;
598
+ else if (patch.projectId !== undefined)
599
+ job.projectId = patch.projectId;
600
+ if (patch.rootId === null)
601
+ delete job.rootId;
602
+ else if (patch.rootId !== undefined)
603
+ job.rootId = patch.rootId;
568
604
  if (patch.permissionLevel !== undefined)
569
605
  job.permissionLevel = patch.permissionLevel;
570
606
  // Re-arm only when the schedule definition changed, or when an enabled job
@@ -63,6 +63,15 @@ function normalizeJob(value, strict) {
63
63
  (typeof raw.cwd !== "string" || raw.cwd.length > 32_768 || raw.cwd.includes("\0"))) {
64
64
  return invalid("cwd");
65
65
  }
66
+ for (const field of ["projectId", "rootId"]) {
67
+ if (raw[field] !== undefined &&
68
+ (typeof raw[field] !== "string" ||
69
+ !raw[field] ||
70
+ raw[field].length > 512 ||
71
+ raw[field].includes("\0"))) {
72
+ return invalid(field);
73
+ }
74
+ }
66
75
  if (raw.timezone !== undefined &&
67
76
  (typeof raw.timezone !== "string" || raw.timezone.length > 128 || raw.timezone.includes("\0"))) {
68
77
  return invalid("timezone");
@@ -96,7 +105,9 @@ function normalizeJob(value, strict) {
96
105
  }
97
106
  let templateSource;
98
107
  if (raw.templateSource !== undefined) {
99
- if (!raw.templateSource || typeof raw.templateSource !== "object" || Array.isArray(raw.templateSource)) {
108
+ if (!raw.templateSource ||
109
+ typeof raw.templateSource !== "object" ||
110
+ Array.isArray(raw.templateSource)) {
100
111
  return invalid("templateSource");
101
112
  }
102
113
  const source = raw.templateSource;
@@ -137,6 +148,8 @@ function normalizeJob(value, strict) {
137
148
  ...(typeof raw.lastRun === "number" ? { lastRun: raw.lastRun } : {}),
138
149
  ...(typeof raw.nextRun === "number" ? { nextRun: raw.nextRun } : {}),
139
150
  ...(typeof raw.cwd === "string" ? { cwd: raw.cwd } : {}),
151
+ ...(typeof raw.projectId === "string" ? { projectId: raw.projectId } : {}),
152
+ ...(typeof raw.rootId === "string" ? { rootId: raw.rootId } : {}),
140
153
  ...(typeof raw.timezone === "string" ? { timezone: raw.timezone } : {}),
141
154
  ...(raw.permissionLevel === "read-only" ||
142
155
  raw.permissionLevel === "workspace-write" ||
@@ -18,6 +18,7 @@ export interface CapabilityEngineHookContribution {
18
18
  }
19
19
  export interface CapabilityDynamicContext {
20
20
  cwd: string;
21
+ workspace: import("../workspace/workspace-context.js").WorkspaceContext;
21
22
  preset: AgentPreset;
22
23
  }
23
24
  export type CapabilityDynamicContextProvider = (context: CapabilityDynamicContext) => string | undefined | Promise<string | undefined>;
@@ -38,7 +38,7 @@ import { validateSettings } from "../settings/schema.js";
38
38
  import { AgentServer } from "../protocol/server.js";
39
39
  import { StdioTransport } from "../protocol/transport.js";
40
40
  import { createNotification, Methods } from "../protocol/types.js";
41
- import { setCronChangedSink } from "../tool-system/builtin/cron.js";
41
+ import { setCronChangedSink, setCronCreateAuthority } from "../tool-system/builtin/cron.js";
42
42
  import { setModelCatalogChangedSink } from "../tool-system/builtin/edit-model-catalog.js";
43
43
  import { setCapabilityChangedSink } from "../tool-system/builtin/install-capability.js";
44
44
  import { SettingsManager, noRepoDir } from "../settings/manager.js";
@@ -54,6 +54,7 @@ import { cronScheduler } from "../automation/scheduler.js";
54
54
  import { CronStore, defaultCronStorePath } from "../automation/store.js";
55
55
  import { resolveLLMConfigForTag } from "../engine/resolve-llm-config.js";
56
56
  import { createIpcCredentialAccess, setDefaultCredentialAccess } from "../credentials/access.js";
57
+ import { createDesktopAutomationAuthorityClient } from "../automation/desktop-authority-client.js";
57
58
  import { compileComposition } from "../composition/compiler.js";
58
59
  /**
59
60
  * Load AgentModules from CODE_SHELL_CAPABILITY_MODULES: comma-separated
@@ -260,6 +261,7 @@ const chatManager = new ChatSessionManager({
260
261
  // cwd). The slice.cwd spread below is now redundant with this but kept
261
262
  // for clarity / explicitness.
262
263
  cwd: sessionCwd,
264
+ workspaceContext: slice.workspaceContext,
263
265
  runtime,
264
266
  // This stdio worker exists only to serve the desktop app, so every
265
267
  // session it creates is a desktop-origin session.
@@ -326,6 +328,7 @@ cronScheduler.loadJobs();
326
328
  // ─── Step 5: AgentServer over stdio ──────────────────────────────
327
329
  const stdioTransport = new StdioTransport(process.stdin, process.stdout);
328
330
  setDefaultCredentialAccess(createIpcCredentialAccess(stdioTransport));
331
+ setCronCreateAuthority(createDesktopAutomationAuthorityClient(stdioTransport));
329
332
  // Cron jobs are persisted by this worker but only main arms/executes their
330
333
  // timers (this worker keeps setExecutionEnabled(false) above). When an AI tool
331
334
  // creates/deletes a cron job here, notify main over stdio so it reloads the
@@ -333,9 +336,10 @@ setDefaultCredentialAccess(createIpcCredentialAccess(stdioTransport));
333
336
  setCronChangedSink(() => {
334
337
  stdioTransport.send(createNotification("agent/cronChanged", {}));
335
338
  });
336
- // EditModelCatalog writes in this worker process, while the mounted settings
337
- // and connection pages live in the desktop renderer. Notify them immediately
338
- // after a successful write instead of relying on a later turn_complete event.
339
+ // Catalog and model-connection tools write in this worker process, while the
340
+ // mounted settings/connection pages live in the desktop renderer. Notify them
341
+ // immediately after a successful write instead of relying on a later
342
+ // turn_complete event.
339
343
  setModelCatalogChangedSink(() => {
340
344
  stdioTransport.send(createNotification(Methods.SettingsChanged, {}));
341
345
  });
@@ -0,0 +1,35 @@
1
+ import type { SessionProjectBinding, SessionState, SessionWorkspace } from "../types.js";
2
+ import { type SessionManager } from "../session/session-manager.js";
3
+ import type { SessionMessageRouter, SessionMessageToolService } from "../session/session-message.js";
4
+ import { type WorkspaceContext } from "../workspace/workspace-context.js";
5
+ export declare class SessionWorkspaceAuthorityTracker {
6
+ private readonly contexts;
7
+ remember(sessionId: string, context: WorkspaceContext): void;
8
+ delete(sessionId: string): void;
9
+ clear(): void;
10
+ }
11
+ export declare function createAuthorizedSessionMessageService(options: {
12
+ sessionManager: SessionManager;
13
+ router: SessionMessageRouter | undefined;
14
+ sourceSessionId: string;
15
+ rawTargets: unknown;
16
+ }): SessionMessageToolService | undefined;
17
+ export declare function migrateOwnedSessionMainRoot(options: {
18
+ sessionManager: SessionManager;
19
+ activeState?: SessionState;
20
+ authorities: SessionWorkspaceAuthorityTracker;
21
+ sessionId: string;
22
+ project: SessionProjectBinding;
23
+ mainRoot: string;
24
+ }): SessionWorkspace;
25
+ export declare function setOwnedSessionWorkspace(options: {
26
+ sessionManager: SessionManager;
27
+ activeState?: SessionState;
28
+ sessionId: string;
29
+ workspace: SessionWorkspace;
30
+ }): SessionWorkspace | null;
31
+ export declare function releaseOwnedSessionWorkspace(options: {
32
+ sessionManager: SessionManager;
33
+ activeState?: SessionState;
34
+ sessionId: string;
35
+ }): SessionWorkspace | null;
@@ -0,0 +1,136 @@
1
+ import { assertSafeSessionId } from "../session/session-manager.js";
2
+ import { clearSessionPathApprovalsUnderRoot } from "../tool-system/path-policy.js";
3
+ import { canonicalKey } from "../workspace/canonical-key.js";
4
+ import { removedWorkspaceRootPaths, } from "../workspace/workspace-context.js";
5
+ export class SessionWorkspaceAuthorityTracker {
6
+ contexts = new Map();
7
+ remember(sessionId, context) {
8
+ const previous = this.contexts.get(sessionId);
9
+ if (previous) {
10
+ for (const root of removedWorkspaceRootPaths(previous, context)) {
11
+ clearSessionPathApprovalsUnderRoot(sessionId, root);
12
+ }
13
+ }
14
+ this.contexts.set(sessionId, context);
15
+ }
16
+ delete(sessionId) {
17
+ this.contexts.delete(sessionId);
18
+ }
19
+ clear() {
20
+ this.contexts.clear();
21
+ }
22
+ }
23
+ export function createAuthorizedSessionMessageService(options) {
24
+ const { sessionManager, router, sourceSessionId } = options;
25
+ const sourceRoot = sessionManager.readSessionMainRoot(sourceSessionId);
26
+ if (!sourceRoot || !router)
27
+ return undefined;
28
+ const sourceBinding = sessionManager.readSessionProjectBinding(sourceSessionId);
29
+ const catalog = [];
30
+ const seen = new Set();
31
+ const rawTargets = Array.isArray(options.rawTargets) ? [...options.rawTargets] : [];
32
+ for (const raw of rawTargets.slice(0, 100)) {
33
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
34
+ continue;
35
+ const candidate = raw;
36
+ const sessionId = typeof candidate.sessionId === "string" ? candidate.sessionId : "";
37
+ try {
38
+ assertSafeSessionId(sessionId);
39
+ }
40
+ catch {
41
+ continue;
42
+ }
43
+ if (seen.has(sessionId))
44
+ continue;
45
+ const candidateRoot = typeof candidate.workspaceRoot === "string" ? candidate.workspaceRoot : "";
46
+ const targetBinding = sessionManager.readSessionProjectBinding(sessionId);
47
+ if (sourceBinding) {
48
+ if (targetBinding) {
49
+ if (targetBinding.projectId !== sourceBinding.projectId)
50
+ continue;
51
+ }
52
+ else if (canonicalKey(candidateRoot) !== canonicalKey(sourceRoot)) {
53
+ continue;
54
+ }
55
+ }
56
+ else if (canonicalKey(candidateRoot) !== canonicalKey(sourceRoot)) {
57
+ continue;
58
+ }
59
+ const title = typeof candidate.title === "string" ? candidate.title.trim() : "";
60
+ if (!title || title.length > 512)
61
+ continue;
62
+ const workspaceProfile = typeof candidate.workspaceProfile === "string"
63
+ ? candidate.workspaceProfile.trim().slice(0, 256)
64
+ : "";
65
+ seen.add(sessionId);
66
+ catalog.push({
67
+ sessionId,
68
+ title,
69
+ workspaceRoot: sourceRoot,
70
+ ...(workspaceProfile ? { workspaceProfile } : {}),
71
+ });
72
+ }
73
+ const targets = catalog.filter((target) => target.sessionId !== sourceSessionId);
74
+ return {
75
+ targets,
76
+ send: async ({ targetSessionId, message }) => {
77
+ const target = targets.find((candidate) => candidate.sessionId === targetSessionId);
78
+ if (!target)
79
+ throw new Error("target Session is not in the host-authorized project list");
80
+ if (!message.trim())
81
+ throw new Error("message is required");
82
+ if (message.length > 48_000)
83
+ throw new Error("message exceeds 48000 characters");
84
+ await router({ sourceSessionId, target, message, catalog });
85
+ return target;
86
+ },
87
+ };
88
+ }
89
+ export function migrateOwnedSessionMainRoot(options) {
90
+ const { sessionManager, activeState, authorities, sessionId, project, mainRoot } = options;
91
+ if (!sessionId || !sessionManager.exists(sessionId)) {
92
+ throw new Error(`Session ${sessionId} does not exist`);
93
+ }
94
+ const workspace = { root: mainRoot, kind: "main" };
95
+ const stateRevision = sessionManager.migrateSessionMainRoot(sessionId, project, mainRoot);
96
+ if (activeState?.sessionId === sessionId) {
97
+ Object.assign(activeState, {
98
+ project: { ...project },
99
+ cwd: mainRoot,
100
+ workspace,
101
+ stateRevision,
102
+ });
103
+ }
104
+ authorities.delete(sessionId);
105
+ return workspace;
106
+ }
107
+ export function setOwnedSessionWorkspace(options) {
108
+ const { sessionManager, activeState, sessionId, workspace } = options;
109
+ if (!sessionId || !sessionManager.exists(sessionId))
110
+ return null;
111
+ try {
112
+ const stateRevision = sessionManager.setSessionWorkspace(sessionId, workspace);
113
+ if (activeState?.sessionId === sessionId) {
114
+ Object.assign(activeState, { workspace, stateRevision });
115
+ }
116
+ return workspace;
117
+ }
118
+ catch {
119
+ return null;
120
+ }
121
+ }
122
+ export function releaseOwnedSessionWorkspace(options) {
123
+ const { sessionManager, activeState, sessionId } = options;
124
+ if (!sessionId || !sessionManager.exists(sessionId))
125
+ return null;
126
+ const mainRoot = sessionManager.readSessionMainRoot(sessionId) ??
127
+ (activeState?.sessionId === sessionId ? activeState.cwd : undefined);
128
+ if (!mainRoot)
129
+ return null;
130
+ return setOwnedSessionWorkspace({
131
+ sessionManager,
132
+ activeState,
133
+ sessionId,
134
+ workspace: { root: mainRoot, kind: "main" },
135
+ });
136
+ }
@@ -125,6 +125,7 @@ export declare class Engine {
125
125
  private lastMessages;
126
126
  private lastSessionId;
127
127
  private compactedMessagesBySession;
128
+ private workspaceAuthorities;
128
129
  /**
129
130
  * SIDs whose ctx-bar seed we've already emitted in this process. The seed
130
131
  * is a rough char/4 estimate; only useful before the first real
@@ -187,6 +188,7 @@ export declare class Engine {
187
188
  * writers are additionally fenced by SessionManager's persisted revision CAS.
188
189
  */
189
190
  private runInProgress;
191
+ private disposed;
190
192
  private agentControlStateListener?;
191
193
  private agentDirectionsDeliveredListener?;
192
194
  /** Permission update requested while runInProgress. Applied in run() finally. */
@@ -511,6 +513,8 @@ export declare class Engine {
511
513
  private persistFinalRunState;
512
514
  private persistRunProgress;
513
515
  getConfig(): EngineConfig;
516
+ /** Release Engine-local registrations after its owning ChatSession is replaced. */
517
+ dispose(): Promise<void>;
514
518
  /**
515
519
  * Config hot-reload "layer 2": merge a disk-default config patch into this
516
520
  * ALREADY-RUNNING session's `this.config`, reload settings hooks, and
@@ -593,17 +597,8 @@ export declare class Engine {
593
597
  goalId?: string;
594
598
  revision?: number;
595
599
  }): boolean;
596
- /**
597
- * Persist a workspace pointer through the Engine that owns the live bundle.
598
- * Host-side workspace actions use this RPC-facing seam so advancing the disk
599
- * revision also rebases the active run before its next progress write.
600
- */
601
600
  setSessionWorkspace(sessionId: string, workspace: SessionWorkspace): SessionWorkspace | null;
602
- /**
603
- * Reset a session's workspace pointer back to its main root. If the session is
604
- * actively running, mutate that live SessionBundle first so the run's next
605
- * saveState cannot resurrect a stale worktree pointer.
606
- */
601
+ migrateSessionMainRoot(sessionId: string, project: import("../types.js").SessionProjectBinding, mainRoot: string): SessionWorkspace;
607
602
  releaseSessionWorkspace(sessionId: string): SessionWorkspace | null;
608
603
  injectContext(sessionId: string, content: string): void;
609
604
  /**