stratagate-dsh 0.2.58 → 0.2.61

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/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { Context } from '@deepseek-ai/cordis';
2
2
  import z from '@deepseek-ai/schemastery';
3
3
 
4
4
  type NamespaceMode = 'project' | 'session' | 'global';
5
+ type StructuredReasoningEffortMode = 'auto' | 'force-off';
5
6
  interface Config {
6
7
  database?: string;
7
8
  namespaceMode?: NamespaceMode;
@@ -14,12 +15,14 @@ interface Config {
14
15
  model?: string;
15
16
  maxOutputTokens?: number;
16
17
  structuredTaskTimeoutMs?: number;
18
+ structuredReasoningEffort?: StructuredReasoningEffortMode;
17
19
  }
18
20
  declare const Config: z<Config>;
19
21
 
20
22
  declare const name = "stratagate-memory";
21
23
  declare const inject: string[];
24
+ declare const STRATAGATE_SETTINGS_NAMESPACE = "stratagate-memory";
22
25
 
23
26
  declare function apply(ctx: Context, config: Config): Promise<() => Promise<void>>;
24
27
 
25
- export { Config, Config as PluginConfig, apply, inject, name };
28
+ export { Config, Config as PluginConfig, STRATAGATE_SETTINGS_NAMESPACE, apply, inject, name };
package/dist/index.js CHANGED
@@ -5,6 +5,9 @@ import { createUserMessage as createUserMessage3 } from "@deepseek-ai/dsh-llm";
5
5
 
6
6
  // src/config.ts
7
7
  import z from "@deepseek-ai/schemastery";
8
+ var StructuredReasoningEffortSettings = z.object({
9
+ structuredReasoningEffort: z.union(["auto", "force-off"]).default("auto").description("\u8BB0\u5FC6\u5904\u7406\u7ED3\u6784\u5316\u8C03\u7528\u7684\u63A8\u7406\u6863\u4F4D\u7B56\u7565").comment("auto\uFF1A\u4EC5\u5728\u6A21\u578B\u660E\u786E\u652F\u6301 off \u65F6\u4F7F\u7528\uFF1Bforce-off\uFF1A\u4F18\u5148\u4F7F\u7528 off\uFF0C\u4E0D\u652F\u6301\u6216\u80FD\u529B\u68C0\u67E5\u5931\u8D25\u65F6\u5B89\u5168\u964D\u7EA7\uFF0C\u5E76\u5BF9\u540C\u4E00\u6A21\u578B\u53EA\u544A\u8B66\u4E00\u6B21\u3002")
10
+ });
8
11
  var Config = z.object({
9
12
  database: z.string().required(),
10
13
  namespaceMode: z.union(["project", "session", "global"]).default("project"),
@@ -16,7 +19,8 @@ var Config = z.object({
16
19
  provider: z.string(),
17
20
  model: z.string(),
18
21
  maxOutputTokens: z.natural().min(256).default(2048),
19
- structuredTaskTimeoutMs: z.natural().min(1e3).default(45e3)
22
+ structuredTaskTimeoutMs: z.natural().min(1e3).default(45e3),
23
+ structuredReasoningEffort: z.union(["auto", "force-off"]).default("auto")
20
24
  });
21
25
  function resolveConfig(config) {
22
26
  const database = config.database?.trim() ?? "";
@@ -38,7 +42,8 @@ function resolveConfig(config) {
38
42
  ingestSubagents: config.ingestSubagents ?? false,
39
43
  ...provider && model ? { provider, model } : {},
40
44
  maxOutputTokens: Math.max(256, Math.floor(config.maxOutputTokens ?? 2048)),
41
- structuredTaskTimeoutMs: Math.max(1e3, Math.floor(config.structuredTaskTimeoutMs ?? 45e3))
45
+ structuredTaskTimeoutMs: Math.max(1e3, Math.floor(config.structuredTaskTimeoutMs ?? 45e3)),
46
+ structuredReasoningEffort: config.structuredReasoningEffort ?? "auto"
42
47
  };
43
48
  }
44
49
 
@@ -2986,35 +2991,6 @@ var StrataGate = class _StrataGate {
2986
2991
  listSummaryJobs() {
2987
2992
  return [...this.summaryJobs.values()];
2988
2993
  }
2989
- /** Give one terminally failed Block Summary job a fresh, user-requested retry budget. */
2990
- async retryBlockSummary(id) {
2991
- const blockId = id.trim();
2992
- if (!blockId) throw new TypeError("Block id must not be empty");
2993
- const block = this.blocks.find((candidate) => candidate.id === blockId);
2994
- if (!block) throw new Error(`Unknown block: ${blockId}`);
2995
- if (block.processingStatus !== "pending") throw new Error(`Block ${blockId} is already ready`);
2996
- await this.commitMutation(() => {
2997
- const job = this.summaryJobs.get(blockId);
2998
- if (!job) throw new Error(`Missing summary job for block: ${blockId}`);
2999
- if (job.status !== "failed" || job.nextRetryAt !== null) {
3000
- throw new Error(`Block ${blockId} Summary has not exhausted automatic retries`);
3001
- }
3002
- this.summaryJobs.set(blockId, {
3003
- ...job,
3004
- status: "pending",
3005
- attempts: 0,
3006
- lastError: null,
3007
- nextRetryAt: null,
3008
- updatedAt: toUtc8Iso(this.now())
3009
- });
3010
- });
3011
- const extractedEvents = await this.processBlock(block, { retryFailed: false });
3012
- const processed = this.blocks.find((candidate) => candidate.id === blockId);
3013
- const readyBlocks = processed?.processingStatus === "ready" ? [processed] : [];
3014
- const projectedElements = await this.projectEligibleElements() ?? [];
3015
- await this.projectEligibleGraph();
3016
- return { sealedBlocks: [], readyBlocks, extractedEvents, projectedElements };
3017
- }
3018
2994
  listElementProjectionJobs() {
3019
2995
  return [...this.elementProjectionJobs.values()];
3020
2996
  }
@@ -4918,6 +4894,7 @@ var DshModelBridge = class {
4918
4894
  constructor(ctx, config) {
4919
4895
  this.ctx = ctx;
4920
4896
  this.config = config;
4897
+ this.structuredReasoningEffort = config.structuredReasoningEffort ?? "auto";
4921
4898
  this.ctx.on?.("llm/adapters-updated", () => {
4922
4899
  this.offCapabilities.clear();
4923
4900
  });
@@ -4927,6 +4904,11 @@ var DshModelBridge = class {
4927
4904
  sessions = new AsyncLocalStorage();
4928
4905
  successfulResponses = [];
4929
4906
  offCapabilities = /* @__PURE__ */ new Map();
4907
+ warnedOffFallbackRoutes = /* @__PURE__ */ new Set();
4908
+ structuredReasoningEffort;
4909
+ setStructuredReasoningEffort(mode) {
4910
+ this.structuredReasoningEffort = mode;
4911
+ }
4930
4912
  run(session, operation) {
4931
4913
  return this.sessions.run({ session, sessionId: session.id }, operation);
4932
4914
  }
@@ -5146,7 +5128,7 @@ ${JSON_RETRY_INSTRUCTION}`,
5146
5128
  if (useOff && isOffRejection(error)) {
5147
5129
  this.offCapabilities.set(routeKey, "unsupported");
5148
5130
  useOff = false;
5149
- this.ctx.logger.warn(`stratagate-memory ${baseRoute.provider}/${baseRoute.model} rejected reasoningEffort=off; retrying once without it`);
5131
+ this.warnOffFallbackOnce(routeKey, `${baseRoute.provider}/${baseRoute.model} rejected reasoningEffort=off; retrying once without it`);
5150
5132
  attempt -= 1;
5151
5133
  continue;
5152
5134
  }
@@ -5158,7 +5140,7 @@ ${JSON_RETRY_INSTRUCTION}`,
5158
5140
  if (useOff && isOffRejection(finish.failure)) {
5159
5141
  this.offCapabilities.set(routeKey, "unsupported");
5160
5142
  useOff = false;
5161
- this.ctx.logger.warn(`stratagate-memory ${baseRoute.provider}/${baseRoute.model} rejected reasoningEffort=off; retrying once without it`);
5143
+ this.warnOffFallbackOnce(routeKey, `${baseRoute.provider}/${baseRoute.model} rejected reasoningEffort=off; retrying once without it`);
5162
5144
  attempt -= 1;
5163
5145
  continue;
5164
5146
  }
@@ -5236,8 +5218,19 @@ ${JSON_RETRY_INSTRUCTION}`,
5236
5218
  async shouldUseOff(route) {
5237
5219
  const key = `${route.provider}\0${route.model}`;
5238
5220
  const cached = this.offCapabilities.get(key);
5239
- if (cached) return cached === "supported";
5240
- if (typeof this.ctx.llm.resolveModelInfo !== "function") return true;
5221
+ if (cached) {
5222
+ if (cached === "unsupported" && this.structuredReasoningEffort === "force-off") {
5223
+ this.warnOffFallbackOnce(key, `${route.provider}/${route.model} does not support reasoningEffort=off; using the model default`);
5224
+ }
5225
+ return cached === "supported";
5226
+ }
5227
+ if (typeof this.ctx.llm.resolveModelInfo !== "function") {
5228
+ this.offCapabilities.set(key, "unsupported");
5229
+ if (this.structuredReasoningEffort === "force-off") {
5230
+ this.warnOffFallbackOnce(key, `${route.provider}/${route.model} capabilities are unavailable; using the model default`);
5231
+ }
5232
+ return false;
5233
+ }
5241
5234
  const controller = new AbortController();
5242
5235
  const lookupTimeoutMs = Math.min(5e3, this.config.structuredTaskTimeoutMs ?? DEFAULT_STRUCTURED_TIMEOUT_MS);
5243
5236
  let timer;
@@ -5252,16 +5245,28 @@ ${JSON_RETRY_INSTRUCTION}`,
5252
5245
  timer.unref?.();
5253
5246
  })
5254
5247
  ]);
5255
- if (!info.reasoning) return true;
5248
+ if (!info.reasoning) return this.structuredReasoningEffort === "force-off";
5256
5249
  const supported = info.reasoning.efforts.some(({ id }) => String(id) === "off");
5257
5250
  this.offCapabilities.set(key, supported ? "supported" : "unsupported");
5251
+ if (!supported && this.structuredReasoningEffort === "force-off") {
5252
+ this.warnOffFallbackOnce(key, `${route.provider}/${route.model} does not support reasoningEffort=off; using the model default`);
5253
+ }
5258
5254
  return supported;
5259
5255
  } catch {
5260
- return true;
5256
+ this.offCapabilities.set(key, "unsupported");
5257
+ if (this.structuredReasoningEffort === "force-off") {
5258
+ this.warnOffFallbackOnce(key, `${route.provider}/${route.model} capability lookup failed; using the model default`);
5259
+ }
5260
+ return false;
5261
5261
  } finally {
5262
5262
  if (timer) clearTimeout(timer);
5263
5263
  }
5264
5264
  }
5265
+ warnOffFallbackOnce(routeKey, message) {
5266
+ if (this.warnedOffFallbackRoutes.has(routeKey)) return;
5267
+ this.warnedOffFallbackRoutes.add(routeKey);
5268
+ this.ctx.logger.warn(`stratagate-memory ${message}`);
5269
+ }
5265
5270
  async consumeStructuredStream(request, assembler) {
5266
5271
  const timeoutMs = this.config.structuredTaskTimeoutMs ?? DEFAULT_STRUCTURED_TIMEOUT_MS;
5267
5272
  const controller = new AbortController();
@@ -5529,6 +5534,10 @@ var DshMetadataStore = class {
5529
5534
  };
5530
5535
 
5531
5536
  // src/runtime.ts
5537
+ var DRAIN_THRESHOLD = 3;
5538
+ var DRAIN_EAGER_MS = 150;
5539
+ var DRAIN_BASE_BACKOFF_MS = 2e3;
5540
+ var DRAIN_MAX_BACKOFF_MS = 6e4;
5532
5541
  var AUTO_EVENT_LIMIT = 4;
5533
5542
  var AUTO_ELEMENT_LIMIT = 4;
5534
5543
  var AUTO_MEMORY_TOKEN_BUDGET = 900;
@@ -5595,17 +5604,19 @@ var StrataGateRuntime = class {
5595
5604
  migrationTimers = /* @__PURE__ */ new Map();
5596
5605
  derivationTimers = /* @__PURE__ */ new Map();
5597
5606
  derivationRuns = /* @__PURE__ */ new Map();
5598
- knownSessions = /* @__PURE__ */ new Map();
5599
- pendingSurfaceSync = /* @__PURE__ */ new Map();
5600
5607
  adminSnapshotCache = /* @__PURE__ */ new Map();
5601
5608
  externalImportRuns = /* @__PURE__ */ new Map();
5602
5609
  feedbackDrafts = /* @__PURE__ */ new Map();
5603
5610
  pendingFeedbackSuggestionSessions = /* @__PURE__ */ new Set();
5604
- ingestTail = Promise.resolve();
5611
+ pendingTurns = /* @__PURE__ */ new Map();
5612
+ drainTimers = /* @__PURE__ */ new Map();
5613
+ drainBackoffMs = /* @__PURE__ */ new Map();
5614
+ drainTails = /* @__PURE__ */ new Map();
5615
+ drainErrors = /* @__PURE__ */ new Map();
5616
+ sessionsById = /* @__PURE__ */ new Map();
5605
5617
  settingsTail = Promise.resolve();
5606
5618
  batchSequence = 0;
5607
5619
  closed = false;
5608
- ingestError;
5609
5620
  blockTurnSize;
5610
5621
  blockDecayLambda;
5611
5622
  transientLastFeedbackPromptAt = null;
@@ -5614,20 +5625,86 @@ var StrataGateRuntime = class {
5614
5625
  if (!this.config.ingestSubagents && session.header.origin === "subagent") return;
5615
5626
  const turn = this.folder.accept(session, event);
5616
5627
  if (!turn) return;
5617
- this.ingestTail = this.ingestTail.catch(() => {
5618
- }).then(async () => {
5619
- const memory = await this.space(session);
5620
- try {
5621
- const result = await memory.appendTurn(turn, { deferDerivation: true });
5622
- if (result.sealedBlock) this.scheduleBlockDerivation(session, memory);
5623
- } finally {
5624
- await this.persistSuccessfulResponses(memory);
5625
- }
5626
- }).catch((error) => {
5627
- this.ingestError = error;
5628
+ const key = String(session.id);
5629
+ this.sessionsById.set(key, session);
5630
+ const queued = this.pendingTurns.get(key) ?? [];
5631
+ queued.push(turn);
5632
+ this.pendingTurns.set(key, queued);
5633
+ this.scheduleDrain(session);
5634
+ }
5635
+ /** Serialize drains per session while allowing unrelated sessions to settle independently. */
5636
+ enqueueDrain(session) {
5637
+ const key = String(session.id);
5638
+ const prior = this.drainTails.get(key) ?? Promise.resolve();
5639
+ const next = prior.catch(() => {
5640
+ }).then(() => this.drain(session));
5641
+ this.drainTails.set(key, next);
5642
+ void next.then(() => {
5643
+ if ((this.pendingTurns.get(key)?.length ?? 0) === 0) this.drainErrors.delete(key);
5644
+ }, (error) => {
5645
+ this.drainErrors.set(key, error);
5628
5646
  this.notePluginError(session, error);
5629
5647
  this.onIngestError(error);
5630
5648
  });
5649
+ return next;
5650
+ }
5651
+ /** Batch short bursts and retry failed drains with bounded exponential backoff. */
5652
+ scheduleDrain(session, overrideDelayMs) {
5653
+ const key = String(session.id);
5654
+ if (this.closed) return;
5655
+ const queued = this.pendingTurns.get(key)?.length ?? 0;
5656
+ if (queued === 0) return;
5657
+ const delay = overrideDelayMs ?? (queued >= DRAIN_THRESHOLD ? DRAIN_EAGER_MS : Math.min(this.drainBackoffMs.get(key) ?? DRAIN_BASE_BACKOFF_MS, DRAIN_MAX_BACKOFF_MS));
5658
+ const pending = this.drainTimers.get(key);
5659
+ if (pending) {
5660
+ if (delay >= DRAIN_BASE_BACKOFF_MS) return;
5661
+ clearTimeout(pending);
5662
+ this.drainTimers.delete(key);
5663
+ }
5664
+ const timer = setTimeout(() => {
5665
+ this.drainTimers.delete(key);
5666
+ if (this.closed) return;
5667
+ void this.enqueueDrain(session).then(() => {
5668
+ this.drainBackoffMs.delete(key);
5669
+ if ((this.pendingTurns.get(key)?.length ?? 0) > 0) {
5670
+ this.scheduleDrain(session, DRAIN_BASE_BACKOFF_MS);
5671
+ }
5672
+ }, () => {
5673
+ const backoff = this.drainBackoffMs.get(key) ?? DRAIN_BASE_BACKOFF_MS;
5674
+ this.drainBackoffMs.set(key, Math.min(backoff * 2, DRAIN_MAX_BACKOFF_MS));
5675
+ const pendingRetry = this.drainTimers.get(key);
5676
+ if (pendingRetry) clearTimeout(pendingRetry);
5677
+ this.drainTimers.delete(key);
5678
+ this.scheduleDrain(session);
5679
+ });
5680
+ }, delay);
5681
+ timer.unref?.();
5682
+ this.drainTimers.set(key, timer);
5683
+ }
5684
+ /** Restore failed and unvisited turns ahead of turns that arrived during the drain. */
5685
+ async drain(session) {
5686
+ const key = String(session.id);
5687
+ const turns = this.pendingTurns.get(key);
5688
+ if (!turns || turns.length === 0) return;
5689
+ this.pendingTurns.delete(key);
5690
+ let completed = 0;
5691
+ let memory;
5692
+ try {
5693
+ memory = await this.space(session);
5694
+ for (const turn of turns) {
5695
+ const result = await memory.appendTurn(turn, { deferDerivation: true });
5696
+ completed += 1;
5697
+ if (result.sealedBlock) this.scheduleBlockDerivation(session, memory);
5698
+ }
5699
+ } catch (error) {
5700
+ const remaining = turns.slice(completed);
5701
+ if (remaining.length > 0) {
5702
+ this.pendingTurns.set(key, [...remaining, ...this.pendingTurns.get(key) ?? []]);
5703
+ }
5704
+ throw error;
5705
+ } finally {
5706
+ if (memory) await this.persistSuccessfulResponses(memory);
5707
+ }
5631
5708
  }
5632
5709
  async searchEvents(session, query, options = {}) {
5633
5710
  await this.flush();
@@ -5967,38 +6044,20 @@ var StrataGateRuntime = class {
5967
6044
  }
5968
6045
  return changed;
5969
6046
  }
5970
- /** Finish native-surface replacement when an admin retry ran without the target session loaded. */
5971
- async syncPendingRetrySurface(session, memory) {
5972
- const namespace = this.namespaceFor(session);
5973
- const threadId = String(session.id);
5974
- const pending = [...this.pendingSurfaceSync.entries()].filter(([, item]) => item.namespace === namespace);
5975
- if (pending.length === 0) return;
5976
- const contexts = new Map(memory.getBlockContext(threadId).map((context) => [context.id, context]));
5977
- const existing = currentBlockSurfaceMessages(session);
5978
- let changed = false;
5979
- for (const [key, { blockId }] of pending) {
5980
- if (existing.has(blockId)) {
5981
- this.pendingSurfaceSync.delete(key);
5982
- continue;
5983
- }
5984
- const block = memory.listBlocks().find((candidate) => candidate.id === blockId && candidate.threadId === threadId);
5985
- const context = contexts.get(blockId);
5986
- if (!block || block.processingStatus !== "ready" || !context) continue;
5987
- try {
5988
- this.replaceSealedSurface(session, block, context, dshTurnAtBlockEnd(session, block));
5989
- this.pendingSurfaceSync.delete(key);
5990
- changed = true;
5991
- } catch (error) {
5992
- this.onIngestError(error);
5993
- }
5994
- }
5995
- if (changed) await this.flushNativeSession(session);
5996
- }
5997
6047
  // Keep the ingestion error for callers that explicitly require a flushed run.
5998
6048
  async settleIngestion() {
5999
- await this.ingestTail;
6000
- const error = this.ingestError;
6001
- this.ingestError = void 0;
6049
+ do {
6050
+ for (const [key, session] of this.sessionsById) {
6051
+ if ((this.pendingTurns.get(key)?.length ?? 0) === 0) continue;
6052
+ const timer = this.drainTimers.get(key);
6053
+ if (timer) clearTimeout(timer);
6054
+ this.drainTimers.delete(key);
6055
+ this.enqueueDrain(session);
6056
+ }
6057
+ await Promise.allSettled(this.drainTails.values());
6058
+ } while (this.drainErrors.size === 0 && [...this.pendingTurns.values()].some((turns) => turns.length > 0));
6059
+ const error = this.drainErrors.values().next().value;
6060
+ this.drainErrors.clear();
6002
6061
  return error;
6003
6062
  }
6004
6063
  async close() {
@@ -6006,6 +6065,8 @@ var StrataGateRuntime = class {
6006
6065
  this.closed = true;
6007
6066
  for (const timer of this.migrationTimers.values()) clearTimeout(timer);
6008
6067
  this.migrationTimers.clear();
6068
+ for (const timer of this.drainTimers.values()) clearTimeout(timer);
6069
+ this.drainTimers.clear();
6009
6070
  for (const timer of this.derivationTimers.values()) clearTimeout(timer);
6010
6071
  this.derivationTimers.clear();
6011
6072
  let flushError;
@@ -6018,6 +6079,7 @@ var StrataGateRuntime = class {
6018
6079
  await Promise.allSettled(this.derivationRuns.values());
6019
6080
  await Promise.allSettled(this.externalImportRuns.values());
6020
6081
  await Promise.all(settled.flatMap((result) => result.status === "fulfilled" ? [result.value.close()] : []));
6082
+ this.sessionsById.clear();
6021
6083
  if (flushError !== void 0) throw flushError;
6022
6084
  }
6023
6085
  namespaceFor(session) {
@@ -6513,44 +6575,6 @@ var StrataGateRuntime = class {
6513
6575
  });
6514
6576
  return update;
6515
6577
  }
6516
- async adminRetryBlockSummary(namespace, id) {
6517
- const key = namespace.trim();
6518
- const blockId = id.trim();
6519
- if (!key) throw new TypeError("StrataGate admin namespace must not be empty");
6520
- if (!blockId) throw new TypeError("Block id must not be empty");
6521
- const update = this.settingsTail.catch(() => {
6522
- }).then(async () => {
6523
- await this.flush();
6524
- const { memory, owned } = await this.openAdminMemory(key, { derivation: true });
6525
- try {
6526
- await this.refreshExternalImportMemory(key, memory);
6527
- const block = memory.listBlocks().find((candidate) => candidate.id === blockId);
6528
- if (!block) throw new Error(`Unknown block: ${blockId}`);
6529
- const threadId = block.threadId ?? `admin-summary-retry:${blockId}`;
6530
- const session = block.threadId ? this.knownSessions.get(block.threadId)?.deref() : void 0;
6531
- const resumed = session && this.namespaceFor(session) === key ? await this.models.run(session, () => memory.retryBlockSummary(blockId)) : await this.models.runDetached(threadId, () => memory.retryBlockSummary(blockId));
6532
- await this.persistSuccessfulResponses(memory);
6533
- if (resumed.readyBlocks.some(({ id: readyId }) => readyId === blockId) && block.threadId) {
6534
- this.pendingSurfaceSync.set(`${key}\0${blockId}`, { namespace: key, blockId });
6535
- if (session && this.namespaceFor(session) === key) await this.syncPendingRetrySurface(session, memory);
6536
- }
6537
- const summaryJob = memory.listSummaryJobs().find(({ blockId: candidateId }) => candidateId === blockId);
6538
- return {
6539
- blockId,
6540
- processingStatus: block.processingStatus,
6541
- ready: block.processingStatus === "ready",
6542
- summaryJob: summaryJob ?? null,
6543
- surfaceUpdated: !this.pendingSurfaceSync.has(`${key}\0${blockId}`)
6544
- };
6545
- } finally {
6546
- if (owned) await memory.close();
6547
- }
6548
- });
6549
- this.settingsTail = update.then(() => {
6550
- }, () => {
6551
- });
6552
- return update;
6553
- }
6554
6578
  async applyBlockDecayLambda(value) {
6555
6579
  await this.flush();
6556
6580
  this.blockDecayLambda = value;
@@ -6613,9 +6637,8 @@ var StrataGateRuntime = class {
6613
6637
  }
6614
6638
  }
6615
6639
  }
6616
- async space(session) {
6640
+ space(session) {
6617
6641
  const namespace = this.namespaceFor(session);
6618
- this.knownSessions.set(String(session.id), new WeakRef(session));
6619
6642
  this.rememberWorkspace(namespace, session.header.cwd);
6620
6643
  let opening = this.spaces.get(namespace);
6621
6644
  if (!opening) {
@@ -6628,20 +6651,20 @@ var StrataGateRuntime = class {
6628
6651
  extractor: this.models.extractor,
6629
6652
  graphProjector: this.models.graphProjector,
6630
6653
  disableElementProjection: true
6631
- }).then(async (memory2) => {
6654
+ }).then(async (memory) => {
6632
6655
  try {
6633
6656
  try {
6634
- await memory2.resumePendingWork({ deferDerivation: true, threadId: String(session.id) });
6635
- const contexts = memory2.getBlockContext(String(session.id));
6657
+ await memory.resumePendingWork({ deferDerivation: true, threadId: String(session.id) });
6658
+ const contexts = memory.getBlockContext(String(session.id));
6636
6659
  this.syncDecayedBlockSurface(session, contexts);
6637
6660
  } finally {
6638
- await this.persistSuccessfulResponses(memory2);
6661
+ await this.persistSuccessfulResponses(memory);
6639
6662
  }
6640
- this.scheduleGraphMigration(session, memory2);
6641
- this.scheduleBlockDerivation(session, memory2);
6642
- return memory2;
6663
+ this.scheduleGraphMigration(session, memory);
6664
+ this.scheduleBlockDerivation(session, memory);
6665
+ return memory;
6643
6666
  } catch (error) {
6644
- await memory2.close().catch(() => {
6667
+ await memory.close().catch(() => {
6645
6668
  });
6646
6669
  throw error;
6647
6670
  }
@@ -6651,9 +6674,7 @@ var StrataGateRuntime = class {
6651
6674
  if (this.spaces.get(namespace) === opening) this.spaces.delete(namespace);
6652
6675
  });
6653
6676
  }
6654
- const memory = await opening;
6655
- await this.syncPendingRetrySurface(session, memory);
6656
- return memory;
6677
+ return opening;
6657
6678
  }
6658
6679
  async searchGraph(session, query, limit = 8) {
6659
6680
  await this.flush();
@@ -6757,7 +6778,7 @@ var StrataGateRuntime = class {
6757
6778
  noteNewCoreJobFailures(session, before, memory) {
6758
6779
  if ([...this.failedCoreJobs(memory)].some((failure) => !before.has(failure))) this.notePluginError(session);
6759
6780
  }
6760
- async openAdminMemory(namespace, options = {}) {
6781
+ async openAdminMemory(namespace) {
6761
6782
  const active = this.spaces.get(namespace);
6762
6783
  if (active) return { memory: await active, owned: false };
6763
6784
  if (this.config.database === ":memory:" || !existsSync(this.config.database)) {
@@ -6769,10 +6790,6 @@ var StrataGateRuntime = class {
6769
6790
  namespace,
6770
6791
  blockTurnSize: this.blockTurnSize,
6771
6792
  blockDecayLambda: this.blockDecayLambda,
6772
- ...options.derivation ? {
6773
- summarizer: this.models.summarizer,
6774
- extractor: this.models.extractor
6775
- } : {},
6776
6793
  graphProjector: this.models.graphProjector,
6777
6794
  disableElementProjection: true
6778
6795
  }),
@@ -6890,8 +6907,9 @@ function renderMessages(messages) {
6890
6907
  }
6891
6908
  function dshTurnAtBlockEnd(session, block) {
6892
6909
  const blockEnd = Date.parse(block.createdAt);
6893
- for (let index = session.events.length - 1; index >= 0; index -= 1) {
6894
- const event = session.events[index];
6910
+ const events = session.snapshotEvents();
6911
+ for (let index = events.length - 1; index >= 0; index -= 1) {
6912
+ const event = events[index];
6895
6913
  if (event?.type === "turn/end" && event.time === blockEnd) return event.data.turn;
6896
6914
  }
6897
6915
  throw new Error(`Cannot match StrataGate block ${block.id} to its completed DSH turn`);
@@ -6901,13 +6919,14 @@ function sealedSurfaceSeqs(session, endTurn, turnCount) {
6901
6919
  const currentSet = new Set(currentSurface);
6902
6920
  const completed = [];
6903
6921
  let open;
6904
- for (const event of session.events) {
6922
+ const events = session.snapshotEvents();
6923
+ for (const event of events) {
6905
6924
  if (event.type === "turn/start") {
6906
6925
  open = { turn: event.data.turn, start: event.seq };
6907
6926
  continue;
6908
6927
  }
6909
6928
  if (event.type !== "turn/end" || !open || event.data.turn !== open.turn) continue;
6910
- const turnEvents = session.events.slice(open.start + 1, event.seq);
6929
+ const turnEvents = events.slice(open.start + 1, event.seq);
6911
6930
  const hasHumanMessage = turnEvents.some((candidate) => candidate.type === "user/message" && candidate.data.source.kind === "user");
6912
6931
  if (hasHumanMessage && event.data.turn <= endTurn) {
6913
6932
  completed.push({
@@ -7026,7 +7045,7 @@ function currentBlockSurfaceMessages(session) {
7026
7045
  const blocks = /* @__PURE__ */ new Map();
7027
7046
  if (!session.surface?.nodes) return blocks;
7028
7047
  for (const seq of session.surface.nodes) {
7029
- const event = session.events[seq];
7048
+ const event = session.eventAt(seq);
7030
7049
  if (event?.type !== "user/message" || event.data.source.kind !== "plugin" || event.data.source.plugin !== COMPACTION_SOURCE_PLUGIN) continue;
7031
7050
  const text3 = event.data.content.flatMap((block) => block.type === "text" ? [block.text] : []).join("\n");
7032
7051
  const blockId = text3.match(/^\[StrataGate conversation block\]\nBlock: ([^\n]+)/u)?.[1] ?? text3.match(/^\[StrataGate compressed conversation\]\nBlock ([^;\n]+);/u)?.[1];
@@ -7126,8 +7145,9 @@ KnowledgeGraph:
7126
7145
  return lines.join("\n");
7127
7146
  }
7128
7147
  function activeTurn(session) {
7129
- for (let index = session.events.length - 1; index >= 0; index -= 1) {
7130
- const event = session.events[index];
7148
+ const events = session.snapshotEvents();
7149
+ for (let index = events.length - 1; index >= 0; index -= 1) {
7150
+ const event = events[index];
7131
7151
  if (event?.type === "turn/start") return event.data.turn;
7132
7152
  }
7133
7153
  return void 0;
@@ -7418,7 +7438,7 @@ function clusterKnowledgeGraph(rawNodes, rawEdges) {
7418
7438
  }
7419
7439
 
7420
7440
  // src/web.ts
7421
- var STRATAGATE_DSH_VERSION = "0.2.57";
7441
+ var STRATAGATE_DSH_VERSION = "0.2.52";
7422
7442
  var LEGACY_THREAD_ID = "__legacy__";
7423
7443
  var nodeRequire = createRequire(import.meta.url);
7424
7444
  function installedPackageVersion(names) {
@@ -7561,24 +7581,9 @@ async function overview(runtime, cachedEntries) {
7561
7581
  const rows = [];
7562
7582
  for (const { namespace, snapshot } of entries) {
7563
7583
  if (!snapshot) continue;
7564
- const failedJobs = snapshot.summaryJobs.filter(({ status }) => status === "failed").length + snapshot.extractionJobs.filter(({ status }) => status === "failed").length + snapshot.graphProjectionJobs.filter(({ status }) => status === "failed").length;
7584
+ const failedJobs = snapshot.extractionJobs.filter(({ status }) => status === "failed").length + snapshot.graphProjectionJobs.filter(({ status }) => status === "failed").length;
7565
7585
  const processingJobs = snapshot.summaryJobs.filter(({ status, nextRetryAt }) => status === "pending" || status === "running" || status === "failed" && nextRetryAt !== null).length + snapshot.extractionJobs.filter(({ status, nextRetryAt }) => status === "running" || status === "failed" && nextRetryAt !== null).length + snapshot.graphProjectionJobs.filter(({ status }) => status === "pending" || status === "running").length;
7566
7586
  const failedJobDetails = [
7567
- ...snapshot.summaryJobs.filter(({ status }) => status === "failed").map((job) => {
7568
- const block = snapshot.blocks.find(({ id }) => id === job.blockId);
7569
- return {
7570
- id: job.blockId,
7571
- kind: "block-summary",
7572
- attempts: job.attempts,
7573
- nextRetryAt: job.nextRetryAt,
7574
- lastError: job.lastError?.slice(0, 500) ?? null,
7575
- lastErrorFull: job.lastError,
7576
- updatedAt: job.updatedAt,
7577
- threadId: block?.threadId ?? null,
7578
- sequence: block?.sequence ?? null,
7579
- turnRange: block ? [block.startTurn, block.endTurn] : null
7580
- };
7581
- }),
7582
7587
  ...snapshot.extractionJobs.filter(({ status }) => status === "failed").map((job) => ({
7583
7588
  id: job.blockId,
7584
7589
  kind: "event-extraction",
@@ -7973,7 +7978,7 @@ async function memories(runtime, url) {
7973
7978
  const failedProjection = projections.find(({ status: status2 }) => status2 === "failed");
7974
7979
  const pendingProjection = projections.some(({ status: status2 }) => status2 === "pending" || status2 === "running");
7975
7980
  const needsExtraction = source.shouldExtract === true;
7976
- const status = summary?.status === "failed" ? "failed" : summary?.status === "pending" || summary?.status === "running" || !summary && source.processingStatus === "pending" ? "processing" : extraction?.status === "failed" || failedProjection ? "failed" : extraction?.status === "succeeded" || extraction?.status === "skipped" ? pendingProjection ? "processing" : "organized" : needsExtraction ? "waiting" : "organized";
7981
+ const status = extraction?.status === "failed" || failedProjection ? "failed" : extraction?.status === "succeeded" || extraction?.status === "skipped" ? pendingProjection ? "processing" : "organized" : needsExtraction ? "waiting" : "organized";
7977
7982
  const blockPosition = scopedBlocks.findIndex(({ id }) => id === block.id) + 1;
7978
7983
  const latestBlockPosition = scopedBlocks.length;
7979
7984
  const currentLevel = getDecayedBlockLevel(
@@ -8131,20 +8136,6 @@ async function expandBlock(runtime, url) {
8131
8136
  if (!/^L?[0-5]$/i.test(target)) throw new AdminHttpError(400, "level must be L0 through L5");
8132
8137
  return runtime.adminExpandBlock(namespace, blockId, target);
8133
8138
  }
8134
- async function retryBlockSummary(runtime, url) {
8135
- const namespace = url.searchParams.get("namespace")?.trim() ?? "";
8136
- const blockId = url.searchParams.get("blockId")?.trim() ?? "";
8137
- if (!namespace) throw new AdminHttpError(400, "namespace is required");
8138
- if (!blockId) throw new AdminHttpError(400, "blockId is required");
8139
- if (blockId.startsWith("virtual:")) throw new AdminHttpError(409, "Recovered legacy fragments cannot be retried");
8140
- const snapshot = await requiredSnapshot(runtime, namespace);
8141
- const job = snapshot.summaryJobs.find(({ blockId: candidateId }) => candidateId === blockId);
8142
- if (!job) throw new AdminHttpError(404, `Unknown Block Summary job: ${blockId}`);
8143
- if (job.status !== "failed" || job.nextRetryAt !== null) {
8144
- throw new AdminHttpError(409, "Block Summary is not in a terminal failed state");
8145
- }
8146
- return runtime.adminRetryBlockSummary(namespace, blockId);
8147
- }
8148
8139
  function receiptSources(snapshot, receipt) {
8149
8140
  const events = snapshot.events.filter(({ id }) => receipt.eventIds.includes(id));
8150
8141
  const elements = snapshot.elements.filter(({ id }) => receipt.elementIds.includes(id));
@@ -8257,9 +8248,6 @@ async function handleAdminRequest(runtime, req, res) {
8257
8248
  } else if (path === "/api/stratagate/blocks/expand") {
8258
8249
  if (req.method !== "PATCH") throw new AdminHttpError(405, "StrataGate Block expansion requires PATCH");
8259
8250
  sendJson(res, 200, await expandBlock(runtime, url));
8260
- } else if (path === "/api/stratagate/blocks/retry-summary") {
8261
- if (req.method !== "POST") throw new AdminHttpError(405, "StrataGate Block Summary retry requires POST");
8262
- sendJson(res, 200, await retryBlockSummary(runtime, url));
8263
8251
  } else if (path === "/api/stratagate/import") {
8264
8252
  if (req.method === "GET") {
8265
8253
  const operation = url.searchParams.get("operation");
@@ -8299,6 +8287,7 @@ function registerAdminRoutes(ctx, runtime) {
8299
8287
  // src/index.ts
8300
8288
  var name = "stratagate-memory";
8301
8289
  var inject = ["tools", "systemPrompt", "llm", "agentDefaultModel"];
8290
+ var STRATAGATE_SETTINGS_NAMESPACE = "stratagate-memory";
8302
8291
  var MEMORY_PROTOCOL = `[StrataGate memory protocol]
8303
8292
  StrataGate provides durable, evidence-gated memory through memory_* tools.
8304
8293
 
@@ -8336,6 +8325,24 @@ async function apply(ctx, config) {
8336
8325
  await ctx.sessions.flush(session);
8337
8326
  }, () => feedbackWebOrigin(ctx));
8338
8327
  await runtime.syncConfiguredSettings();
8328
+ const effortEntry = {
8329
+ structuredReasoningEffort: resolved.structuredReasoningEffort ?? "auto"
8330
+ };
8331
+ let effortSource = () => effortEntry;
8332
+ ctx.inject(["settings"], (settingsCtx) => {
8333
+ settingsCtx.settings.installSection(
8334
+ ctx,
8335
+ STRATAGATE_SETTINGS_NAMESPACE,
8336
+ StructuredReasoningEffortSettings,
8337
+ effortEntry,
8338
+ {
8339
+ setSource: (current) => {
8340
+ effortSource = current;
8341
+ },
8342
+ onChange: () => models.setStructuredReasoningEffort(effortSource().structuredReasoningEffort)
8343
+ }
8344
+ );
8345
+ });
8339
8346
  ctx.systemPrompt.section({ name: "tool:stratagate-memory", order: 113, text: MEMORY_PROTOCOL });
8340
8347
  ctx.systemPrompt.section({ name: "tool:stratagate-feedback", order: 114, text: FEEDBACK_PROTOCOL });
8341
8348
  ctx.on("system-prompt/assemble", async (_assembly, context, next) => {
@@ -8381,6 +8388,7 @@ async function apply(ctx, config) {
8381
8388
  }
8382
8389
  export {
8383
8390
  Config,
8391
+ STRATAGATE_SETTINGS_NAMESPACE,
8384
8392
  apply,
8385
8393
  inject,
8386
8394
  name