stratagate-dsh 0.2.57 → 0.2.60

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.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(12e4),
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 ?? 12e4)),
46
+ structuredReasoningEffort: config.structuredReasoningEffort ?? "auto"
42
47
  };
43
48
  }
44
49
 
@@ -4744,7 +4749,7 @@ function extractorPayload(context) {
4744
4749
  }
4745
4750
  var JSON_RESPONSE_ATTEMPTS = 2;
4746
4751
  var JSON_RETRY_INSTRUCTION = "Your previous response did not make one valid call to the requested tool. Do not spend output on analysis or reasoning. Immediately call that tool exactly once with complete arguments. Do not return an answer as text or markdown.";
4747
- var DEFAULT_STRUCTURED_TIMEOUT_MS = 45e3;
4752
+ var DEFAULT_STRUCTURED_TIMEOUT_MS = 12e4;
4748
4753
  var STRUCTURED_FIELDS = {
4749
4754
  summarizer: ["l0Title", "l0Tags", "l1Summary", "l2Keypoints", "shouldExtract"],
4750
4755
  extractor: ["shouldExtract", "reason", "events"],
@@ -4918,6 +4923,7 @@ var DshModelBridge = class {
4918
4923
  constructor(ctx, config) {
4919
4924
  this.ctx = ctx;
4920
4925
  this.config = config;
4926
+ this.structuredReasoningEffort = config.structuredReasoningEffort ?? "auto";
4921
4927
  this.ctx.on?.("llm/adapters-updated", () => {
4922
4928
  this.offCapabilities.clear();
4923
4929
  });
@@ -4927,6 +4933,11 @@ var DshModelBridge = class {
4927
4933
  sessions = new AsyncLocalStorage();
4928
4934
  successfulResponses = [];
4929
4935
  offCapabilities = /* @__PURE__ */ new Map();
4936
+ warnedOffFallbackRoutes = /* @__PURE__ */ new Set();
4937
+ structuredReasoningEffort;
4938
+ setStructuredReasoningEffort(mode) {
4939
+ this.structuredReasoningEffort = mode;
4940
+ }
4930
4941
  run(session, operation) {
4931
4942
  return this.sessions.run({ session, sessionId: session.id }, operation);
4932
4943
  }
@@ -5146,7 +5157,7 @@ ${JSON_RETRY_INSTRUCTION}`,
5146
5157
  if (useOff && isOffRejection(error)) {
5147
5158
  this.offCapabilities.set(routeKey, "unsupported");
5148
5159
  useOff = false;
5149
- this.ctx.logger.warn(`stratagate-memory ${baseRoute.provider}/${baseRoute.model} rejected reasoningEffort=off; retrying once without it`);
5160
+ this.warnOffFallbackOnce(routeKey, `${baseRoute.provider}/${baseRoute.model} rejected reasoningEffort=off; retrying once without it`);
5150
5161
  attempt -= 1;
5151
5162
  continue;
5152
5163
  }
@@ -5158,7 +5169,7 @@ ${JSON_RETRY_INSTRUCTION}`,
5158
5169
  if (useOff && isOffRejection(finish.failure)) {
5159
5170
  this.offCapabilities.set(routeKey, "unsupported");
5160
5171
  useOff = false;
5161
- this.ctx.logger.warn(`stratagate-memory ${baseRoute.provider}/${baseRoute.model} rejected reasoningEffort=off; retrying once without it`);
5172
+ this.warnOffFallbackOnce(routeKey, `${baseRoute.provider}/${baseRoute.model} rejected reasoningEffort=off; retrying once without it`);
5162
5173
  attempt -= 1;
5163
5174
  continue;
5164
5175
  }
@@ -5236,8 +5247,19 @@ ${JSON_RETRY_INSTRUCTION}`,
5236
5247
  async shouldUseOff(route) {
5237
5248
  const key = `${route.provider}\0${route.model}`;
5238
5249
  const cached = this.offCapabilities.get(key);
5239
- if (cached) return cached === "supported";
5240
- if (typeof this.ctx.llm.resolveModelInfo !== "function") return true;
5250
+ if (cached) {
5251
+ if (cached === "unsupported" && this.structuredReasoningEffort === "force-off") {
5252
+ this.warnOffFallbackOnce(key, `${route.provider}/${route.model} does not support reasoningEffort=off; using the model default`);
5253
+ }
5254
+ return cached === "supported";
5255
+ }
5256
+ if (typeof this.ctx.llm.resolveModelInfo !== "function") {
5257
+ this.offCapabilities.set(key, "unsupported");
5258
+ if (this.structuredReasoningEffort === "force-off") {
5259
+ this.warnOffFallbackOnce(key, `${route.provider}/${route.model} capabilities are unavailable; using the model default`);
5260
+ }
5261
+ return false;
5262
+ }
5241
5263
  const controller = new AbortController();
5242
5264
  const lookupTimeoutMs = Math.min(5e3, this.config.structuredTaskTimeoutMs ?? DEFAULT_STRUCTURED_TIMEOUT_MS);
5243
5265
  let timer;
@@ -5252,16 +5274,28 @@ ${JSON_RETRY_INSTRUCTION}`,
5252
5274
  timer.unref?.();
5253
5275
  })
5254
5276
  ]);
