mixdog 1.3.3 → 1.3.5

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 (210) hide show
  1. package/LICENSES/oh-my-pi-MIT.txt +23 -0
  2. package/NOTICE.md +15 -0
  3. package/README.md +11 -0
  4. package/package.json +3 -2
  5. package/scripts/embed-google-oauth-client.mjs +49 -0
  6. package/scripts/lib/test-spawn-ledger.mjs +46 -1
  7. package/scripts/test.mjs +5 -0
  8. package/src/defaults/shipped-history.json +8 -0
  9. package/src/defaults/skills/browser-use/SKILL.md +21 -9
  10. package/src/defaults/skills/setup/SKILL.md +7 -2
  11. package/src/defaults/skills/setup/references/actions.md +82 -27
  12. package/src/defaults/skills/setup/references/surfaces.md +67 -34
  13. package/src/defaults/skills/setup-guide/SKILL.md +3 -3
  14. package/src/lib/keychain-cjs.cjs +359 -29
  15. package/src/rules/agent-mode/AGENT_MODE.md +10 -2
  16. package/src/rules/agent-mode/MIXY.md +12 -0
  17. package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +193 -0
  18. package/src/runtime/agent/orchestrator/config.mjs +18 -0
  19. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +2 -2
  20. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  21. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +31 -2
  22. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +16 -3
  23. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +42 -7
  24. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +21 -2
  25. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +23 -0
  26. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +11 -1
  27. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +68 -23
  28. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +22 -1
  29. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +18 -5
  30. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  31. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -3
  32. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +52 -8
  33. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +10 -1
  34. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +22 -6
  35. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +15 -4
  36. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +17 -2
  37. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +19 -2
  38. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +10 -4
  40. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +10 -217
  41. package/src/runtime/agent/orchestrator/providers/cursor.mjs +7 -0
  42. package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +409 -0
  43. package/src/runtime/agent/orchestrator/providers/devin-wire-families.mjs +187 -0
  44. package/src/runtime/agent/orchestrator/providers/devin-wire-identity.mjs +342 -0
  45. package/src/runtime/agent/orchestrator/providers/devin-wire-models.mjs +541 -0
  46. package/src/runtime/agent/orchestrator/providers/devin-wire-protobuf.mjs +305 -0
  47. package/src/runtime/agent/orchestrator/providers/devin-wire-request.mjs +558 -0
  48. package/src/runtime/agent/orchestrator/providers/devin-wire-schema.mjs +880 -0
  49. package/src/runtime/agent/orchestrator/providers/devin-wire-session.mjs +190 -0
  50. package/src/runtime/agent/orchestrator/providers/devin-wire-tool-args.mjs +351 -0
  51. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +560 -0
  52. package/src/runtime/agent/orchestrator/providers/devin.mjs +491 -0
  53. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -0
  54. package/src/runtime/agent/orchestrator/providers/gemini-output-limit.mjs +46 -0
  55. package/src/runtime/agent/orchestrator/providers/gemini-reasoning-relay.mjs +30 -0
  56. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +7 -11
  57. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +61 -22
  58. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -0
  59. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -4
  60. package/src/runtime/agent/orchestrator/providers/gemini.mjs +25 -2
  61. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  62. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +96 -5
  63. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +23 -5
  64. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +17 -0
  65. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +39 -3
  66. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  67. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
  68. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +6 -1
  69. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +1 -0
  70. package/src/runtime/agent/orchestrator/providers/provider-availability.mjs +19 -0
  71. package/src/runtime/agent/orchestrator/providers/registry.mjs +17 -3
  72. package/src/runtime/agent/orchestrator/providers/wire-protobuf-codec.mjs +368 -0
  73. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
  74. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +1 -1
  75. package/src/runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs +133 -0
  76. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  77. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +23 -5
  78. package/src/runtime/agent/orchestrator/runtime-core/tool-profile.mjs +35 -10
  79. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +57 -10
  80. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +9 -6
  81. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
  83. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +0 -3
  84. package/src/runtime/agent/orchestrator/session/agent-room.mjs +10 -4
  85. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +15 -3
  86. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +5 -0
  87. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +36 -4
  88. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +6 -0
  89. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +15 -3
  90. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +8 -0
  91. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +8 -1
  92. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -1
  93. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +50 -2
  94. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +3 -0
  95. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +11 -2
  96. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +11 -2
  97. package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +60 -0
  98. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +2 -0
  99. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +14 -0
  100. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -1
  101. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +13 -0
  102. package/src/runtime/agent/orchestrator/session/message-ids.mjs +5 -3
  103. package/src/runtime/agent/orchestrator/session/message-reactions.mjs +89 -0
  104. package/src/runtime/agent/orchestrator/session/message-reactions.test-support.mjs +75 -0
  105. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +0 -2
  106. package/src/runtime/agent/orchestrator/session/routine-result.mjs +11 -2
  107. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +20 -2
  108. package/src/runtime/agent/orchestrator/session/store-agent-worker-header-cache.mjs +85 -0
  109. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +125 -12
  110. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +11 -5
  111. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +4 -0
  112. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -0
  113. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +7 -1
  114. package/src/runtime/channels/lib/schedule-errors.mjs +51 -0
  115. package/src/runtime/channels/lib/scheduler.mjs +109 -14
  116. package/src/runtime/channels/lib/schedules-db.mjs +49 -0
  117. package/src/runtime/channels/lib/tool-dispatch.mjs +13 -0
  118. package/src/runtime/channels/lib/worker-main.mjs +9 -0
  119. package/src/runtime/office/com/com-adapter.mjs +17 -1
  120. package/src/runtime/office/com/office-com-host.ps1 +51 -1
  121. package/src/runtime/office/pdf/cfb-fixture.mjs +328 -0
  122. package/src/runtime/office/pdf/cfb-protection.mjs +311 -0
  123. package/src/runtime/office/pdf/document-preview.mjs +326 -8
  124. package/src/runtime/office/pdf/ooxml-crypto.mjs +469 -0
  125. package/src/runtime/office/pdf/ooxml-key-worker.mjs +14 -0
  126. package/src/runtime/office/portable/portable-soffice.mjs +45 -5
  127. package/src/runtime/shared/agent-room-host.mjs +1 -1
  128. package/src/runtime/shared/bot-hops.mjs +62 -2
  129. package/src/runtime/shared/config.mjs +49 -22
  130. package/src/runtime/shared/connector-catalog.d.mts +4 -2
  131. package/src/runtime/shared/connector-catalog.mjs +16 -8
  132. package/src/runtime/shared/developer-options.mjs +32 -1
  133. package/src/runtime/shared/google-oauth-client.generated.json +4 -0
  134. package/src/runtime/shared/google-oauth-client.mjs +34 -9
  135. package/src/runtime/shared/llm/anthropic-betas.mjs +7 -0
  136. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +12 -0
  137. package/src/runtime/shared/llm/provider-catalog-registry.mjs +1 -0
  138. package/src/runtime/shared/llm/usage-rollup.mjs +4 -383
  139. package/src/runtime/shared/llm/well-formed.mjs +66 -0
  140. package/src/runtime/shared/provider-accounts.mjs +224 -58
  141. package/src/runtime/shared/provider-api-key.mjs +19 -8
  142. package/src/runtime/shared/routine-trigger.mjs +42 -0
  143. package/src/runtime/shared/tool-execution-contract.mjs +2 -0
  144. package/src/runtime/shared/transcript-metadata.mjs +38 -0
  145. package/src/runtime/shared/user-data-guard.mjs +30 -2
  146. package/src/runtime/shared/worktrees/activity-gate.mjs +12 -5
  147. package/src/session-runtime/agent-home-delivery.mjs +57 -13
  148. package/src/session-runtime/agent-room-delivery.mjs +28 -1
  149. package/src/session-runtime/agent-task-sessions.mjs +292 -79
  150. package/src/session-runtime/agent-tool-routing.mjs +49 -4
  151. package/src/session-runtime/boot/begin.mjs +3 -2
  152. package/src/session-runtime/boot/tools.mjs +4 -0
  153. package/src/session-runtime/bot-chain.mjs +32 -0
  154. package/src/session-runtime/bot-status.mjs +7 -4
  155. package/src/session-runtime/channel-config-api.mjs +63 -17
  156. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +45 -0
  157. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +72 -5
  158. package/src/session-runtime/internal-tool-executor.mjs +4 -0
  159. package/src/session-runtime/mcp-glue/server-catalog.mjs +7 -1
  160. package/src/session-runtime/mixy-heartbeat.mjs +807 -0
  161. package/src/session-runtime/model-route/route-tuning.mjs +10 -6
  162. package/src/session-runtime/owned-automation-run.mjs +33 -12
  163. package/src/session-runtime/provider-auth/login.mjs +7 -0
  164. package/src/session-runtime/provider-models/row-hydration.mjs +13 -2
  165. package/src/session-runtime/routine-home-turn.mjs +33 -6
  166. package/src/session-runtime/schedule-session-run.mjs +7 -3
  167. package/src/session-runtime/services/agent-tool/close-flow.mjs +14 -2
  168. package/src/session-runtime/services/agent-tool/execute.mjs +6 -1
  169. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +11 -2
  170. package/src/session-runtime/services/agent-tool/render.mjs +3 -1
  171. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +8 -12
  172. package/src/session-runtime/services/agent-tool/tool-def.mjs +1 -1
  173. package/src/session-runtime/services/channel-admin.mjs +66 -18
  174. package/src/session-runtime/services/channel-worker.mjs +1 -1
  175. package/src/session-runtime/services/provider-admin.mjs +128 -25
  176. package/src/session-runtime/services/schedule-access.mjs +14 -0
  177. package/src/session-runtime/services/usage-dashboard-model.mjs +1 -0
  178. package/src/session-runtime/session-create/session-options.mjs +7 -4
  179. package/src/session-runtime/settings-system-api.mjs +16 -1
  180. package/src/session-runtime/setup-tool/executor.mjs +49 -14
  181. package/src/session-runtime/setup-tool/extended-actions.mjs +29 -3
  182. package/src/session-runtime/setup-tool/settings-contract.mjs +65 -4
  183. package/src/session-runtime/setup-tool/tool-defs.mjs +30 -3
  184. package/src/session-runtime/task-session-route.mjs +99 -0
  185. package/src/session-runtime/tool-policy/policy-stage.mjs +5 -2
  186. package/src/session-runtime/tool-policy/surface-helpers.mjs +2 -3
  187. package/src/session-runtime/tool-surface.mjs +4 -0
  188. package/src/session-runtime/usage-stats-api.mjs +1 -1
  189. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +98 -21
  190. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -1
  191. package/src/session-runtime/workspace/transitions.mjs +10 -2
  192. package/src/standalone/daemon-session-control.mjs +88 -20
  193. package/src/standalone/daemon.mjs +8 -0
  194. package/src/standalone/session-protocol.mjs +5 -0
  195. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +6 -2
  196. package/src/standalone/session-service/session-calls/session-turns.mjs +6 -0
  197. package/src/standalone/session-service.mjs +13 -0
  198. package/src/standalone/session-tool-output.mjs +8 -5
  199. package/src/tui/app/model-options.mjs +2 -0
  200. package/src/tui/dist/index.mjs +335 -172
  201. package/src/tui/session/notice-surface.mjs +17 -1
  202. package/src/tui/session/session-api/integrations.mjs +1 -0
  203. package/src/tui/session/session-api/lifecycle.mjs +8 -0
  204. package/src/tui/session/session-api/settings.mjs +14 -2
  205. package/src/tui/session/turn-stream.mjs +17 -1
  206. package/src/tui/session/turn.mjs +22 -2
  207. package/src/vendor/statusline/src/gateway/claude-current.mjs +0 -58
  208. package/src/vendor/statusline/src/gateway/route-meta.mjs +1 -361
  209. package/src/vendor/statusline/src/gateway/session-routes.mjs +0 -55
  210. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +0 -285
