@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.
Files changed (123) hide show
  1. package/lib/{command-errors-BkCHTQHj.js → command-errors-CScdshCl.js} +12 -12
  2. package/lib/{command-trees-5HFyC5sI.js → command-trees-Dc17MYfV.js} +2 -2
  3. package/lib/{component-identity-De91VHwW.js → component-identity-DFJD_d-P.js} +3 -3
  4. package/lib/{dist-CsDWUvQ4.js → dist-w_sIm4VD.js} +231 -231
  5. package/lib/{extension-events-CdYCRP0o.js → extension-events-qXmhzmHo.js} +26 -26
  6. package/lib/{frames-DOX-Jgk8.js → frames-B89znn_0.js} +4 -4
  7. package/lib/{host-access-Bk4xps5E.js → host-access-B0CRAwsX.js} +1 -1
  8. package/lib/{keymap-CJ1kvLR2.js → keymap-CIoVICBI.js} +2 -2
  9. package/lib/{plugin-XR7ncbnW.js → plugin-BKQsL2xn.js} +1638 -1363
  10. package/lib/{scenes-CAmWncZD.js → scenes-BRf-zMpF.js} +3 -3
  11. package/lib/{settings-sections-CakcvBv9.js → settings-sections-4gMhveN_.js} +3 -3
  12. package/lib/{statusBarSettings-BPPS_Usm.js → statusBarSettings-CnmdSHkO.js} +3 -3
  13. package/lib/{supports-color-DyChQwlI.js → supports-color-CcW-FGjr.js} +2 -2
  14. package/lib/{themes-OjcUpZzw.js → themes-Ce97Zsri.js} +12 -12
  15. package/lib/tui-compat-command-trees.js +1 -1
  16. package/lib/tui-compat-extensions.js +3 -3
  17. package/lib/tui-compat-plugin-host.js +5 -5
  18. package/lib/tui-compat-scenes.js +1 -1
  19. package/lib/tui-compat-settings-sections.js +1 -1
  20. package/lib/tui-compat-working-activity.js +4 -4
  21. package/lib/tui-compat-workspaces.js +1 -1
  22. package/lib/tui-compat.js +5 -5
  23. package/lib/{workspaces-XY7UdfXp.js → workspaces-1xrm-e3V.js} +2 -2
  24. package/package.json +29 -28
  25. package/runtime/manifest.json +2 -2
  26. package/runtime/node_modules/@deepseek-ai/dsh-agent-default-model/lib/index.js +118 -22
  27. package/runtime/node_modules/@deepseek-ai/dsh-agent-default-model/lib/types/index.d.ts +20 -3
  28. package/runtime/node_modules/@deepseek-ai/dsh-api-remotes/lib/client.js +113 -39
  29. package/runtime/node_modules/@deepseek-ai/dsh-api-remotes/lib/index.js +4 -0
  30. package/runtime/node_modules/@deepseek-ai/dsh-api-remotes/lib/types/remote-events.d.ts +3 -0
  31. package/runtime/node_modules/@deepseek-ai/dsh-api-remotes/lib/types/remote-events.js +1 -0
  32. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/index.js +173 -34
  33. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/typert.host.js +48 -28
  34. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/typert.remote-client.js +24 -19
  35. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/agent.d.ts +9 -1
  36. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/agent.js +36 -6
  37. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/archived-session-gate.js +1 -1
  38. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/catalog.js +4 -1
  39. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/client/sessions/service.d.ts +1 -1
  40. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/commands.d.ts +2 -2
  41. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/commands.js +24 -8
  42. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/index.d.ts +4 -3
  43. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/index.js +31 -14
  44. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/list.d.ts +6 -0
  45. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/list.js +89 -4
  46. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/remote-events.d.ts +1 -1
  47. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/types.d.ts +18 -1
  48. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/client.js +30 -0
  49. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/index.js +83 -2
  50. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/typert.host.js +104 -11
  51. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/typert.remote-client.d.ts +5 -1
  52. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/typert.remote-client.js +78 -11
  53. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/client/model.d.ts +14 -1
  54. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/client/model.js +17 -0
  55. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/client/service.d.ts +15 -0
  56. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/client/service.js +12 -0
  57. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/commands.d.ts +13 -1
  58. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/commands.js +44 -2
  59. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/index.d.ts +13 -1
  60. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/index.js +22 -0
  61. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/types.d.ts +17 -0
  62. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/client.js +931 -7
  63. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/index.js +57 -19
  64. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/types/client/index.d.ts +5 -2
  65. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/types/config.d.ts +12 -0
  66. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/types/events.d.ts +4 -4
  67. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/types/index.d.ts +3 -8
  68. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/package.json +6 -2
  69. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-commands/lib/client.js +10 -0
  70. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-deliverables/lib/types/client/present-open.d.ts +1 -1
  71. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-model-selection/lib/client.js +8 -4
  72. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-settings-account/lib/client.js +2 -2
  73. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-settings-general/lib/client.js +1 -1
  74. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-sidebar/lib/client.js +3 -1
  75. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/client.js +1917 -206
  76. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/ArchivedSessions.d.ts +13 -0
  77. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/contract/slots.d.ts +4 -0
  78. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/locales.d.ts +40 -0
  79. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/recent.d.ts +11 -0
  80. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/rows/RecentList.d.ts +15 -0
  81. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/rows/Rows.d.ts +6 -1
  82. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/session-actions/DeleteSession.d.ts +24 -0
  83. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/stores.d.ts +2 -2
  84. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/tree.d.ts +15 -13
  85. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/package.json +3 -1
  86. package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/index.js +16 -0
  87. package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/typert.host.js +7 -3
  88. package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/typert.remote-client.js +6 -2
  89. package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/types/index.js +20 -0
  90. package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/types/types.d.ts +5 -0
  91. package/runtime/node_modules/@deepseek-ai/dsh-cordis-client-runner/lib/client.js +5 -2
  92. package/runtime/node_modules/@deepseek-ai/dsh-schedule/lib/index.js +4 -0
  93. package/runtime/node_modules/@deepseek-ai/dsh-schedule/lib/typert.host.js +5 -5
  94. package/runtime/node_modules/@deepseek-ai/dsh-schedule/lib/typert.remote-client.js +5 -5
  95. package/runtime/node_modules/@deepseek-ai/dsh-schedule/lib/types/index.js +5 -0
  96. package/runtime/node_modules/@deepseek-ai/dsh-session/lib/types/index.d.ts +21 -0
  97. package/runtime/node_modules/@deepseek-ai/dsh-session-persistence/lib/index.js +12 -0
  98. package/runtime/node_modules/@deepseek-ai/dsh-session-persistence/lib/types/index.d.ts +15 -0
  99. package/runtime/node_modules/@deepseek-ai/dsh-session-persistence-jsonl/lib/index.js +95 -2
  100. package/runtime/node_modules/@deepseek-ai/dsh-session-persistence-jsonl/lib/types/index.d.ts +3 -1
  101. package/runtime/node_modules/@deepseek-ai/dsh-session-projection-cache/lib/index.js +25 -1
  102. package/runtime/node_modules/@deepseek-ai/dsh-session-projection-cache/lib/types/index.d.ts +1 -0
  103. package/runtime/node_modules/@deepseek-ai/dsh-session-projection-cache/package.json +2 -1
  104. package/runtime/node_modules/@deepseek-ai/dsh-session-query/lib/index.js +13 -0
  105. package/runtime/node_modules/@deepseek-ai/dsh-session-query/lib/types/observation.d.ts +5 -0
  106. package/runtime/node_modules/@deepseek-ai/dsh-session-query-sqlite/lib/index.js +26 -0
  107. package/runtime/node_modules/@deepseek-ai/dsh-session-query-sqlite/package.json +2 -1
  108. package/runtime/node_modules/@deepseek-ai/dsh-storage-domain/lib/index.js +5 -1
  109. package/runtime/node_modules/@deepseek-ai/dsh-storage-domain/lib/types/domain.d.ts +2 -2
  110. package/runtime/node_modules/@deepseek-ai/dsh-subagent/lib/types/control.d.ts +1 -1
  111. package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/index.js +106 -56
  112. package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/types/config.d.ts +2 -2
  113. package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/types/index.d.ts +2 -1
  114. package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/types/services/tianti/config.d.ts +4 -2
  115. package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/types/tools.d.ts +2 -2
  116. package/runtime/node_modules/@deepseek-ai/dsh-tianti/package.json +2 -0
  117. package/runtime/node_modules/@deepseek-ai/dsh-tool-cordis/lib/types/api-catalog.js +103 -12
  118. package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/index.js +215 -4
  119. package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/invariant.js +6 -1
  120. package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/types/index.d.ts +31 -0
  121. package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/types/index.js +219 -2
  122. package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/types/spec.d.ts +4 -0
  123. package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/types/spec.js +4 -0