5255
- if (!info.reasoning) return true;
5277
+ if (!info.reasoning) return this.structuredReasoningEffort === "force-off";
5256
5278
  const supported = info.reasoning.efforts.some(({ id }) => String(id) === "off");
5257
5279
  this.offCapabilities.set(key, supported ? "supported" : "unsupported");
5280
+ if (!supported && this.structuredReasoningEffort === "force-off") {
5281
+ this.warnOffFallbackOnce(key, `${route.provider}/${route.model} does not support reasoningEffort=off; using the model default`);
5282
+ }
5258
5283
  return supported;
5259
5284
  } catch {
5260
- return true;
5285
+ this.offCapabilities.set(key, "unsupported");
5286
+ if (this.structuredReasoningEffort === "force-off") {
5287
+ this.warnOffFallbackOnce(key, `${route.provider}/${route.model} capability lookup failed; using the model default`);
5288
+ }
5289
+ return false;
5261
5290
  } finally {
5262
5291
  if (timer) clearTimeout(timer);
5263
5292
  }
5264
5293
  }
5294
+ warnOffFallbackOnce(routeKey, message) {
5295
+ if (this.warnedOffFallbackRoutes.has(routeKey)) return;
5296
+ this.warnedOffFallbackRoutes.add(routeKey);
5297
+ this.ctx.logger.warn(`stratagate-memory ${message}`);
5298
+ }
5265
5299
  async consumeStructuredStream(request, assembler) {
5266
5300
  const timeoutMs = this.config.structuredTaskTimeoutMs ?? DEFAULT_STRUCTURED_TIMEOUT_MS;
5267
5301
  const controller = new AbortController();
@@ -5529,6 +5563,10 @@ var DshMetadataStore = class {
5529
5563
  };
5530
5564
 
5531
5565
  // src/runtime.ts
5566
+ var DRAIN_THRESHOLD = 3;
5567
+ var DRAIN_EAGER_MS = 150;
5568
+ var DRAIN_BASE_BACKOFF_MS = 2e3;
5569
+ var DRAIN_MAX_BACKOFF_MS = 6e4;
5532
5570
  var AUTO_EVENT_LIMIT = 4;
5533
5571
  var AUTO_ELEMENT_LIMIT = 4;
5534
5572
  var AUTO_MEMORY_TOKEN_BUDGET = 900;
@@ -5601,11 +5639,15 @@ var StrataGateRuntime = class {
5601
5639
  externalImportRuns = /* @__PURE__ */ new Map();
5602
5640
  feedbackDrafts = /* @__PURE__ */ new Map();
5603
5641
  pendingFeedbackSuggestionSessions = /* @__PURE__ */ new Set();
5604
- ingestTail = Promise.resolve();
5642
+ pendingTurns = /* @__PURE__ */ new Map();
5643
+ drainTimers = /* @__PURE__ */ new Map();
5644
+ drainBackoffMs = /* @__PURE__ */ new Map();
5645
+ drainTails = /* @__PURE__ */ new Map();
5646
+ drainErrors = /* @__PURE__ */ new Map();
5647
+ sessionsById = /* @__PURE__ */ new Map();
5605
5648
  settingsTail = Promise.resolve();
5606
5649
  batchSequence = 0;
5607
5650
  closed = false;
5608
- ingestError;
5609
5651
  blockTurnSize;
5610
5652
  blockDecayLambda;
5611
5653
  transientLastFeedbackPromptAt = null;
@@ -5614,20 +5656,89 @@ var StrataGateRuntime = class {
5614
5656
  if (!this.config.ingestSubagents && session.header.origin === "subagent") return;
5615
5657
  const turn = this.folder.accept(session, event);
5616
5658
  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;
5659
+ const key = String(session.id);
5660
+ this.sessionsById.set(key, session);
5661
+ const queued = this.pendingTurns.get(key) ?? [];
5662
+ queued.push(turn);
5663
+ this.pendingTurns.set(key, queued);
5664
+ this.scheduleDrain(session);
5665
+ }
5666
+ /** Serialize drains per session while allowing unrelated sessions to settle independently. */
5667
+ enqueueDrain(session) {
5668
+ const key = String(session.id);
5669
+ const prior = this.drainTails.get(key) ?? Promise.resolve();
5670
+ const next = prior.catch(() => {
5671
+ }).then(() => this.drain(session));
5672
+ this.drainTails.set(key, next);
5673
+ void next.then(() => {
5674
+ if ((this.pendingTurns.get(key)?.length ?? 0) === 0) this.drainErrors.delete(key);
5675
+ }, (error) => {
5676
+ this.drainErrors.set(key, error);
5628
5677
  this.notePluginError(session, error);
5629
5678
  this.onIngestError(error);
5630
5679
  });
5680
+ return next;
5681
+ }
5682
+ /** Batch short bursts and retry failed drains with bounded exponential backoff. */
5683
+ scheduleDrain(session, overrideDelayMs) {
5684
+ const key = String(session.id);
5685
+ if (this.closed) return;
5686
+ const queued = this.pendingTurns.get(key)?.length ?? 0;
5687
+ if (queued === 0) return;
5688
+ const delay = overrideDelayMs ?? (queued >= DRAIN_THRESHOLD ? DRAIN_EAGER_MS : Math.min(this.drainBackoffMs.get(key) ?? DRAIN_BASE_BACKOFF_MS, DRAIN_MAX_BACKOFF_MS));
5689
+ const pending = this.drainTimers.get(key);
5690
+ if (pending) {
5691
+ if (delay >= DRAIN_BASE_BACKOFF_MS) return;
5692
+ clearTimeout(pending);
5693
+ this.drainTimers.delete(key);
5694
+ }
5695
+ const timer = setTimeout(() => {
5696
+ this.drainTimers.delete(key);
5697
+ if (this.closed) return;
5698
+ void this.enqueueDrain(session).then(() => {
5699
+ this.drainBackoffMs.delete(key);
5700
+ if ((this.pendingTurns.get(key)?.length ?? 0) > 0) {
5701
+ this.scheduleDrain(session, DRAIN_BASE_BACKOFF_MS);
5702
+ }
5703
+ }, () => {
5704
+ const backoff = this.drainBackoffMs.get(key) ?? DRAIN_BASE_BACKOFF_MS;
5705
+ this.drainBackoffMs.set(key, Math.min(backoff * 2, DRAIN_MAX_BACKOFF_MS));
5706
+ const pendingRetry = this.drainTimers.get(key);
5707
+ if (pendingRetry) clearTimeout(pendingRetry);
5708
+ this.drainTimers.delete(key);
5709
+ this.scheduleDrain(session);
5710
+ });
5711
+ }, delay);
5712
+ timer.unref?.();
5713
+ this.drainTimers.set(key, timer);
5714
+ }
5715
+ /**
5716
+ * Persist one detached batch. On failure, the failed and unvisited turns are
5717
+ * restored ahead of turns that arrived meanwhile, preserving original order.
5718
+ */
5719
+ async drain(session) {
5720
+ const key = String(session.id);
5721
+ const turns = this.pendingTurns.get(key);
5722
+ if (!turns || turns.length === 0) return;
5723
+ this.pendingTurns.delete(key);
5724
+ let completed = 0;
5725
+ let memory;
5726
+ try {
5727
+ memory = await this.space(session);
5728
+ for (const turn of turns) {
5729
+ const result = await memory.appendTurn(turn, { deferDerivation: true });
5730
+ completed += 1;
5731
+ if (result.sealedBlock) this.scheduleBlockDerivation(session, memory);
5732
+ }
5733
+ } catch (error) {
5734
+ const remaining = turns.slice(completed);
5735
+ if (remaining.length > 0) {
5736
+ this.pendingTurns.set(key, [...remaining, ...this.pendingTurns.get(key) ?? []]);
5737
+ }
5738
+ throw error;
5739
+ } finally {
5740
+ if (memory) await this.persistSuccessfulResponses(memory);
5741
+ }
5631
5742
  }
5632
5743
  async searchEvents(session, query, options = {}) {
5633
5744
  await this.flush();
@@ -5996,9 +6107,18 @@ var StrataGateRuntime = class {
5996
6107
  }
5997
6108
  // Keep the ingestion error for callers that explicitly require a flushed run.
5998
6109
  async settleIngestion() {
5999
- await this.ingestTail;
6000
- const error = this.ingestError;
6001
- this.ingestError = void 0;
6110
+ do {
6111
+ for (const [key, session] of this.sessionsById) {
6112
+ if ((this.pendingTurns.get(key)?.length ?? 0) === 0) continue;
6113
+ const timer = this.drainTimers.get(key);
6114
+ if (timer) clearTimeout(timer);
6115
+ this.drainTimers.delete(key);
6116
+ this.enqueueDrain(session);
6117
+ }
6118
+ await Promise.allSettled(this.drainTails.values());
6119
+ } while (this.drainErrors.size === 0 && [...this.pendingTurns.values()].some((turns) => turns.length > 0));
6120
+ const error = this.drainErrors.values().next().value;
6121
+ this.drainErrors.clear();
6002
6122
  return error;
6003
6123
  }
6004
6124
  async close() {
@@ -6006,6 +6126,8 @@ var StrataGateRuntime = class {
6006
6126
  this.closed = true;
6007
6127
  for (const timer of this.migrationTimers.values()) clearTimeout(timer);
6008
6128
  this.migrationTimers.clear();
6129
+ for (const timer of this.drainTimers.values()) clearTimeout(timer);
6130
+ this.drainTimers.clear();
6009
6131
  for (const timer of this.derivationTimers.values()) clearTimeout(timer);
6010
6132
  this.derivationTimers.clear();
6011
6133
  let flushError;
@@ -6018,6 +6140,7 @@ var StrataGateRuntime = class {
6018
6140
  await Promise.allSettled(this.derivationRuns.values());
6019
6141
  await Promise.allSettled(this.externalImportRuns.values());
6020
6142
  await Promise.all(settled.flatMap((result) => result.status === "fulfilled" ? [result.value.close()] : []));
6143
+ this.sessionsById.clear();
6021
6144
  if (flushError !== void 0) throw flushError;
6022
6145
  }
6023
6146
  namespaceFor(session) {
@@ -6890,8 +7013,9 @@ function renderMessages(messages) {
6890
7013
  }
6891
7014
  function dshTurnAtBlockEnd(session, block) {
6892
7015
  const blockEnd = Date.parse(block.createdAt);
6893
- for (let index = session.events.length - 1; index >= 0; index -= 1) {
6894
- const event = session.events[index];
7016
+ const events = session.snapshotEvents();
7017
+ for (let index = events.length - 1; index >= 0; index -= 1) {
7018
+ const event = events[index];
6895
7019
  if (event?.type === "turn/end" && event.time === blockEnd) return event.data.turn;
6896
7020
  }
6897
7021
  throw new Error(`Cannot match StrataGate block ${block.id} to its completed DSH turn`);
@@ -6901,13 +7025,14 @@ function sealedSurfaceSeqs(session, endTurn, turnCount) {
6901
7025
  const currentSet = new Set(currentSurface);
6902
7026
  const completed = [];
6903
7027
  let open;
6904
- for (const event of session.events) {
7028
+ const events = session.snapshotEvents();
7029
+ for (const event of events) {
6905
7030
  if (event.type === "turn/start") {
6906
7031
  open = { turn: event.data.turn, start: event.seq };
6907
7032
  continue;
6908
7033
  }
6909
7034
  if (event.type !== "turn/end" || !open || event.data.turn !== open.turn) continue;
6910
- const turnEvents = session.events.slice(open.start + 1, event.seq);
7035
+ const turnEvents = events.slice(open.start + 1, event.seq);
6911
7036
  const hasHumanMessage = turnEvents.some((candidate) => candidate.type === "user/message" && candidate.data.source.kind === "user");
6912
7037
  if (hasHumanMessage && event.data.turn <= endTurn) {
6913
7038
  completed.push({
@@ -7026,7 +7151,7 @@ function currentBlockSurfaceMessages(session) {
7026
7151
  const blocks = /* @__PURE__ */ new Map();
7027
7152
  if (!session.surface?.nodes) return blocks;
7028
7153
  for (const seq of session.surface.nodes) {
7029
- const event = session.events[seq];
7154
+ const event = session.eventAt(seq);
7030
7155
  if (event?.type !== "user/message" || event.data.source.kind !== "plugin" || event.data.source.plugin !== COMPACTION_SOURCE_PLUGIN) continue;
7031
7156
  const text3 = event.data.content.flatMap((block) => block.type === "text" ? [block.text] : []).join("\n");
7032
7157
  const blockId = text3.match(/^\[StrataGate conversation block\]\nBlock: ([^\n]+)/u)?.[1] ?? text3.match(/^\[StrataGate compressed conversation\]\nBlock ([^;\n]+);/u)?.[1];
@@ -7126,8 +7251,9 @@ KnowledgeGraph:
7126
7251
  return lines.join("\n");
7127
7252
  }
7128
7253
  function activeTurn(session) {
7129
- for (let index = session.events.length - 1; index >= 0; index -= 1) {
7130
- const event = session.events[index];
7254
+ const events = session.snapshotEvents();
7255
+ for (let index = events.length - 1; index >= 0; index -= 1) {
7256
+ const event = events[index];
7131
7257
  if (event?.type === "turn/start") return event.data.turn;
7132
7258
  }
7133
7259
  return void 0;
@@ -8299,6 +8425,7 @@ function registerAdminRoutes(ctx, runtime) {
8299
8425
  // src/index.ts
8300
8426
  var name = "stratagate-memory";
8301
8427
  var inject = ["tools", "systemPrompt", "llm", "agentDefaultModel"];
8428
+ var STRATAGATE_SETTINGS_NAMESPACE = "stratagate-memory";
8302
8429
  var MEMORY_PROTOCOL = `[StrataGate memory protocol]
8303
8430
  StrataGate provides durable, evidence-gated memory through memory_* tools.
8304
8431
 
@@ -8336,6 +8463,24 @@ async function apply(ctx, config) {
8336
8463
  await ctx.sessions.flush(session);
8337
8464
  }, () => feedbackWebOrigin(ctx));
8338
8465
  await runtime.syncConfiguredSettings();
8466
+ const effortEntry = {
8467
+ structuredReasoningEffort: resolved.structuredReasoningEffort ?? "auto"
8468
+ };
8469
+ let effortSource = () => effortEntry;
8470
+ ctx.inject(["settings"], (settingsCtx) => {
8471
+ settingsCtx.settings.installSection(
8472
+ ctx,
8473
+ STRATAGATE_SETTINGS_NAMESPACE,
8474
+ StructuredReasoningEffortSettings,
8475
+ effortEntry,
8476
+ {
8477
+ setSource: (current) => {
8478
+ effortSource = current;
8479
+ },
8480
+ onChange: () => models.setStructuredReasoningEffort(effortSource().structuredReasoningEffort)
8481
+ }
8482
+ );
8483
+ });
8339
8484
  ctx.systemPrompt.section({ name: "tool:stratagate-memory", order: 113, text: MEMORY_PROTOCOL });
8340
8485
  ctx.systemPrompt.section({ name: "tool:stratagate-feedback", order: 114, text: FEEDBACK_PROTOCOL });
8341
8486
  ctx.on("system-prompt/assemble", async (_assembly, context, next) => {
@@ -8381,6 +8526,7 @@ async function apply(ctx, config) {
8381
8526
  }
8382
8527
  export {
8383
8528
  Config,
8529
+ STRATAGATE_SETTINGS_NAMESPACE,
8384
8530
  apply,
8385
8531
  inject,
8386
8532
  name