@@ -609,14 +609,47 @@ function balanceFromAnthropicSpend(spend) {
609
609
  return null;
610
610
  }
611
611
 
612
+ // The server's limit id, in the one spelling headers and the usage payload share.
613
+ const codexLimitId = (value) => (cleanString(value) || '').toLowerCase().replace(/-/g, '_');
614
+
615
+ // Model-specific limits beside the plan's own: one per server limit id
616
+ // (`metered_feature`), whose `limit_name` names the model it meters. Each
617
+ // window gates that model alone.
618
+ function codexAdditionalLimits(list) {
619
+ return (Array.isArray(list) ? list : []).flatMap((entry) => {
620
+ const limit = codexLimitId(entry?.metered_feature || entry?.limit_name);
621
+ if (!limit) return [];
622
+ const name = cleanString(entry.limit_name) || limit;
623
+ const rate = entry.rate_limit && typeof entry.rate_limit === 'object' ? entry.rate_limit : {};
624
+ const windows = [
625
+ windowFromPercent('5H', rate.primary_window || rate.primary, 'openai-codex-oauth'),
626
+ windowFromPercent('7D', rate.secondary_window || rate.secondary, 'openai-codex-oauth'),
627
+ ]
628
+ .filter(Boolean)
629
+ .map((window) => ({ ...window, label: `${window.label} ${name}`, limit, models: [name.toLowerCase()] }));
630
+ // A limit without a usable window meters nothing we can read: its model
631
+ // stays under the plan's windows.
632
+ return windows.length ? [{ model: name.toLowerCase(), windows }] : [];
633
+ });
634
+ }
635
+
612
636
  export function normalizeOpenAIWhamUsage(data) {
613
637
  const rate = data?.rate_limit && typeof data.rate_limit === 'object' ? data.rate_limit : null;
614
638
  if (!rate) return null;
639
+ const additional = codexAdditionalLimits(data.additional_rate_limits);
640
+ // The plan's windows gate every model that no model-specific limit meters.
641
+ const plan = {
642
+ limit: 'codex',
643
+ ...(additional.length ? { except: additional.map((entry) => entry.model) } : {}),
644
+ };
615
645
  const windows = [
616
646
  windowFromPercent('5H', rate.primary_window || rate.primary, 'openai-codex-oauth'),
617
647
  windowFromPercent('7D', rate.secondary_window || rate.secondary, 'openai-codex-oauth'),
618
- ].filter(Boolean);
648
+ ]
649
+ .filter(Boolean)
650
+ .map((window) => ({ ...window, ...plan }));
619
651
  if (!windows.length) return null;
