@takboo/dsh-session-bin 0.1.0 → 0.2.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.
@@ -64,6 +64,9 @@ export declare class NativeMetadataAdapter {
64
64
  private readonly targets;
65
65
  private readonly retained;
66
66
  private readonly followers;
67
+ private readonly followerStops;
68
+ private readonly activationTarget;
69
+ private readonly apiHandles;
67
70
  private readonly operations;
68
71
  private readonly queueTarget;
69
72
  private readonly receivers;
@@ -78,6 +81,7 @@ export declare class NativeMetadataAdapter {
78
81
  private readonly queryProvider;
79
82
  private readonly cacheTable;
80
83
  private readonly cacheDirectory;
84
+ private readonly agents;
81
85
  private moveCacheDocument;
82
86
  private clearCacheStage;
83
87
  private removeCacheStage;
@@ -90,6 +94,9 @@ export declare class NativeMetadataAdapter {
90
94
  private refuse;
91
95
  private assertInstances;
92
96
  private assertCold;
97
+ /** Explicit deletion preparation may unload an idle API-owned lifecycle.
98
+ * No bare registry Agent, foreign handle, or unknown reader is disposed. */
99
+ releaseIdle(id: string): Promise<void>;
93
100
  private cacheView;
94
101
  private openCacheDocument;
95
102
  private pathAbsent;
package/dist/index.js CHANGED
@@ -1834,6 +1834,20 @@ async function nativeRetirementSdkFingerprint() {
1834
1834
  if (createHash3("sha256").update(await readFile(join2(dirname(path), file))).digest("hex") !== digest2) return null;
1835
1835
  }
1836
1836
  }
1837
+ const runtime = createRequire(base.resolve("@deepseek-ai/dsh-base/package.json"));
1838
+ for (const [name2, expected] of Object.entries({
1839
+ "dsh-agent": "7b9aa39474de83115cf890dd7f8904cf0d59df0811cb335ab85565a04f1aac93",
1840
+ "dsh-agent-loop": "00c4814d63f3d1e2754eb09144832d19b30499d6ea444730473f740b587948c2"
1841
+ })) {
1842
+ let path;
1843
+ try {
1844
+ path = runtime.resolve(`@deepseek-ai/${name2}/package.json`);
1845
+ } catch (error) {
1846
+ if (error.code === "MODULE_NOT_FOUND") return null;
1847
+ throw error;
1848
+ }
1849
+ if (JSON.parse(await readFile(path, "utf8")).version !== "0.2.0-rc.2" || createHash3("sha256").update(await readFile(join2(dirname(path), "lib/index.js"))).digest("hex") !== expected) return null;
1850
+ }
1837
1851
  return createHash3("sha256").update(JSON.stringify(nativeRetirementSdkSources)).digest("hex");
1838
1852
  }
1839
1853
 
@@ -2448,10 +2462,14 @@ var NativeMetadataAdapter = class _NativeMetadataAdapter {
2448
2462
  this.queryProvider = !query ? "none" : query.config.openAt === "never" ? "sqlite-memory-disabled-0.2.0-rc.2" : "sqlite-0.2.0-rc.2";
2449
2463
  this.cacheTable = cache?.requireTable();
2450
2464
  this.cacheDirectory = this.cacheTable?.host.unit.dir;
2465
+ this.agents = controller ? _NativeMetadataAdapter.get(ctx, "agents") : void 0;
2451
2466
  }
2452
2467
  targets = /* @__PURE__ */ new Set();
2453
2468
  retained = /* @__PURE__ */ new Map();
2454
2469
  followers = /* @__PURE__ */ new Map();
2470
+ followerStops = /* @__PURE__ */ new Map();
2471
+ activationTarget = new AsyncLocalStorage();
2472
+ apiHandles = /* @__PURE__ */ new Map();
2455
2473
  operations = /* @__PURE__ */ new Map();
2456
2474
  queueTarget = new AsyncLocalStorage();
2457
2475
  receivers = new AsyncLocalStorage();
@@ -2466,6 +2484,7 @@ var NativeMetadataAdapter = class _NativeMetadataAdapter {
2466
2484
  queryProvider;
2467
2485
  cacheTable;
2468
2486
  cacheDirectory;
2487
+ agents;
2469
2488
  moveCacheDocument = renameWindowsWriteThrough;
2470
2489
  clearCacheStage = async (handle) => {
2471
2490
  await handle.truncate(0);
@@ -2494,7 +2513,7 @@ var NativeMetadataAdapter = class _NativeMetadataAdapter {
2494
2513
  const disabledMemory = query.config?.openAt === "never" && query.config.path === ":memory:" && query._db === void 0 && query._ready === void 0;
2495
2514
  if (query.constructor.name !== "SqliteSessionQueryEngine" || !enabled && !disabledMemory || typeof query.config?.path !== "string" || !methods(query, ["searchSessions", "listSessions", "observeSession", "_serialized"]) || !(query._observations?.cache instanceof Map) || !methods(query._observations, ["read", "store"])) return void 0;
2496
2515
  }
2497
- if (controller && (controller.constructor.name !== "SessionController" || !(controller.history?.closeFollowers instanceof Set) || !methods(controller.history, ["page", "follow", "sourceFor"]))) return void 0;
2516
+ if (controller && (controller.constructor.name !== "SessionController" || !(controller.history?.closeFollowers instanceof Set) || !methods(controller.history, ["page", "follow", "sourceFor"]) || controller.agents?.constructor.name !== "ApiSessionAgentController" || !methods(controller.agents, ["resumeObserved", "createOrAdopt"]) || !methods(controller.listState ?? {}, ["summarizeCold"]) || !methods(this.get(ctx, "agents") ?? {}, ["create", "resume", "get"]))) return void 0;
2498
2517
  return new _NativeMetadataAdapter(ctx, admission, workspace, sessions, cache, query, controller);
2499
2518
  }
2500
2519
  assert(id) {
@@ -2533,7 +2552,7 @@ var NativeMetadataAdapter = class _NativeMetadataAdapter {
2533
2552
  if (this.guardedMethods.some(({ port, name: name2, replacement }) => port[name2] !== replacement)) {
2534
2553
  this.refuse("native/guard-replaced", "A native metadata admission guard was replaced.");
2535
2554
  }
2536
- if (_NativeMetadataAdapter.get(this.ctx, "workspaceRegistry") !== this.workspace || _NativeMetadataAdapter.get(this.ctx, "sessions") !== this.sessions || _NativeMetadataAdapter.get(this.ctx, "sessionProjectionCache") !== this.cache || _NativeMetadataAdapter.get(this.ctx, "sessionQuery") !== this.query || _NativeMetadataAdapter.get(this.ctx, "sessionController") !== this.controller || this.cache && (this.cache.requireTable() !== this.cacheTable || this.cacheTable.host.unit.dir !== this.cacheDirectory) || this.query && (this.query.config.path !== this.queryConfiguration.path || this.query.config.openAt !== this.queryConfiguration.openAt) || this.queryProvider === "sqlite-memory-disabled-0.2.0-rc.2" && (this.query._db !== void 0 || this.query._ready !== void 0)) {
2555
+ if (_NativeMetadataAdapter.get(this.ctx, "workspaceRegistry") !== this.workspace || _NativeMetadataAdapter.get(this.ctx, "sessions") !== this.sessions || _NativeMetadataAdapter.get(this.ctx, "sessionProjectionCache") !== this.cache || _NativeMetadataAdapter.get(this.ctx, "sessionQuery") !== this.query || _NativeMetadataAdapter.get(this.ctx, "sessionController") !== this.controller || this.controller && _NativeMetadataAdapter.get(this.ctx, "agents") !== this.agents || this.cache && (this.cache.requireTable() !== this.cacheTable || this.cacheTable.host.unit.dir !== this.cacheDirectory) || this.query && (this.query.config.path !== this.queryConfiguration.path || this.query.config.openAt !== this.queryConfiguration.openAt) || this.queryProvider === "sqlite-memory-disabled-0.2.0-rc.2" && (this.query._db !== void 0 || this.query._ready !== void 0)) {
2537
2556
  this.refuse("native/composition-changed", "Native metadata service composition changed.");
2538
2557
  }
2539
2558
  }
@@ -2548,6 +2567,34 @@ var NativeMetadataAdapter = class _NativeMetadataAdapter {
2548
2567
  if ((this.controller?.history.closeFollowers.size ?? 0) > knownFollowers) this.refuse("native/unknown-follow", "A history follower predates native admission tracking.");
2549
2568
  if (this.cache && [...this.cache.dirty.keys()].some((session) => session.id === id)) this.refuse("native/cache-dirty", "Target cache write-behind is not quiescent.");
2550
2569
  }
2570
+ /** Explicit deletion preparation may unload an idle API-owned lifecycle.
2571
+ * No bare registry Agent, foreign handle, or unknown reader is disposed. */
2572
+ async releaseIdle(id) {
2573
+ this.assertInstances();
2574
+ const handle = this.apiHandles.get(id);
2575
+ if (!handle) return;
2576
+ const agent = handle.agent;
2577
+ const registry = this.agents;
2578
+ if (registry.get(id) !== agent || this.sessions.get(id) !== agent.session) return;
2579
+ const activity = await this.admission.withoutBypass(() => this.ctx.waterfall(
2580
+ "workspace/session-activity",
2581
+ { sessionId: id },
2582
+ async () => []
2583
+ ));
2584
+ if (activity.length || agent.constructor.name !== "ReactLoopAgent" || agent.phase?.kind !== "idle" || agent.status !== "idle" || !Array.isArray(agent.inbox?.nextTurn) || !Array.isArray(agent.inbox?.nextStep) || agent.inbox.nextTurn.length || agent.inbox.nextStep.length) {
2585
+ this.refuse("native/session-active", "Native deletion refuses active or queued work.");
2586
+ }
2587
+ if (!this.workspace.archivedSessionIds.includes(id)) this.refuse("native/not-archived", "Only an archived target may be unloaded.");
2588
+ if (registry.get(id) !== agent || this.sessions.get(id) !== agent.session) this.refuse("native/session-live", "The API lifecycle changed.");
2589
+ await this.admission.bypass(id, async () => {
2590
+ await handle.dispose();
2591
+ for (const stop of [...this.followerStops.get(id) ?? []]) await stop();
2592
+ if (this.cache) await this.cache.requireTable().host.enqueue(async () => void 0);
2593
+ const summary = this.controller.listState.summarizeCold(agent.session.header);
2594
+ this.admission.withoutBypass(() => this.ctx.emit("api-session/added", summary));
2595
+ });
2596
+ this.apiHandles.delete(id);
2597
+ }
2551
2598
  async cacheView(table, id, acceptedStage) {
2552
2599
  table.host.assertReadable();
2553
2600
  if (!/^[a-zA-Z0-9_-]+$/.test(id)) this.refuse("native/cache-key-unsupported", "The pinned cache backend cannot own this record key.");
@@ -2912,19 +2959,54 @@ var NativeMetadataAdapter = class _NativeMetadataAdapter {
2912
2959
  }
2913
2960
  if (this.controller) {
2914
2961
  const adapter = this;
2962
+ for (const name2 of ["resumeObserved", "createOrAdopt"]) {
2963
+ this.wrap(this.controller.agents, name2, (original) => (id, ...args) => this.tracked(id, () => this.activationTarget.run(id, async () => original(id, ...args))));
2964
+ }
2965
+ const agents = this.agents;
2966
+ for (const name2 of ["create", "resume"]) {
2967
+ this.wrap(agents, name2, (original) => (options, ...args) => {
2968
+ const id = String(name2 === "create" ? options.sessionId : options.resumeSessionId);
2969
+ return this.tracked(id, async () => {
2970
+ const handle = await original(options, ...args);
2971
+ if (this.activationTarget.getStore() === id && options.parentAgent === void 0 && handle.agent.id === id && handle.agent.session.header.origin !== "subagent" && typeof handle.dispose === "function") this.apiHandles.set(id, handle);
2972
+ return handle;
2973
+ });
2974
+ });
2975
+ }
2976
+ this.restores.push(this.ctx.on("agent/disposed", ({ agent }) => {
2977
+ if (this.apiHandles.get(agent.id)?.agent === agent) this.apiHandles.delete(agent.id);
2978
+ }, { global: true }));
2915
2979
  this.wrap(this.controller.history, "follow", (original) => function(request, signal) {
2916
2980
  const id = String(request.address.sessionId);
2917
2981
  adapter.assert(id);
2918
2982
  return (async function* () {
2919
2983
  adapter.assert(id);
2920
2984
  adapter.increment(adapter.followers, id, 1);
2985
+ const cancellation = new AbortController();
2986
+ const iterator = original(request, AbortSignal.any([signal, cancellation.signal]));
2987
+ const stops = adapter.followerStops.get(id) ?? /* @__PURE__ */ new Set();
2988
+ adapter.followerStops.set(id, stops);
2989
+ let finished = false;
2990
+ const finish = () => {
2991
+ if (finished) return;
2992
+ finished = true;
2993
+ adapter.increment(adapter.followers, id, -1);
2994
+ stops.delete(stop);
2995
+ if (!stops.size) adapter.followerStops.delete(id);
2996
+ };
2997
+ const stop = async () => {
2998
+ cancellation.abort();
2999
+ await iterator.return();
3000
+ finish();
3001
+ };
3002
+ stops.add(stop);
2921
3003
  try {
2922
- for await (const frame of original(request, signal)) {
3004
+ for await (const frame of iterator) {
2923
3005
  adapter.assert(id);
2924
3006
  yield frame;
2925
3007
  }
2926
3008
  } finally {
2927
- adapter.increment(adapter.followers, id, -1);
3009
+ finish();
2928
3010
  }
2929
3011
  })();
2930
3012
  });
@@ -2939,6 +3021,7 @@ var NativeMetadataAdapter = class _NativeMetadataAdapter {
2939
3021
  return () => closing ??= (async () => {
2940
3022
  this.fallbackRetired = new Set([...this.targets].filter((id) => this.admission.isRetired(id)));
2941
3023
  this.disposed = true;
3024
+ this.apiHandles.clear();
2942
3025
  if (this.fallbackRetired.size === 0) {
2943
3026
  for (const restore of [...this.restores].reverse()) restore();
2944
3027
  this.restores.length = 0;
@@ -2957,6 +3040,7 @@ var NativeMetadataAdapter = class _NativeMetadataAdapter {
2957
3040
  this.restores.length = 0;
2958
3041
  this.guardedMethods.length = 0;
2959
3042
  this.fallbackRetired.clear();
3043
+ this.apiHandles.clear();
2960
3044
  }
2961
3045
  async converge(input, expected, request) {
2962
3046
  const snapshot = nativeMetadataSnapshotSchema.parse(input);
@@ -4088,16 +4172,19 @@ var NativeRetirementOwner = class _NativeRetirementOwner {
4088
4172
  async eligibilityInner(id) {
4089
4173
  if (nativeServiceInstance(this.ctx.sessionPersistence) !== this.originalProvider) return "native/composition-changed";
4090
4174
  if (this.admission.isRetired(id)) return "native/session-retired";
4091
- if (this.provider.busy(id)) return "native/persistence-retained";
4092
4175
  try {
4093
4176
  const scope = this.scopes.get(id);
4094
4177
  if (scope) {
4178
+ if (this.provider.busy(id)) return "native/persistence-retained";
4095
4179
  if (scope.blocked) return scope.blocked;
4096
4180
  await this.metadata.capture(id);
4097
4181
  return null;
4098
4182
  }
4099
4183
  const release = await this.admission.acquire(id);
4100
4184
  try {
4185
+ await this.metadata.releaseIdle(id);
4186
+ await this.admission.drain(id);
4187
+ if (this.provider.busy(id)) return "native/persistence-retained";
4101
4188
  await this.metadata.capture(id);
4102
4189
  const snapshot = await this.provider.stat(id);
4103
4190
  if (!snapshot) return "session-not-found";