stratagate-dsh 0.2.64 → 0.2.66

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
@@ -17,7 +17,9 @@ interface Config {
17
17
  maxOutputTokens?: number;
18
18
  structuredTaskTimeoutMs?: number;
19
19
  structuredReasoningEffort?: StructuredReasoningEffortMode;
20
+ showStrataGateStatus?: boolean;
20
21
  showShortTermStatus?: boolean;
22
+ showRetrievalStatus?: boolean;
21
23
  }
22
24
  declare const Config: z__default<Config>;
23
25
 
package/dist/plugin.d.ts CHANGED
@@ -17,7 +17,9 @@ interface Config {
17
17
  maxOutputTokens?: number;
18
18
  structuredTaskTimeoutMs?: number;
19
19
  structuredReasoningEffort?: StructuredReasoningEffortMode;
20
+ showStrataGateStatus?: boolean;
20
21
  showShortTermStatus?: boolean;
22
+ showRetrievalStatus?: boolean;
21
23
  }
22
24
  declare const Config: z__default<Config>;
23
25
 
package/dist/plugin.js CHANGED
@@ -7,7 +7,9 @@ import { createUserMessage as createUserMessage3 } from "@deepseek-ai/dsh-llm";
7
7
  import z from "@deepseek-ai/schemastery";
8
8
  var StructuredReasoningEffortSettings = z.object({
9
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
- showShortTermStatus: z.boolean().default(true).description("\u5728\u804A\u5929\u5185\u5BB9\u4E2D\u663E\u793A\u77ED\u671F\u8BB0\u5FC6\u72B6\u6001").comment("\u5173\u95ED\u540E\u4E0D\u518D\u663E\u793A\u6BCF\u8F6E\u56DE\u7B54\u4E0B\u65B9\u7684\u77ED\u671F\u8BB0\u5FC6\u72B6\u6001\u884C\uFF1B\u8BB0\u5FC6\u91C7\u96C6\u4E0E\u5904\u7406\u4E0D\u53D7\u5F71\u54CD\u3002")
10
+ showStrataGateStatus: z.boolean().default(true).description("\u663E\u793A StrataGate \u72B6\u6001\u63D0\u793A").comment("\u5173\u95ED\u540E\u9690\u85CF\u804A\u5929\u754C\u9762\u4E2D\u7684 StrataGate \u72B6\u6001\u4FE1\u606F\uFF1B\u8BB0\u5FC6\u3001\u68C0\u7D22\u548C\u540E\u53F0\u5904\u7406\u4E0D\u53D7\u5F71\u54CD\u3002"),
11
+ showShortTermStatus: z.boolean().default(true).description("\u663E\u793A\u77ED\u671F\u8BB0\u5FC6\u5757\u72B6\u6001").comment("\u63A7\u5236\u77ED\u671F\u8BB0\u5FC6\u5757\u8FDB\u5EA6\u3001Block \u5C01\u5B58\u4E0E\u6574\u7406\u72B6\u6001\u7684\u804A\u5929\u5185\u63D0\u793A\uFF1B\u540E\u53F0\u5904\u7406\u4E0D\u53D7\u5F71\u54CD\u3002"),
12
+ showRetrievalStatus: z.boolean().default(true).description("\u663E\u793A\u8BB0\u5FC6\u68C0\u7D22\u72B6\u6001").comment("\u63A7\u5236\u68C0\u7D22\u6B21\u6570\u3001\u8FD4\u56DE\u6570\u91CF\u548C\u8BB0\u5FC6\u91C7\u7528\u4FE1\u606F\u7684\u804A\u5929\u5185\u63D0\u793A\uFF1B\u68C0\u7D22\u4E0E\u91C7\u7528\u4E0D\u53D7\u5F71\u54CD\u3002")
11
13
  });
