mixdog 1.3.2 → 1.3.4

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 (182) hide show
  1. package/package.json +8 -8
  2. package/scripts/verify-runtime-dependency-versions.mjs +158 -0
  3. package/src/agents/reviewer/AGENT.md +5 -7
  4. package/src/agents/reviewer/agent.json +1 -1
  5. package/src/defaults/shipped-history.json +4 -0
  6. package/src/defaults/skills/browser-use/SKILL.md +6 -3
  7. package/src/defaults/skills/local-provider/SKILL.md +33 -6
  8. package/src/rules/lead/LEAD.md +5 -8
  9. package/src/rules/shared/45-destructive.md +5 -1
  10. package/src/rules/shared/60-verification.md +6 -4
  11. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +4 -1
  12. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +38 -11
  13. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +15 -3
  14. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  15. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +17 -4
  16. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +14 -1
  17. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  18. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +14 -1
  19. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  20. package/src/runtime/agent/orchestrator/providers/registry.mjs +10 -0
  21. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +13 -1
  22. package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +5 -0
  23. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +10 -1
  24. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +40 -5
  25. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +32 -11
  26. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +6 -3
  27. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -1
  28. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +13 -0
  29. package/src/runtime/agent/orchestrator/session/agent-room.mjs +68 -12
  30. package/src/runtime/agent/orchestrator/session/compact/continuation.mjs +15 -6
  31. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +31 -8
  32. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +44 -0
  33. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +117 -19
  34. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +1 -1
  35. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +3 -0
  36. package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +147 -137
  37. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +37 -74
  38. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +0 -2
  39. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +2 -2
  40. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +13 -7
  41. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +5 -0
  42. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +2 -0
  43. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/empty-nudge.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +62 -13
  45. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +22 -6
  46. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -0
  47. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +12 -20
  48. package/src/runtime/agent/orchestrator/session/loop/tool-exec/call-prep.mjs +5 -0
  49. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +11 -5
  50. package/src/runtime/agent/orchestrator/session/manager/agent-listing-notice.mjs +102 -0
  51. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +2 -0
  52. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +13 -1
  53. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +24 -15
  54. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +8 -0
  55. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +1 -0
  56. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +32 -1
  57. package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +17 -1
  58. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +6 -0
  59. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +36 -0
  60. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -9
  61. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +1 -1
  62. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +7 -5
  63. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +7 -3
  64. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +10 -1
  65. package/src/runtime/agent/orchestrator/session/routine-result.mjs +7 -4
  66. package/src/runtime/agent/orchestrator/session/runtime-user-context.mjs +3 -1
  67. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +11 -2
  68. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +5 -4
  69. package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +140 -0
  70. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +5 -0
  71. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +7 -2
  72. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +13 -0
  73. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +76 -0
  74. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +33 -1
  75. package/src/runtime/agent/orchestrator/session/store-summary-visibility.d.mts +5 -0
  76. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +37 -0
  77. package/src/runtime/agent/orchestrator/session/store.mjs +1 -0
  78. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +18 -7
  79. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +2 -0
  80. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +127 -20
  81. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +557 -0
  82. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +6 -0
  83. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +8 -2
  84. package/src/runtime/attachments/legacy-office.mjs +110 -0
  85. package/src/runtime/attachments/prompt-restore.mjs +70 -0
  86. package/src/runtime/attachments/store.mjs +60 -3
  87. package/src/runtime/effort-judge/judge-client.mjs +61 -14
  88. package/src/runtime/effort-judge/model-install.mjs +34 -2
  89. package/src/runtime/memory/lib/memory-chunk-quality.mjs +14 -2
  90. package/src/runtime/office/portable/portable-soffice.mjs +132 -30
  91. package/src/runtime/shared/agent-avatar-sprites.mjs +23 -8
  92. package/src/runtime/shared/agent-avatar.mjs +3 -2
  93. package/src/runtime/shared/agent-room-host.d.mts +31 -0
  94. package/src/runtime/shared/agent-room-host.mjs +58 -0
  95. package/src/runtime/shared/bot-hops.mjs +16 -11
  96. package/src/runtime/shared/llm/usage-rollup.mjs +4 -383
  97. package/src/runtime/shared/redact-directories.mjs +18 -0
  98. package/src/runtime/shared/remove-directory.mjs +28 -0
  99. package/src/runtime/shared/session-sidebar-host.d.mts +11 -2
  100. package/src/runtime/shared/session-sidebar-host.mjs +16 -3
  101. package/src/runtime/shared/tool-execution-contract.mjs +4 -0
  102. package/src/runtime/shared/tool-work-units.mjs +12 -9
  103. package/src/runtime/shared/turn-snapshot.mjs +233 -122
  104. package/src/runtime/shared/user-data-guard.mjs +51 -19
  105. package/src/runtime/shared/worktrees/activity-gate.mjs +209 -227
  106. package/src/runtime/shared/worktrees/contract.mjs +11 -3
  107. package/src/runtime/shared/worktrees/maintenance.mjs +11 -2
  108. package/src/runtime/shared/worktrees/manager.mjs +419 -52
  109. package/src/runtime/shared/worktrees/registry.mjs +3 -1
  110. package/src/runtime/shared/worktrees/tool-host.mjs +39 -0
  111. package/src/runtime/tidy/process.mjs +41 -8
  112. package/src/runtime/tidy/resolve.mjs +10 -7
  113. package/src/session-runtime/agent-home-delivery.mjs +73 -91
  114. package/src/session-runtime/agent-profile-changes.mjs +16 -0
  115. package/src/session-runtime/agent-room-delivery.mjs +2 -63
  116. package/src/session-runtime/agent-task-sessions.mjs +194 -64
  117. package/src/session-runtime/agent-tool-routing.mjs +49 -4
  118. package/src/session-runtime/boot/apis.mjs +3 -0
  119. package/src/session-runtime/boot/facade.mjs +5 -6
  120. package/src/session-runtime/boot/tools.mjs +23 -1
  121. package/src/session-runtime/held-requests.mjs +20 -25
  122. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +5 -22
  123. package/src/session-runtime/model-recency.mjs +1 -0
  124. package/src/session-runtime/provider-models/row-hydration.mjs +8 -1
  125. package/src/session-runtime/quick-model-rows/model-meta.mjs +2 -0
  126. package/src/session-runtime/quick-model-rows/provider-rows.mjs +7 -1
  127. package/src/session-runtime/runtime-review-api.mjs +4 -1
  128. package/src/session-runtime/services/agent-tool/close-flow.mjs +14 -2
  129. package/src/session-runtime/services/agent-tool/execute.mjs +6 -1
  130. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +44 -20
  131. package/src/session-runtime/services/agent-tool/render.mjs +3 -1
  132. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -0
  133. package/src/session-runtime/services/agent-tool/tool-def.mjs +1 -1
  134. package/src/session-runtime/services/provider-admin.mjs +12 -2
  135. package/src/session-runtime/services/worktree-access.mjs +27 -0
  136. package/src/session-runtime/session-hooks.mjs +9 -17
  137. package/src/session-runtime/settings-builtin-tools-api.mjs +2 -0
  138. package/src/session-runtime/setup-tool/executor.mjs +4 -0
  139. package/src/session-runtime/setup-tool/settings-contract.mjs +3 -0
  140. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +98 -14
  141. package/src/session-runtime/tool-policy/policy-stage.mjs +23 -5
  142. package/src/session-runtime/tool-policy-refresh.mjs +1 -0
  143. package/src/session-runtime/tool-surface.mjs +9 -5
  144. package/src/session-runtime/turn/session-ops.mjs +8 -0
  145. package/src/session-runtime/turn/turn-run.mjs +5 -1
  146. package/src/session-runtime/usage-stats-api.mjs +1 -1
  147. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +10 -2
  148. package/src/session-runtime/workflow-agents-api/agent-route.mjs +39 -17
  149. package/src/session-runtime/workspace/transition-steps.mjs +1 -18
  150. package/src/session-runtime/workspace/transitions.mjs +279 -67
  151. package/src/session-runtime/workspace/worktree-tool.mjs +194 -0
  152. package/src/standalone/daemon-lag-profiler.mjs +3 -1
  153. package/src/standalone/daemon-session-control.mjs +140 -23
  154. package/src/standalone/daemon-telemetry.mjs +9 -0
  155. package/src/standalone/daemon.mjs +5 -0
  156. package/src/standalone/local-session-runtime.mjs +11 -2
  157. package/src/standalone/session-protocol.mjs +1 -0
  158. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +2 -0
  159. package/src/standalone/session-service/agent-route-follow.mjs +69 -0
  160. package/src/standalone/session-service/session-calls/session-turns.mjs +33 -4
  161. package/src/standalone/session-service.mjs +10 -0
  162. package/src/standalone/session-tool-output.mjs +1 -0
  163. package/src/standalone/staged-import.mjs +47 -0
  164. package/src/tui/App.jsx +2 -0
  165. package/src/tui/app/message-selector.mjs +15 -2
  166. package/src/tui/app/model-options.mjs +16 -2
  167. package/src/tui/app/model-picker.mjs +2 -1
  168. package/src/tui/app/route-pickers.mjs +3 -1
  169. package/src/tui/dist/index.mjs +403 -210
  170. package/src/tui/paste-attachments.mjs +0 -1
  171. package/src/tui/paste-text-policy.mjs +4 -5
  172. package/src/tui/session/agent-job-feed/execution-delivery.mjs +4 -1
  173. package/src/tui/session/notice-surface.mjs +3 -0
  174. package/src/tui/session/session-api/intake/abort.mjs +33 -17
  175. package/src/tui/session/session-api/intake/rewind.mjs +39 -4
  176. package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
  177. package/src/tui/session/tool-approval.mjs +11 -48
  178. package/src/vendor/statusline/src/gateway/claude-current.mjs +0 -58
  179. package/src/vendor/statusline/src/gateway/route-meta.mjs +1 -361
  180. package/src/vendor/statusline/src/gateway/session-routes.mjs +0 -55
  181. package/src/session-runtime/room-member-gate.mjs +0 -61
  182. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +0 -285
