mixdog 0.9.86 → 0.9.88

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 (180) hide show
  1. package/package.json +14 -8
  2. package/scripts/tool-failures.mjs +60 -24
  3. package/src/agents/heavy-worker/AGENT.md +3 -4
  4. package/src/agents/worker/AGENT.md +1 -2
  5. package/src/cli.mjs +8 -0
  6. package/src/defaults/cycle3-review-prompt.md +4 -4
  7. package/src/rules/agent/00-core.md +3 -0
  8. package/src/rules/agent/42-cycle3-agent.md +5 -6
  9. package/src/rules/lead/01-general.md +2 -0
  10. package/src/rules/shared/01-tool.md +5 -1
  11. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +3 -51
  12. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +59 -0
  13. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -2
  14. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +67 -0
  15. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +59 -4
  16. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +2 -3
  17. package/src/runtime/agent/orchestrator/config.mjs +29 -11
  18. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -1
  19. package/src/runtime/agent/orchestrator/internal-agents.mjs +1 -1
  20. package/src/runtime/agent/orchestrator/mcp/client.mjs +0 -24
  21. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +3 -3
  22. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +25 -17
  23. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +321 -22
  24. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +35 -22
  25. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +28 -23
  26. package/src/runtime/agent/orchestrator/providers/gemini.mjs +10 -2
  27. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +0 -1
  28. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +0 -1
  29. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +2 -5
  30. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +27 -0
  31. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +23 -0
  32. package/src/runtime/agent/orchestrator/providers/lib/stream-outcome.mjs +329 -0
  33. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +147 -16
  34. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +107 -20
  35. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +4 -3
  36. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +124 -12
  37. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +19 -2
  38. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +1 -1
  40. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +110 -75
  41. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +96 -113
  42. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +144 -10
  43. package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +147 -117
  44. package/src/runtime/agent/orchestrator/session/loop/stop-hooks.mjs +88 -0
  45. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +25 -0
  46. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +47 -6
  48. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +485 -24
  49. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +100 -2
  50. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -0
  51. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +80 -14
  52. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +142 -2
  53. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +128 -32
  54. package/src/runtime/agent/orchestrator/session/manager.mjs +8 -1
  55. package/src/runtime/agent/orchestrator/session/save-session-worker.mjs +77 -4
  56. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +70 -39
  57. package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +91 -0
  58. package/src/runtime/agent/orchestrator/session/store/listing.mjs +141 -64
  59. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +253 -0
  60. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +211 -28
  61. package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +313 -0
  62. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +622 -53
  63. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +43 -10
  64. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +53 -8
  65. package/src/runtime/agent/orchestrator/session/store/summary-rebuild-worker.mjs +20 -0
  66. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +113 -9
  67. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +2 -0
  68. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +476 -52
  69. package/src/runtime/agent/orchestrator/session/store.mjs +721 -196
  70. package/src/runtime/agent/orchestrator/session/token-native.mjs +2 -4
  71. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +19 -0
  72. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +4 -6
  73. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +9 -2
  74. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +146 -90
  75. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +6 -0
  76. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +93 -17
  77. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -0
  78. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +7 -7
  79. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +2 -0
  80. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +52 -0
  81. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +54 -51
  82. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +14 -15
  83. package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +8 -0
  84. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +132 -8
  85. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +67 -3
  86. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +6 -1
  87. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +203 -38
  88. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +13 -1
  89. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +73 -19
  90. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
  91. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +12 -8
  92. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +12 -2
  93. package/src/runtime/agent/orchestrator/tools/token-manifest.json +23 -2
  94. package/src/runtime/media/lanes.mjs +2 -2
  95. package/src/runtime/media/renditions.mjs +101 -17
  96. package/src/runtime/media/renditions.test.mjs +54 -0
  97. package/src/runtime/media/store.mjs +111 -43
  98. package/src/runtime/media/store.test.mjs +53 -11
  99. package/src/runtime/memory/index.mjs +26 -50
  100. package/src/runtime/memory/lib/core-memory-candidates.mjs +2 -7
  101. package/src/runtime/memory/lib/core-memory-store.mjs +2 -9
  102. package/src/runtime/memory/lib/cycle-scheduler.mjs +10 -0
  103. package/src/runtime/memory/lib/embedding-provider.mjs +3 -3
  104. package/src/runtime/memory/lib/embedding-worker.mjs +60 -15
  105. package/src/runtime/memory/lib/http-router.mjs +4 -0
  106. package/src/runtime/memory/lib/ko-morph.mjs +49 -6
  107. package/src/runtime/memory/lib/memory-action-handlers.mjs +25 -19
  108. package/src/runtime/memory/lib/memory-cycle2.mjs +3 -3
  109. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -3
  110. package/src/runtime/memory/lib/memory-recall-store.mjs +24 -0
  111. package/src/runtime/memory/lib/query-handlers.mjs +26 -28
  112. package/src/runtime/memory/tool-defs.mjs +22 -6
  113. package/src/runtime/shared/atomic-file.mjs +5 -1
  114. package/src/runtime/shared/child-guardian.mjs +52 -2
  115. package/src/runtime/shared/provider-api-key.mjs +22 -5
  116. package/src/runtime/shared/tool-execution-contract.mjs +53 -0
  117. package/src/session-runtime/config-helpers.mjs +6 -5
  118. package/src/session-runtime/lifecycle-api.mjs +27 -0
  119. package/src/session-runtime/media-api.mjs +31 -16
  120. package/src/session-runtime/prewarm.mjs +2 -24
  121. package/src/session-runtime/provider-auth-api.mjs +8 -0
  122. package/src/session-runtime/provider-usage.mjs +30 -5
  123. package/src/session-runtime/runtime-core.mjs +298 -494
  124. package/src/session-runtime/runtime-tunables.mjs +0 -3
  125. package/src/session-runtime/session-lifecycle.mjs +372 -0
  126. package/src/session-runtime/session-title.mjs +228 -0
  127. package/src/session-runtime/session-turn-api.mjs +11 -7
  128. package/src/session-runtime/workflow-agents-api.mjs +16 -0
  129. package/src/session-runtime/workflow.mjs +7 -5
  130. package/src/standalone/agent-tool/job-views.mjs +383 -0
  131. package/src/standalone/agent-tool/spawn-flow.mjs +438 -0
  132. package/src/standalone/agent-tool/tag-registry.mjs +340 -0
  133. package/src/standalone/agent-tool/worker-index.mjs +8 -0
  134. package/src/standalone/agent-tool/worker-rows.mjs +2 -1
  135. package/src/standalone/agent-tool.mjs +115 -1166
  136. package/src/standalone/agent-watchdog-registry.mjs +19 -2
  137. package/src/standalone/channel-daemon.mjs +8 -0
  138. package/src/standalone/memory-runtime-proxy.mjs +4 -7
  139. package/src/standalone/projects.mjs +4 -1
  140. package/src/standalone/usage-dashboard.mjs +47 -3
  141. package/src/tui/App.jsx +367 -2579
  142. package/src/tui/app/app-view.jsx +504 -0
  143. package/src/tui/app/create-app-pickers.mjs +313 -0
  144. package/src/tui/app/prompt-submit.mjs +501 -0
  145. package/src/tui/app/resume-picker.mjs +7 -2
  146. package/src/tui/app/shell-layout.mjs +563 -0
  147. package/src/tui/app/slash-dispatch.mjs +13 -3
  148. package/src/tui/app/transcript-row-estimate.mjs +1 -1
  149. package/src/tui/app/transcript-window.mjs +17 -93
  150. package/src/tui/app/usage-context-panels.mjs +288 -0
  151. package/src/tui/app/use-copy-selection.mjs +56 -0
  152. package/src/tui/app/use-global-key-input.mjs +151 -0
  153. package/src/tui/app/use-pasted-buffers.mjs +115 -0
  154. package/src/tui/app/use-prompt-draft-flow.mjs +167 -0
  155. package/src/tui/app/use-prompt-hint.mjs +58 -0
  156. package/src/tui/app/use-prompt-queue-history.mjs +100 -0
  157. package/src/tui/app/use-terminal-chrome.mjs +72 -0
  158. package/src/tui/app/use-transcript-activity.mjs +131 -0
  159. package/src/tui/app/use-transcript-scroll.mjs +32 -2
  160. package/src/tui/app/use-transcript-window.mjs +64 -114
  161. package/src/tui/app/use-welcome-prompt-hint.mjs +96 -0
  162. package/src/tui/components/Markdown.jsx +19 -2
  163. package/src/tui/components/Message.jsx +8 -3
  164. package/src/tui/components/ToolExecution.jsx +7 -3
  165. package/src/tui/dist/index.mjs +9889 -8999
  166. package/src/tui/engine/live-share.mjs +15 -5
  167. package/src/tui/engine/render-timing.mjs +2 -2
  168. package/src/tui/engine/session-api-ext.mjs +97 -4
  169. package/src/tui/engine/session-flow.mjs +3 -1
  170. package/src/tui/engine.mjs +2 -2
  171. package/src/tui/hooks/useSharedTick.mjs +0 -2
  172. package/src/tui/index.jsx +5 -5
  173. package/src/ui/statusline-agents.mjs +44 -11
  174. package/src/vendor/statusline/bin/statusline-route.mjs +26 -3
  175. package/src/workflows/default/WORKFLOW.md +3 -3
  176. package/src/workflows/solo/WORKFLOW.md +1 -1
  177. package/src/runtime/media/index.mjs +0 -18
  178. package/src/runtime/memory/lib/embedding-warmup.mjs +0 -68
  179. package/src/standalone/agent-shard/shard-child.mjs +0 -300
  180. package/src/standalone/agent-shard/shard-pool.mjs +0 -442
