@chatcode/chatcode-cli 3.0.4-rc.1 → 3.0.4-rc.2
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/lib/{command-errors-BkCHTQHj.js → command-errors-CScdshCl.js} +12 -12
- package/lib/{command-trees-5HFyC5sI.js → command-trees-Dc17MYfV.js} +2 -2
- package/lib/{component-identity-De91VHwW.js → component-identity-DFJD_d-P.js} +3 -3
- package/lib/{dist-CsDWUvQ4.js → dist-w_sIm4VD.js} +231 -231
- package/lib/{extension-events-CdYCRP0o.js → extension-events-qXmhzmHo.js} +26 -26
- package/lib/{frames-DOX-Jgk8.js → frames-B89znn_0.js} +4 -4
- package/lib/{host-access-Bk4xps5E.js → host-access-B0CRAwsX.js} +1 -1
- package/lib/{keymap-CJ1kvLR2.js → keymap-CIoVICBI.js} +2 -2
- package/lib/{plugin-XR7ncbnW.js → plugin-BKQsL2xn.js} +1638 -1363
- package/lib/{scenes-CAmWncZD.js → scenes-BRf-zMpF.js} +3 -3
- package/lib/{settings-sections-CakcvBv9.js → settings-sections-4gMhveN_.js} +3 -3
- package/lib/{statusBarSettings-BPPS_Usm.js → statusBarSettings-CnmdSHkO.js} +3 -3
- package/lib/{supports-color-DyChQwlI.js → supports-color-CcW-FGjr.js} +2 -2
- package/lib/{themes-OjcUpZzw.js → themes-Ce97Zsri.js} +12 -12
- package/lib/tui-compat-command-trees.js +1 -1
- package/lib/tui-compat-extensions.js +3 -3
- package/lib/tui-compat-plugin-host.js +5 -5
- package/lib/tui-compat-scenes.js +1 -1
- package/lib/tui-compat-settings-sections.js +1 -1
- package/lib/tui-compat-working-activity.js +4 -4
- package/lib/tui-compat-workspaces.js +1 -1
- package/lib/tui-compat.js +5 -5
- package/lib/{workspaces-XY7UdfXp.js → workspaces-1xrm-e3V.js} +2 -2
- package/package.json +29 -28
- package/runtime/manifest.json +2 -2
- package/runtime/node_modules/@deepseek-ai/dsh-agent-default-model/lib/index.js +118 -22
- package/runtime/node_modules/@deepseek-ai/dsh-agent-default-model/lib/types/index.d.ts +20 -3
- package/runtime/node_modules/@deepseek-ai/dsh-api-remotes/lib/client.js +113 -39
- package/runtime/node_modules/@deepseek-ai/dsh-api-remotes/lib/index.js +4 -0
- package/runtime/node_modules/@deepseek-ai/dsh-api-remotes/lib/types/remote-events.d.ts +3 -0
- package/runtime/node_modules/@deepseek-ai/dsh-api-remotes/lib/types/remote-events.js +1 -0
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/index.js +173 -34
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/typert.host.js +48 -28
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/typert.remote-client.js +24 -19
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/agent.d.ts +9 -1
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/agent.js +36 -6
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/archived-session-gate.js +1 -1
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/catalog.js +4 -1
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/client/sessions/service.d.ts +1 -1
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/commands.d.ts +2 -2
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/commands.js +24 -8
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/index.d.ts +4 -3
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/index.js +31 -14
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/list.d.ts +6 -0
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/list.js +89 -4
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/remote-events.d.ts +1 -1
- package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/types.d.ts +18 -1
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/client.js +30 -0
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/index.js +83 -2
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/typert.host.js +104 -11
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/typert.remote-client.d.ts +5 -1
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/typert.remote-client.js +78 -11
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/client/model.d.ts +14 -1
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/client/model.js +17 -0
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/client/service.d.ts +15 -0
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/client/service.js +12 -0
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/commands.d.ts +13 -1
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/commands.js +44 -2
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/index.d.ts +13 -1
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/index.js +22 -0
- package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/types.d.ts +17 -0
- package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/client.js +931 -7
- package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/index.js +57 -19
- package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/types/client/index.d.ts +5 -2
- package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/types/config.d.ts +12 -0
- package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/types/events.d.ts +4 -4
- package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/types/index.d.ts +3 -8
- package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/package.json +6 -2
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-commands/lib/client.js +10 -0
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-deliverables/lib/types/client/present-open.d.ts +1 -1
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-model-selection/lib/client.js +8 -4
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-settings-account/lib/client.js +2 -2
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-settings-general/lib/client.js +1 -1
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-sidebar/lib/client.js +3 -1
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/client.js +1917 -206
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/ArchivedSessions.d.ts +13 -0
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/contract/slots.d.ts +4 -0
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/locales.d.ts +40 -0
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/recent.d.ts +11 -0
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/rows/RecentList.d.ts +15 -0
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/rows/Rows.d.ts +6 -1
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/session-actions/DeleteSession.d.ts +24 -0
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/stores.d.ts +2 -2
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/tree.d.ts +15 -13
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/package.json +3 -1
- package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/index.js +16 -0
- package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/typert.host.js +7 -3
- package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/typert.remote-client.js +6 -2
- package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/types/index.js +20 -0
- package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/types/types.d.ts +5 -0
- package/runtime/node_modules/@deepseek-ai/dsh-cordis-client-runner/lib/client.js +5 -2
- package/runtime/node_modules/@deepseek-ai/dsh-schedule/lib/index.js +4 -0
- package/runtime/node_modules/@deepseek-ai/dsh-schedule/lib/typert.host.js +5 -5
- package/runtime/node_modules/@deepseek-ai/dsh-schedule/lib/typert.remote-client.js +5 -5
- package/runtime/node_modules/@deepseek-ai/dsh-schedule/lib/types/index.js +5 -0
- package/runtime/node_modules/@deepseek-ai/dsh-session/lib/types/index.d.ts +21 -0
- package/runtime/node_modules/@deepseek-ai/dsh-session-persistence/lib/index.js +12 -0
- package/runtime/node_modules/@deepseek-ai/dsh-session-persistence/lib/types/index.d.ts +15 -0
- package/runtime/node_modules/@deepseek-ai/dsh-session-persistence-jsonl/lib/index.js +95 -2
- package/runtime/node_modules/@deepseek-ai/dsh-session-persistence-jsonl/lib/types/index.d.ts +3 -1
- package/runtime/node_modules/@deepseek-ai/dsh-session-projection-cache/lib/index.js +25 -1
- package/runtime/node_modules/@deepseek-ai/dsh-session-projection-cache/lib/types/index.d.ts +1 -0
- package/runtime/node_modules/@deepseek-ai/dsh-session-projection-cache/package.json +2 -1
- package/runtime/node_modules/@deepseek-ai/dsh-session-query/lib/index.js +13 -0
- package/runtime/node_modules/@deepseek-ai/dsh-session-query/lib/types/observation.d.ts +5 -0
- package/runtime/node_modules/@deepseek-ai/dsh-session-query-sqlite/lib/index.js +26 -0
- package/runtime/node_modules/@deepseek-ai/dsh-session-query-sqlite/package.json +2 -1
- package/runtime/node_modules/@deepseek-ai/dsh-storage-domain/lib/index.js +5 -1
- package/runtime/node_modules/@deepseek-ai/dsh-storage-domain/lib/types/domain.d.ts +2 -2
- package/runtime/node_modules/@deepseek-ai/dsh-subagent/lib/types/control.d.ts +1 -1
- package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/index.js +106 -56
- package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/types/config.d.ts +2 -2
- package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/types/index.d.ts +2 -1
- package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/types/services/tianti/config.d.ts +4 -2
- package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/types/tools.d.ts +2 -2
- package/runtime/node_modules/@deepseek-ai/dsh-tianti/package.json +2 -0
- package/runtime/node_modules/@deepseek-ai/dsh-tool-cordis/lib/types/api-catalog.js +103 -12
- package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/index.js +215 -4
- package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/invariant.js +6 -1
- package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/types/index.d.ts +31 -0
- package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/types/index.js +219 -2
- package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/types/spec.d.ts +4 -0
- package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/types/spec.js +4 -0
|
@@ -4,9 +4,62 @@
|
|
|
4
4
|
* domain data form.
|
|
5
5
|
* @module @deepseek-ai/dsh-workspace
|
|
6
6
|
*/
|
|
7
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
8
|
+
if (value !== null && value !== void 0) {
|
|
9
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
10
|
+
var dispose, inner;
|
|
11
|
+
if (async) {
|
|
12
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
13
|
+
dispose = value[Symbol.asyncDispose];
|
|
14
|
+
}
|
|
15
|
+
if (dispose === void 0) {
|
|
16
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
17
|
+
dispose = value[Symbol.dispose];
|
|
18
|
+
if (async) inner = dispose;
|
|
19
|
+
}
|
|
20
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
21
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
22
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
23
|
+
}
|
|
24
|
+
else if (async) {
|
|
25
|
+
env.stack.push({ async: true });
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
};
|
|
29
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
30
|
+
return function (env) {
|
|
31
|
+
function fail(e) {
|
|
32
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
33
|
+
env.hasError = true;
|
|
34
|
+
}
|
|
35
|
+
var r, s = 0;
|
|
36
|
+
function next() {
|
|
37
|
+
while (r = env.stack.pop()) {
|
|
38
|
+
try {
|
|
39
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
40
|
+
if (r.dispose) {
|
|
41
|
+
var result = r.dispose.call(r.value);
|
|
42
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
43
|
+
}
|
|
44
|
+
else s |= 1;
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
fail(e);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
51
|
+
if (env.hasError) throw env.error;
|
|
52
|
+
}
|
|
53
|
+
return next();
|
|
54
|
+
};
|
|
55
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
56
|
+
var e = new Error(message);
|
|
57
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
58
|
+
});
|
|
7
59
|
import { randomUUID } from 'node:crypto';
|
|
8
60
|
import { mkdir, stat } from 'node:fs/promises';
|
|
9
61
|
import { Service } from '@deepseek-ai/cordis';
|
|
62
|
+
import { SessionAlreadyOwnedError } from '@deepseek-ai/dsh-session-persistence';
|
|
10
63
|
import { WorkspaceEntity } from "./entity.js";
|
|
11
64
|
export { WorkspaceMoveInvalidError } from "./entity.js";
|
|
12
65
|
import { defaultWorkspaceTitle, fullyQualifiedWorkspacePath, realpathNormalize } from "./paths.js";
|
|
@@ -69,6 +122,21 @@ export class WorkspaceArchivedSessionPinError extends Error {
|
|
|
69
122
|
this.name = 'WorkspaceArchivedSessionPinError';
|
|
70
123
|
}
|
|
71
124
|
}
|
|
125
|
+
/** Permanent deletion requires an archived root and no active Session ownership. */
|
|
126
|
+
export class WorkspaceSessionDeletionError extends Error {
|
|
127
|
+
sessionId;
|
|
128
|
+
reason;
|
|
129
|
+
/**
|
|
130
|
+
* @param sessionId - identity whose lifecycle prevents the requested operation.
|
|
131
|
+
* @param reason - stable refusal discriminant shared by the Web and TUI.
|
|
132
|
+
*/
|
|
133
|
+
constructor(sessionId, reason) {
|
|
134
|
+
super(`cannot delete session '${sessionId}': ${reason}`);
|
|
135
|
+
this.sessionId = sessionId;
|
|
136
|
+
this.reason = reason;
|
|
137
|
+
this.name = 'WorkspaceSessionDeletionError';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
72
140
|
/** A workspace reorder named a source or anchor absent from the durable registry order. */
|
|
73
141
|
export class WorkspaceOrderInvalidError extends Error {
|
|
74
142
|
workspaceId;
|
|
@@ -134,6 +202,7 @@ export class WorkspaceRegistry extends Service {
|
|
|
134
202
|
await domain.refresh();
|
|
135
203
|
this.state = domain.global.get();
|
|
136
204
|
await this.recoverPendingMutation();
|
|
205
|
+
await this.recoverSessionDeletion();
|
|
137
206
|
this.validateStoredState(this.requireState());
|
|
138
207
|
if (!this.requireState().initialized) {
|
|
139
208
|
const headers = await this.listStoredHeaders();
|
|
@@ -296,6 +365,144 @@ export class WorkspaceRegistry extends Service {
|
|
|
296
365
|
get archivedSessionIds() {
|
|
297
366
|
return this.requireState().archivedSessionIds;
|
|
298
367
|
}
|
|
368
|
+
/** Permanently deleted identities retained for cross-process invalidation and stale-operation refusal. */
|
|
369
|
+
get deletedSessionIds() {
|
|
370
|
+
return this.requireState().deletedSessionIds ?? [];
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Permanently delete an archived, inactive root and its owned subagent descendants.
|
|
374
|
+
* Independent forks are preserved. Reserve every writer before recording a recovery
|
|
375
|
+
* marker; a failure after that marker is retried on refresh or restart.
|
|
376
|
+
* @param sessionId - archived root identity; repeated deletion is idempotent.
|
|
377
|
+
* @param expectedIds - identities confirmed by the user; a changed scope refuses deletion.
|
|
378
|
+
* @returns deleted root and descendant identities after durable cleanup.
|
|
379
|
+
*/
|
|
380
|
+
deleteSession(sessionId, expectedIds) {
|
|
381
|
+
return this.enqueueOperation(async () => {
|
|
382
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
383
|
+
try {
|
|
384
|
+
if (this.deletedSessionIds.includes(sessionId))
|
|
385
|
+
return [sessionId];
|
|
386
|
+
if (!this.archivedSessionIds.includes(sessionId))
|
|
387
|
+
throw new WorkspaceSessionDeletionError(sessionId, 'not-archived');
|
|
388
|
+
const ids = new Set(await this.collectSessionDeletionScope(sessionId));
|
|
389
|
+
if (expectedIds !== undefined && (expectedIds.length !== ids.size
|
|
390
|
+
|| new Set(expectedIds).size !== ids.size || expectedIds.some(id => !ids.has(id)))) {
|
|
391
|
+
throw new WorkspaceSessionDeletionError(sessionId, 'scope-changed');
|
|
392
|
+
}
|
|
393
|
+
for (const id of ids) {
|
|
394
|
+
const activity = await this.ctx.waterfall('workspace/session-activity', { sessionId: id }, () => Promise.resolve([]));
|
|
395
|
+
if (activity.length > 0)
|
|
396
|
+
throw new WorkspaceActiveSessionError(id, activity);
|
|
397
|
+
}
|
|
398
|
+
const targets = [...ids];
|
|
399
|
+
await this.ctx.parallel('session/delete-release', { sessionIds: targets });
|
|
400
|
+
for (const id of ids) {
|
|
401
|
+
if (this.ctx.get('sessions')?.get(id) !== undefined)
|
|
402
|
+
throw new WorkspaceSessionDeletionError(id, 'in-use');
|
|
403
|
+
}
|
|
404
|
+
const reservation = __addDisposableResource(env_1, await this.ctx.sessionPersistence.prepareDeletion(targets).catch((error) => {
|
|
405
|
+
if (error instanceof SessionAlreadyOwnedError)
|
|
406
|
+
throw new WorkspaceSessionDeletionError(sessionId, 'in-use');
|
|
407
|
+
throw error;
|
|
408
|
+
}), true);
|
|
409
|
+
await this.setState({ ...this.requireState(), pendingSessionDeletion: targets });
|
|
410
|
+
await reservation.commit();
|
|
411
|
+
await this.finishSessionDeletion(targets);
|
|
412
|
+
return targets;
|
|
413
|
+
}
|
|
414
|
+
catch (e_1) {
|
|
415
|
+
env_1.error = e_1;
|
|
416
|
+
env_1.hasError = true;
|
|
417
|
+
}
|
|
418
|
+
finally {
|
|
419
|
+
const result_1 = __disposeResources(env_1);
|
|
420
|
+
if (result_1)
|
|
421
|
+
await result_1;
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Preview the root and owned descendants without opening their histories.
|
|
427
|
+
* @param sessionId - archived root to preview.
|
|
428
|
+
* @returns identities to show in the deletion confirmation.
|
|
429
|
+
*/
|
|
430
|
+
sessionDeletionScope(sessionId) {
|
|
431
|
+
return this.enqueueOperation(async () => {
|
|
432
|
+
if (!this.archivedSessionIds.includes(sessionId))
|
|
433
|
+
throw new WorkspaceSessionDeletionError(sessionId, 'not-archived');
|
|
434
|
+
return this.collectSessionDeletionScope(sessionId);
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
async collectSessionDeletionScope(sessionId) {
|
|
438
|
+
const headers = await this.listStoredHeaders();
|
|
439
|
+
const ids = new Set([sessionId]);
|
|
440
|
+
for (let changed = true; changed;) {
|
|
441
|
+
changed = false;
|
|
442
|
+
for (const header of headers) {
|
|
443
|
+
if (header.origin === 'subagent' && header.parentSession !== undefined
|
|
444
|
+
&& ids.has(header.parentSession) && !ids.has(header.id)) {
|
|
445
|
+
ids.add(header.id);
|
|
446
|
+
changed = true;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return [...ids];
|
|
451
|
+
}
|
|
452
|
+
async recoverSessionDeletion() {
|
|
453
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
454
|
+
try {
|
|
455
|
+
const ids = this.requireState().pendingSessionDeletion;
|
|
456
|
+
if (ids === undefined)
|
|
457
|
+
return;
|
|
458
|
+
const reservation = __addDisposableResource(env_2, await this.ctx.sessionPersistence.prepareDeletion(ids), true);
|
|
459
|
+
await reservation.commit();
|
|
460
|
+
await this.finishSessionDeletion(ids);
|
|
461
|
+
}
|
|
462
|
+
catch (e_2) {
|
|
463
|
+
env_2.error = e_2;
|
|
464
|
+
env_2.hasError = true;
|
|
465
|
+
}
|
|
466
|
+
finally {
|
|
467
|
+
const result_2 = __disposeResources(env_2);
|
|
468
|
+
if (result_2)
|
|
469
|
+
await result_2;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
notifyDeleted(ids) {
|
|
473
|
+
try {
|
|
474
|
+
this.ctx.emit('session/deleted', ids);
|
|
475
|
+
}
|
|
476
|
+
catch (error) {
|
|
477
|
+
this.ctx.logger.warn(`workspace: deletion listener failed: ${String(error)}`);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
async finishSessionDeletion(ids) {
|
|
481
|
+
await this.ctx.parallel('session/deleting', { sessionIds: ids });
|
|
482
|
+
const removed = new Set(ids);
|
|
483
|
+
for (const [id, record] of this.requireTable().entries()) {
|
|
484
|
+
if (record.sessionIds.some(member => removed.has(member))) {
|
|
485
|
+
await this.requireTable().update(id, current => ({
|
|
486
|
+
...current, sessionIds: current.sessionIds.filter(member => !removed.has(member)), updatedAt: new Date().toISOString(),
|
|
487
|
+
}));
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
const state = this.requireState();
|
|
491
|
+
await this.setState({
|
|
492
|
+
...state,
|
|
493
|
+
archivedSessionIds: state.archivedSessionIds.filter(id => !removed.has(id)),
|
|
494
|
+
pinnedSessionIds: state.pinnedSessionIds.filter(id => !removed.has(id)),
|
|
495
|
+
deletedSessionIds: [...new Set([...(state.deletedSessionIds ?? []), ...ids])],
|
|
496
|
+
pendingSessionDeletion: undefined,
|
|
497
|
+
});
|
|
498
|
+
for (const id of ids) {
|
|
499
|
+
this.headers.delete(id);
|
|
500
|
+
this.sessionPaths.delete(id);
|
|
501
|
+
this.invalidSessionPaths.delete(id);
|
|
502
|
+
}
|
|
503
|
+
this.rebuildEntities();
|
|
504
|
+
this.notifyDeleted(ids);
|
|
505
|
+
}
|
|
299
506
|
/**
|
|
300
507
|
* Archive one session durably. The session must exist (live or in session
|
|
301
508
|
* persistence); its workspace accounting — or lack of one — is irrelevant.
|
|
@@ -349,6 +556,8 @@ export class WorkspaceRegistry extends Service {
|
|
|
349
556
|
*/
|
|
350
557
|
unarchiveSession(sessionId) {
|
|
351
558
|
return this.enqueueOperation(async () => {
|
|
559
|
+
if (this.deletedSessionIds.includes(sessionId))
|
|
560
|
+
throw new WorkspaceSessionDeletionError(sessionId, 'deleted');
|
|
352
561
|
// The chain slot serializes against every other registry write, so this
|
|
353
562
|
// check-then-write pair cannot interleave with a concurrent archive.
|
|
354
563
|
const state = this.requireState();
|
|
@@ -422,6 +631,8 @@ export class WorkspaceRegistry extends Service {
|
|
|
422
631
|
* masquerade as an unknown session.
|
|
423
632
|
*/
|
|
424
633
|
async sessionKnown(id) {
|
|
634
|
+
if (this.deletedSessionIds.includes(id))
|
|
635
|
+
return false;
|
|
425
636
|
if (this.ctx.get('sessions')?.get(id) !== undefined)
|
|
426
637
|
return true;
|
|
427
638
|
if (this.headers.has(id))
|
|
@@ -766,13 +977,14 @@ export class WorkspaceRegistry extends Service {
|
|
|
766
977
|
/** Every stored session's header, projected from the persistence snapshot listing. */
|
|
767
978
|
async listStoredHeaders() {
|
|
768
979
|
const snapshots = await this.ctx.sessionPersistence.list();
|
|
769
|
-
|
|
980
|
+
const deleted = new Set(this.deletedSessionIds);
|
|
981
|
+
return snapshots.map(snapshot => snapshot.header).filter(header => !deleted.has(header.id));
|
|
770
982
|
}
|
|
771
983
|
async indexLiveSessions() {
|
|
772
984
|
const sessions = this.ctx.get('sessions');
|
|
773
985
|
if (sessions === undefined)
|
|
774
986
|
return;
|
|
775
|
-
await this.indexHeaders(sessions.list().map(session => session.header));
|
|
987
|
+
await this.indexHeaders(sessions.list().filter(session => !this.deletedSessionIds.includes(session.id)).map(session => session.header));
|
|
776
988
|
}
|
|
777
989
|
reportFilteredCandidates() {
|
|
778
990
|
for (const entity of this.entities.values()) {
|
|
@@ -829,6 +1041,7 @@ export class WorkspaceRegistry extends Service {
|
|
|
829
1041
|
throw new Error('workspace registry is not started yet');
|
|
830
1042
|
return await domain.exclusive(async () => {
|
|
831
1043
|
const before = JSON.stringify([...this.entities.values()].map(entity => [entity.id, entity.sessionIds]));
|
|
1044
|
+
const previousDeleted = new Set(this.deletedSessionIds);
|
|
832
1045
|
await domain.refresh();
|
|
833
1046
|
const changed = this.snapshotIdentity() !== this.refreshedState;
|
|
834
1047
|
this.state = domain.global.get();
|
|
@@ -839,6 +1052,10 @@ export class WorkspaceRegistry extends Service {
|
|
|
839
1052
|
this.rebuildEntities();
|
|
840
1053
|
}
|
|
841
1054
|
await this.recoverPendingMutation();
|
|
1055
|
+
await this.recoverSessionDeletion();
|
|
1056
|
+
const newlyDeleted = this.deletedSessionIds.filter(id => !previousDeleted.has(id));
|
|
1057
|
+
if (newlyDeleted.length > 0)
|
|
1058
|
+
this.notifyDeleted(newlyDeleted);
|
|
842
1059
|
this.validateStoredState(this.requireState());
|
|
843
1060
|
const after = JSON.stringify([...this.entities.values()].map(entity => [entity.id, entity.sessionIds]));
|
|
844
1061
|
if (changed || before !== after) {
|
|
@@ -46,6 +46,8 @@ export declare const workspaceDomainState: z.ZodObject<{
|
|
|
46
46
|
operation: z.ZodLiteral<"delete">;
|
|
47
47
|
workspaceId: z.ZodPipe<z.ZodString, z.ZodTransform<WorkspaceId, string>>;
|
|
48
48
|
}, z.core.$strip>], "operation">>;
|
|
49
|
+
deletedSessionIds: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<SessionId, string>>>>;
|
|
50
|
+
pendingSessionDeletion: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<SessionId, string>>>>;
|
|
49
51
|
}, z.core.$strip>;
|
|
50
52
|
/** Durable registry state inferred from {@link workspaceDomainState}. */
|
|
51
53
|
export type WorkspaceDomainState = z.infer<typeof workspaceDomainState>;
|
|
@@ -72,6 +74,8 @@ export declare const workspaceDomainSpec: {
|
|
|
72
74
|
operation: z.ZodLiteral<"delete">;
|
|
73
75
|
workspaceId: z.ZodPipe<z.ZodString, z.ZodTransform<WorkspaceId, string>>;
|
|
74
76
|
}, z.core.$strip>], "operation">>;
|
|
77
|
+
deletedSessionIds: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<SessionId, string>>>>;
|
|
78
|
+
pendingSessionDeletion: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<SessionId, string>>>>;
|
|
75
79
|
}, z.core.$strip>;
|
|
76
80
|
initial: {
|
|
77
81
|
initialized: boolean;
|
|
@@ -51,6 +51,10 @@ export const workspaceDomainState = z.object({
|
|
|
51
51
|
archivedSessionIds: z.array(sessionId).default([]),
|
|
52
52
|
pinnedSessionIds: z.array(sessionId).default([]),
|
|
53
53
|
pendingMutation: workspacePendingMutation.optional(),
|
|
54
|
+
/** Payload deletion committed identities; old processes must never restore these Sessions. */
|
|
55
|
+
deletedSessionIds: z.array(sessionId).optional(),
|
|
56
|
+
/** Complete deletion scope retained until payloads and secondary owners finish cleanup. */
|
|
57
|
+
pendingSessionDeletion: z.array(sessionId).optional(),
|
|
54
58
|
});
|
|
55
59
|
/**
|
|
56
60
|
* The workspace domain spec: one `workspaces` table keyed by
|