mixdog 1.1.0 → 1.1.2

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 (174) hide show
  1. package/README.md +13 -6
  2. package/package.json +1 -2
  3. package/scripts/latest-guard.mjs +63 -0
  4. package/scripts/lib/run-node-tests.mjs +103 -27
  5. package/scripts/lib/test-failure-summary.mjs +7 -2
  6. package/scripts/merge-mac-update-feed.mjs +107 -0
  7. package/scripts/mobile-release.mjs +210 -0
  8. package/scripts/post-release-check.mjs +353 -0
  9. package/scripts/publish-asset-release.mjs +43 -0
  10. package/scripts/release-guard.mjs +63 -0
  11. package/scripts/release-paths.mjs +39 -2
  12. package/scripts/test.mjs +29 -7
  13. package/src/defaults/shipped-history.json +2 -0
  14. package/src/defaults/skills/computer-use/SKILL.md +1 -1
  15. package/src/defaults/skills/setup/references/actions.md +1 -0
  16. package/src/headless-role.mjs +2 -2
  17. package/src/lib/keychain-cjs.cjs +582 -17
  18. package/src/lib/rules-builder.cjs +0 -23
  19. package/src/rules/lead/LEAD.md +2 -2
  20. package/src/runtime/agent/orchestrator/config-storage.mjs +32 -4
  21. package/src/runtime/agent/orchestrator/config.mjs +18 -0
  22. package/src/runtime/agent/orchestrator/context/instruction-files.mjs +130 -0
  23. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +4 -0
  24. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +11 -0
  25. package/src/runtime/agent/orchestrator/providers/lib/open-login-url.mjs +9 -0
  26. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +254 -0
  27. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +218 -0
  28. package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +239 -0
  29. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +233 -0
  30. package/src/runtime/agent/orchestrator/providers/muse-code-oauth-login.mjs +247 -0
  31. package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +225 -0
  32. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +542 -0
  33. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +25 -0
  34. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +4 -1
  35. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +13 -0
  36. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +14 -6
  37. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +99 -0
  38. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +4 -8
  39. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +2 -7
  40. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +8 -0
  41. package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -0
  42. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +2 -0
  43. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +15 -1
  44. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +1 -5
  45. package/src/runtime/agent/orchestrator/session/context-utils.mjs +12 -14
  46. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +11 -1
  47. package/src/runtime/agent/orchestrator/session/loop/tool-call-ids.mjs +90 -0
  48. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +19 -11
  49. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +1 -0
  50. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +28 -0
  51. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +8 -0
  52. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
  53. package/src/runtime/agent/orchestrator/session/manager.mjs +1 -5
  54. package/src/runtime/agent/orchestrator/session/read-dedup.mjs +0 -1
  55. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +3 -0
  56. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +2 -0
  57. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +2 -0
  58. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +23 -0
  59. package/src/runtime/agent/orchestrator/session/turn-effort.mjs +18 -0
  60. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +11 -8
  61. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +9 -4
  62. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +245 -0
  63. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +13 -1
  64. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +3 -1
  65. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  66. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +49 -0
  67. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +3 -3
  68. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +9 -7
  69. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +61 -8
  70. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +28 -3
  71. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +73 -1
  72. package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +2 -2
  73. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +3 -1
  74. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +17 -6
  75. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +23 -5
  76. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +93 -43
  77. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +32 -6
  78. package/src/runtime/agent/orchestrator/tools/builtin.mjs +9 -2
  79. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +3 -1
  80. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +12 -15
  81. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background.mjs +2 -2
  82. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +4 -2
  83. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +124 -91
  84. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +19 -4
  85. package/src/runtime/channels/index.mjs +1 -5
  86. package/src/runtime/channels/lib/runtime-paths.mjs +8 -23
  87. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -13
  88. package/src/runtime/channels/lib/worker-ipc.mjs +2 -2
  89. package/src/runtime/channels/lib/worker-main.mjs +4 -34
  90. package/src/runtime/computer-bridge/client.mjs +25 -1
  91. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +84 -0
  92. package/src/runtime/computer-bridge/linux-readiness.d.mts +52 -0
  93. package/src/runtime/computer-bridge/linux-readiness.mjs +411 -0
  94. package/src/runtime/computer-bridge/test-linux-display.mjs +5 -0
  95. package/src/runtime/effort-judge/compact-tokenizer.mjs +13 -8
  96. package/src/runtime/effort-judge/judge-input.mjs +5 -2
  97. package/src/runtime/effort-judge/model-install.mjs +9 -3
  98. package/src/runtime/effort-judge/model-manifest.json +7 -7
  99. package/src/runtime/memory/lib/embedding-worker.mjs +9 -3
  100. package/src/runtime/memory/lib/pg/supervisor.mjs +3 -0
  101. package/src/runtime/shared/config.mjs +50 -2
  102. package/src/runtime/shared/developer-options.mjs +7 -0
  103. package/src/runtime/shared/instruction-files-mode.mjs +9 -0
  104. package/src/runtime/shared/llm/model-catalog.mjs +2 -1
  105. package/src/runtime/shared/llm/openai-daybreak.mjs +79 -0
  106. package/src/runtime/shared/pristine-execution-contract.json +9 -0
  107. package/src/runtime/shared/pristine-execution.mjs +1 -1
  108. package/src/runtime/shared/provider-accounts.mjs +2 -0
  109. package/src/runtime/shared/resource-admission.mjs +10 -3
  110. package/src/runtime/shared/schedule-model-ref.mjs +11 -0
  111. package/src/runtime/shared/semver.d.mts +1 -0
  112. package/src/runtime/shared/semver.mjs +57 -0
  113. package/src/runtime/shared/session-approval-wait.mjs +35 -0
  114. package/src/runtime/shared/shell-display.mjs +4 -2
  115. package/src/runtime/shared/tool-surface.mjs +2 -2
  116. package/src/runtime/shared/turn-worktree-snapshot.mjs +11 -10
  117. package/src/runtime/shared/update-checker.mjs +2 -31
  118. package/src/session-runtime/boot/apis.mjs +5 -0
  119. package/src/session-runtime/boot/begin.mjs +0 -2
  120. package/src/session-runtime/boot/tools.mjs +5 -4
  121. package/src/session-runtime/model-recency.mjs +3 -0
  122. package/src/session-runtime/provider-auth/login.mjs +10 -10
  123. package/src/session-runtime/provider-auth/setup.mjs +22 -2
  124. package/src/session-runtime/provider-models/row-hydration.mjs +12 -4
  125. package/src/session-runtime/provider-readiness.mjs +4 -0
  126. package/src/session-runtime/resource-mcp-api.mjs +7 -3
  127. package/src/session-runtime/runtime-bootstrap.mjs +0 -4
  128. package/src/session-runtime/runtime-facade.mjs +5 -0
  129. package/src/session-runtime/runtime-paths.mjs +0 -1
  130. package/src/session-runtime/schedule-session-run.mjs +3 -1
  131. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +55 -14
  132. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +15 -3
  133. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +2 -0
  134. package/src/session-runtime/services/agent-tool/worker-index/approval-wait.mjs +97 -0
  135. package/src/session-runtime/services/agent-tool/worker-index/mutation-batch.mjs +4 -2
  136. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -2
  137. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +3 -0
  138. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +58 -2
  139. package/src/session-runtime/services/agent-tool/worker-index.mjs +69 -15
  140. package/src/session-runtime/services/agent-tool/worker-rows.mjs +2 -0
  141. package/src/session-runtime/services/channel-admin.mjs +4 -2
  142. package/src/session-runtime/services/channel-worker.mjs +1 -1
  143. package/src/session-runtime/services/provider-admin.mjs +80 -17
  144. package/src/session-runtime/services/seeds.mjs +7 -10
  145. package/src/session-runtime/services/usage-dashboard-model.mjs +6 -0
  146. package/src/session-runtime/settings-builtin-tools-api.mjs +15 -0
  147. package/src/session-runtime/settings-profile-api.mjs +27 -2
  148. package/src/session-runtime/setup-tool/executor.mjs +5 -0
  149. package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
  150. package/src/session-runtime/starter-guide-state.mjs +62 -0
  151. package/src/session-runtime/tool-policy-refresh.mjs +3 -0
  152. package/src/session-runtime/webhook-session-run.mjs +3 -1
  153. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -2
  154. package/src/standalone/channel-binding.mjs +1 -2
  155. package/src/standalone/channel-transport/call-dispatch.mjs +4 -10
  156. package/src/standalone/daemon-channels-loader.mjs +2 -3
  157. package/src/standalone/local-session-runtime.mjs +2 -78
  158. package/src/standalone/session-protocol.mjs +2 -0
  159. package/src/tui/app/model-options.mjs +13 -1
  160. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +10 -4
  161. package/src/tui/app/provider-setup/oauth-actions.mjs +5 -0
  162. package/src/tui/dist/index.mjs +84 -34
  163. package/src/tui/session/context-state.mjs +2 -0
  164. package/src/tui/session/live-share/owner.mjs +17 -0
  165. package/src/tui/session/oauth-flows.mjs +2 -0
  166. package/src/tui/session/session-api/extensions.mjs +12 -8
  167. package/src/tui/session/session-api/integrations.mjs +9 -8
  168. package/src/tui/session/session-api/routes.mjs +2 -2
  169. package/src/tui/session/session-api/settings.mjs +1 -0
  170. package/src/tui/session/session-api/shared.mjs +10 -6
  171. package/src/tui/session/tool-card-results/grouped-result.mjs +11 -2
  172. package/src/tui/session/transcript-spill/spill-dir.mjs +22 -6
  173. package/src/tui/spinner-meta.mjs +26 -10
  174. package/src/runtime/channels/tool-defs.mjs +0 -17