@@ -32,6 +32,15 @@ export function createProgressWatchdogRegistry({ mgr }) {
32
32
  const snapshot = typeof mgr.getSessionProgressSnapshot === 'function'
33
33
  ? mgr.getSessionProgressSnapshot(sessionId)
34
34
  : null;
35
+ // Turn boundary: askStartedAt is re-stamped by markSessionAskStart for
36
+ // EVERY turn of one askSession call, including the follow-up turns drained
37
+ // from the pending-message queue. The sweep already reads this snapshot, so
38
+ // observing the change is the cheapest per-turn signal available here.
39
+ const askStartedAt = Number(snapshot?.askStartedAt || 0);
40
+ if (askStartedAt > 0 && askStartedAt !== state.lastAskStartedAt) {
41
+ state.lastAskStartedAt = askStartedAt;
42
+ try { state.onTurnStart?.(askStartedAt); } catch { /* bookkeeping only */ }
43
+ }
35
44
  const abortErr = snapshot ? evaluateAgentWatchdogAbort(snapshot, now, watchdogPolicy) : null;
36
45
  const sess = typeof mgr.getSession === 'function' ? mgr.getSession(sessionId) : null;
37
46
  const iteration = typeof sess?.lastIterationIndex === 'number' ? sess.lastIterationIndex : null;
@@ -79,7 +88,7 @@ export function createProgressWatchdogRegistry({ mgr }) {
79
88
  return {
80
89
  /** Watch a session until the returned handle is stopped; null when the
81
90
  * policy is off or the manager cannot link an abort signal. */
82
- start(sessionId, watchdogPolicy, agent = null) {
91
+ start(sessionId, watchdogPolicy, agent = null, { onTurnStart = null } = {}) {
83
92
  if (!sessionId || !agentWatchdogPolicyActive(watchdogPolicy)) return null;
84
93
  if (typeof mgr.getSessionProgressSnapshot !== 'function'
85
94
  && typeof mgr.getSessionLastProgressAt !== 'function') return null;
@@ -87,7 +96,15 @@ export function createProgressWatchdogRegistry({ mgr }) {
87
96
  const controller = new AbortController();
88
97
  const anchorTs = Date.now();
89
98
  try { mgr.linkParentSignalToSession(sessionId, controller.signal); } catch { return null; }
90
- const state = { sessionId, watchdogPolicy, agent, controller, anchorTs };
99
+ const state = {
100
+ sessionId,
101
+ watchdogPolicy,
102
+ agent,
103
+ controller,
104
+ anchorTs,
105
+ onTurnStart: typeof onTurnStart === 'function' ? onTurnStart : null,
106
+ lastAskStartedAt: 0,
107
+ };
91
108
  watched.set(sessionId, state);
92
109
  ensureTimer();
93
110
  return {
@@ -15,6 +15,14 @@
15
15
  process.env.MIXDOG_CHANNEL_DAEMON = '1';
16
16
  process.env.MIXDOG_WORKER_MODE = process.env.MIXDOG_WORKER_MODE || '1';
17
17
 
18
+ // V8 compile cache: the daemon is a standalone child entry (not via cli.mjs);
19
+ // caching compiled bytecode across restarts removes the channels+memory
20
+ // runtime parse cost from every daemon boot. Best-effort.
21
+ try {
22
+ const { enableCompileCache } = await import('node:module');
23
+ enableCompileCache?.();
24
+ } catch { /* launch-speed optimization only */ }
25
+
18
26
  import os from 'node:os';
19
27
  import path from 'node:path';
20
28
  import { mkdirSync, appendFileSync } from 'node:fs';
@@ -446,13 +446,10 @@ export function createStandaloneMemoryRuntime({
446
446
  MIXDOG_OWNER_LEAD_PID: String(process.pid),
447
447
  MIXDOG_MEMORY_SECONDARY: '0',
448
448
  MIXDOG_PG_ATTACH_ONLY: '0',
449
- // seeds.mjs defaults MIXDOG_EMBED_WARMUP to '0' for the HOST process
450
- // (TUI boot must not pay the ONNX session-create cost). The daemon is
451
- // the long-lived embed owner, so warmup is force-enabled here like the
452
- // secondary/attach flags above — otherwise the daemon boots cold and
453
- // every recall runs lexical-only until the first fallback flush
454
- // lazily loads the model (observed 1.5h '(no results)' windows).
455
- MIXDOG_EMBED_WARMUP: '1',
449
+ // The daemon owns the model, but ownership does not imply eager
450
+ // residency. A recall starts warmup on demand and immediately uses
451
+ // lexical search while it loads, keeping desktop/TUI boot lightweight.
452
+ MIXDOG_EMBED_WARMUP: process.env.MIXDOG_EMBED_WARMUP ?? '0',
456
453
  MIXDOG_MEMORY_DISABLE_CYCLES: process.env.MIXDOG_MEMORY_DISABLE_CYCLES ?? '0',
457
454
  MIXDOG_MEMORY_DISABLE_LLM_WORKER: process.env.MIXDOG_MEMORY_DISABLE_LLM_WORKER ?? '0',
458
455
  MIXDOG_QUIET_SESSION_LOG: process.env.MIXDOG_QUIET_SESSION_LOG ?? '1',
@@ -12,7 +12,9 @@ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSyn
12
12
  import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
13
13
 
14
14
  const MIXDOG_HOME = process.env.MIXDOG_HOME || join(homedir(), '.mixdog');
15
- const PROJECTS_FILE = join(MIXDOG_HOME, 'projects.json');
15
+ const PROJECTS_FILE = process.env.MIXDOG_PROJECTS_FILE
16
+ ? resolve(process.env.MIXDOG_PROJECTS_FILE)
17
+ : join(MIXDOG_HOME, 'projects.json');
16
18
 
17
19
  /** Resolve a raw path to an absolute path against the user's cwd. */
18
20
  function toAbsolute(rawPath) {
@@ -76,6 +78,7 @@ function writeStore(store) {
76
78
  */
77
79
  function ensureProjectIdMarker(absPath, name) {
78
80
  try {
81
+ if (process.env.MIXDOG_DISABLE_PROJECT_MARKERS === '1') return;
79
82
  if (!absPath) return;
80
83
  // Only when the project folder actually exists as a directory on disk.
81
84
  try {
@@ -82,6 +82,35 @@ function normaliseWindows(value, source = 'config') {
82
82
  }).filter(Boolean);
83
83
  }
84
84
 
85
+ function normaliseResetCredits(value) {
86
+ if (!value || typeof value !== 'object') return null;
87
+ const availableCount = num(value.availableCount ?? value.available_count, null);
88
+ const offerRevision = clean(value.offerRevision ?? value.offer_revision);
89
+ if (availableCount === null || !offerRevision) return null;
90
+ const nextExpiresAt = num(value.nextExpiresAt ?? value.next_expires_at, null);
91
+ const rawAvailableCredits = value.availableCredits ?? value.available_credits;
92
+ const availableCredits = (Array.isArray(rawAvailableCredits) ? rawAvailableCredits : [])
93
+ .map((credit) => {
94
+ if (!credit || typeof credit !== 'object' || Array.isArray(credit)) return null;
95
+ const expiresAt = num(credit.expiresAt ?? credit.expires_at, null);
96
+ const grantedAt = num(credit.grantedAt ?? credit.granted_at, null);
97
+ return {
98
+ ...(expiresAt && expiresAt > 0 ? { expiresAt } : {}),
99
+ ...(grantedAt && grantedAt > 0 ? { grantedAt } : {}),
100
+ };
101
+ })
102
+ .filter(Boolean)
103
+ .sort((left, right) =>
104
+ (left.expiresAt ?? Number.POSITIVE_INFINITY)
105
+ - (right.expiresAt ?? Number.POSITIVE_INFINITY));
106
+ return {
107
+ availableCount: Math.max(0, Math.floor(availableCount)),
108
+ offerRevision,
109
+ availableCredits,
110
+ ...(nextExpiresAt && nextExpiresAt > 0 ? { nextExpiresAt } : {}),
111
+ };
112
+ }
113
+
85
114
  function localBudget(providerCfg = {}) {
86
115
  const limitUsd = num(
87
116
  providerCfg.limitUsd ?? providerCfg.limit_usd ?? providerCfg.budgetUsd ?? providerCfg.budget_usd
@@ -221,15 +250,28 @@ function rowTone(row) {
221
250
  async function oauthSnapshot(providerId, { refresh, getProvider, log }) {
222
251
  const cached = readCachedOAuthUsageSnapshot({ provider: providerId, model: '' });
223
252
  if (!refresh && cached) return cached;
253
+ // A rate-limited or failed forced refresh (Anthropic /oauth/usage 429s
254
+ // aggressively) must not blank a known quota row: fall back to the last
255
+ // contentful snapshot — a stale meter with its updatedAt beats an empty row.
256
+ const staleFallback = () => cached
257
+ || readCachedOAuthUsageSnapshot({ provider: providerId, model: '' }, { allowStale: true });
224
258
  if (typeof getProvider === 'function') {
225
259
  try {
226
260
  const providerObj = getProvider(providerId);
227
- if (providerObj) return await fetchOAuthUsageSnapshot({ provider: providerId, model: '' }, providerObj, log);
261
+ if (providerObj) {
262
+ const fresh = await fetchOAuthUsageSnapshot(
263
+ { provider: providerId, model: '' },
264
+ providerObj,
265
+ log,
266
+ { force: refresh === true },
267
+ );
268
+ return fresh || staleFallback();
269
+ }
228
270
  } catch {
229
- return null;
271
+ return staleFallback();
230
272
  }
231
273
  }
232
- return cached;
274
+ return staleFallback();
233
275
  }
234
276
 
235
277
  async function apiSnapshot(providerId, { refresh } = {}) {
@@ -544,7 +586,9 @@ export async function createUsageDashboard(config = {}, options = {}) {
544
586
  const snapshot = await oauthSnapshot(item.id, options);
545
587
  const known = snapshotRemaining(snapshot);
546
588
  const windows = normaliseWindows(snapshot?.quotaWindows, clean(snapshot?.source) || 'provider-api');
589
+ const resetCredits = normaliseResetCredits(snapshot?.resetCredits);
547
590
  row.windows = windows;
591
+ if (resetCredits) row.resetCredits = resetCredits;
548
592
  row.updatedAt = num(snapshot?.cachedAt, null);
549
593
  if (known) {
550
594
  applyKnownRemaining(row, known, { estimated: false });