652
+ windows.push(...additional.flatMap((entry) => entry.windows));
620
653
  return {
621
654
  provider: 'openai-oauth',
622
655
  source: 'openai-codex-wham',
@@ -687,12 +720,14 @@ function anthropicLimitWindows(limits, source) {
687
720
  .filter(Boolean);
688
721
  }
689
722
 
690
- // The extra-usage (pay-as-you-go) window when it is enabled, else null.
723
+ // The extra-usage (pay-as-you-go) window when it is enabled, else null. It is
724
+ // shown, never a limit: paid overage spent to its cap leaves the plan's own
725
+ // windows serving, so it gates no model (`models: []`).
691
726
  function anthropicExtraUsageWindow(extraUsage) {
692
727
  if (extraUsage?.is_enabled !== true) return null;
693
728
  const limitUsd = num(extraUsage.monthly_limit, 0) / 100;
694
729
  const usedUsd = num(extraUsage.used_credits, 0) / 100;
695
- return windowFromPercent(
730
+ const window = windowFromPercent(
696
731
  'EXTRA',
697
732
  {
698
733
  utilization: extraUsage.utilization,
@@ -702,6 +737,7 @@ function anthropicExtraUsageWindow(extraUsage) {
702
737
  },
703
738
  'anthropic-oauth-extra'
704
739
  );
740
+ return window ? { ...window, models: [] } : null;
705
741
  }
706
742
 
707
743
  export function normalizeAnthropicUsage(data, source = 'anthropic-oauth') {
@@ -23,6 +23,8 @@ export function createCompatStreamState({
23
23
  onToolCall,
24
24
  onTextDelta,
25
25
  onStreamItemStart,
26
+ // false: text stays text; only native tool_calls deltas become calls.
27
+ leakedToolCallRecovery = true,
26
28
  }) {
27
29
  const toolDedupe = createToolCallDedupe();
28
30
  return {
@@ -49,7 +51,7 @@ export function createCompatStreamState({
49
51
  rawUsage: null,
50
52
  toolAcc: createToolCallAccumulator(),
51
53
  streamEmitState: { emittedToolCallKeys: new Set(), emittedToolCall: false, _toolDedupe: toolDedupe },
52
- leakGuard: createLeakGuard({ knownToolNames, harmony: true }),
54
+ leakGuard: createLeakGuard({ knownToolNames: leakedToolCallRecovery ? knownToolNames : [], harmony: true }),
53
55
  leakedCalls: [],
54
56
  pendingLeakedCalls: [],
55
57
  };
@@ -44,6 +44,7 @@ export async function consumeCompatChatCompletionStream(
44
44
  knownToolNames,
45
45
  semanticIdleTimeoutMs,
46
46
  firstByteTimeoutMs,
47
+ leakedToolCallRecovery = true,
47
48
  } = {}
48
49
  ) {
49
50
  const idleOverrideEnabled = Number.isFinite(Number(semanticIdleTimeoutMs)) && Number(semanticIdleTimeoutMs) > 0;
@@ -57,6 +58,7 @@ export async function consumeCompatChatCompletionStream(
57
58
  onToolCall,
58
59
  onTextDelta,
59
60
  onStreamItemStart,
61
+ leakedToolCallRecovery,
60
62
  });
61
63
  reportTransport(state);
62
64
  const iterator = stream[Symbol.asyncIterator]();
@@ -7,6 +7,7 @@
7
7
  * headers response headers (HTTP, WS handshake, nested error.headers)
8
8
  * retryAfterMs server advice, uncapped (Retry-After / Retry-After-Ms)
9
9
  * resetAt epoch ms from quota reset fields (resets_at, resets_in_seconds)
10
+ * quotaLimit limit id from x-codex-active-limit, `-` normalized to `_`
10
11
  *
11
12
  * Pure: no I/O. Message text is never parsed for status or code.
12
13
  */
@@ -18,7 +19,7 @@ const isObject = (value) => value !== null && typeof value === 'object' && !Arra
18
19
  * Parse an HTTP error body ONCE into its contract fields. `message` is the
19
20
  * body's own message (untruncated); callers truncate for diagnostics.
20
21
  */
21
- export function parseErrorBody(text) {
22
+ function parseErrorBody(text) {
22
23
  let json = null;
23
24
  try {
24
25
  json = JSON.parse(String(text || ''));
@@ -206,6 +207,10 @@ export function applyErrorContract(err, { status, headers, code, error, event }
206
207
  const wireHeaders =
207
208
  headers ?? error?.headers ?? event?.error?.headers ?? event?.response?.error?.headers ?? event?.headers;
208
209
  if (wireHeaders && typeof wireHeaders === 'object') err.headers = wireHeaders;
210
+ // The limit a refusal hit, by the server's limit id (`codex` is the plan's
211
+ // own; others are model-specific), so the account pool blocks only it.
212
+ const activeLimit = headerValue(err.headers, 'x-codex-active-limit').toLowerCase().replace(/-/g, '_');
213
+ if (activeLimit) err.quotaLimit = activeLimit;
209
214
  const rawCode = code ?? error?.code ?? error?.type ?? null;
210
215
  if (typeof rawCode === 'string' && rawCode.trim()) err.code = rawCode.trim().toLowerCase();
211
216
  const resetAt = resetAtFrom(error, event?.error, event?.response?.error, event);
@@ -144,6 +144,7 @@ export class OpenCodeGoGeminiEngine extends GeminiProvider {
144
144
  );
145
145
  },
146
146
  afterToolResult: geminiEndsWithFunctionResponse(contents),
147
+ failureUsage: geminiFailureUsage(opts, null, useModel),
147
148
  run: async ({ signal: attemptSignal, connected }) => {
148
149
  try {
149
150
  opts.onStageChange?.('requesting');
@@ -0,0 +1,19 @@
1
+ import { modelProxyClientMode } from '../../../shared/model-proxy-mode.mjs';
2
+ import { isOAuthProviderAvailable } from './oauth-credential-probes.mjs';
3
+
4
+ /**
5
+ * Whether the named provider may be looked up or sent through right now. Remote
6
+ * client mode serves the PC's providers, whose availability the PC already
7
+ * decided; only a locally run provider answers to the local Developer option.
8
+ */
9
+ export function providerAvailable(name) {
10
+ return modelProxyClientMode() || isOAuthProviderAvailable(name);
11
+ }
12
+
13
+ /** The error a send raises when its provider was switched off after lookup. */
14
+ export function providerUnavailableError(name) {
15
+ return Object.assign(
16
+ new Error(`Provider "${name}" is disabled: its Developer option was switched off before the request was sent.`),
17
+ { code: 'EPROVIDERUNAVAILABLE', provider: name }
18
+ );
19
+ }
@@ -1,5 +1,6 @@
1
1
  import { OPENAI_COMPAT_PRESETS } from './openai-compat-presets.mjs';
2
2
  import { oauthCredentialProbeState } from './oauth-credential-probes.mjs';
3
+ import { providerAvailable } from './provider-availability.mjs';
3
4
  import { refreshCatalog as refreshMetadataCatalog } from './model-catalog.mjs';
4
5
  import { wrapProviderAdmission } from './admission-scheduler.mjs';
5
6
  import { createAccountPoolProvider } from './account-pool.mjs';
@@ -33,6 +34,7 @@ export const OAUTH_PROVIDER_NAMES = new Set([
33
34
  'command-code-oauth',
34
35
  'mimo-oauth',
35
36
  'cursor-oauth',
37
+ 'devin-oauth',
36
38
  'antigravity-oauth',
37
39
  'opencode-go-oauth',
38
40
  ]);
@@ -176,6 +178,7 @@ const PROVIDER_MODULES = {
176
178
  'command-code-oauth': ['./command-code-oauth.mjs', 'CommandCodeOAuthProvider'],
177
179
  'mimo-oauth': ['./mimo-oauth.mjs', 'MimoOAuthProvider'],
178
180
  'cursor-oauth': ['./cursor.mjs', 'CursorOAuthProvider'],
181
+ 'devin-oauth': ['./devin.mjs', 'DevinOAuthProvider'],
179
182
  'antigravity-oauth': ['./antigravity-oauth.mjs', 'AntigravityOAuthProvider'],
180
183
  'cursor-api': ['./cursor.mjs', 'CursorApiProvider'],
181
184
  openai: ['./openai-ws.mjs', 'OpenAIDirectProvider'],
@@ -290,6 +293,9 @@ async function _initProvidersUnsynchronized(config, signal = null, proxyChannel
290
293
  const enabledResults = await Promise.all(
291
294
  entries.map(async ([name, cfg]) => {
292
295
  if (!cfg.enabled) return null;
296
+ // A dev-gated provider whose Developer option is off is never admitted,
297
+ // even from a config that still says enabled:true.
298
+ if (!providerAvailable(name)) return null;
293
299
  // A retired provider's saved config is inert: its replacement is its own entry.
294
300
  if (Object.hasOwn(RETIRED_PROVIDER_ALIASES, name)) return null;
295
301
  // Idempotent reuse: an enabled provider whose config signature is
@@ -450,6 +456,9 @@ export function getProvider(requestedName) {
450
456
  const name = Object.hasOwn(RETIRED_PROVIDER_ALIASES, requestedName)
451
457
  ? RETIRED_PROVIDER_ALIASES[requestedName]
452
458
  : requestedName;
459
+ // Availability first: switching a dev-gated provider's option off takes
460
+ // effect on the next lookup, even for an already-initialized instance.
461
+ if (!providerAvailable(name)) return undefined;
453
462
  const cached = providers.get(name);
454
463
  if (cached) return cached;
455
464
  // OAuth lazy fallback. Covers the boot-time race where the credential probe
@@ -539,7 +548,12 @@ export function providerConsumesReadWindows(name) {
539
548
  export function getAllProviders() {
540
549
  // Defensive copy — callers must not mutate the live registry or retain
541
550
  // stale entries across re-init (initProviders rebuilds the map in place).
542
- return new Map(providers);
551
+ return new Map(availableProviderEntries());
552
+ }
553
+
554
+ // Live instances minus dev-gated providers whose Developer option is off.
555
+ function availableProviderEntries() {
556
+ return [...providers].filter(([name]) => providerAvailable(name));
543
557
  }
544
558
 
545
559
  export function disableProvider(name) {
@@ -629,7 +643,7 @@ export function refreshProviderCatalogsOnStartup() {
629
643
  if (_startupCatalogRefreshPromise) return _startupCatalogRefreshPromise;
630
644
  armNextCatalogRefresh();
631
645
  const pending = [];
632
- for (const [name, provider] of providers) {
646
+ for (const [name, provider] of availableProviderEntries()) {
633
647
  const refreshFn = providerCatalogRefreshFn(provider);
634
648
  if (!refreshFn) continue;
635
649
  pending.push(boundedCatalogRefresh(name, provider, refreshFn, 'startup catalog refresh'));
@@ -669,7 +683,7 @@ export function refreshCatalogs(options = {}) {
669
683
  const msg = error instanceof Error ? error.message : String(error);
670
684
  process.stderr.write(`[model-catalog] metadata refresh failed: ${msg}\n`);
671
685
  });
672
- for (const [name, provider] of providers) {
686
+ for (const [name, provider] of availableProviderEntries()) {
673
687
  const refreshFn = providerCatalogRefreshFn(provider);
674
688
  if (!refreshFn) continue;
675
689
  pending.push(metadataReady.then(() => boundedCatalogRefresh(name, provider, refreshFn, 'catalog refresh')));
@@ -0,0 +1,368 @@
1
+ // Schema-driven protobuf wire codec shared by the handwritten provider wires.
2
+ //
3
+ // A schema table maps message type names to `protobufFields([...])` rows of
4
+ // `[name, fieldNo, type, options]`. Types are scalars ('string', 'bytes',
5
+ // 'raw', 'bool', 'int', 'uint', 'double', 'float'), another message type name
6
+ // from the same table, or `mapOf(keyType, valueType)`. Options:
7
+ // repeated — the field is an array (numeric ones packed only when the
8
+ // codec is created with `packed: true`).
9
+ // presence — explicit presence (proto3 `optional` / oneof member): the value
10
+ // is written even when it equals the type default.
11
+ // 'int' covers int32/int64/enum (two's-complement varint); 'uint' covers
12
+ // uint32/uint64. Both decode to number.
13
+ // Width-specific varint types follow protobuf's per-width rules instead:
14
+ // 'int32' / 'enum' — integer number, truncated to 32 bits; decodes to number.
15
+ // 'uint32' — non-negative integer number, truncated to 32 bits; a
16
+ // varint longer than 5 bytes is rejected; decodes to number.
17
+ // 'int64' — bigint, integer number or decimal string, truncated to
18
+ // 64 bits; decodes to a signed bigint.
19
+ // 'uint64' — as int64 but negative input is rejected; decodes to an
20
+ // unsigned bigint.
21
+ // Decoded objects carry only fields seen on the wire, plus `$unknown` for
22
+ // unrecognised fields.
23
+
24
+ export function protobufFields(entries) {
25
+ return Object.fromEntries(entries.map(([name, no, type, options = {}]) => [no, { name, no, type, ...options }]));
26
+ }
27
+
28
+ export const mapOf = (key, value) => ({ map: [key, value] });
29
+ export const repeated = Object.freeze({ repeated: true });
30
+ export const presence = Object.freeze({ presence: true });
31
+
32
+ const textEncoder = new TextEncoder();
33
+ const WIDTH_TYPES = new Set(['int32', 'enum', 'uint32', 'int64', 'uint64']);
34
+ const PACKABLE_TYPES = new Set(['bool', 'int', 'uint', 'double', 'float', ...WIDTH_TYPES]);
35
+
36
+ export function concatBytes(parts) {
37
+ const length = parts.reduce((sum, part) => sum + part.length, 0);
38
+ const output = new Uint8Array(length);
39
+ let offset = 0;
40
+ for (const part of parts) {
41
+ output.set(part, offset);
42
+ offset += part.length;
43
+ }
44
+ return output;
45
+ }
46
+
47
+ export function encodeVarint(value) {
48
+ let current = BigInt(value ?? 0);
49
+ if (current < 0) current = BigInt.asUintN(64, current);
50
+ const bytes = [];
51
+ do {
52
+ let byte = Number(current & 0x7fn);
53
+ current >>= 7n;
54
+ if (current) byte |= 0x80;
55
+ bytes.push(byte);
56
+ } while (current);
57
+ return Uint8Array.from(bytes);
58
+ }
59
+
60
+ export function encodeTag(no, wireType) {
61
+ return encodeVarint((BigInt(no) << 3n) | BigInt(wireType));
62
+ }
63
+
64
+ function isDefaultScalar(value) {
65
+ if (value === 0 || value === 0n || value === false || value === '') return true;
66
+ return value instanceof Uint8Array && value.length === 0;
67
+ }
68
+
69
+ // `label` prefixes every error ("Cursor protobuf"). `strict` rejects wire-type
70
+ // mismatches, field number 0, malformed UTF-8, varints longer than 10 bytes and
71
+ // truncated fixed-width values instead of decoding them leniently.
72
+ // `omitDefaults` skips proto3 implicit-presence fields that hold their type
73
+ // default, which yields canonical proto3 bytes; repeated elements are always
74
+ // written, defaults included. `packed` encodes repeated numeric fields packed
75
+ // and decodes them from either packed or unpacked wire form.
76
+ export function createProtobufCodec(
77
+ schemas,
78
+ { label = 'protobuf', strict = false, omitDefaults = false, packed = false } = {}
79
+ ) {
80
+ // A protobuf string is data, not a text document with a disposable BOM.
81
+ const textDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: strict });
82
+
83
+ const isMessageType = (type) => typeof type === 'string' && Object.hasOwn(schemas, type);
84
+
85
+ function wireTypeFor(type) {
86
+ if (type === 'double') return 1;
87
+ if (type === 'float') return 5;
88
+ if (type?.map || ['string', 'bytes', 'raw'].includes(type) || isMessageType(type)) return 2;
89
+ return 0;
90
+ }
91
+
92
+ function scalarBytes(type, value) {
93
+ if (type === 'string') return textEncoder.encode(String(value));
94
+ if (type === 'bytes' || type === 'raw') return value instanceof Uint8Array ? value : new Uint8Array(value);
95
+ if (type === 'double' || type === 'float') {
96
+ const size = type === 'double' ? 8 : 4;
97
+ const buffer = new ArrayBuffer(size);
98
+ const view = new DataView(buffer);
99
+ if (size === 8) view.setFloat64(0, Number(value), true);
100
+ else view.setFloat32(0, Number(value), true);
101
+ return new Uint8Array(buffer);
102
+ }
103
+ if (type === 'bool') return encodeVarint(value ? 1 : 0);
104
+ if (type === 'int' || type === 'uint') return encodeVarint(value);
105
+ if (WIDTH_TYPES.has(type)) return encodeVarint(BigInt.asUintN(64, BigInt(widthValue(type, value))));
106
+ if (isMessageType(type)) return encodeMessage(type, value);
107
+ throw new Error(`Unknown ${label} type: ${type}`);
108
+ }
109
+
110
+ // Validated, width-truncated value of a width-specific varint field.
111
+ function widthValue(type, value) {
112
+ if (type === 'int64' || type === 'uint64') {
113
+ let big;
114
+ if (typeof value === 'bigint') big = value;
115
+ else if (typeof value === 'number' && Number.isInteger(value)) big = BigInt(value);
116
+ else if (typeof value === 'string') {
117
+ try {
118
+ big = BigInt(value);
119
+ } catch {
120
+ // Reported below with the field type.
121
+ }
122
+ }
123
+ if (big === undefined) throw new Error(`Invalid ${label} ${type} value: ${typeof value}`);
124
+ if (type === 'uint64' && big < 0n) throw new Error(`Invalid ${label} uint64 value: negative`);
125
+ return big;
126
+ }
127
+ if (typeof value !== 'number' || !Number.isInteger(value)) {
128
+ throw new Error(`Invalid ${label} ${type} value: ${typeof value}`);
129
+ }
130
+ if (type === 'uint32') {
131
+ if (value < 0) throw new Error(`Invalid ${label} uint32 value: negative`);
132
+ return value >>> 0;
133
+ }
134
+ return value | 0;
135
+ }
136
+
137
+ function shouldWrite(type, value) {
138
+ if (value == null) return false;
139
+ if (WIDTH_TYPES.has(type)) return !(omitDefaults && BigInt(widthValue(type, value)) === 0n);
140
+ if (type === 'string') return value !== '';
141
+ if (omitDefaults && !isMessageType(type)) return !isDefaultScalar(value);
142
+ return true;
143
+ }
144
+
145
+ function encodeField(no, type, value) {
146
+ const wireType = wireTypeFor(type);
147
+ const payload = scalarBytes(type, value);
148
+ if (wireType === 2) return concatBytes([encodeTag(no, wireType), encodeVarint(payload.length), payload]);
149
+ return concatBytes([encodeTag(no, wireType), payload]);
150
+ }
151
+
152
+ function encodeMapField(no, [keyType, valueType], value) {
153
+ const parts = [];
154
+ for (const [key, entryValue] of Object.entries(value || {})) {
155
+ const entry = concatBytes([encodeField(1, keyType, key), encodeField(2, valueType, entryValue)]);
156
+ parts.push(concatBytes([encodeTag(no, 2), encodeVarint(entry.length), entry]));
157
+ }
158
+ return parts;
159
+ }
160
+
161
+ function encodeMessage(type, value = {}) {
162
+ const schema = schemas[type];
163
+ if (!schema) throw new Error(`Unknown ${label} message: ${type}`);
164
+ const parts = [];
165
+ for (const descriptor of Object.values(schema)) {
166
+ const fieldValue = value[descriptor.name];
167
+ if (descriptor.type?.map) {
168
+ parts.push(...encodeMapField(descriptor.no, descriptor.type.map, fieldValue));
169
+ continue;
170
+ }
171
+ let values = [fieldValue];
172
+ if (descriptor.repeated) {
173
+ values = Array.isArray(fieldValue) ? fieldValue : [];
174
+ if (packed && PACKABLE_TYPES.has(descriptor.type)) {
175
+ if (values.length) {
176
+ const payload = concatBytes(values.map((item) => scalarBytes(descriptor.type, item)));
177
+ parts.push(concatBytes([encodeTag(descriptor.no, 2), encodeVarint(payload.length), payload]));
178
+ }
179
+ continue;
180
+ }
181
+ }
182
+ for (const item of values) {
183
+ // A oneof's explicit empty string is a value, not an absent field;
184
+ // canonical proto3 keeps every repeated element.
185
+ const explicit = descriptor.presence || (omitDefaults && descriptor.repeated);
186
+ if ((explicit && item != null) || shouldWrite(descriptor.type, item)) {
187
+ parts.push(encodeField(descriptor.no, descriptor.type, item));
188
+ }
189
+ }
190
+ }
191
+ return concatBytes(parts);
192
+ }
193
+
194
+ class ProtoReader {
195
+ constructor(bytes) {
196
+ this.bytes = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
197
+ this.offset = 0;
198
+ }
199
+
200
+ get done() {
201
+ return this.offset >= this.bytes.length;
202
+ }
203
+
204
+ varint() {
205
+ let value = 0n;
206
+ let shift = 0n;
207
+ while (!this.done) {
208
+ const byte = this.bytes[this.offset++];
209
+ value |= BigInt(byte & 0x7f) << shift;
210
+ if ((byte & 0x80) === 0) return value;
211
+ shift += 7n;
212
+ if (shift > (strict ? 63n : 70n)) throw new Error(`Invalid ${label} varint`);
213
+ }
214
+ throw new Error(`Truncated ${label} varint`);
215
+ }
216
+
217
+ // A 32-bit varint: at most 5 bytes, bits past 32 dropped.
218
+ varint32() {
219
+ let value = 0n;
220
+ for (let index = 0; index < 5; index++) {
221
+ if (this.done) throw new Error(`Truncated ${label} varint`);
222
+ const byte = this.bytes[this.offset++];
223
+ value |= BigInt(byte & 0x7f) << BigInt(7 * index);
224
+ if ((byte & 0x80) === 0) return Number(value & 0xffffffffn);
225
+ }
226
+ throw new Error(`Invalid ${label} 32-bit varint`);
227
+ }
228
+
229
+ take(length) {
230
+ const end = this.offset + length;
231
+ if (end > this.bytes.length) throw new Error(`Truncated ${label} field`);
232
+ const value = this.bytes.subarray(this.offset, end);
233
+ this.offset = end;
234
+ return value;
235
+ }
236
+
237
+ bytesValue() {
238
+ return this.take(Number(this.varint()));
239
+ }
240
+
241
+ skip(wireType) {
242
+ if (wireType === 0) this.varint();
243
+ else if (wireType === 1) this.take(8);
244
+ else if (wireType === 2) this.bytesValue();
245
+ else if (wireType === 5) this.take(4);
246
+ else throw new Error(`Unsupported ${label} wire type: ${wireType}`);
247
+ }
248
+ }
249
+
250
+ function decodeString(bytes) {
251
+ try {
252
+ return textDecoder.decode(bytes);
253
+ } catch (error) {
254
+ throw new Error(`Invalid ${label} UTF-8 string`, { cause: error });
255
+ }
256
+ }
257
+
258
+ function decodeScalar(reader, type, wireType) {
259
+ if (type === 'string') return decodeString(reader.bytesValue());
260
+ if (type === 'bytes' || type === 'raw') return new Uint8Array(reader.bytesValue());
261
+ if (type === 'double') {
262
+ if (wireType !== 1) throw new Error(`Invalid ${label} double`);
263
+ if (strict) {
264
+ const bytes = reader.take(8);
265
+ return new DataView(bytes.buffer, bytes.byteOffset, 8).getFloat64(0, true);
266
+ }
267
+ // Lenient read: the view is bounded by the backing buffer only.
268
+ const value = new DataView(reader.bytes.buffer, reader.bytes.byteOffset + reader.offset, 8).getFloat64(0, true);
269
+ reader.offset += 8;
270
+ return value;
271
+ }
272
+ if (type === 'float') {
273
+ if (wireType !== 5) throw new Error(`Invalid ${label} float`);
274
+ const bytes = reader.take(4);
275
+ return new DataView(bytes.buffer, bytes.byteOffset, 4).getFloat32(0, true);
276
+ }
277
+ if (type === 'bool') return reader.varint() !== 0n;
278
+ if (type === 'int') return Number(BigInt.asIntN(64, reader.varint()));
279
+ if (type === 'uint') return Number(reader.varint());
280
+ if (type === 'int32' || type === 'enum') return Number(BigInt.asIntN(32, reader.varint()));
281
+ if (type === 'uint32') return reader.varint32();
282
+ if (type === 'int64') return BigInt.asIntN(64, reader.varint());
283
+ if (type === 'uint64') return BigInt.asUintN(64, reader.varint());
284
+ if (isMessageType(type)) return decodeMessage(type, reader.bytesValue());
285
+ throw new Error(`Unknown ${label} type: ${type}`);
286
+ }
287
+
288
+ function checkWireType(descriptorName, type, wireType) {
289
+ if (strict && wireType !== wireTypeFor(type)) {
290
+ throw new Error(`Invalid ${label} wire type ${wireType} for ${descriptorName}`);
291
+ }
292
+ }
293
+
294
+ function decodeMapEntry(bytes, [keyType, valueType]) {
295
+ const reader = new ProtoReader(bytes);
296
+ let key = '';
297
+ let value = valueType === 'string' ? '' : undefined;
298
+ while (!reader.done) {
299
+ const tag = reader.varint();
300
+ const no = Number(tag >> 3n);
301
+ const wireType = Number(tag & 7n);
302
+ if (no === 1) {
303
+ checkWireType('map key', keyType, wireType);
304
+ key = decodeScalar(reader, keyType, wireType);
305
+ } else if (no === 2) {
306
+ checkWireType('map value', valueType, wireType);
307
+ value = decodeScalar(reader, valueType, wireType);
308
+ } else reader.skip(wireType);
309
+ }
310
+ return [String(key), value];
311
+ }
312
+
313
+ function decodePacked(bytes, type) {
314
+ const reader = new ProtoReader(bytes);
315
+ const wireType = wireTypeFor(type);
316
+ const values = [];
317
+ while (!reader.done) values.push(decodeScalar(reader, type, wireType));
318
+ return values;
319
+ }
320
+
321
+ function decodeMessage(type, bytes) {
322
+ const schema = schemas[type];
323
+ if (!schema) throw new Error(`Unknown ${label} message: ${type}`);
324
+ const reader = new ProtoReader(bytes);
325
+ const output = {};
326
+ while (!reader.done) {
327
+ const tag = reader.varint();
328
+ const no = Number(tag >> 3n);
329
+ const wireType = Number(tag & 7n);
330
+ if (strict && no === 0) throw new Error(`Invalid ${label} field number 0`);
331
+ const descriptor = schema[no];
332
+ if (!descriptor) {
333
+ const start = reader.offset;
334
+ reader.skip(wireType);
335
+ output.$unknown ||= [];
336
+ output.$unknown.push({
337
+ no,
338
+ wireType,
339
+ data: new Uint8Array(reader.bytes.subarray(start, reader.offset)),
340
+ });
341
+ continue;
342
+ }
343
+ if (descriptor.type?.map) {
344
+ checkWireType(descriptor.name, descriptor.type, wireType);
345
+ const [key, value] = decodeMapEntry(reader.bytesValue(), descriptor.type.map);
346
+ output[descriptor.name] ||= {};
347
+ output[descriptor.name][key] = value;
348
+ continue;
349
+ }
350
+ if (packed && descriptor.repeated && wireType === 2 && PACKABLE_TYPES.has(descriptor.type)) {
351
+ output[descriptor.name] ||= [];
352
+ output[descriptor.name].push(...decodePacked(reader.bytesValue(), descriptor.type));
353
+ continue;
354
+ }
355
+ checkWireType(descriptor.name, descriptor.type, wireType);
356
+ const value = decodeScalar(reader, descriptor.type, wireType);
357
+ if (descriptor.repeated) {
358
+ output[descriptor.name] ||= [];
359
+ output[descriptor.name].push(value);
360
+ } else {
361
+ output[descriptor.name] = value;
362
+ }
363
+ }
364
+ return output;
365
+ }
366
+
367
+ return { encodeMessage, decodeMessage, encodeField, ProtoReader };
368
+ }
@@ -158,6 +158,8 @@ export function featureDisallowedToolsFor(
158
158
  ...(builtinFeatureActive(configLike, 'media') ? [] : ['media']),
159
159
  ...(builtinFeatureActive(configLike, 'tidy') ? [] : ['tidy']),
160
160
  ...(builtinFeatureActive(configLike, 'visualize') ? [] : ['visualize']),
161
+ // Bots' emoji reactions (Settings → General → Reactions); on unless turned off.
162
+ ...(configLike?.reactions === false ? ['react'] : []),
161
163
  ];
162
164
  // Headless exec uses the Lead surface and excludes Skill/MCP tools. When
163
165
  // only its eager defaults remain, neither schemas nor loader guidance help.
@@ -27,7 +27,7 @@ substantial coding to a new agent task session (\`session create\`) with
27
27
  a self-contained brief: start it when already requested, otherwise suggest it. Then relay the
28
28
  result in your own words. Promise a follow-up only when the session result allows it.`;
29
29
 
30
- /** Mixy only: the user's other bots, its contact catalog's intro. */
30
+ /** An agent-mode Lead's contact catalog intro: the user's other bots. */
31
31
  export const MIXY_CONTACTS_INTRO = `The user's other bots. Hand one a request with \`session send\` to its chat and a
32
32
  self-contained brief.`;
33
33