@@ -11,16 +11,30 @@ import { resolve } from 'node:path';
11
11
  import { WORKER_INDEX_FILE } from './tool-def.mjs';
12
12
  import { clean, positiveInt, registerExitFlush, rowMatchesContext } from './helpers.mjs';
13
13
  import { applyWorkerRowUpsert, recoverTagTombstoneOwner, workerRowKey } from './worker-rows.mjs';
14
+ import {
15
+ applyAwaitingUser,
16
+ awaitingUserWouldChange,
17
+ clearAwaitingUser,
18
+ registerApprovalWaitWriter,
19
+ } from './worker-index/approval-wait.mjs';
14
20
  import { createMutationBatch } from './worker-index/mutation-batch.mjs';
15
- import { createRowLiveness, isActiveWorkerRow } from './worker-index/row-liveness.mjs';
21
+ import { createRowLiveness, isActiveWorkerRow, isStaleApprovalWait } from './worker-index/row-liveness.mjs';
16
22
  import { normalizeWorkerRows, workerRowFromSession } from './worker-index/row-shape.mjs';
17
- import { createWorkerRowStore } from './worker-index/row-store.mjs';
23
+ import { acquireWorkerRowStore } from './worker-index/row-store.mjs';
18
24
  import { findTagTombstone, tagTombstoneKey, tombstoneBlocksWork } from '../../../runtime/shared/agent-reap-state.mjs';
19
25
 
