mixdog 1.0.4 → 1.0.6

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 (158) hide show
  1. package/README.md +117 -434
  2. package/package.json +18 -17
  3. package/scripts/gate-local.mjs +93 -0
  4. package/scripts/lib/gate-scheduler.mjs +24 -0
  5. package/scripts/lib/run-node-tests.mjs +5 -1
  6. package/scripts/release-paths.mjs +22 -1
  7. package/scripts/test-direct.mjs +2 -1
  8. package/scripts/test.mjs +3 -1
  9. package/src/defaults/agents.json +0 -12
  10. package/src/defaults/mixdog-config.template.json +1 -2
  11. package/src/defaults/skills/docx/SKILL.md +7 -4
  12. package/src/defaults/skills/docx/references/html.md +46 -0
  13. package/src/defaults/skills/docx/references/native-authoring.md +18 -4
  14. package/src/defaults/skills/pdf/SKILL.md +10 -2
  15. package/src/defaults/skills/pdf/references/html.md +64 -0
  16. package/src/defaults/skills/pptx/references/html.md +1 -1
  17. package/src/defaults/skills/xlsx/SKILL.md +7 -3
  18. package/src/defaults/skills/xlsx/references/html.md +40 -0
  19. package/src/defaults/skills/xlsx/references/report-design.md +21 -4
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -1
  21. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  22. package/src/runtime/agent/orchestrator/internal-agents.mjs +1 -1
  23. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +6 -5
  24. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +6 -3
  25. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +6 -2
  26. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +50 -9
  27. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +28 -1
  28. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +4 -1
  29. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +34 -8
  30. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +10 -2
  31. package/src/runtime/agent/orchestrator/session/transcript-restore/agent-envelope.mjs +1 -1
  32. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +1 -0
  33. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +3 -1
  34. package/src/runtime/attachments/pdf-extract.mjs +7 -3
  35. package/src/runtime/computer-bridge/client.mjs +1 -1
  36. package/src/runtime/memory/index.mjs +4 -15
  37. package/src/runtime/memory/lib/core-memory-store.mjs +1 -1
  38. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +0 -2
  39. package/src/runtime/memory/lib/cycle-scheduler/backlog-probe.mjs +3 -11
  40. package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +1 -3
  41. package/src/runtime/memory/lib/cycle-scheduler/scheduled-enqueue.mjs +1 -11
  42. package/src/runtime/memory/lib/cycle-scheduler/tick-loop.mjs +0 -2
  43. package/src/runtime/memory/lib/cycle-scheduler.mjs +4 -13
  44. package/src/runtime/memory/lib/cycle-signatures.mjs +0 -6
  45. package/src/runtime/memory/lib/cycle1/cycle1-rows.mjs +1 -1
  46. package/src/runtime/memory/lib/cycle1/cycle1-window.mjs +2 -2
  47. package/src/runtime/memory/lib/embedding-reindex.mjs +1 -1
  48. package/src/runtime/memory/lib/http-router/lifecycle-routes.mjs +0 -1
  49. package/src/runtime/memory/lib/memory-action-handlers/cycle-actions.mjs +7 -52
  50. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +6 -22
  51. package/src/runtime/memory/lib/memory-action-handlers/cycle-rebuild-action.mjs +10 -17
  52. package/src/runtime/memory/lib/memory-action-handlers/maintenance-actions.mjs +1 -3
  53. package/src/runtime/memory/lib/memory-action-handlers/manage-actions.mjs +2 -4
  54. package/src/runtime/memory/lib/memory-action-handlers.mjs +2 -4
  55. package/src/runtime/memory/lib/memory-cycle-requests.mjs +1 -1
  56. package/src/runtime/memory/lib/{memory-cycle2-shared.mjs → memory-cycle-shared.mjs} +14 -16
  57. package/src/runtime/memory/lib/memory-cycle.mjs +3 -2
  58. package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
  59. package/src/runtime/memory/lib/memory-embed.mjs +14 -1
  60. package/src/runtime/memory/lib/memory-ops-policy.mjs +8 -11
  61. package/src/runtime/memory/lib/memory-schema/entries.mjs +0 -1
  62. package/src/runtime/memory/lib/memory.mjs +2 -6
  63. package/src/runtime/memory/lib/pg/adapter.mjs +1 -1
  64. package/src/runtime/memory/lib/query-handlers.mjs +1 -1
  65. package/src/runtime/memory/lib/query-hybrid-search.mjs +1 -1
  66. package/src/runtime/memory/lib/query-maintenance-handlers.mjs +0 -6
  67. package/src/runtime/memory/lib/recall-format.mjs +1 -1
  68. package/src/runtime/office/authoring/docx-html-build.mjs +375 -0
  69. package/src/runtime/office/authoring/docx-html-measure.mjs +561 -0
  70. package/src/runtime/office/authoring/docx-html-runner.mjs +102 -0
  71. package/src/runtime/office/authoring/html-browser.mjs +61 -0
  72. package/src/runtime/office/authoring/html-document-author.mjs +181 -0
  73. package/src/runtime/office/authoring/html-source-drift.mjs +36 -0
  74. package/src/runtime/office/authoring/pdf-author-action.mjs +266 -0
  75. package/src/runtime/office/authoring/pdf-html-charts.mjs +379 -0
  76. package/src/runtime/office/authoring/pdf-html-runner.mjs +317 -0
  77. package/src/runtime/office/authoring/pptx-author-action.mjs +44 -22
  78. package/src/runtime/office/authoring/pptx-author-session.mjs +12 -6
  79. package/src/runtime/office/authoring/pptx-html-build.mjs +23 -4
  80. package/src/runtime/office/authoring/pptx-html-measure.mjs +38 -44
  81. package/src/runtime/office/authoring/pptx-html-runner.mjs +18 -3
  82. package/src/runtime/office/authoring/pptx-receipt.mjs +39 -31
  83. package/src/runtime/office/authoring/pptx-review-artifacts.mjs +9 -1
  84. package/src/runtime/office/authoring/pptx-script-runner.mjs +6 -2
  85. package/src/runtime/office/authoring/xlsx-html-build.mjs +365 -0
  86. package/src/runtime/office/authoring/xlsx-html-measure.mjs +257 -0
  87. package/src/runtime/office/authoring/xlsx-html-runner.mjs +139 -0
  88. package/src/runtime/office/capabilities-catalog.mjs +8 -0
  89. package/src/runtime/office/capabilities-signatures.mjs +18 -6
  90. package/src/runtime/office/capabilities.mjs +26 -1
  91. package/src/runtime/office/com/office-com-host.ps1 +133 -17
  92. package/src/runtime/office/core/office-actions-batch.mjs +16 -1
  93. package/src/runtime/office/core/office-actions-inspect.mjs +3 -2
  94. package/src/runtime/office/core/office-actions-lifecycle.mjs +6 -2
  95. package/src/runtime/office/core/office-actions-render.mjs +4 -1
  96. package/src/runtime/office/core/office-finalize/review-stage.mjs +15 -2
  97. package/src/runtime/office/core/office-qa/design-review-stage.mjs +9 -0
  98. package/src/runtime/office/core/office-session-dispatch.mjs +3 -0
  99. package/src/runtime/office/core/office-sessionless-actions.mjs +7 -2
  100. package/src/runtime/office/core/office-sessions.mjs +19 -2
  101. package/src/runtime/office/pdf/pdf-writer.mjs +1 -1
  102. package/src/runtime/office/portable/docx-formatting.mjs +21 -0
  103. package/src/runtime/office/portable/ooxml-validator.mjs +22 -1
  104. package/src/runtime/office/portable/portable-chart.mjs +10 -2
  105. package/src/runtime/office/portable/portable-docx-edits.mjs +12 -6
  106. package/src/runtime/office/portable/portable-docx-operations.mjs +75 -1
  107. package/src/runtime/office/portable/portable-docx-xml.mjs +49 -6
  108. package/src/runtime/office/portable/portable-docx.mjs +2 -0
  109. package/src/runtime/office/portable/portable-snapshot-docx.mjs +2 -0
  110. package/src/runtime/office/portable/portable-validation.mjs +6 -3
  111. package/src/runtime/office/portable/portable-xlsx-cell-values.mjs +87 -0
  112. package/src/runtime/office/portable/portable-xlsx-charts.mjs +55 -13
  113. package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +1 -1
  114. package/src/runtime/office/portable/xlsx-contract.mjs +82 -3
  115. package/src/runtime/office/quality/assurance-checklist.mjs +2 -2
  116. package/src/runtime/office/quality/assurance-rendered.mjs +27 -1
  117. package/src/runtime/office/quality/assurance-structure-docx.mjs +44 -0
  118. package/src/runtime/office/quality/assurance-structure-pptx.mjs +24 -6
  119. package/src/runtime/office/quality/assurance-structure-xlsx.mjs +50 -1
  120. package/src/runtime/office/quality/design-review-critique.mjs +3 -2
  121. package/src/runtime/office/quality/document-brief.mjs +44 -0
  122. package/src/runtime/office/quality/inline-audit.mjs +7 -2
  123. package/src/runtime/office/quality/quality-pipeline.mjs +9 -1
  124. package/src/runtime/office/tool-defs.mjs +2 -2
  125. package/src/runtime/shared/llm/quota-value-estimate.mjs +317 -0
  126. package/src/runtime/shared/llm/usage-ledger-quota.mjs +266 -63
  127. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +35 -0
  128. package/src/runtime/shared/llm/usage-ledger-worker.mjs +2 -1
  129. package/src/runtime/shared/llm/usage-ledger.mjs +10 -1
  130. package/src/runtime/shared/provider-accounts.mjs +11 -1
  131. package/src/runtime/shared/statusline/statusline-segments.mjs +4 -4
  132. package/src/runtime/shared/tool-execution-contract.mjs +1 -1
  133. package/src/runtime/shared/wait-until.test-support.mjs +33 -0
  134. package/src/session-runtime/boot/apis.mjs +1 -1
  135. package/src/session-runtime/services/channel-admin.mjs +4 -3
  136. package/src/session-runtime/services/usage-stats-model.mjs +22 -0
  137. package/src/session-runtime/setup-tool/settings-contract.mjs +6 -1
  138. package/src/session-runtime/turn/session-ops.mjs +15 -9
  139. package/src/session-runtime/usage-stats-api.mjs +34 -2
  140. package/src/standalone/agent-dispatch-broker.mjs +1 -1
  141. package/src/standalone/local-session-runtime.mjs +488 -0
  142. package/src/standalone/session-protocol.mjs +1 -0
  143. package/src/standalone/session-runtime-inline-host.mjs +1 -1
  144. package/src/tui/hooks/useSession.mjs +1 -1
  145. package/src/tui/session/session-api/commands.mjs +1 -1
  146. package/src/tui/session/session-api/integrations.mjs +3 -0
  147. package/src/tui/session/session-flow/auto-clear.mjs +6 -7
  148. package/src/tui/session/tool-card-results.mjs +1 -1
  149. package/src/tui/session/turn.mjs +5 -0
  150. package/src/tui/session-local.mjs +3 -488
  151. package/src/tui/session.mjs +1 -1
  152. package/src/ui/statusline-agents.mjs +0 -1
  153. package/src/rules/agent/41-cycle2-agent.md +0 -24
  154. package/src/runtime/memory/lib/cycle-scheduler/cycle2-runs.mjs +0 -120
  155. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +0 -110
  156. package/src/runtime/memory/lib/memory-cycle2-quarantine.mjs +0 -99
  157. package/src/runtime/memory/lib/memory-cycle2-review.mjs +0 -240
  158. package/src/runtime/memory/lib/memory-cycle2.mjs +0 -191