12
14
  var Config = z.object({
13
15
  database: z.string().required(),
@@ -23,7 +25,9 @@ var Config = z.object({
23
25
  maxOutputTokens: z.natural().min(256).default(2048),
24
26
  structuredTaskTimeoutMs: z.natural().min(1e3).default(12e4),
25
27
  structuredReasoningEffort: z.union(["auto", "force-off"]).default("auto"),
26
- showShortTermStatus: z.boolean().default(true)
28
+ showStrataGateStatus: z.boolean().default(true),
29
+ showShortTermStatus: z.boolean().default(true),
30
+ showRetrievalStatus: z.boolean().default(true)
27
31
  });
28
32
  function resolveConfig(config) {
29
33
  const database = config.database?.trim() ?? "";
@@ -49,7 +53,9 @@ function resolveConfig(config) {
49
53
  maxOutputTokens: Math.max(256, Math.floor(config.maxOutputTokens ?? 2048)),
50
54
  structuredTaskTimeoutMs: Math.max(1e3, Math.floor(config.structuredTaskTimeoutMs ?? 12e4)),
51
55
  structuredReasoningEffort: config.structuredReasoningEffort ?? "auto",
52
- showShortTermStatus: config.showShortTermStatus ?? true
56
+ showStrataGateStatus: config.showStrataGateStatus ?? true,
57
+ showShortTermStatus: config.showShortTermStatus ?? true,
58
+ showRetrievalStatus: config.showRetrievalStatus ?? true
53
59
  };
54
60
  }
55
61
 
@@ -5706,6 +5712,8 @@ var DRAIN_THRESHOLD = 3;
5706
5712
  var DRAIN_EAGER_MS = 150;
5707
5713
  var DRAIN_BASE_BACKOFF_MS = 2e3;
5708
5714
  var DRAIN_MAX_BACKOFF_MS = 6e4;
5715
+ var BACKGROUND_WORKER_INITIAL_DELAY_MS = 250;
5716
+ var BACKGROUND_WORKER_INTERVAL_MS = 3e3;
5709
5717
  var AUTO_EVENT_LIMIT = 4;
5710
5718
  var AUTO_ELEMENT_LIMIT = 4;
5711
5719
  var AUTO_MEMORY_TOKEN_BUDGET = 900;
@@ -5758,6 +5766,7 @@ var StrataGateRuntime = class {
5758
5766
  this.feedbackOrigin = feedbackOrigin;
5759
5767
  this.blockTurnSize = config.blockTurnSize;
5760
5768
  this.blockDecayLambda = config.blockDecayLambda;
5769
+ this.scheduleBackgroundWorker(BACKGROUND_WORKER_INITIAL_DELAY_MS);
5761
5770
  }
5762
5771
  config;
5763
5772
  models;
@@ -5785,12 +5794,93 @@ var StrataGateRuntime = class {
5785
5794
  drainTails = /* @__PURE__ */ new Map();
5786
5795
  drainErrors = /* @__PURE__ */ new Map();
5787
5796
  sessionsById = /* @__PURE__ */ new Map();
5797
+ backgroundNamespaceRuns = /* @__PURE__ */ new Map();
5798
+ backgroundWorkerTimer;
5799
+ backgroundWorkerRun;
5788
5800
  settingsTail = Promise.resolve();
5789
5801
  batchSequence = 0;
5790
5802
  closed = false;
5791
5803
  blockTurnSize;
5792
5804
  blockDecayLambda;
5793
5805
  transientLastFeedbackPromptAt = null;
5806
+ /** Keep durable model jobs moving even when no host session emits events. */
5807
+ scheduleBackgroundWorker(delayMs) {
5808
+ if (this.closed || this.backgroundWorkerTimer || this.backgroundWorkerRun) return;
5809
+ this.backgroundWorkerTimer = setTimeout(() => {
5810
+ this.backgroundWorkerTimer = void 0;
5811
+ const run = this.runBackgroundWorker();
5812
+ this.backgroundWorkerRun = run;
5813
+ void run.finally(() => {
5814
+ if (this.backgroundWorkerRun === run) this.backgroundWorkerRun = void 0;
5815
+ this.scheduleBackgroundWorker(BACKGROUND_WORKER_INTERVAL_MS);
5816
+ });
5817
+ }, delayMs);
5818
+ this.backgroundWorkerTimer.unref?.();
5819
+ }
5820
+ async runBackgroundWorker() {
5821
+ if (this.closed) return;
5822
+ let namespaces;
5823
+ try {
5824
+ namespaces = await this.adminNamespaces();
5825
+ } catch (error) {
5826
+ this.onIngestError(error);
5827
+ return;
5828
+ }
5829
+ for (const namespace of namespaces) {
5830
+ if (this.closed) return;
5831
+ try {
5832
+ await this.runBackgroundNamespace(namespace);
5833
+ } catch (error) {
5834
+ this.onIngestError(error);
5835
+ }
5836
+ }
5837
+ }
5838
+ async runBackgroundNamespace(namespace) {
5839
+ const existing = this.backgroundNamespaceRuns.get(namespace);
5840
+ if (existing) return existing;
5841
+ const run = (async () => {
5842
+ const active = this.spaces.get(namespace);
5843
+ const memory = active ? await active : (await this.openAdminMemory(namespace, { derivation: true })).memory;
5844
+ const owned = !active;
5845
+ try {
5846
+ const runnable = [
5847
+ ...memory.listSummaryJobs(),
5848
+ ...memory.listExtractionJobs()
5849
+ ].some((job) => job.status === "pending" || job.status === "failed" && job.nextRetryAt !== null && Date.parse(job.nextRetryAt) <= Date.now()) || memory.listGraphProjectionJobs().some(({ status }) => status === "pending");
5850
+ if (!runnable) return;
5851
+ const hasActiveSessionWork = [
5852
+ ...this.derivationTimers.keys(),
5853
+ ...this.derivationRuns.keys(),
5854
+ ...this.migrationTimers.keys()
5855
+ ].some((key) => key.startsWith(`${namespace}\0`) || key === namespace);
5856
+ if (hasActiveSessionWork) return;
5857
+ const resumed = await this.models.runDetached(
5858
+ `stratagate-worker:${namespace}`,
5859
+ () => memory.resumePendingWork()
5860
+ );
5861
+ await this.persistSuccessfulResponses(memory);
5862
+ const sessionsToSync = /* @__PURE__ */ new Set();
5863
+ for (const block of resumed.readyBlocks) {
5864
+ if (!block.threadId) continue;
5865
+ this.pendingSurfaceSync.set(
5866
+ `${namespace}\0${block.id}`,
5867
+ { namespace, blockId: block.id }
5868
+ );
5869
+ const session = this.knownSessions.get(block.threadId)?.deref();
5870
+ if (session && this.namespaceFor(session) === namespace) sessionsToSync.add(session);
5871
+ }
5872
+ for (const session of sessionsToSync) await this.syncPendingRetrySurface(session, memory);
5873
+ } finally {
5874
+ if (owned) await memory.close();
5875
+ }
5876
+ })();
5877
+ this.backgroundNamespaceRuns.set(namespace, run);
5878
+ try {
5879
+ await run;
5880
+ } finally {
5881
+ if (this.backgroundNamespaceRuns.get(namespace) === run) this.backgroundNamespaceRuns.delete(namespace);
5882
+ }
5883
+ }
5794
5884
  acceptEvent(session, event) {
5795
5885
  if (this.closed) return;
5796
5886
  if (!this.config.ingestSubagents && session.header.origin === "subagent") return;
@@ -6264,6 +6354,8 @@ var StrataGateRuntime = class {
6264
6354
  async close() {
6265
6355
  if (this.closed) return;
6266
6356
  this.closed = true;
6357
+ if (this.backgroundWorkerTimer) clearTimeout(this.backgroundWorkerTimer);
6358
+ this.backgroundWorkerTimer = void 0;
6267
6359
  for (const timer of this.migrationTimers.values()) clearTimeout(timer);
6268
6360
  this.migrationTimers.clear();
6269
6361
  for (const timer of this.drainTimers.values()) clearTimeout(timer);
@@ -6277,6 +6369,8 @@ var StrataGateRuntime = class {
6277
6369
  flushError = error;
6278
6370
  }
6279
6371
  const settled = await Promise.allSettled(this.spaces.values());
6372
+ await Promise.allSettled(this.backgroundNamespaceRuns.values());
6373
+ if (this.backgroundWorkerRun) await Promise.allSettled([this.backgroundWorkerRun]);
6280
6374
  await Promise.allSettled(this.derivationRuns.values());
6281
6375
  await Promise.allSettled(this.externalImportRuns.values());
6282
6376
  await Promise.all(settled.flatMap((result) => result.status === "fulfilled" ? [result.value.close()] : []));
@@ -7710,9 +7804,17 @@ function clusterKnowledgeGraph(rawNodes, rawEdges) {
7710
7804
  }
7711
7805
 
7712
7806
  // src/web.ts
7713
- var STRATAGATE_DSH_VERSION = "0.2.64";
7714
7807
  var LEGACY_THREAD_ID = "__legacy__";
7715
7808
  var nodeRequire2 = createRequire2(import.meta.url);
7809
+ function currentPluginVersion() {
7810
+ try {
7811
+ const value = nodeRequire2("../package.json");
7812
+ if (typeof value.version === "string" && value.version.trim()) return value.version;
7813
+ } catch {
7814
+ }
7815
+ return "unknown";
7816
+ }
7817
+ var STRATAGATE_DSH_VERSION = currentPluginVersion();
7716
7818
  function installedPackageVersion(names) {
7717
7819
  for (const name2 of names) {
7718
7820
  try {
@@ -7853,80 +7955,80 @@ async function overview(runtime, cachedEntries) {
7853
7955
  const rows = [];
7854
7956
  for (const { namespace, snapshot } of entries) {
7855
7957
  if (!snapshot) continue;
7958
+ const recovered = recoverSnapshotView(snapshot);
7959
+ const blockDetails = (blockIds) => [...new Set(blockIds)].flatMap((blockId) => {
7960
+ const displayBlocks = recovered.blocks.filter(({ source, threadId }) => source.id === blockId && threadId !== LEGACY_THREAD_ID);
7961
+ if (displayBlocks.length > 0) {
7962
+ return displayBlocks.map((block2) => ({
7963
+ id: block2.id,
7964
+ sourceId: block2.source.id,
7965
+ sequence: block2.source.sequence,
7966
+ title: block2.source.l0Title ?? null,
7967
+ threadId: block2.threadId,
7968
+ turnRange: block2.turnRange,
7969
+ shouldExtract: block2.source.shouldExtract ?? null
7970
+ }));
7971
+ }
7972
+ const block = snapshot.blocks.find(({ id }) => id === blockId);
7973
+ return block ? [{
7974
+ id: block.id,
7975
+ sourceId: block.id,
7976
+ sequence: block.sequence,
7977
+ title: block.l0Title ?? null,
7978
+ threadId: block.threadId ?? null,
7979
+ turnRange: [block.startTurn, block.endTurn],
7980
+ shouldExtract: block.shouldExtract ?? null
7981
+ }] : [];
7982
+ });
7983
+ const describeBlockJob = (kind, job) => {
7984
+ const details = blockDetails([job.blockId]);
7985
+ return {
7986
+ id: job.blockId,
7987
+ kind,
7988
+ status: job.status,
7989
+ attempts: job.attempts,
7990
+ nextRetryAt: job.nextRetryAt,
7991
+ lastError: job.lastError?.slice(0, 500) ?? null,
7992
+ lastErrorFull: job.lastError,
7993
+ updatedAt: job.updatedAt,
7994
+ threadId: details[0]?.threadId ?? null,
7995
+ blockIds: [job.blockId],
7996
+ threadIds: [...new Set(details.flatMap(({ threadId }) => threadId ?? []))],
7997
+ blockDetails: details,
7998
+ sequence: details[0]?.sequence ?? null,
7999
+ turnRange: details[0]?.turnRange ?? null
8000
+ };
8001
+ };
8002
+ const describeGraphJob = (job) => {
8003
+ const blockIds = [...new Set(job.sourceEventIds.flatMap((eventId) => snapshot.events.find(({ id }) => id === eventId)?.sourceBlockId ?? []))];
8004
+ const details = blockDetails(blockIds);
8005
+ return {
8006
+ id: job.id,
8007
+ kind: "graph-projection",
8008
+ status: job.status,
8009
+ attempts: job.attempts,
8010
+ nextRetryAt: null,
8011
+ lastError: job.lastError?.slice(0, 500) ?? null,
8012
+ lastErrorFull: job.lastError,
8013
+ updatedAt: job.updatedAt,
8014
+ blockIds,
8015
+ threadIds: [...new Set(details.flatMap(({ threadId }) => threadId ?? []))],
8016
+ blockDetails: details,
8017
+ sequences: details.map(({ sequence }) => sequence),
8018
+ sourceEventIds: job.sourceEventIds
8019
+ };
8020
+ };
7856
8021
  const failedJobs = snapshot.summaryJobs.filter(({ status }) => status === "failed").length + snapshot.extractionJobs.filter(({ status }) => status === "failed").length + snapshot.graphProjectionJobs.filter(({ status }) => status === "failed").length;
7857
8022
  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;
7858
8023
  const failedJobDetails = [
7859
- ...snapshot.summaryJobs.filter(({ status }) => status === "failed").map((job) => {
7860
- const block = snapshot.blocks.find(({ id }) => id === job.blockId);
7861
- return {
7862
- id: job.blockId,
7863
- kind: "block-summary",
7864
- attempts: job.attempts,
7865
- nextRetryAt: job.nextRetryAt,
7866
- lastError: job.lastError?.slice(0, 500) ?? null,
7867
- lastErrorFull: job.lastError,
7868
- updatedAt: job.updatedAt,
7869
- threadId: block?.threadId ?? null,
7870
- blockIds: [job.blockId],
7871
- threadIds: block?.threadId ? [block.threadId] : [],
7872
- blockDetails: block ? [{
7873
- id: block.id,
7874
- sequence: block.sequence,
7875
- title: block.l0Title ?? null,
7876
- threadId: block.threadId ?? null,
7877
- turnRange: [block.startTurn, block.endTurn]
7878
- }] : [],
7879
- sequence: block?.sequence ?? null,
7880
- turnRange: block ? [block.startTurn, block.endTurn] : null
7881
- };
7882
- }),
7883
- ...snapshot.extractionJobs.filter(({ status }) => status === "failed").map((job) => {
7884
- const block = snapshot.blocks.find(({ id }) => id === job.blockId);
7885
- return {
7886
- id: job.blockId,
7887
- kind: "event-extraction",
7888
- attempts: job.attempts,
7889
- nextRetryAt: job.nextRetryAt,
7890
- lastError: job.lastError?.slice(0, 500) ?? null,
7891
- lastErrorFull: job.lastError,
7892
- updatedAt: job.updatedAt,
7893
- blockIds: [job.blockId],
7894
- threadIds: block?.threadId ? [block.threadId] : [],
7895
- blockDetails: block ? [{
7896
- id: block.id,
7897
- sequence: block.sequence,
7898
- title: block.l0Title ?? null,
7899
- threadId: block.threadId ?? null,
7900
- turnRange: [block.startTurn, block.endTurn]
7901
- }] : [],
7902
- sequence: block?.sequence ?? null,
7903
- turnRange: block ? [block.startTurn, block.endTurn] : null
7904
- };
7905
- }),
7906
- ...snapshot.graphProjectionJobs.filter(({ status }) => status === "failed").map((job) => {
7907
- const blockIds = [...new Set(job.sourceEventIds.flatMap((eventId) => snapshot.events.find(({ id }) => id === eventId)?.sourceBlockId ?? []))];
7908
- const blocks = blockIds.flatMap((blockId) => snapshot.blocks.find(({ id }) => id === blockId) ?? []);
7909
- return {
7910
- id: job.id,
7911
- kind: "graph-projection",
7912
- attempts: job.attempts,
7913
- nextRetryAt: null,
7914
- lastError: job.lastError?.slice(0, 500) ?? null,
7915
- lastErrorFull: job.lastError,
7916
- updatedAt: job.updatedAt,
7917
- blockIds,
7918
- threadIds: [...new Set(blocks.flatMap(({ threadId }) => threadId ?? []))],
7919
- blockDetails: blocks.map((block) => ({
7920
- id: block.id,
7921
- sequence: block.sequence,
7922
- title: block.l0Title ?? null,
7923
- threadId: block.threadId ?? null,
7924
- turnRange: [block.startTurn, block.endTurn]
7925
- })),
7926
- sequences: blocks.map(({ sequence }) => sequence),
7927
- sourceEventIds: job.sourceEventIds
7928
- };
7929
- })
8024
+ ...snapshot.summaryJobs.filter(({ status }) => status === "failed").map((job) => describeBlockJob("block-summary", job)),
8025
+ ...snapshot.extractionJobs.filter(({ status }) => status === "failed").map((job) => describeBlockJob("event-extraction", job)),
8026
+ ...snapshot.graphProjectionJobs.filter(({ status }) => status === "failed").map(describeGraphJob)
8027
+ ];
8028
+ const processingJobDetails = [
8029
+ ...snapshot.summaryJobs.filter(({ status, nextRetryAt }) => status === "pending" || status === "running" || status === "failed" && nextRetryAt !== null).map((job) => describeBlockJob("block-summary", job)),
8030
+ ...snapshot.extractionJobs.filter(({ status, nextRetryAt }) => status === "running" || status === "failed" && nextRetryAt !== null).map((job) => describeBlockJob("event-extraction", job)),
8031
+ ...snapshot.graphProjectionJobs.filter(({ status }) => status === "pending" || status === "running").map(describeGraphJob)
7930
8032
  ];
7931
8033
  const timestamps = [
7932
8034
  ...snapshot.blocks.map(({ createdAt }) => createdAt),
@@ -7961,6 +8063,7 @@ async function overview(runtime, cachedEntries) {
7961
8063
  failedJobs,
7962
8064
  processingJobs,
7963
8065
  failedJobDetails,
8066
+ processingJobDetails,
7964
8067
  successfulModelResponses: snapshot.successfulModelResponses ?? [],
7965
8068
  lastActivityAt: timestamps.at(-1) ?? null
7966
8069
  });
@@ -8539,7 +8642,7 @@ async function dashboard(runtime, url, ifNoneMatch) {
8539
8642
  const selected = entries.find(({ namespace }) => namespace === requestedNamespace) ?? entries[0];
8540
8643
  const threadId = url.searchParams.get("threadId")?.trim() ?? "";
8541
8644
  const revisionKey = entries.map(({ namespace, revision }) => `${namespace}:${revision}`).join("|");
8542
- const etag = `"${createHash2("sha256").update(`${revisionKey}\0${selected?.namespace ?? ""}\0${threadId}`).digest("base64url").slice(0, 24)}"`;
8645
+ const etag = `"${createHash2("sha256").update(`${STRATAGATE_DSH_VERSION}\0${revisionKey}\0${selected?.namespace ?? ""}\0${threadId}`).digest("base64url").slice(0, 24)}"`;
8543
8646
  if (ifNoneMatch.split(",").map((value) => value.trim()).includes(etag)) return { etag, notModified: true };
8544
8647
  const overviewValue = await overview(runtime, entries);
8545
8648
  if (!selected) {
@@ -9016,7 +9119,9 @@ async function apply(ctx, config) {
9016
9119
  await runtime.syncConfiguredSettings();
9017
9120
  const effortEntry = {
9018
9121
  structuredReasoningEffort: resolved.structuredReasoningEffort ?? "auto",
9019
- showShortTermStatus: resolved.showShortTermStatus ?? true
9122
+ showStrataGateStatus: resolved.showStrataGateStatus ?? true,
9123
+ showShortTermStatus: resolved.showShortTermStatus ?? true,
9124
+ showRetrievalStatus: resolved.showRetrievalStatus ?? true
9020
9125
  };
9021
9126
  let effortSource = () => effortEntry;
9022
9127
  ctx.inject(["settings"], (settingsCtx) => {