20
26
  export function createWorkerIndex({ dataDir, cfgMod, mgr, tags, tagAgents, tagCwds }) {
21
27
  const file = dataDir ? resolve(dataDir, WORKER_INDEX_FILE) : null;
22
- const store = createWorkerRowStore(file);
23
- const batch = createMutationBatch(store.write);
28
+ // Shared with every other worker index of this process over the same file.
29
+ const { store, release: releaseStore } = acquireWorkerRowStore(file);
30
+ // Sessions whose deferred rows still wait in the batch; reconciled once the
31
+ // batch lands in the store.
32
+ const unreconciled = new Set();
33
+ const batch = createMutationBatch(store.write, () => {
34
+ const ids = [...unreconciled];
35
+ unreconciled.clear();
36
+ for (const id of ids) reconcileAwaitingUser(id);
37
+ });
24
38
  const liveness = createRowLiveness({ dataDir, cfgMod });
25
39
  // Rows this process marked active; settled to idle on exit so a crash of
26
40
  // the host never leaves them "running" forever.
@@ -62,10 +76,15 @@ export function createWorkerIndex({ dataDir, cfgMod, mgr, tags, tagAgents, tagCw
62
76
  if (isActiveWorkerRow(normalized)) activeWorkerKeys.add(key);
63
77
  else activeWorkerKeys.delete(key);
64
78
  bindTag(normalized);
65
- if (defer) return file ? batch.queue((byKey) => applyWorkerRowUpsert(byKey, normalized)) : false;
79
+ if (defer) {
80
+ if (!file) return false;
81
+ unreconciled.add(normalized.sessionId);
82
+ return batch.queue((byKey) => applyWorkerRowUpsert(byKey, normalized));
83
+ }
66
84
  store.write((byKey) => {
67
85
  applyWorkerRowUpsert(byKey, normalized);
68
86
  });
87
+ reconcileAwaitingUser(normalized.sessionId);
69
88
  return true;
70
89
  }
71
90
 
@@ -85,6 +104,31 @@ export function createWorkerIndex({ dataDir, cfgMod, mgr, tags, tagAgents, tagCw
85
104
  });
86
105
  }
87
106
 
107
+ /** Persist (or clear) a worker's approval wait as a field update fenced to
108
+ * the session and its turn (`turnAt`, ms): a row whose turn started later is
109
+ * left alone. Writes only when a row of that session exists and would
110
+ * change, so a Lead's own approvals cost nothing; a row that is not in the
111
+ * store yet takes the wait when it lands (reconcileAwaitingUser). */
112
+ function setAwaitingUser(sessionId, awaitingUser, { turnAt = 0 } = {}) {
113
+ const fence = {
114
+ sessionId: clean(sessionId),
115
+ awaitingUser: awaitingUser === 'approval' ? 'approval' : null,
116
+ turnAt: Number(turnAt) || 0,
117
+ };
118
+ if (!fence.sessionId || !awaitingUserWouldChange(store.readAll(), fence)) return false;
119
+ store.update((byKey) => applyAwaitingUser(byKey, fence));
120
+ return true;
121
+ }
122
+
123
+ /** The runtime of a session this process runs is the authority on its
124
+ * approval wait (the runtime-liveness counter behind `awaitingUser`): every
125
+ * row it creates or refreshes takes that state, so a missed or stale
126
+ * transition heals on the next write. No runtime here: not ours to judge. */
127
+ function reconcileAwaitingUser(sessionId) {
128
+ const runtime = mgr.getSessionRuntime?.(sessionId);
129
+ if (runtime) setAwaitingUser(sessionId, runtime.awaitingUser, { turnAt: runtime.askStartedAt });
130
+ }
131
+
88
132
  function refreshTagsFromIndex(context = {}) {
89
133
  const rows = readWorkerRows(context);
90
134
  for (const row of rows) bindTag(row);
@@ -105,18 +149,24 @@ export function createWorkerIndex({ dataDir, cfgMod, mgr, tags, tagAgents, tagCw
105
149
  });
106
150
  }
107
151
 
152
+ // Startup recovery: stale active rows settle to idle, and an approval wait
153
+ // left behind by a runtime that is gone (daemon restart) is cleared.
108
154
  function recoverStaleWorkerRows() {
109
155
  const now = Date.now();
110
- const stale = store.readAll().filter((row) => liveness.isStaleActive(row, now));
111
- if (!stale.length) return false;
112
- store.write((byKey) => {
113
- for (const row of stale) {
114
- const key = workerRowKey(row);
115
- const current = byKey.get(key);
116
- if (!current || !liveness.isStaleActive(current, now)) continue;
117
- byKey.set(key, liveness.idleRow(current, now));
118
- }
119
- });
156
+ const rows = store.readAll();
157
+ const staleActive = rows.filter((row) => liveness.isStaleActive(row, now));
158
+ const staleWaits = rows.some(isStaleApprovalWait);
159
+ if (!staleActive.length && !staleWaits) return false;
160
+ if (staleActive.length) {
161
+ store.write((byKey) => {
162
+ for (const row of staleActive) {
163
+ const key = workerRowKey(row);
164
+ const current = byKey.get(key);
165
+ if (current && liveness.isStaleActive(current, now)) byKey.set(key, liveness.idleRow(current, now));
166
+ }
167
+ });
168
+ }
169
+ if (staleWaits) store.update((byKey) => clearAwaitingUser(byKey, isStaleApprovalWait));
120
170
  return true;
121
171
  }
122
172
 
@@ -129,6 +179,7 @@ export function createWorkerIndex({ dataDir, cfgMod, mgr, tags, tagAgents, tagCw
129
179
 
130
180
  recoverStaleWorkerRows();
131
181
  const unregisterExitFlush = registerExitFlush(flushWorkerIndexOnExit);
182
+ const unregisterApprovalWaits = registerApprovalWaitWriter(file, setAwaitingUser);
132
183
  let disposed = false;
133
184
 
134
185
  /** Persist queued rows, then release the exit registration. Active rows are
@@ -142,6 +193,8 @@ export function createWorkerIndex({ dataDir, cfgMod, mgr, tags, tagAgents, tagCw
142
193
  store.flushSync();
143
194
  } finally {
144
195
  unregisterExitFlush();
196
+ unregisterApprovalWaits();
197
+ releaseStore();
145
198
  }
146
199
  }
147
200
 
@@ -168,6 +221,7 @@ export function createWorkerIndex({ dataDir, cfgMod, mgr, tags, tagAgents, tagCw
168
221
  upsertWorkerSessionDeferred: (session, fallbackTag = '', extra = {}) =>
169
222
  upsertWorkerRow(rowFromSession(session, fallbackTag, extra), { defer: true }),
170
223
  removeWorkerRow,
224
+ setAwaitingUser,
171
225
  refreshTagsFromIndex,
172
226
  flushActiveWorkerRows,
173
227
  flushWorkerIndexOnExit,
@@ -112,6 +112,8 @@ export function applyWorkerRowUpsert(byKey, normalized) {
112
112
  merged[field] = prev[field];
113
113
  }
114
114
  }
115
+ // `awaitingUser` is not decided here: the worker row store keeps the current
116
+ // row's flag across every whole-row write and drops it once the row settles.
115
117
  byKey.set(key, {
116
118
  ...merged,
117
119
  createdAt: normalized.createdAt || prev.createdAt || new Date().toISOString(),
@@ -23,6 +23,7 @@ import {
23
23
  setEndpointEnabled as dbSetEndpointEnabled,
24
24
  } from '../../runtime/channels/lib/webhooks-db.mjs';
25
25
  import { normalizeAutomationAttachments } from '../../runtime/shared/automation-attachments.mjs';
26
+ import { migrateCodexModelRef } from '../../runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs';
26
27
  import { hasEnabledAutomation } from '../../runtime/shared/automation-presence.mjs';
27
28
  import { resolveScheduleTimezone, validateScheduleCron } from '../../runtime/shared/schedule-time.mjs';
28
29
  import { readHookPublicBase } from '../../runtime/channels/lib/webhook/relay-tunnel.mjs';
@@ -192,7 +193,8 @@ function scheduleToDisplay(s) {
192
193
  whenAt: s.whenAt || undefined,
193
194
  whenCron: s.whenCron || undefined,
194
195
  timezone: s.timezone || undefined,
195
- model: s.model || undefined,
196
+ // Shown (and re-saved by the editor) on the catalog successor of a superseded model.
197
+ model: s.model ? migrateCodexModelRef(s.model) : undefined,
196
198
  cwd: s.cwd || undefined,
197
199
  workflow: s.workflow || undefined,
198
200
  attachments: s.attachments || undefined,
@@ -287,7 +289,7 @@ async function listWebhooks() {
287
289
  name: ep.name,
288
290
  description: ep.description || '',
289
291
  parser: ep.parser || 'github',
290
- ...(ep.model ? { model: ep.model } : {}),
292
+ ...(ep.model ? { model: migrateCodexModelRef(ep.model) } : {}),
291
293
  ...(ep.cwd ? { cwd: ep.cwd } : {}),
292
294
  ...(ep.workflow ? { workflow: ep.workflow } : {}),
293
295
  ...(ep.attachments ? { attachments: ep.attachments } : {}),
@@ -18,7 +18,7 @@ import { daemonEnv, spawnDaemonCandidate } from './channel-worker/daemon-spawn.m
18
18
 
19
19
  export { pruneStaleChannelClientHeartbeats };
20
20
 
21
- const CHANNEL_TOOLS = new Set(['activate_channel_bridge', 'reload_config', 'rebind_current_transcript']);
21
+ const CHANNEL_TOOLS = new Set(['activate_channel_bridge', 'reload_config']);
22
22
  const CALL_RETRIES = 3;
23
23
 
24
24
  function logLine(path, line) {
@@ -4,10 +4,11 @@ import { sameAccountIdentity } from '../../runtime/agent/orchestrator/providers/
4
4
  import {
5
5
  AGENT_PROVIDER_ENV,
6
6
  SECRET_ACCOUNTS,
7
- deleteSecret,
7
+ deleteSecretAsync,
8
8
  getAgentApiKey,
9
+ getKeychainStatus,
9
10
  hasStoredSecret,
10
- saveSecret,
11
+ saveSecretAsync,
11
12
  } from '../../runtime/shared/config.mjs';
12
13
  import {
13
14
  beginOAuthLogin as beginAnthropicOAuthLogin,
@@ -35,6 +36,24 @@ import {
35
36
  beginOAuthLogin as beginGrokOAuthLogin,
36
37
  loginOAuth as loginGrokOAuth,
37
38
  } from '../../runtime/agent/orchestrator/providers/grok-oauth-login.mjs';
39
+ import {
40
+ describeMimoOAuthCredentials,
41
+ forgetMimoOAuthCredentials,
42
+ hasMimoOAuthCredentials,
43
+ } from '../../runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs';
44
+ import {
45
+ beginOAuthLogin as beginMimoOAuthLogin,
46
+ loginOAuth as loginMimoOAuth,
47
+ } from '../../runtime/agent/orchestrator/providers/mimo-oauth-login.mjs';
48
+ import {
49
+ describeMuseCodeOAuthCredentials,
50
+ forgetMuseCodeOAuthCredentials,
51
+ hasMuseCodeOAuthCredentials,
52
+ } from '../../runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs';
53
+ import {
54
+ beginOAuthLogin as beginMuseCodeOAuthLogin,
55
+ loginOAuth as loginMuseCodeOAuth,
56
+ } from '../../runtime/agent/orchestrator/providers/muse-code-oauth-login.mjs';
38
57
  import {
39
58
  beginCursorOAuthLogin,
40
59
  describeCursorOAuthCredentials,
@@ -125,6 +144,26 @@ const OAUTH_PROVIDERS = Object.freeze([
125
144
  begin: beginGrokOAuthLogin,
126
145
  login: loginGrokOAuth,
127
146
  }),
147
+ Object.freeze({
148
+ id: 'muse-code-oauth',
149
+ name: 'Muse Code',
150
+ desc: 'Sign in with your Meta account (Muse Code subscription)',
151
+ has: hasMuseCodeOAuthCredentials,
152
+ describe: describeMuseCodeOAuthCredentials,
153
+ forget: forgetMuseCodeOAuthCredentials,
154
+ begin: beginMuseCodeOAuthLogin,
155
+ login: loginMuseCodeOAuth,
156
+ }),
157
+ Object.freeze({
158
+ id: 'mimo-oauth',
159
+ name: 'MiMo',
160
+ desc: 'Sign in with your Xiaomi MiMo account',
161
+ has: hasMimoOAuthCredentials,
162
+ describe: describeMimoOAuthCredentials,
163
+ forget: forgetMimoOAuthCredentials,
164
+ begin: beginMimoOAuthLogin,
165
+ login: loginMimoOAuth,
166
+ }),
128
167
  Object.freeze({
129
168
  id: 'cursor-oauth',
130
169
  name: 'Cursor OAuth',
@@ -152,7 +191,7 @@ const BUILTIN_PROVIDER_IDS = new Set(['mixdog-local']);
152
191
  const API_PROVIDER_IDS = new Set(API_PROVIDERS.map((p) => p.id));
153
192
  const OAUTH_BY_ID = new Map(OAUTH_PROVIDERS.map((p) => [p.id, p]));
154
193
 
155
- // Dev-only entries (cursor-oauth, antigravity-oauth) are unknown to
194
+ // Dev-only entries (cursor-oauth, antigravity-oauth, mimo-oauth) are unknown to
156
195
  // settings/login while the Developer → Providers option is off. Filtered per
157
196
  // call, so toggling the option takes effect without a restart.
158
197
  function availableOAuthProviders() {
@@ -230,6 +269,24 @@ function describeOAuthProvider(p, { detail = false } = {}) {
230
269
  return { authenticated, status: authenticated ? 'Set' : 'Not Set', ...(detail ? { detail: p.desc } : {}) };
231
270
  }
232
271
 
272
+ // When the OS keyring is locked/missing a stored key reads as absent; say so
273
+ // (the UI shows the unlock / install hint) instead of a bare "No Key".
274
+ // Fixed, translatable hints keyed by keyring state; internal error text never
275
+ // reaches the UI.
276
+ const KEYCHAIN_HINTS = Object.freeze({
277
+ unreachable: 'The keyring is not reachable — make sure it is running, then press Unlock keyring.',
278
+ locked: 'Keyring is locked — unlock it (log in again or open Passwords and Keys).',
279
+ missing: 'Install libsecret-tools (sudo apt install libsecret-tools) to use the system keyring.',
280
+ error: 'The system keyring could not be read — check that it is running and unlocked.',
281
+ });
282
+
283
+ export function keychainIssueFor(checkSecrets) {
284
+ if (!checkSecrets) return null;
285
+ const { state } = getKeychainStatus();
286
+ const message = KEYCHAIN_HINTS[state];
287
+ return message ? { state, message } : null;
288
+ }
289
+
233
290
  function apiProviderSetup(p, providers, checkSecrets) {
234
291
  const configured = providers[p.id] || {};
235
292
  const envName = AGENT_PROVIDER_ENV[p.id] || p.env;
@@ -245,8 +302,11 @@ function apiProviderSetup(p, providers, checkSecrets) {
245
302
  if (stored) detail = 'stored in keychain';
246
303
  else if (env) detail = envName;
247
304
  else if (authenticated) detail = 'runtime credential';
305
+ const keychainIssue = authenticated ? null : keychainIssueFor(checkSecrets);
306
+ if (keychainIssue) detail = keychainIssue.message;
248
307
  return {
249
308
  ...p,
309
+ ...(keychainIssue ? { keychainIssue } : {}),
250
310
  group: 'api',
251
311
  type: 'api-key',
252
312
  enabled: configuredEnabled || authenticated,
@@ -406,7 +466,9 @@ function customProviderRows(providers, checkSecrets) {
406
466
  export async function providerSetup(config = {}, options = {}) {
407
467
  const providers = config.providers || {};
408
468
  const checkSecrets = options?.checkSecrets !== false;
469
+ const keychain = checkSecrets ? keychainIssueFor(true) : null;
409
470
  return {
471
+ ...(keychain ? { keychain } : {}),
410
472
  api: [
411
473
  ...API_PROVIDERS.map((p) => apiProviderSetup(p, providers, checkSecrets)),
412
474
  ...customProviderRows(providers, checkSecrets),
@@ -500,7 +562,7 @@ export async function authenticateProvider(provider, secret) {
500
562
  }
501
563
  const value = String(secret || '').trim();
502
564
  if (!value) throw new Error(`API key is required for ${id}`);
503
- saveSecret(SECRET_ACCOUNTS.agentApiKey(id), value);
565
+ await saveSecretAsync(SECRET_ACCOUNTS.agentApiKey(id), value);
504
566
  return { provider: id, type: 'api-key', authenticated: true };
505
567
  }
506
568
 
@@ -638,6 +700,7 @@ export async function beginOAuthProviderLogin(cfgMod, provider, options = {}) {
638
700
  type: 'oauth',
639
701
  url: started.url,
640
702
  manualUrl: started.manualUrl || null,
703
+ userCode: started.userCode || null,
641
704
  waitForCallback: started.waitForCallback?.then(finish),
642
705
  cancel: () => {
643
706
  cancelled = true;
@@ -704,12 +767,12 @@ export function updateProviderAccounts(provider, change) {
704
767
  return listProviderAccounts(provider);
705
768
  }
706
769
 
707
- export function saveProviderApiKey(cfgMod, provider, secret) {
770
+ export async function saveProviderApiKey(cfgMod, provider, secret) {
708
771
  const id = String(provider || '').trim();
709
772
  if (!API_PROVIDER_IDS.has(id)) throw new Error(`unknown API-key provider "${id}"`);
710
773
  const value = String(secret || '').trim();
711
774
  if (!value) throw new Error(`API key is required for ${id}`);
712
- saveSecret(SECRET_ACCOUNTS.agentApiKey(id), value);
775
+ await saveSecretAsync(SECRET_ACCOUNTS.agentApiKey(id), value);
713
776
  updateConfigProvider(cfgMod, id, { enabled: true });
714
777
  return { provider: id, type: 'api-key', authenticated: true };
715
778
  }
@@ -813,7 +876,7 @@ export async function saveCustomProvider(cfgMod, rawInput) {
813
876
  const entry = { ...config, type: 'custom', enabled: config.enabled !== false };
814
877
  const account = SECRET_ACCOUNTS.agentApiKey(id);
815
878
  const previousKey = typed && existingId ? getAgentApiKey(id) : null;
816
- if (typed) saveSecret(account, typed);
879
+ if (typed) await saveSecretAsync(account, typed);
817
880
  try {
818
881
  const current = cfgMod.loadConfig();
819
882
  cfgMod.saveConfig(
@@ -822,8 +885,8 @@ export async function saveCustomProvider(cfgMod, rawInput) {
822
885
  );
823
886
  } catch (error) {
824
887
  if (typed) {
825
- if (previousKey) saveSecret(account, previousKey);
826
- else deleteSecret(account);
888
+ if (previousKey) await saveSecretAsync(account, previousKey);
889
+ else await deleteSecretAsync(account);
827
890
  }
828
891
  throw error;
829
892
  }
@@ -912,13 +975,13 @@ export async function updateCustomProviderModel(cfgMod, rawId, rawModelId, patch
912
975
  return customProviderRow(id, saved);
913
976
  }
914
977
 
915
- export function removeCustomProvider(cfgMod, rawId) {
978
+ export async function removeCustomProvider(cfgMod, rawId) {
916
979
  const id = String(rawId || '').trim();
917
980
  existingCustomEntry(cfgMod, id);
918
981
  const current = cfgMod.loadConfig();
919
982
  const { [id]: _removed, ...providers } = current.providers || {};
920
983
  cfgMod.saveConfig({ ...current, providers }, { baseConfig: current });
921
- deleteSecret(SECRET_ACCOUNTS.agentApiKey(id));
984
+ await deleteSecretAsync(SECRET_ACCOUNTS.agentApiKey(id));
922
985
  return { provider: id, type: 'custom', removed: true };
923
986
  }
924
987
 
@@ -977,15 +1040,15 @@ function discoveryErrorView(error, apiKey) {
977
1040
  };
978
1041
  }
979
1042
 
980
- export function saveOpenAIUsageSessionKey(cfgMod, secret) {
1043
+ export async function saveOpenAIUsageSessionKey(cfgMod, secret) {
981
1044
  const value = String(secret || '').trim();
982
1045
  if (!value) throw new Error('OpenAI usage session key is required for credit lookup');
983
- saveSecret(SECRET_ACCOUNTS.openaiUsageSessionKey, value);
1046
+ await saveSecretAsync(SECRET_ACCOUNTS.openaiUsageSessionKey, value);
984
1047
  updateConfigProvider(cfgMod, 'openai', { enabled: true });
985
1048
  return { provider: 'openai', type: 'usage-auth', authenticated: true };
986
1049
  }
987
1050
 
988
- export function saveOpenCodeGoUsageAuth(cfgMod, { apiKey } = {}) {
1051
+ export async function saveOpenCodeGoUsageAuth(cfgMod, { apiKey } = {}) {
989
1052
  const secret = String(apiKey || '').trim();
990
1053
  if (!secret) throw new Error('OpenCode console API key is required for usage lookup');
991
1054
  // Subscription meters are read with a console service-account key holding the
@@ -993,7 +1056,7 @@ export function saveOpenCodeGoUsageAuth(cfgMod, { apiKey } = {}) {
993
1056
  // rejecting anything else here keeps unusable auth out of the keychain.
994
1057
  if (!/^(?:oc_sk_|sk-)/.test(secret))
995
1058
  throw new Error('OpenCode usage auth must be a console API key (oc_sk_… or sk-…) with all permissions');
996
- saveSecret(SECRET_ACCOUNTS.opencodeGoConsoleKey, secret);
1059
+ await saveSecretAsync(SECRET_ACCOUNTS.opencodeGoConsoleKey, secret);
997
1060
  // Usage auth may be a different key from the inference key. Only flip the
998
1061
  // model provider on when a model key actually exists; otherwise usage-only
999
1062
  // setups get routed to the provider with apiKey 'no-key' and fail with 401s.
@@ -1001,7 +1064,7 @@ export function saveOpenCodeGoUsageAuth(cfgMod, { apiKey } = {}) {
1001
1064
  return { provider: 'opencode-go', type: 'usage-auth', authenticated: true };
1002
1065
  }
1003
1066
 
1004
- export function forgetProviderAuth(cfgModOrProvider, maybeProvider, requestedAccountId) {
1067
+ export async function forgetProviderAuth(cfgModOrProvider, maybeProvider, requestedAccountId) {
1005
1068
  const cfgMod = maybeProvider === undefined ? null : cfgModOrProvider;
1006
1069
  const id = String(maybeProvider === undefined ? cfgModOrProvider : maybeProvider || '').trim();
1007
1070
  const oauth = availableOAuthProvider(id);
@@ -1022,7 +1085,7 @@ export function forgetProviderAuth(cfgModOrProvider, maybeProvider, requestedAcc
1022
1085
  if (!API_PROVIDER_IDS.has(id)) {
1023
1086
  throw new Error(`unknown provider "${id}"`);
1024
1087
  }
1025
- deleteSecret(SECRET_ACCOUNTS.agentApiKey(id));
1088
+ await deleteSecretAsync(SECRET_ACCOUNTS.agentApiKey(id));
1026
1089
  if (cfgMod) updateConfigProvider(cfgMod, id, { enabled: false });
1027
1090
  return { provider: id, type: 'api-key', forgotten: true };
1028
1091
  }
@@ -59,16 +59,13 @@ function cleanupRetiredChannelSecrets(dataDir) {
59
59
  const marker = join(dataDir, '.retired-channel-secrets-cleaned');
60
60
  if (existsSync(marker)) return;
61
61
  void import('../../runtime/shared/config.mjs')
62
- .then(({ deleteSecret }) => {
63
- try {
64
- deleteSecret('discord.token');
65
- } catch {
66
- /* best-effort */
67
- }
68
- try {
69
- deleteSecret('telegram.token');
70
- } catch {
71
- /* best-effort */
62
+ .then(async ({ deleteSecretAsync }) => {
63
+ for (const account of ['discord.token', 'telegram.token']) {
64
+ try {
65
+ await deleteSecretAsync(account);
66
+ } catch {
67
+ /* best-effort */
68
+ }
72
69
  }
73
70
  try {
74
71
  writeFileSync(marker, `${new Date().toISOString()}\n`);
@@ -234,6 +234,10 @@ function providerDescription(id, group) {
234
234
  return 'Anthropic OAuth subscription quota';
235
235
  case 'grok-oauth':
236
236
  return 'Grok Build subscription quota';
237
+ case 'muse-code-oauth':
238
+ return 'Muse Code subscription quota';
239
+ case 'mimo-oauth':
240
+ return 'MiMo Token Plan subscription quota';
237
241
  case 'antigravity-oauth':
238
242
  return 'Antigravity subscription quota';
239
243
  case 'opencode-go':
@@ -338,6 +342,8 @@ const PROVIDER_RANKS = {
338
342
  'openai-oauth': 10,
339
343
  'anthropic-oauth': 20,
340
344
  'grok-oauth': 30,
345
+ 'muse-code-oauth': 33,
346
+ 'mimo-oauth': 34,
341
347
  'cursor-oauth': 35,
342
348
  'antigravity-oauth': 36,
343
349
  'opencode-go': 40,
@@ -8,6 +8,10 @@ import {
8
8
  setBuiltinInstalledInConfig,
9
9
  } from '../runtime/agent/orchestrator/runtime-core/builtin-features.mjs';
10
10
  import { LOCAL_PROVIDER_ID } from '../runtime/local-provider/managed-runtime.mjs';
11
+ import {
12
+ INSTRUCTION_FILES_MODES,
13
+ normalizeInstructionFilesMode,
14
+ } from '../runtime/shared/instruction-files-mode.mjs';
11
15
  import { getEmbeddingInfo } from '../runtime/memory/lib/embedding-provider.mjs';
12
16
  import { onnxRuntimeSupported } from '../runtime/shared/onnx-runtime-support.mjs';
13
17
  import {
@@ -79,6 +83,7 @@ export function createBuiltinToolSettings(
79
83
  const localProvider = getLocalProviderStatus?.() || {};
80
84
  const localProviderRuntimeInstalled = localProvider?.runtime?.installed === true;
81
85
  return {
86
+ instructionFiles: normalizeInstructionFilesMode(config?.instructionFiles),
82
87
  webSearch: { enabled: webSearchEnabled() },
83
88
  memory: {
84
89
  enabled: memoryToolsEnabledFn(),
@@ -118,6 +123,16 @@ export function createBuiltinToolSettings(
118
123
  await refreshEmptySessionToolPolicy?.();
119
124
  return this.getToolModuleSettings();
120
125
  },
126
+ /** Which user instruction files (AGENTS.md / CLAUDE.md) new sessions load:
127
+ * 'all' | 'agents' | 'claude' | 'off'. The empty session rebuilds now. */
128
+ async setInstructionFiles(mode) {
129
+ if (!INSTRUCTION_FILES_MODES.includes(mode)) {
130
+ throw new TypeError(`Instruction files must be one of ${INSTRUCTION_FILES_MODES.join(', ')}.`);
131
+ }
132
+ saveConfigAndAdopt({ ...getConfig(), instructionFiles: mode });
133
+ await refreshEmptySessionToolPolicy?.();
134
+ return this.getToolModuleSettings();
135
+ },
121
136
  async setMemoryToolsEnabled(enabled) {
122
137
  const config = getConfig();
123
138
  const memoryEnabled = enabled !== false;
@@ -1,6 +1,13 @@
1
1
  // Onboarding, the user profile (/profile) and the disabled-skill list. Methods
2
2
  // reference `this.*` so cross-member calls resolve against the facade object
3
3
  // these are spread into.
4
+ import {
5
+ mergeStarterGuide,
6
+ requiredStarterGuideState,
7
+ storedStarterGuide,
8
+ withStarterGuideOnCompletion,
9
+ } from './starter-guide-state.mjs';
10
+
4
11
  export function createProfileSettings({
5
12
  getConfig,
6
13
  adoptConfig,
@@ -19,21 +26,39 @@ export function createProfileSettings({
19
26
  version: nextConfig?.onboarding?.version || 0,
20
27
  default: nextConfig?.default || null,
21
28
  workflowRoutes: summarizeWorkflowRoutes(nextConfig),
29
+ starterGuide: storedStarterGuide(nextConfig?.onboarding?.starterGuide),
22
30
  };
23
31
  },
32
+ // Persist the desktop "Getting started" guide state beside the onboarding
33
+ // flags so it survives a lost renderer localStorage. Malformed input is
34
+ // rejected; a valid save merges forward, so a stale one cannot reopen a
35
+ // closed guide or forget a visited step.
36
+ saveStarterGuideState(state) {
37
+ const next = requiredStarterGuideState(state);
38
+ const config = getConfig();
39
+ const onboarding = config.onboarding || {};
40
+ saveConfigAndAdopt({
41
+ ...config,
42
+ onboarding: {
43
+ ...onboarding,
44
+ starterGuide: mergeStarterGuide(storedStarterGuide(onboarding.starterGuide), next),
45
+ },
46
+ });
47
+ return this.getOnboardingStatus();
48
+ },
24
49
  // Mark onboarding as done WITHOUT touching routes/agents/provider. Used by
25
50
  // the TUI "skip" (Esc) path so the wizard doesn't reappear next launch,
26
51
  // while leaving any existing config routes untouched.
27
52
  skipOnboarding() {
28
53
  const config = getConfig();
29
54
  const nextConfig = { ...config };
30
- nextConfig.onboarding = {
55
+ nextConfig.onboarding = withStarterGuideOnCompletion(config.onboarding, {
31
56
  ...(nextConfig.onboarding || {}),
32
57
  completed: true,
33
58
  version: ONBOARDING_VERSION,
34
59
  completedAt: new Date().toISOString(),
35
60
  skipped: true,
36
- };
61
+ });
37
62
  saveConfigAndAdopt(nextConfig);
38
63
  return this.getOnboardingStatus();
39
64
  },
@@ -5,6 +5,7 @@
5
5
  * registers the tool executor before it finishes assembling the API object. */
6
6
  import { builtinFeatureActive } from '../../runtime/agent/orchestrator/runtime-core/builtin-features.mjs';
7
7
  import { ORCHESTRATION_MODES } from '../../runtime/shared/orchestration.mjs';
8
+ import { INSTRUCTION_FILES_MODES } from '../../runtime/shared/instruction-files-mode.mjs';
8
9
  import { MEDIA_KINDS } from '../../runtime/media/tool-defs.mjs';
9
10
  import { SETUP_DESKTOP_DOMAINS, SETUP_HANDOFFS } from './settings-contract.mjs';
10
11
  import { executeExtendedSetupAction, publicAutomation, publicHookRule, validateMcpInput } from './extended-actions.mjs';
@@ -205,6 +206,8 @@ async function featureStatus(rt, getConfig) {
205
206
  return {
206
207
  ...Object.fromEntries(
207
208
  Object.entries(rt.getToolModuleSettings?.() || {}).map(([name, settings]) => {
209
+ // Scalar settings (instructionFiles mode) pass through as-is.
210
+ if (!settings || typeof settings !== 'object') return [name, settings];
208
211
  const keys = [...FEATURE_STATUS_KEYS, ...(FEATURE_EXTRA_STATUS_KEYS[name] || [])];
209
212
  return [name, Object.fromEntries(Object.entries(settings).filter(([key]) => keys.includes(key)))];
210
213
  })
@@ -448,6 +451,8 @@ const SETUP_ACTION_HANDLERS = {
448
451
  set_memory_enabled: (rt, args) => rt.setMemoryToolsEnabled(requireBoolean(args.enabled)),
449
452
  set_recap_enabled: (rt, args) => rt.setRecapEnabled(requireBoolean(args.enabled)),
450
453
  set_web_search_enabled: (rt, args) => rt.setWebSearchEnabled(requireBoolean(args.enabled)),
454
+ set_instruction_files: (rt, args) =>
455
+ rt.setInstructionFiles(requireEnum(args.instructionFiles, INSTRUCTION_FILES_MODES, 'instructionFiles')),
451
456
  set_builtin_enabled: (rt, args, { requestDesktop }) => {
452
457
  const name = requireEnum(args.name, SETUP_BUILTIN_TOGGLE_FEATURES, 'name');
453
458
  if (DESKTOP_HOSTED_FEATURES.includes(name)) return requestDesktop(args);
@@ -5,6 +5,7 @@ import {
5
5
  SETUP_MCP_SCHEMA,
6
6
  } from './settings-contract.mjs';
7
7
  import { MEDIA_KINDS } from '../../runtime/media/tool-defs.mjs';
8
+ import { INSTRUCTION_FILES_MODES } from '../../runtime/shared/instruction-files-mode.mjs';
8
9
 
9
10
  /** Built-in `setup` tool: the model's handle on a Mixdog user's persisted
10
11
  * configuration. The description is a contract only; how to use each action
@@ -78,6 +79,7 @@ export const SETUP_ACTION_FIELDS = Object.freeze({
78
79
  set_memory_enabled: 'enabled',
79
80
  set_recap_enabled: 'enabled',
80
81
  set_web_search_enabled: 'enabled',
82
+ set_instruction_files: 'instructionFiles',
81
83
  set_builtin_enabled: 'name enabled',
82
84
  install_builtin: 'name',
83
85
  install_local_model: 'modelId',
@@ -212,6 +214,12 @@ export const SETUP_TOOL_DEFS = Object.freeze([
212
214
  description:
213
215
  'Boolean setters (set_*_enabled, set_compaction, set_auto_update, set_mcp_enabled, set_plugin_enabled).',
214
216
  },
217
+ instructionFiles: {
218
+ type: 'string',
219
+ enum: [...INSTRUCTION_FILES_MODES],
220
+ description:
221
+ 'set_instruction_files: which user instruction files new sessions load (global ~/.codex/AGENTS.md, ~/.claude/CLAUDE.md, then the Project root\'s AGENTS.md/CLAUDE.md).',
222
+ },
215
223
  name: {
216
224
  type: 'string',
217
225
  description: `set_builtin_enabled: ${SETUP_BUILTIN_TOGGLE_FEATURES.join('|')}; install_builtin also accepts memory. Memory toggles use set_memory_enabled. revoke_linked_device: client id from status connection, not its display name; set_developer_option: option id from status developer; read_definition/delete_definition: the definition id; else MCP/plugin/provider id.`,