@@ -1,285 +0,0 @@
1
- // Shared Mixdog settings.json env writer — the SINGLE source of truth for
2
- // reading/mutating ~/.mixdog/settings.json gateway env entries, used by
3
- // BOTH scripts/gateway-model.mjs (--enable / --disable) AND scripts/uninstall.mjs
4
- // (restoreGateway). One helper guarantees enable, disable, and uninstall never
5
- // diverge on path resolution or write mechanics.
6
- //
7
- // Node-only (node:fs / node:os / node:path) — no plugin deps — so uninstall.mjs
8
- // keeps its zero-dependency restore contract.
9
- //
10
- // Reuses the exact safe read-merge-write injectStatusLine() uses in
11
- // hooks/session-start.cjs:485-532: read settings.json -> JSON.parse -> mutate
12
- // the object -> write a .mixdog-tmp sibling -> atomic rename. Touches ONLY
13
- // gateway routing env and the paired auto-compact window restore marker.
14
- import { readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';
15
- import { homedir } from 'node:os';
16
- import { join, dirname } from 'node:path';
17
-
18
- // Location: MIXDOG_CONFIG_DIR || ~/.mixdog.
19
- export function resolveSettingsPath() {
20
- const base = process.env.MIXDOG_CONFIG_DIR || join(homedir(), '.mixdog');
21
- return join(base, 'settings.json');
22
- }
23
-
24
- export function resolveGlobalClaudeConfigPath() {
25
- return join(homedir(), '.mixdog.json');
26
- }
27
-
28
- const GATEWAY_ENV_KEYS = Object.freeze([
29
- 'ANTHROPIC_BASE_URL',
30
- ]);
31
- const AUTO_COMPACT_WINDOW_ENV = 'CLAUDE_CODE_AUTO_COMPACT_WINDOW';
32
- const PREVIOUS_AUTO_COMPACT_WINDOW_ENV = 'MIXDOG_PREVIOUS_CLAUDE_CODE_AUTO_COMPACT_WINDOW';
33
- const PREVIOUS_ABSENT = '__mixdog_absent__';
34
- export const ANTHROPIC_OFFICIAL_BASE_URL = 'https://api.anthropic.com';
35
-
36
- function normalizeAutoCompactWindow(value) {
37
- const n = Number(value);
38
- return Number.isFinite(n) && n > 0 ? String(Math.floor(n)) : null;
39
- }
40
-
41
- // Set (string url) or remove (url === null) gateway-owned settings.env entries.
42
- // Returns { ok, changed?, path, error?, missing?, dryRun? }. `dryRun` previews
43
- // without writing. A no-op (already matching / already absent) → ok:true,
44
- // changed:false. Missing settings.json on remove → ok:true (treated clean);
45
- // on add → ok:false (cannot create the file from scratch here).
46
- export function setAnthropicBaseUrl(url, { dryRun = false, autoCompactWindow = null, preserveBaseUrl = false } = {}) {
47
- const p = resolveSettingsPath();
48
- let raw;
49
- try {
50
- raw = readFileSync(p, 'utf8');
51
- } catch (e) {
52
- if (e && e.code === 'ENOENT') {
53
- return url === null
54
- ? { ok: true, changed: false, path: p, missing: true }
55
- : { ok: false, error: `cannot read ${p}: ${e.message}`, path: p };
56
- }
57
- return { ok: false, error: `cannot read ${p}: ${e.message}`, path: p };
58
- }
59
- let settings;
60
- try {
61
- settings = JSON.parse(raw);
62
- } catch (e) {
63
- return { ok: false, error: `cannot parse ${p}: ${e.message}`, path: p };
64
- }
65
- if (typeof settings !== 'object' || settings === null || Array.isArray(settings)) {
66
- return { ok: false, error: `${p} is not a JSON object`, path: p };
67
- }
68
- const envIsObj = settings.env && typeof settings.env === 'object' && !Array.isArray(settings.env);
69
- const compactWindow = normalizeAutoCompactWindow(autoCompactWindow);
70
- let changed = false;
71
- if (url === null) {
72
- // REMOVE: only our keys; leave the rest of env untouched. No-op if absent.
73
- if (!envIsObj) {
74
- if (!compactWindow) return { ok: true, changed: false, path: p };
75
- settings.env = {};
76
- }
77
- if (!compactWindow && !GATEWAY_ENV_KEYS.some((key) => key in settings.env) && !(PREVIOUS_AUTO_COMPACT_WINDOW_ENV in settings.env)) {
78
- return { ok: true, changed: false, path: p };
79
- }
80
- if (!preserveBaseUrl && 'ANTHROPIC_BASE_URL' in settings.env) {
81
- changed = true;
82
- if (!dryRun) delete settings.env.ANTHROPIC_BASE_URL;
83
- }
84
- if (compactWindow) {
85
- if (!(PREVIOUS_AUTO_COMPACT_WINDOW_ENV in settings.env)) {
86
- changed = true;
87
- if (!dryRun) {
88
- settings.env[PREVIOUS_AUTO_COMPACT_WINDOW_ENV] = AUTO_COMPACT_WINDOW_ENV in settings.env
89
- ? String(settings.env[AUTO_COMPACT_WINDOW_ENV])
90
- : PREVIOUS_ABSENT;
91
- }
92
- }
93
- if (settings.env[AUTO_COMPACT_WINDOW_ENV] !== compactWindow) {
94
- changed = true;
95
- if (!dryRun) settings.env[AUTO_COMPACT_WINDOW_ENV] = compactWindow;
96
- }
97
- } else if (PREVIOUS_AUTO_COMPACT_WINDOW_ENV in settings.env) {
98
- changed = true;
99
- if (!dryRun) {
100
- const prev = settings.env[PREVIOUS_AUTO_COMPACT_WINDOW_ENV];
101
- if (prev === PREVIOUS_ABSENT) delete settings.env[AUTO_COMPACT_WINDOW_ENV];
102
- else settings.env[AUTO_COMPACT_WINDOW_ENV] = prev;
103
- delete settings.env[PREVIOUS_AUTO_COMPACT_WINDOW_ENV];
104
- }
105
- }
106
- } else {
107
- if (!envIsObj) settings.env = {};
108
- if (settings.env.ANTHROPIC_BASE_URL !== url) {
109
- changed = true;
110
- if (!dryRun) settings.env.ANTHROPIC_BASE_URL = url;
111
- }
112
- if (compactWindow) {
113
- if (!(PREVIOUS_AUTO_COMPACT_WINDOW_ENV in settings.env)) {
114
- changed = true;
115
- if (!dryRun) {
116
- settings.env[PREVIOUS_AUTO_COMPACT_WINDOW_ENV] = AUTO_COMPACT_WINDOW_ENV in settings.env
117
- ? String(settings.env[AUTO_COMPACT_WINDOW_ENV])
118
- : PREVIOUS_ABSENT;
119
- }
120
- }
121
- if (settings.env[AUTO_COMPACT_WINDOW_ENV] !== compactWindow) {
122
- changed = true;
123
- if (!dryRun) settings.env[AUTO_COMPACT_WINDOW_ENV] = compactWindow;
124
- }
125
- }
126
- }
127
- if (!changed) return { ok: true, changed: false, path: p };
128
- if (dryRun) return { ok: true, changed: true, path: p, dryRun: true };
129
- try {
130
- mkdirSync(dirname(p), { recursive: true });
131
- const tmp = p + '.mixdog-tmp';
132
- writeFileSync(tmp, JSON.stringify(settings, null, 2) + '\n', 'utf8');
133
- renameSync(tmp, p);
134
- } catch (e) {
135
- return { ok: false, error: `cannot write ${p}: ${e.message}`, path: p };
136
- }
137
- return { ok: true, changed: true, path: p };
138
- }
139
-
140
- // RUNTIME base-url sync for live /model changes.
141
- //
142
- // The host client re-applies settings.env into process.env while the session is
143
- // alive, but deleting ANTHROPIC_BASE_URL does not unset the already-present
144
- // process env key. For native Claude choices, write the official Anthropic base
145
- // URL instead. For mixdog choices, write the local gateway URL. This helper is
146
- // deliberately narrower than setAnthropicBaseUrl(): it never creates restore
147
- // markers and, by default, only acts when ANTHROPIC_BASE_URL already exists so
148
- // it cannot enable the gateway for a user who disabled it.
149
- export function syncAnthropicBaseUrl(url, { dryRun = false, requireExisting = true } = {}) {
150
- const target = typeof url === 'string' && url.trim() ? url.trim() : '';
151
- const p = resolveSettingsPath();
152
- if (!target) return { ok: true, changed: false, path: p, skipped: 'missing-url' };
153
- let raw;
154
- try {
155
- raw = readFileSync(p, 'utf8');
156
- } catch (e) {
157
- return { ok: false, error: `cannot read ${p}: ${e.message}`, path: p };
158
- }
159
- let settings;
160
- try {
161
- settings = JSON.parse(raw);
162
- } catch (e) {
163
- return { ok: false, error: `cannot parse ${p}: ${e.message}`, path: p };
164
- }
165
- if (typeof settings !== 'object' || settings === null || Array.isArray(settings)) {
166
- return { ok: false, error: `${p} is not a JSON object`, path: p };
167
- }
168
- const envIsObj = settings.env && typeof settings.env === 'object' && !Array.isArray(settings.env);
169
- if (!envIsObj) {
170
- if (requireExisting) return { ok: true, changed: false, path: p, skipped: 'gateway-inactive' };
171
- settings.env = {};
172
- }
173
- if (requireExisting && !('ANTHROPIC_BASE_URL' in settings.env)) {
174
- return { ok: true, changed: false, path: p, skipped: 'gateway-inactive' };
175
- }
176
- if (settings.env.ANTHROPIC_BASE_URL === target) {
177
- return { ok: true, changed: false, path: p };
178
- }
179
- if (dryRun) return { ok: true, changed: true, path: p, dryRun: true };
180
- settings.env.ANTHROPIC_BASE_URL = target;
181
- try {
182
- mkdirSync(dirname(p), { recursive: true });
183
- const tmp = p + '.mixdog-tmp';
184
- writeFileSync(tmp, JSON.stringify(settings, null, 2) + '\n', 'utf8');
185
- renameSync(tmp, p);
186
- } catch (e) {
187
- return { ok: false, error: `cannot write ${p}: ${e.message}`, path: p };
188
- }
189
- return { ok: true, changed: true, path: p };
190
- }
191
-
192
- // RUNTIME compact-window sync (Stage 4) — used by the gateway request path when
193
- // the routed model's context window changes at runtime (/model switch). The host
194
- // client watches settings.json (chokidar) and re-applies env to process.env
195
- // without restart, and getEffectiveContextWindowSize reads process.env live, so
196
- // rewriting CLAUDE_CODE_AUTO_COMPACT_WINDOW here makes the auto-compact
197
- // threshold track the live model next turn.
198
- //
199
- // Deliberately NARROW vs setAnthropicBaseUrl():
200
- // - NEVER touches ANTHROPIC_BASE_URL.
201
- // - NEVER touches the MIXDOG_PREVIOUS_* restore marker (that is enable/disable
202
- // only — runtime sync must not interfere with the restore contract).
203
- // - NEVER creates settings.json (missing/unparseable → ok:false).
204
- // - ONLY acts when ANTHROPIC_BASE_URL is already present (gateway active);
205
- // otherwise skipped without a write.
206
- // Returns { ok, changed, path, error?, skipped? }.
207
- export function syncAutoCompactWindow(window, { dryRun = false } = {}) {
208
- const p = resolveSettingsPath();
209
- const compactWindow = normalizeAutoCompactWindow(window);
210
- if (!compactWindow) return { ok: true, changed: false, path: p };
211
- let raw;
212
- try {
213
- raw = readFileSync(p, 'utf8');
214
- } catch (e) {
215
- return { ok: false, error: `cannot read ${p}: ${e.message}`, path: p };
216
- }
217
- let settings;
218
- try {
219
- settings = JSON.parse(raw);
220
- } catch (e) {
221
- return { ok: false, error: `cannot parse ${p}: ${e.message}`, path: p };
222
- }
223
- if (typeof settings !== 'object' || settings === null || Array.isArray(settings)) {
224
- return { ok: false, error: `${p} is not a JSON object`, path: p };
225
- }
226
- const envIsObj = settings.env && typeof settings.env === 'object' && !Array.isArray(settings.env);
227
- // Gateway must already be pointed at this file (active). Never write otherwise.
228
- if (!envIsObj || !('ANTHROPIC_BASE_URL' in settings.env)) {
229
- return { ok: true, changed: false, path: p, skipped: 'gateway-inactive' };
230
- }
231
- if (String(settings.env[AUTO_COMPACT_WINDOW_ENV]) === compactWindow) {
232
- return { ok: true, changed: false, path: p };
233
- }
234
- if (dryRun) return { ok: true, changed: true, path: p, dryRun: true };
235
- settings.env[AUTO_COMPACT_WINDOW_ENV] = compactWindow;
236
- try {
237
- mkdirSync(dirname(p), { recursive: true });
238
- const tmp = p + '.mixdog-tmp';
239
- writeFileSync(tmp, JSON.stringify(settings, null, 2) + '\n', 'utf8');
240
- renameSync(tmp, p);
241
- } catch (e) {
242
- return { ok: false, error: `cannot write ${p}: ${e.message}`, path: p };
243
- }
244
- return { ok: true, changed: true, path: p };
245
- }
246
-
247
- export function syncAdditionalModelOptions(options, { dryRun = false, configPath = null } = {}) {
248
- const p = configPath || resolveGlobalClaudeConfigPath();
249
- const nextOptions = Array.isArray(options)
250
- ? options
251
- .filter(opt => opt && typeof opt.value === 'string' && typeof opt.label === 'string' && typeof opt.description === 'string')
252
- .map(opt => ({ value: opt.value, label: opt.label, description: opt.description }))
253
- : [];
254
- let raw;
255
- try {
256
- raw = readFileSync(p, 'utf8');
257
- } catch (e) {
258
- return { ok: false, changed: false, error: `cannot read ${p}: ${e.message}`, path: p };
259
- }
260
- let config;
261
- try {
262
- config = JSON.parse(raw);
263
- } catch (e) {
264
- return { ok: false, changed: false, error: `cannot parse ${p}: ${e.message}`, path: p };
265
- }
266
- if (typeof config !== 'object' || config === null || Array.isArray(config)) {
267
- return { ok: false, changed: false, error: `${p} is not a JSON object`, path: p };
268
- }
269
- const current = Array.isArray(config.additionalModelOptionsCache) ? config.additionalModelOptionsCache : [];
270
- const preserved = current.filter(opt => !(opt && typeof opt.value === 'string' && opt.value.startsWith('mixdog/')));
271
- const merged = [...preserved, ...nextOptions];
272
- if (JSON.stringify(current) === JSON.stringify(merged)) {
273
- return { ok: true, changed: false, path: p };
274
- }
275
- if (dryRun) return { ok: true, changed: true, path: p, dryRun: true };
276
- config.additionalModelOptionsCache = merged;
277
- try {
278
- const tmp = p + '.mixdog-tmp';
279
- writeFileSync(tmp, JSON.stringify(config, null, 2) + '\n', 'utf8');
280
- renameSync(tmp, p);
281
- } catch (e) {
282
- return { ok: false, changed: false, error: `cannot write ${p}: ${e.message}`, path: p };
283
- }
284
- return { ok: true, changed: true, path: p };
285
- }