@@ -2,7 +2,7 @@ import z from "@deepseek-ai/schemastery";
2
2
  import { SessionFormatUnsupportedMigrationError, createSessionFormatCatalogWithChildren, historicalSessionFormatCatalog, sessionFormatCatalog } from "@deepseek-ai/dsh-session-format-catalog";
3
3
  import { readdirSync } from "node:fs";
4
4
  import { link, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rm, stat, truncate } from "node:fs/promises";
5
- import { basename, dirname, isAbsolute, join, parse, resolve, toNamespacedPath } from "node:path";
5
+ import { basename, dirname, isAbsolute, join, parse, resolve, sep, toNamespacedPath } from "node:path";
6
6
  import { performance } from "node:perf_hooks";
7
7
  import { scheduler } from "node:timers/promises";
8
8
  import { createHash, randomBytes } from "node:crypto";
@@ -2282,6 +2282,8 @@ async function prepareCatalogFacts(parentId, sources, compression, signal) {
2282
2282
  * log, so the memo only needs the sessions in flight between those steps.
2283
2283
  */
2284
2284
  const COLD_LOG_MEMO_MAX_ENTRIES = 2;
2285
+ /** Retained with the stable lock inode after payload deletion. Contains no conversation data. */
2286
+ const DELETED_FILENAME = ".deleted";
2285
2287
  const DEFAULT_COMPRESSION = "zstd";
2286
2288
  /**
2287
2289
  * Internal scheduling constant, not deployment configuration: balance
@@ -2434,6 +2436,7 @@ var JsonlSessionPersistence = class extends SessionPersistence {
2434
2436
  toHeaderLine(snapshot, options?.inheritedEventCount);
2435
2437
  const inheritedEventCount = SessionLogOffset(options?.inheritedEventCount ?? 0);
2436
2438
  await this.ensureRootEncoding();
2439
+ if (await this.wasDeleted(header.id)) throw new SessionAlreadyExistsError(header.id);
2437
2440
  options?.signal?.throwIfAborted();
2438
2441
  if (this.tracker.hasPending(snapshot.id) || await this.findLog(snapshot.id, options?.signal) !== void 0) throw new SessionAlreadyExistsError(snapshot.id);
2439
2442
  options?.signal?.throwIfAborted();
@@ -2596,6 +2599,88 @@ var JsonlSessionPersistence = class extends SessionPersistence {
2596
2599
  signal?.throwIfAborted();
2597
2600
  return snapshots;
2598
2601
  }
2602
+ async prepareDeletion(sessionIds) {
2603
+ const claimed = [];
2604
+ const held = [];
2605
+ let closed = false;
2606
+ const release = async () => {
2607
+ if (closed) return;
2608
+ closed = true;
2609
+ try {
2610
+ const failures = (await Promise.allSettled(held.map((item) => item.lease.release()))).flatMap((result) => result.status === "rejected" ? [result.reason] : []);
2611
+ if (failures.length > 0) throw new AggregateError(failures, "Session deletion lock release failed");
2612
+ } finally {
2613
+ for (const id of claimed) this.tracker.releaseClaim(id);
2614
+ }
2615
+ };
2616
+ try {
2617
+ await this.ensureRootEncoding();
2618
+ const root = await realpath(this.root).catch((error) => {
2619
+ if (isENOENT(error)) return this.root;
2620
+ throw error;
2621
+ });
2622
+ for (const id of [...new Set(sessionIds)].sort()) {
2623
+ this.tracker.claimWrite(id);
2624
+ claimed.push(id);
2625
+ await this.stat(id);
2626
+ for (const project of await this.listProjectDirs()) {
2627
+ const dir = join(project, encodeSegment(id));
2628
+ const info = await lstat(dir).catch((error) => {
2629
+ if (isENOENT(error)) return void 0;
2630
+ throw error;
2631
+ });
2632
+ if (info === void 0) continue;
2633
+ if (!info.isDirectory()) throw new Error("Session deletion requires an owned directory");
2634
+ const resolved = await realpath(dir).catch((error) => {
2635
+ if (isENOENT(error)) return void 0;
2636
+ throw error;
2637
+ });
2638
+ if (resolved === void 0) continue;
2639
+ if (!resolved.startsWith(root + sep)) throw new Error("Session deletion path escapes its storage root");
2640
+ held.push({
2641
+ dir,
2642
+ lease: await SessionWriteLease.acquire(dir, id)
2643
+ });
2644
+ }
2645
+ }
2646
+ return {
2647
+ commit: async () => {
2648
+ if (closed) throw new Error("Session deletion reservation is closed");
2649
+ for (const { dir } of held) {
2650
+ const marker = await open(join(dir, DELETED_FILENAME), "a", 384);
2651
+ try {
2652
+ await marker.sync();
2653
+ } finally {
2654
+ await marker.close();
2655
+ }
2656
+ await this.syncDirPosix(dir);
2657
+ for (const entry of await readdir(dir)) if (entry !== "session.lock" && entry !== DELETED_FILENAME) await rm(join(dir, entry), {
2658
+ recursive: true,
2659
+ force: true
2660
+ });
2661
+ await this.syncDirPosix(dir);
2662
+ }
2663
+ for (const id of claimed) {
2664
+ this.coldLogMemo.delete(id);
2665
+ this.migrationPreparations.delete(id);
2666
+ }
2667
+ },
2668
+ [Symbol.asyncDispose]: release
2669
+ };
2670
+ } catch (error) {
2671
+ await release();
2672
+ throw error;
2673
+ }
2674
+ }
2675
+ async wasDeleted(id) {
2676
+ for (const project of await this.listProjectDirs()) try {
2677
+ await stat(join(project, encodeSegment(id), DELETED_FILENAME));
2678
+ return true;
2679
+ } catch (error) {
2680
+ if (!isENOENT(error)) throw error;
2681
+ }
2682
+ return false;
2683
+ }
2599
2684
  /** Resolve and read one stored log, refusing loudly when the artifact is absent. */
2600
2685
  async requireStoredLog(id, signal) {
2601
2686
  const selected = await this.findLog(id, signal);
@@ -2931,7 +3016,14 @@ var JsonlSessionPersistence = class extends SessionPersistence {
2931
3016
  */
2932
3017
  async acquireWriteLease(header) {
2933
3018
  await this.rejectOppositeArtifact(header.cwd, header.id);
2934
- return this.acquireLease(header.id, header.cwd);
3019
+ const lease = await this.acquireLease(header.id, header.cwd);
3020
+ try {
3021
+ if (await this.wasDeleted(header.id)) throw new SessionAlreadyExistsError(header.id);
3022
+ return lease;
3023
+ } catch (error) {
3024
+ await lease.release();
3025
+ throw error;
3026
+ }
2935
3027
  }
2936
3028
  /** Decode complete frames and retain complete JSONL records from a torn final frame. */
2937
3029
  async readZstdPrefix(buffer, signal) {
@@ -3334,6 +3426,7 @@ var JsonlSessionPersistence = class extends SessionPersistence {
3334
3426
  throw error;
3335
3427
  }
3336
3428
  signal?.throwIfAborted();
3429
+ if (entries.some((entry) => entry.name === DELETED_FILENAME)) return void 0;
3337
3430
  const generations = [];
3338
3431
  const opposite = [];
3339
3432
  for (const entry of entries) {
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { Context } from '@deepseek-ai/cordis';
9
9
  import z from '@deepseek-ai/schemastery';
10
- import { SessionPersistence, type SessionAccess, type SessionHandle, type SessionHandleReadResult, type SessionPersistenceCreateOptions, type SessionPersistenceListOptions, type SessionPersistenceOpenOptions, type SessionPersistenceSnapshot, type SessionPersistenceStatOptions, type SessionPersistenceRevision as PersistenceRevision } from '@deepseek-ai/dsh-session-persistence';
10
+ import { SessionPersistence, type SessionAccess, type SessionHandle, type SessionDeletion, type SessionHandleReadResult, type SessionPersistenceCreateOptions, type SessionPersistenceListOptions, type SessionPersistenceOpenOptions, type SessionPersistenceSnapshot, type SessionPersistenceStatOptions, type SessionPersistenceRevision as PersistenceRevision } from '@deepseek-ai/dsh-session-persistence';
11
11
  import { JsonlSessionHandle } from './storage.ts';
12
12
  import { SessionWriteLease } from './lease.ts';
13
13
  import type { SessionEvent, SessionId, SessionHeader, SessionLogOffset as SessionLogOffsetType } from '@deepseek-ai/dsh-session';
@@ -118,6 +118,8 @@ declare class JsonlSessionPersistence extends SessionPersistence {
118
118
  * @returns one snapshot per session, in no promised order.
119
119
  */
120
120
  list(options?: SessionPersistenceListOptions): Promise<readonly SessionPersistenceSnapshot[]>;
121
+ prepareDeletion(sessionIds: readonly SessionId[]): Promise<SessionDeletion>;
122
+ private wasDeleted;
121
123
  /** Resolve and read one stored log, refusing loudly when the artifact is absent. */
122
124
  private requireStoredLog;
123
125
  /** Probe the memo and otherwise decode one historical generation under backend cancellation. */
@@ -155,6 +155,7 @@ var SessionProjectionCache = class extends Service {
155
155
  listingTable;
156
156
  writes = Promise.resolve();
157
157
  closed = false;
158
+ deleted = /* @__PURE__ */ new Set();
158
159
  dirty = /* @__PURE__ */ new Map();
159
160
  constructor(ctx, config) {
160
161
  super(ctx, "sessionProjectionCache");
@@ -173,6 +174,24 @@ var SessionProjectionCache = class extends Service {
173
174
  await this.writes;
174
175
  }, "sessionProjectionCache.writeDrain");
175
176
  this.installWritePath();
177
+ const cleanup = (ids) => {
178
+ for (const id of ids) this.deleted.add(id);
179
+ return this.enqueueWrite(async () => {
180
+ for (const id of ids) {
181
+ await this.requireTable().delete(id);
182
+ await this.requireListingTable().delete(id);
183
+ }
184
+ });
185
+ };
186
+ this.ctx.on("session/deleting", ({ sessionIds }) => cleanup(sessionIds));
187
+ this.ctx.on("session/deleted", (ids) => {
188
+ cleanup(ids).catch((error) => {
189
+ this.ctx.logger.warn(`session projection cache: deletion cleanup failed: ${String(error)}`);
190
+ });
191
+ });
192
+ this.ctx.inject(["workspaceRegistry"], async (ctx) => {
193
+ await cleanup(ctx.workspaceRegistry.deletedSessionIds);
194
+ });
176
195
  }
177
196
  /**
178
197
  * The stored record for one session, accepted only when its bound log
@@ -185,6 +204,7 @@ var SessionProjectionCache = class extends Service {
185
204
  * @returns the identity-matching record, or `undefined` (absent or unrelated).
186
205
  */
187
206
  async recordFor(id, expected) {
207
+ if (this.deleted.has(id)) return void 0;
188
208
  const record = await this.requireTable().read(id);
189
209
  if (record === void 0) return void 0;
190
210
  return identityMatches(record.identity, expected) ? record : void 0;
@@ -240,14 +260,17 @@ var SessionProjectionCache = class extends Service {
240
260
  /** Read a small listing record, deriving it once from an older full checkpoint when needed. */
241
261
  async listingRecord(id) {
242
262
  await this.writes;
263
+ if (this.deleted.has(id)) return void 0;
243
264
  const signature = this.ctx.sessionProjections.checkpointViewSignature();
244
265
  const stored = await this.requireListingTable().read(id);
245
266
  if (stored?.signature === signature) return stored.checkpoint;
267
+ if (this.deleted.has(id)) return void 0;
246
268
  const record = await this.requireTable().read(id);
247
269
  if (record === void 0) return void 0;
248
270
  const listing = this.listingOf(record, signature);
249
271
  await this.enqueueWrite(async () => {
250
- if (await this.requireTable().read(id) === record) await this.requireListingTable().put(id, listing);
272
+ const current = await this.requireTable().read(id);
273
+ if (!this.deleted.has(id) && current === record) await this.requireListingTable().put(id, listing);
251
274
  }).catch((error) => {
252
275
  this.ctx.logger.warn(`session projection cache: listing write-back for "${id}" failed: ${String(error)}`);
253
276
  });
@@ -402,6 +425,7 @@ var SessionProjectionCache = class extends Service {
402
425
  rows: detached
403
426
  };
404
427
  await this.enqueueWrite(async () => {
428
+ if (this.deleted.has(id)) return;
405
429
  await this.requireTable().put(id, record);
406
430
  const listing = this.listingOf(record, this.ctx.sessionProjections.checkpointViewSignature());
407
431
  await this.requireListingTable().put(id, listing);
@@ -57,6 +57,7 @@ export declare class SessionProjectionCache extends Service {
57
57
  private listingTable?;
58
58
  private writes;
59
59
  private closed;
60
+ private readonly deleted;
60
61
  private readonly dirty;
61
62
  constructor(ctx: Context, config: Config);
62
63
  /** Open both on-demand domains and install the write-behind listeners. */
@@ -45,6 +45,7 @@
45
45
  "@deepseek-ai/dsh-session-projection": "workspace:*",
46
46
  "@deepseek-ai/dsh-storage": "workspace:*",
47
47
  "@deepseek-ai/dsh-storage-domain": "workspace:*",
48
- "@deepseek-ai/dsh-storage-json": "workspace:*"
48
+ "@deepseek-ai/dsh-storage-json": "workspace:*",
49
+ "@deepseek-ai/dsh-workspace": "workspace:*"
49
50
  }
50
51
  }
@@ -331,6 +331,13 @@ var SessionObservationReader = class {
331
331
  cacheCapacity;
332
332
  cache = /* @__PURE__ */ new Map();
333
333
  /**
334
+ * Drop deleted identities while existing observation leases settle independently.
335
+ * @param ids - permanently deleted Session identities.
336
+ */
337
+ forget(ids) {
338
+ for (const id of ids) this.cache.delete(id);
339
+ }
340
+ /**
334
341
  * @param ctx - context carrying Session and optional persistence/projection services.
335
342
  * @param cacheCapacity - maximum unpinned cold observations retained for reuse.
336
343
  */
@@ -1055,6 +1062,12 @@ var SessionQueryEngine = class extends Service {
1055
1062
  if (!Number.isSafeInteger(preparedSessionCacheSize) || preparedSessionCacheSize < 1) throw new SessionQueryError("session-query: preparedSessionCacheSize must be a positive safe integer", "SESSION_QUERY_INVALID_CONFIG");
1056
1063
  this._corpus = new SessionCorpus(ctx, persistedReadConcurrency);
1057
1064
  this._observations = new SessionObservationReader(ctx, preparedSessionCacheSize);
1065
+ ctx.on("session/deleting", ({ sessionIds }) => {
1066
+ this._observations.forget(sessionIds);
1067
+ });
1068
+ ctx.on("session/deleted", (ids) => {
1069
+ this._observations.forget(ids);
1070
+ });
1058
1071
  }
1059
1072
  /**
1060
1073
  * Observe one exact live or prepared Session without a persistence listing preflight.
@@ -50,6 +50,11 @@ export declare class SessionObservationReader {
50
50
  private readonly ctx;
51
51
  private readonly cacheCapacity;
52
52
  private readonly cache;
53
+ /**
54
+ * Drop deleted identities while existing observation leases settle independently.
55
+ * @param ids - permanently deleted Session identities.
56
+ */
57
+ forget(ids: readonly SessionId[]): void;
53
58
  /**
54
59
  * @param ctx - context carrying Session and optional persistence/projection services.
55
60
  * @param cacheCapacity - maximum unpinned cold observations retained for reuse.
@@ -529,6 +529,32 @@ var SqliteSessionQueryEngine = class extends SessionQueryEngine {
529
529
  return () => this._optionalPersistenceFiber.dispose();
530
530
  }, "sessionQuerySqlite.optionalPersistence");
531
531
  ctx.effect(() => async () => this.close(), "sessionQuerySqlite.close");
532
+ const cleanup = (ids) => this._serialized(void 0, async () => {
533
+ if (ids.length === 0 || this.config.openAt === "never") return;
534
+ await this._ensureReady(void 0);
535
+ const db = this._requireDb();
536
+ db.exec("BEGIN IMMEDIATE");
537
+ try {
538
+ for (const id of ids) {
539
+ this._deleteSession("persisted", id);
540
+ this._deleteSession("live", id);
541
+ }
542
+ db.exec("COMMIT");
543
+ } catch (error) {
544
+ db.exec("ROLLBACK");
545
+ throw error;
546
+ }
547
+ this._globalGeneration += 1;
548
+ });
549
+ ctx.on("session/deleting", ({ sessionIds }) => cleanup(sessionIds));
550
+ ctx.on("session/deleted", (ids) => {
551
+ cleanup(ids).catch((error) => {
552
+ ctx.logger.warn(`session query: deletion cleanup failed: ${String(error)}`);
553
+ });
554
+ });
555
+ ctx.inject(["workspaceRegistry"], async (ctx) => {
556
+ await cleanup(ctx.workspaceRegistry.deletedSessionIds);
557
+ });
532
558
  }
533
559
  /** Open eagerly only when activation owns the configured readiness boundary. */
534
560
  async [Service.init]() {
@@ -47,6 +47,7 @@
47
47
  "@deepseek-ai/dsh-session-persistence-jsonl": "workspace:*",
48
48
  "@deepseek-ai/dsh-session-query": "workspace:*",
49
49
  "@deepseek-ai/cordis": "workspace:~",
50
- "@deepseek-ai/dsh-session-projection": "workspace:*"
50
+ "@deepseek-ai/dsh-session-projection": "workspace:*",
51
+ "@deepseek-ai/dsh-workspace": "workspace:*"
51
52
  }
52
53
  }
@@ -337,9 +337,13 @@ var KvTableImpl = class {
337
337
  }
338
338
  delete(key) {
339
339
  return this.host.enqueue(async () => {
340
- if (!this.records.has(key)) return false;
340
+ if (!(this.records.has(key) || this.host.readStored !== void 0 && await this.host.unit.readRecord(this.tableName, key) !== void 0)) {
341
+ this.loaded.add(key);
342
+ return false;
343
+ }
341
344
  await this.host.unit.deleteRecord(this.tableName, key);
342
345
  this.records.delete(key);
346
+ this.loaded.add(key);
343
347
  this.host.emitChanged({
344
348
  domain: this.host.domainName,
345
349
  table: this.tableName,
@@ -115,8 +115,8 @@ export interface Domain<S extends DomainSpec> {
115
115
  */
116
116
  close(): Promise<void>;
117
117
  }
118
- /** Keyed reads and complete replacements without whole-table enumeration. */
119
- export type RecordTable<K extends string, V> = Pick<KvTable<K, V>, 'read' | 'put'>;
118
+ /** Keyed reads, complete replacements, and removals without whole-table enumeration. */
119
+ export type RecordTable<K extends string, V> = Pick<KvTable<K, V>, 'read' | 'put' | 'delete'>;
120
120
  /** Per-record domain opened without loading its records. No global slot is exposed. */
121
121
  export interface RecordDomain<S extends DomainSpec> {
122
122
  /** Domain name from the spec. */
@@ -12,8 +12,8 @@ declare const CONTROL_ID_SCHEMAS: {
12
12
  childSessionId: z.ZodString;
13
13
  mode: z.ZodLiteral<"continuable">;
14
14
  delivery: z.ZodEnum<{
15
- steer: "steer";
16
15
  queue: "queue";
16
+ steer: "steer";
17
17
  }>;
18
18
  }, z.core.$strip>;
19
19
  readonly 'subagent.interrupt': z.ZodObject<{
@@ -121,15 +121,14 @@ function environmentSwitch(name) {
121
121
  throw new Error(`${name} must be a boolean switch.`);
122
122
  }
123
123
  /** Get tianti runtime config for Tianti operations.
124
- * @returns The invocation's configured endpoints and current integration switches. */
125
- function getTiantiRuntimeConfig() {
126
- const runtime = operation().runtime;
127
- const settings = getInitialSettings().tianti;
128
- const enabled = environmentSwitch("TIANTI_ENABLED") ?? settings?.enabled ?? runtime.config.enabled;
124
+ * @param runtime Mounted Tianti runtime; defaults to the current invocation.
125
+ * @returns The runtime's configured endpoints and effective integration switches. */
126
+ function getTiantiRuntimeConfig(runtime = operation().runtime) {
127
+ const enabled = environmentSwitch("TIANTI_ENABLED") ?? runtime.switches.enabled ?? runtime.config.enabled;
129
128
  return {
130
129
  enabled,
131
130
  enableModelTools: enabled && runtime.config.enableModelTools,
132
- enableWriteActions: environmentSwitch("TIANTI_WRITE_ACTIONS_ENABLED") ?? settings?.enableWriteActions ?? false,
131
+ enableWriteActions: environmentSwitch("TIANTI_WRITE_ACTIONS_ENABLED") ?? runtime.switches.enableWriteActions ?? runtime.config.enableWriteActions,
133
132
  environment: "prod",
134
133
  baseUrl: runtime.config.baseUrl.replace(/\/+$/u, "")
135
134
  };
@@ -2313,7 +2312,15 @@ async function handleList(flags) {
2313
2312
  async function call$1(args) {
2314
2313
  const parsed = parseArgs(args);
2315
2314
  const [command, subCommand, third] = parsed.positionals;
2316
- if (!command || command === "help" || command === "-h") return text$1(help$1());
2315
+ if (!command) return text$1([
2316
+ `天梯功能当前${getTiantiRuntimeConfig().enabled ? "已开启(on)" : "已关闭(off)"},默认关闭。`,
2317
+ "",
2318
+ " /tianti on 打开天梯功能",
2319
+ " /tianti off 关闭天梯功能",
2320
+ "",
2321
+ "开启后可使用天梯相关功能;运行 /tianti help 查看完整命令。"
2322
+ ].join("\n"));
2323
+ if (command === "help" || command === "-h") return text$1(help$1());
2317
2324
  if (command === "status") return handleStatus$1();
2318
2325
  if (command === "on" || command === "enable") {
2319
2326
  await updateTiantiSettings({
@@ -4922,7 +4929,7 @@ function getLatestRunMessage() {
4922
4929
  return "没有找到本地研发流程记录。可以先运行 /tianti-deliver latest 或 /tianti-deliver workitem <id> --title <title>。";
4923
4930
  }
4924
4931
  function ensureTiantiEnabled() {
4925
- if (!getTiantiRuntimeConfig().enabled) return "Tianti integration is off. Run /tianti on before using /tianti-deliver latest.";
4932
+ if (!getTiantiRuntimeConfig().enabled) return "Tianti integration is off. Run /tianti on before using /tianti-deliver.";
4926
4933
  }
4927
4934
  async function createAndSaveRun(workItem, scenario) {
4928
4935
  return saveDevelopmentRun(createDevelopmentRun({
@@ -4931,8 +4938,6 @@ async function createAndSaveRun(workItem, scenario) {
4931
4938
  }));
4932
4939
  }
4933
4940
  async function handleLatest(flags) {
4934
- const disabled = ensureTiantiEnabled();
4935
- if (disabled) return text(disabled);
4936
4941
  const workItem = latestTaskToWorkItem(await createTiantiClient().getLatestDevelopmentTaskForCurrentUser({ includeDetail: true }));
4937
4942
  if (!workItem) return text("没有读取到可交付的天梯开发任务。");
4938
4943
  if (isCompletedWorkItemState(workItem.stateName)) return text(`最新开发任务 ${workItem.id} 当前状态为「${workItem.stateName}」,已完成任务不会进入研发交付流程。请指定一个新建或活动中的开发任务。`);
@@ -5182,6 +5187,8 @@ async function call(args) {
5182
5187
  const parsed = parseArgs(args);
5183
5188
  const [command] = parsed.positionals;
5184
5189
  if (!command || command === "help" || command === "-h") return text(help());
5190
+ const disabled = ensureTiantiEnabled();
5191
+ if (disabled) return text(disabled);
5185
5192
  if (command === "latest") return handleLatest(parsed.flags);
5186
5193
  if (command === "workitem" || command === "task") return handleWorkItem(parsed);
5187
5194
  if (command === "status") return handleStatus(void 0, parsed.positionals[1] ?? taskIdFlag(parsed));
@@ -5218,7 +5225,7 @@ async function call(args) {
5218
5225
  //#region lib/types/config.js
5219
5226
  /** Deployment configuration for the Tianti integration. */
5220
5227
  const Config = z.object({
5221
- enabled: z.boolean().default(true),
5228
+ enabled: z.boolean().default(false),
5222
5229
  enableWriteActions: z.boolean().default(false),
5223
5230
  enableModelTools: z.boolean().default(true),
5224
5231
  baseUrl: z.string().default("https://devops.chinaunicom.cn"),
@@ -5251,10 +5258,10 @@ const id = {
5251
5258
  required: true,
5252
5259
  description: "Tianti numeric id/globalId, rather than the internal Mongo _id."
5253
5260
  };
5254
- /** Install reversible model tools with invocation-owned cancellation.
5261
+ /** Install model tools for one enabled Tianti activation; its owner unregisters them on off.
5255
5262
  * @param ctx Harness tool registry.
5256
5263
  * @param runtime Plugin-owned endpoint and switch state.
5257
- * @param signal Plugin lifetime cancellation.
5264
+ * @param signal Cancellation for this activation, aborted on off or plugin disposal.
5258
5265
  * @param track Track active work until plugin disposal settles.
5259
5266
  * @returns Registration disposers, removed before active work is drained.
5260
5267
  */
@@ -5397,13 +5404,18 @@ function validateEndpoint(value) {
5397
5404
  /** Register migrated commands without changing the agent loop or existing model providers.
5398
5405
  * @param ctx Harness command and tool registries.
5399
5406
  * @param config Deployment endpoints, switches, and timeouts.
5407
+ * @returns Startup after persisted switches and command descriptions are ready.
5400
5408
  */
5401
- function apply(ctx, config) {
5409
+ async function apply(ctx, config) {
5402
5410
  validateEndpoint(config.baseUrl);
5403
5411
  validateEndpoint(config.cvpApiBaseUrl);
5404
5412
  const runtime = new TiantiRuntime(ctx, config);
5413
+ await runtime.loadSwitches();
5405
5414
  const lifetime = new AbortController();
5406
5415
  const active = /* @__PURE__ */ new Set();
5416
+ const switchCommands = [];
5417
+ let disposeModelTools;
5418
+ let appliedState;
5407
5419
  const track = (task) => {
5408
5420
  active.add(task);
5409
5421
  task.then(() => active.delete(task), () => active.delete(task));
@@ -5416,56 +5428,94 @@ function apply(ctx, config) {
5416
5428
  cwd: invocation.agent.session.header.cwd ?? process.cwd()
5417
5429
  }, async () => {
5418
5430
  await runtime.loadSwitches();
5419
- return {
5420
- kind: "success",
5421
- text: (await command(invocation.rawInput)).value
5422
- };
5431
+ try {
5432
+ return {
5433
+ kind: "success",
5434
+ text: (await command(invocation.rawInput)).value
5435
+ };
5436
+ } finally {
5437
+ refreshIntegration();
5438
+ }
5423
5439
  }));
5440
+ const disposeSwitchCommands = () => {
5441
+ for (const dispose of switchCommands.splice(0)) dispose();
5442
+ };
5443
+ const unloadModelTools = () => {
5444
+ const disposal = disposeModelTools?.();
5445
+ disposeModelTools = void 0;
5446
+ runtime.auth.clear();
5447
+ return disposal;
5448
+ };
5449
+ const refreshIntegration = () => {
5450
+ if (lifetime.signal.aborted) return;
5451
+ const enabled = getTiantiRuntimeConfig(runtime).enabled;
5452
+ if (enabled === appliedState) return;
5453
+ const state = enabled ? "on" : "off";
5454
+ unloadModelTools();
5455
+ if (enabled && config.enableModelTools) {
5456
+ const activation = new AbortController();
5457
+ const signal = AbortSignal.any([lifetime.signal, activation.signal]);
5458
+ disposeModelTools = ctx.effect(function* () {
5459
+ yield ctx.on("tools/pre-execute", async (exec, next) => {
5460
+ if (exec.name !== "TiantiProjectSwitch") return next();
5461
+ const decision = await next();
5462
+ if (decision.kind !== "allow") return decision;
5463
+ return {
5464
+ kind: "ask",
5465
+ reason: "Switch the shared Tianti project context.",
5466
+ displayReason: {
5467
+ en: "Switch the Tianti project?",
5468
+ zh: "切换当前天梯项目?"
5469
+ }
5470
+ };
5471
+ });
5472
+ yield* installTools(ctx, runtime, signal, track);
5473
+ yield () => {
5474
+ activation.abort();
5475
+ };
5476
+ }, "enabled tianti model tools");
5477
+ }
5478
+ disposeSwitchCommands();
5479
+ for (const name of ["tianti", "tt"]) switchCommands.push(ctx.commands.register({
5480
+ name,
5481
+ description: `天梯功能(当前:${state},默认关闭):on 打开,off 关闭${name === "tt" ? "(/tianti 的别名)" : ""}`,
5482
+ handler: handler(call$1),
5483
+ input: {
5484
+ hint: `当前:${state} | on 打开 | off 关闭(默认关闭)`,
5485
+ suggestions: [{
5486
+ value: "on",
5487
+ description: `打开天梯功能${enabled ? "(当前:on)" : ""}`
5488
+ }, {
5489
+ value: "off",
5490
+ description: `关闭天梯功能${enabled ? "" : "(当前:off)"}`
5491
+ }]
5492
+ }
5493
+ }));
5494
+ appliedState = enabled;
5495
+ };
5424
5496
  ctx.effect(function* () {
5425
5497
  yield async () => {
5426
5498
  lifetime.abort();
5427
5499
  await Promise.allSettled(active);
5428
5500
  runtime.auth.clear();
5429
5501
  };
5430
- yield ctx.on("tools/pre-execute", async (exec, next) => {
5431
- if (exec.name !== "TiantiProjectSwitch") return next();
5432
- const decision = await next();
5433
- if (decision.kind !== "allow") return decision;
5434
- return {
5435
- kind: "ask",
5436
- reason: "Switch the shared Tianti project context.",
5437
- displayReason: {
5438
- en: "Switch the Tianti project?",
5439
- zh: "切换当前天梯项目?"
5440
- }
5441
- };
5442
- });
5443
- for (const command of [
5444
- {
5445
- name: "tianti",
5446
- description: "查询天梯任务、项目、SCM 和流水线,审核外部写操作",
5447
- handler: handler(call$1)
5448
- },
5449
- {
5450
- name: "tt",
5451
- description: "天梯工具箱(/tianti 的别名)",
5452
- handler: handler(call$1)
5453
- },
5454
- {
5455
- name: "tianti-deliver",
5456
- description: "引导天梯任务从需求、验证到研发交付",
5457
- handler: handler(call)
5458
- },
5459
- {
5460
- name: "td",
5461
- description: "天梯研发交付(/tianti-deliver 的别名)",
5462
- handler: handler(call)
5463
- }
5464
- ]) yield ctx.commands.register({
5465
- ...command,
5502
+ yield disposeSwitchCommands;
5503
+ yield unloadModelTools;
5504
+ refreshIntegration();
5505
+ for (const command of [{
5506
+ name: "tianti-deliver",
5507
+ description: "引导天梯任务从需求、验证到研发交付",
5508
+ handler: handler(call),
5466
5509
  input: { hint: "help | status | 子命令与参数" }
5467
- });
5468
- yield* installTools(ctx, runtime, lifetime.signal, track);
5510
+ }, {
5511
+ name: "td",
5512
+ description: "天梯研发交付(/tianti-deliver 的别名)",
5513
+ handler: handler(call),
5514
+ input: { hint: "help | status | 子命令与参数" }
5515
+ }]) yield ctx.commands.register(command);
5516
+ yield () => {
5517
+ lifetime.abort();
5518
+ };
5469
5519
  }, "tianti lifecycle");
5470
5520
  }
5471
5521
  //#endregion
@@ -2,11 +2,11 @@
2
2
  import z from '@deepseek-ai/schemastery';
3
3
  /** Tianti endpoints, switches, and bounded execution settings. */
4
4
  export interface Config {
5
- /** Enable Tianti resource commands without contacting the platform at boot. */
5
+ /** Initially enable Tianti; defaults off until the user runs /tianti on. */
6
6
  enabled: boolean;
7
7
  /** Allow execution of human-approved external write reviews. */
8
8
  enableWriteActions: boolean;
9
- /** Publish model-facing Tianti query and review-proposal tools. */
9
+ /** Register model-facing query and review-proposal tools only while Tianti is on. */
10
10
  enableModelTools: boolean;
11
11
  /** Tianti DevOps origin; HTTPS except for loopback test deployments. */
12
12
  baseUrl: string;
@@ -7,6 +7,7 @@ export declare const inject: string[];
7
7
  /** Register migrated commands without changing the agent loop or existing model providers.
8
8
  * @param ctx Harness command and tool registries.
9
9
  * @param config Deployment endpoints, switches, and timeouts.
10
+ * @returns Startup after persisted switches and command descriptions are ready.
10
11
  */
11
- export declare function apply(ctx: Context, config: Config): void;
12
+ export declare function apply(ctx: Context, config: Config): Promise<void>;
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,4 @@
1
+ import type { TiantiRuntime } from '../../runtime.ts';
1
2
  import type { TiantiRuntimeConfig } from './types.ts';
2
3
  /** Tianti write actions disabled message for Tianti operations.
3
4
  */
@@ -6,8 +7,9 @@ export declare const TIANTI_WRITE_ACTIONS_DISABLED_MESSAGE = "\u7814\u53D1\u5E73
6
7
  */
7
8
  export declare const TIANTI_WRITE_ACTIONS_ENABLED_MESSAGE = "\u7814\u53D1\u5E73\u53F0\u5199\u64CD\u4F5C\u6267\u884C\u6743\u9650\u5DF2\u5F00\u542F\u3002\u6240\u6709\u5916\u90E8\u5199\u64CD\u4F5C\u4ECD\u5FC5\u987B\u5148\u751F\u6210\u5BA1\u6838\u9879\uFF0C\u5E76\u901A\u8FC7 /tianti approve <reviewId> --execute \u660E\u786E\u6279\u51C6\u540E\u624D\u4F1A\u6267\u884C\u3002";
8
9
  /** Get tianti runtime config for Tianti operations.
9
- * @returns The invocation's configured endpoints and current integration switches. */
10
- export declare function getTiantiRuntimeConfig(): TiantiRuntimeConfig;
10
+ * @param runtime Mounted Tianti runtime; defaults to the current invocation.
11
+ * @returns The runtime's configured endpoints and effective integration switches. */
12
+ export declare function getTiantiRuntimeConfig(runtime?: TiantiRuntime): TiantiRuntimeConfig;
11
13
  /** Is tianti enabled for Tianti operations.
12
14
  * @returns Whether Tianti resource operations are enabled. */
13
15
  export declare function isTiantiEnabled(): boolean;