@@ -0,0 +1,33 @@
1
+ // Deterministic test waits: poll a predicate on the real clock until a
2
+ // deadline instead of draining a fixed number of event-loop turns.
3
+ import { setTimeout as sleep } from 'node:timers/promises';
4
+
5
+ // Resolves with the first truthy `read()` value; throws if the deadline passes.
6
+ export async function waitUntil(read, { timeoutMs = 5_000, intervalMs = 1, message = 'condition' } = {}) {
7
+ const deadline = performance.now() + timeoutMs;
8
+ for (;;) {
9
+ const value = await read();
10
+ if (value) return value;
11
+ if (performance.now() >= deadline) throw new Error(`waitUntil timed out after ${timeoutMs}ms: ${message}`);
12
+ await sleep(intervalMs);
13
+ }
14
+ }
15
+
16
+ // Waits for work whose completion has no observable signal to settle. `quietMs`
17
+ // of real-clock quiet (no change in `read()`) after at least one poll is the
18
+ // settle point; bounded by `timeoutMs`.
19
+ export async function waitForQuiet(read, { quietMs = 25, timeoutMs = 5_000 } = {}) {
20
+ const deadline = performance.now() + timeoutMs;
21
+ let last = await read();
22
+ let since = performance.now();
23
+ while (performance.now() - since < quietMs) {
24
+ if (performance.now() >= deadline) throw new Error(`waitForQuiet timed out after ${timeoutMs}ms`);
25
+ await sleep(1);
26
+ const next = await read();
27
+ if (next !== last) {
28
+ last = next;
29
+ since = performance.now();
30
+ }
31
+ }
32
+ return last;
33
+ }
@@ -66,7 +66,7 @@ export function wireApis(boot) {
66
66
  // though the boot-time autostart window has already passed.
67
67
  ensureAutomationRuntime: () => boot.scheduleChannelStart(0),
68
68
  });
