@cjhyy/code-shell-core 0.8.20 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/automation/desktop-authority-client.d.ts +9 -0
- package/dist/automation/desktop-authority-client.js +47 -0
- package/dist/automation/scheduler.d.ts +8 -0
- package/dist/automation/scheduler.js +37 -1
- package/dist/automation/store.js +14 -1
- package/dist/capabilities/index.d.ts +1 -0
- package/dist/cli/agent-server-stdio.js +4 -1
- package/dist/engine/engine-workspace-authority.d.ts +35 -0
- package/dist/engine/engine-workspace-authority.js +136 -0
- package/dist/engine/engine.d.ts +5 -10
- package/dist/engine/engine.js +84 -101
- package/dist/engine/input-attachments.d.ts +1 -0
- package/dist/engine/input-attachments.js +6 -2
- package/dist/engine/run-environment.d.ts +8 -3
- package/dist/engine/run-environment.js +33 -8
- package/dist/engine/run-image-input.d.ts +1 -0
- package/dist/engine/run-image-input.js +1 -0
- package/dist/engine/run-session-open.d.ts +2 -1
- package/dist/engine/run-session-open.js +6 -0
- package/dist/engine/run-setup.d.ts +1 -0
- package/dist/engine/run-setup.js +2 -1
- package/dist/engine/run-tooling.d.ts +2 -0
- package/dist/engine/run-tooling.js +3 -0
- package/dist/engine/run-types.d.ts +2 -0
- package/dist/engine/run-workspace.d.ts +6 -1
- package/dist/engine/run-workspace.js +47 -0
- package/dist/engine/subagent-spawner.d.ts +1 -0
- package/dist/engine/subagent-spawner.js +1 -0
- package/dist/engine/types.d.ts +2 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.extension.d.ts +1 -1
- package/dist/index.internal.d.ts +2 -2
- package/dist/index.internal.js +1 -0
- package/dist/index.js +1 -1
- package/dist/plugins/pluginAutomationTemplates.d.ts +2 -0
- package/dist/plugins/pluginAutomationTemplates.js +4 -0
- package/dist/prompt/composer.d.ts +4 -1
- package/dist/prompt/composer.js +11 -3
- package/dist/protocol/chat-session-manager.d.ts +42 -1
- package/dist/protocol/chat-session-manager.js +167 -4
- package/dist/protocol/chat-session.d.ts +9 -1
- package/dist/protocol/chat-session.js +20 -2
- package/dist/protocol/mobile-remote-types.d.ts +15 -0
- package/dist/protocol/server.d.ts +1 -2
- package/dist/protocol/server.js +21 -51
- package/dist/protocol/session-workspace-rpc.d.ts +23 -0
- package/dist/protocol/session-workspace-rpc.js +171 -0
- package/dist/protocol/types.d.ts +45 -1
- package/dist/protocol/types.js +4 -0
- package/dist/session/session-manager.d.ts +18 -0
- package/dist/session/session-manager.js +87 -0
- package/dist/tool-system/builtin/cron.d.ts +11 -0
- package/dist/tool-system/builtin/cron.js +27 -2
- package/dist/tool-system/builtin/edit.js +5 -3
- package/dist/tool-system/builtin/write.js +5 -3
- package/dist/tool-system/context.d.ts +4 -1
- package/dist/tool-system/executor.js +1 -1
- package/dist/tool-system/path-policy.d.ts +11 -3
- package/dist/tool-system/path-policy.js +56 -36
- package/dist/types.d.ts +6 -0
- package/dist/workspace/canonical-key.d.ts +7 -0
- package/dist/workspace/canonical-key.js +39 -0
- package/dist/workspace/workspace-context.d.ts +26 -0
- package/dist/workspace/workspace-context.js +112 -0
- 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" ||
|
|
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
|
package/dist/automation/store.js
CHANGED
|
@@ -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 ||
|
|
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
|
|
@@ -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
|
+
}
|
package/dist/engine/engine.d.ts
CHANGED
|
@@ -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
|
/**
|