69
- boot.usageStatsApi = createUsageStatsApi();
69
+ boot.usageStatsApi = createUsageStatsApi({ getSessionId: () => boot.rt.session?.id || null });
70
70
  boot.providerAuthApi = providerAuthApiFor(boot);
71
71
  boot.mediaApi = createMediaApi();
72
72
  boot.sessionTitles = createSessionTitleController({
@@ -210,7 +210,7 @@ export async function listSchedules() {
210
210
  // Register or update a schedule in the PG store. Recurring input maps `time`
211
211
  // (+ optional `days`) to a cron; one-shot input maps an `at` datetime; the two
212
212
  // are mutually exclusive (also enforced by the store's when_at/when_cron XOR).
213
- // `channel` selects a channel target (model required); otherwise session.
213
+ // `channel` selects a channel target; otherwise session. Both require a model.
214
214
  export async function saveSchedule({
215
215
  name,
216
216
  description = '',
@@ -231,7 +231,8 @@ export async function saveSchedule({
231
231
  const id = assertScheduleName(name);
232
232
  const body = String(instructions || '').trim();
233
233
  if (!body) throw new Error('schedule instructions are required');
234
- if (channel && !model) throw new Error('model is required when channel is set');
234
+ const scheduleModel = String(model || '').trim();
235
+ if (!scheduleModel) throw new Error('schedule model is required — choose a model before saving');
235
236
  // 'app' → session-only; 'channel'/'both' → the run result relays to the
236
237
  // main channel (target 'channel', channelId resolved at fire time).
237
238
  const requestedMode = String(delivery || '').trim();
@@ -255,7 +256,7 @@ export async function saveSchedule({
255
256
  timezone: scheduleTimezone,
256
257
  target: mode === 'app' ? 'session' : 'channel',
257
258
  channelId: channel ? String(channel).trim() : null,
258
- model: model ? String(model).trim() : null,
259
+ model: scheduleModel,
259
260
  cwd: cwd ? String(cwd).trim() : null,
260
261
  workflow: workflow ? String(workflow).trim() : null,
261
262
  attachments: normalizeAutomationAttachments(attachments),
@@ -425,6 +425,28 @@ function tokensOf(bucket) {
425
425
  return bucket.input + bucket.output + bucket.cacheRead + bucket.cacheWrite;
426
426
  }
427
427
 
428
+ /** One session's lifetime totals (the /context footer), in a statistics route's terms. */
429
+ export function sessionUsageSnapshot(bucket) {
430
+ return {
431
+ turns: bucket.turns,
432
+ input: bucket.input,
433
+ output: bucket.output,
434
+ cacheRead: bucket.cacheRead,
435
+ cacheWrite: bucket.cacheWrite,
436
+ tokens: tokensOf(bucket),
437
+ unmeasuredTurns: bucket.unmeasuredTurns,
438
+ costUsd: round(bucket.costUsd, 6),
439
+ costKnownTurns: bucket.costKnownTurns,
440
+ costUnpricedTurns: Math.max(0, bucket.turns - bucket.costKnownTurns),
441
+ cacheHitRate: measuredRatio(
442
+ bucket.unmeasuredTurns,
443
+ bucket.cacheRead,
444
+ bucket.input + bucket.cacheRead + bucket.cacheWrite,
445
+ 4
446
+ ),
447
+ };
448
+ }
449
+
428
450
  function exportRoute(bucket, totalTokens) {
429
451
  const tokens = tokensOf(bucket);
430
452
  const fresh = bucket.input + bucket.output;
@@ -129,7 +129,11 @@ export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
129
129
  days: { ...text, description: 'Schedule day selector, e.g. weekdays or mon,wed,fri.' },
130
130
  parser: { type: 'string', enum: ['github', 'generic', 'stripe', 'sentry'] },
131
131
  channel: text,
132
- model: text,
132
+ model: {
133
+ ...text,
134
+ description:
135
+ 'Required for new schedules; no default-model fallback. Use provider/model[@effort][+fast][?parameter=value] or an existing preset name. On overwrite, omission preserves the saved model, which must be non-empty.',
136
+ },
133
137
  cwd: text,
134
138
  workflow: text,
135
139
  attachments: {
@@ -163,6 +167,7 @@ export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
163
167
  desktop: object({
164
168
  keepAwake: boolean,
165
169
  runInBackground: boolean,
170
+ turnNotifications: boolean,
166
171
  usagePinned: boolean,
167
172
  computerObserveOnly: boolean,
168
173
  }),
@@ -60,16 +60,22 @@ export function createSessionOps(deps) {
60
60
  if (getActiveTurnCount() > 0) {
61
61
  return { changed: false, reason: 'compact skipped: turn in progress' };
62
62
  }
63
- // Manual compact bypasses loop.mjs, so its PreCompact/PostCompact never
64
- // fire here — dispatch them explicitly via the session-property hooks.
63
+ // Dispatch PreCompact only after the manager's savings gate. A skipped
64
+ // auto-clear must not run hooks, invalidate context or schedule reminders.
65
+ const trigger = options.requireReduction === true ? 'auto_clear' : 'manual';
66
+ const result = await mgr.compactSessionMessages(session.id, {
67
+ requireReduction: options.requireReduction === true,
68
+ onStageChange: async () => {
69
+ try {
70
+ await session.preCompactHook?.({ trigger });
71
+ } catch {
72
+ /* best-effort: PreCompact hook must never break compact */
73
+ }
74
+ },
75
+ });
76
+ if (result?.skipped) return result;
65
77
  try {
66
- await session.preCompactHook?.({ trigger: 'manual' });
67
- } catch {
68
- /* best-effort: PreCompact hook must never break manual compact */
69
- }
70
- const result = await mgr.compactSessionMessages(session.id);
71
- try {
72
- await session.postCompactHook?.({ trigger: 'manual' });
78
+ await session.postCompactHook?.({ trigger });
73
79
  } catch {
74
80
  /* best-effort: PostCompact hook must never break manual compact */
75
81
  }
@@ -4,10 +4,14 @@ import { getUsageLedger } from '../runtime/shared/llm/usage-ledger.mjs';
4
4
  import { importUsageHistory } from '../runtime/shared/llm/usage-ledger-import.mjs';
5
5
  import { refreshUnpricedUsageAsync } from '../runtime/shared/llm/usage-pricing-refresh.mjs';
6
6
  import { resolvePluginData } from '../runtime/shared/plugin-paths.mjs';
7
- import { usageStatsSnapshot } from './services/usage-stats-model.mjs';
7
+ import { sessionUsageSnapshot, usageStatsSnapshot } from './services/usage-stats-model.mjs';
8
8
  import { resolveUsageStatsPeriod } from './services/usage-stats-period.mjs';
9
9
  import { usageRollupDayKey } from '../runtime/shared/llm/usage-rollup.mjs';
10
- import { ACCOUNT_PROVIDERS, readProviderAccountPool } from '../runtime/shared/provider-accounts.mjs';
10
+ import {
11
+ ACCOUNT_PROVIDERS,
12
+ accountScopedSessionId,
13
+ readProviderAccountPool,
14
+ } from '../runtime/shared/provider-accounts.mjs';
11
15
 
12
16
  const MAX_MODEL_LIMIT = 50;
13
17
 
@@ -53,6 +57,22 @@ function accountsInUse(accountPool) {
53
57
  return inUse;
54
58
  }
55
59
 
60
+ // Every id a session's spend was recorded under: its own, and the per-account
61
+ // provider session each subscription account sent it under before account
62
+ // sends recorded the visible session.
63
+ function sessionUsageIds(sessionId, accountPool) {
64
+ const ids = [sessionId];
65
+ for (const provider of ACCOUNT_PROVIDERS) {
66
+ try {
67
+ for (const account of accountPool(provider).accounts)
68
+ ids.push(accountScopedSessionId(provider, account.id, sessionId));
69
+ } catch {
70
+ /* an unreadable pool leaves that provider's older records out */
71
+ }
72
+ }
73
+ return ids;
74
+ }
75
+
56
76
  /** `null` = all time. `0` = today. Anything else is a trailing day count. */
57
77
  function normalizeDays(value) {
58
78
  if (value === null || value === undefined || value === 'all') return null;
@@ -102,6 +122,7 @@ export function createUsageStatsApi({
102
122
  ledger = getUsageLedger,
103
123
  importHistory = importUsageHistory,
104
124
  accountPool = readProviderAccountPool,
125
+ getSessionId = () => null,
105
126
  } = {}) {
106
127
  let importing = null;
107
128
  let imported = false;
@@ -152,6 +173,17 @@ export function createUsageStatsApi({
152
173
  return snapshot;
153
174
  },
154
175
 
176
+ /** This runtime's session from its first request, compactions included; null before it has one. */
177
+ async getSessionUsage() {
178
+ const sessionId = getSessionId();
179
+ if (!sessionId) return null;
180
+ const store = ledger();
181
+ if (!store) throw new Error('Usage ledger is unavailable');
182
+ await store.settleWrites();
183
+ const usage = await store.sessionUsageAsync(sessionUsageIds(sessionId, accountPool));
184
+ return { sessionId, ...sessionUsageSnapshot(usage) };
185
+ },
186
+
155
187
  /**
156
188
  * How a subscription's own quota meter moved: one limit window since its
157
189
  * last reset (`view: 'window'`, the default, paged by `anchor`), or any
@@ -1,7 +1,7 @@
1
1
  import { createFairCallScheduler } from './fair-call-scheduler.mjs';
2
2
  import { hashStructuredValue } from '../runtime/shared/json-metrics.mjs';
3
3
 
4
- const MEMORY_AGENTS = Object.freeze(['cycle1-agent', 'cycle2-agent']);
4
+ const MEMORY_AGENTS = Object.freeze(['cycle1-agent']);
5
5
  const MEMORY_AGENT_SET = new Set(MEMORY_AGENTS);
6
6
 
7
7
  function abortError(signal) {