mixdog 0.9.2 → 0.9.3

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 (154) hide show
  1. package/package.json +2 -1
  2. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  3. package/scripts/build-tui.mjs +13 -1
  4. package/scripts/explore-bench.mjs +124 -0
  5. package/scripts/hook-bus-test.mjs +191 -0
  6. package/scripts/path-suffix-test.mjs +57 -0
  7. package/scripts/recall-bench.mjs +207 -0
  8. package/scripts/tool-smoke.mjs +7 -4
  9. package/src/agents/debugger/AGENT.md +2 -2
  10. package/src/agents/heavy-worker/AGENT.md +20 -11
  11. package/src/agents/reviewer/AGENT.md +2 -2
  12. package/src/agents/worker/AGENT.md +17 -11
  13. package/src/mixdog-session-runtime.mjs +424 -1812
  14. package/src/repl.mjs +5 -5
  15. package/src/rules/agent/30-explorer.md +8 -11
  16. package/src/rules/lead/lead-tool.md +9 -5
  17. package/src/rules/shared/01-tool.md +11 -5
  18. package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
  19. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  20. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +1 -1
  21. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  22. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +22 -68
  23. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +46 -7
  24. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
  25. package/src/runtime/agent/orchestrator/providers/gemini.mjs +1 -1
  26. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  28. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  29. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
  30. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +7 -5
  31. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +33 -23
  32. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +31 -14
  33. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
  34. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
  35. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  36. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +262 -0
  37. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  38. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  39. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  40. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +49 -0
  41. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  42. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  43. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  44. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  45. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  46. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  47. package/src/runtime/agent/orchestrator/session/loop.mjs +169 -918
  48. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +227 -0
  49. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  50. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +137 -0
  51. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  52. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  53. package/src/runtime/agent/orchestrator/session/manager.mjs +65 -1032
  54. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
  55. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +2 -2
  56. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  57. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +162 -0
  58. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  59. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  60. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
  61. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  62. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +5 -0
  63. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +50 -39
  64. package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -0
  65. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  66. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  67. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  68. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +497 -0
  69. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  70. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  71. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  72. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  73. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  74. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1192 -0
  75. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  76. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  77. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  78. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  79. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
  80. package/src/runtime/agent/orchestrator/tools/patch.mjs +3 -3
  81. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -2
  82. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +1 -2
  83. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  84. package/src/runtime/channels/index.mjs +14 -233
  85. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  86. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  87. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  88. package/src/runtime/channels/lib/output-forwarder.mjs +8 -0
  89. package/src/runtime/channels/lib/telegram-format.mjs +19 -22
  90. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  91. package/src/runtime/memory/index.mjs +314 -359
  92. package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
  93. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  94. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  95. package/src/runtime/memory/lib/memory-cycle2.mjs +56 -3
  96. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  97. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  98. package/src/runtime/memory/lib/memory.mjs +20 -0
  99. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  100. package/src/runtime/memory/lib/recall-format.mjs +183 -0
  101. package/src/runtime/memory/tool-defs.mjs +4 -4
  102. package/src/runtime/shared/abort-controller.mjs +1 -1
  103. package/src/runtime/shared/background-tasks.mjs +2 -3
  104. package/src/runtime/shared/buffered-appender.mjs +149 -0
  105. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  106. package/src/runtime/shared/task-notification-envelope.test.mjs +107 -0
  107. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  108. package/src/runtime/shared/transcript-writer.mjs +29 -2
  109. package/src/session-runtime/config-helpers.mjs +209 -0
  110. package/src/session-runtime/effort.mjs +128 -0
  111. package/src/session-runtime/fs-utils.mjs +10 -0
  112. package/src/session-runtime/model-capabilities.mjs +130 -0
  113. package/src/session-runtime/output-styles.mjs +124 -0
  114. package/src/session-runtime/plugin-mcp.mjs +114 -0
  115. package/src/session-runtime/session-text.mjs +100 -0
  116. package/src/session-runtime/statusline-route.mjs +35 -0
  117. package/src/session-runtime/tool-catalog.mjs +720 -0
  118. package/src/session-runtime/workflow.mjs +358 -0
  119. package/src/standalone/agent-tool.mjs +49 -10
  120. package/src/standalone/channel-worker.mjs +3 -2
  121. package/src/standalone/explore-tool.mjs +10 -3
  122. package/src/standalone/hook-bus.mjs +165 -8
  123. package/src/standalone/opencode-go-login.mjs +121 -0
  124. package/src/standalone/provider-admin.mjs +14 -3
  125. package/src/tui/App.jsx +884 -143
  126. package/src/tui/components/PromptInput.jsx +272 -15
  127. package/src/tui/components/ToolExecution.jsx +20 -10
  128. package/src/tui/components/tool-output-format.mjs +2 -2
  129. package/src/tui/dist/index.mjs +1771 -1947
  130. package/src/tui/engine/agent-envelope.mjs +296 -0
  131. package/src/tui/engine/boot-profile.mjs +21 -0
  132. package/src/tui/engine/labels.mjs +67 -0
  133. package/src/tui/engine/notice-text.mjs +112 -0
  134. package/src/tui/engine/queue-helpers.mjs +161 -0
  135. package/src/tui/engine/session-stats.mjs +46 -0
  136. package/src/tui/engine/tool-call-fields.mjs +23 -0
  137. package/src/tui/engine/tool-result-text.mjs +126 -0
  138. package/src/tui/engine.mjs +311 -851
  139. package/src/tui/input-editing.mjs +58 -8
  140. package/src/tui/input-editing.selection.test.mjs +75 -0
  141. package/src/tui/keyboard-protocol.mjs +2 -2
  142. package/src/tui/lib/voice-recorder.mjs +35 -19
  143. package/src/tui/markdown/format-token.mjs +7 -8
  144. package/src/tui/markdown/format-token.test.mjs +3 -3
  145. package/src/tui/paste-attachments.mjs +38 -0
  146. package/src/tui/paste-fix.test.mjs +119 -0
  147. package/src/tui/themes/base.mjs +2 -2
  148. package/src/tui/themes/kanagawa.mjs +4 -4
  149. package/src/tui/themes/teal.mjs +4 -5
  150. package/src/tui/themes/utils.mjs +1 -1
  151. package/src/ui/statusline.mjs +49 -0
  152. package/src/workflows/default/WORKFLOW.md +16 -9
  153. package/src/workflows/sequential/WORKFLOW.md +16 -11
  154. package/src/workflows/solo/WORKFLOW.md +5 -1
@@ -0,0 +1,114 @@
1
+ // Plugin/project MCP server discovery + normalization, and skill-file counting.
2
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
3
+ import { isAbsolute, join, resolve } from 'node:path';
4
+ import { clean } from './session-text.mjs';
5
+ import { readJsonSafe } from './fs-utils.mjs';
6
+
7
+ // Project-local MCP ingress: read `.mcp.json` from the project root and return
8
+ // a cleaned { name: cfg } map. Best-effort — never throws. Accepts either the
9
+ // standard `{ mcpServers: {...} }` shape or a bare name->cfg map. Self-ref
10
+ // servers (`mixdog` / `trib-plugin`) are stripped for parity with loadConfig.
11
+ // Inputs are not mutated.
12
+ export function readProjectMcpServers(cwd) {
13
+ const path = join(cwd || '.', '.mcp.json');
14
+ if (!existsSync(path)) return {};
15
+ let raw;
16
+ try {
17
+ raw = JSON.parse(readFileSync(path, 'utf8'));
18
+ } catch (error) {
19
+ process.stderr.write(`[mcp-client] Ignoring unparseable .mcp.json at ${path}: ${error?.message || String(error)}\n`);
20
+ return {};
21
+ }
22
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {};
23
+ const map = raw.mcpServers && typeof raw.mcpServers === 'object' && !Array.isArray(raw.mcpServers)
24
+ ? raw.mcpServers
25
+ : raw;
26
+ if (!map || typeof map !== 'object' || Array.isArray(map)) return {};
27
+ const out = {};
28
+ for (const [name, cfg] of Object.entries(map)) {
29
+ const key = clean(name);
30
+ if (!key) continue;
31
+ const lower = key.toLowerCase();
32
+ if (lower === 'mixdog' || lower === 'trib-plugin') continue;
33
+ if (!cfg || typeof cfg !== 'object' || Array.isArray(cfg)) continue;
34
+ // stdio entries (command + no url) spawn relative to the process launch
35
+ // dir, but mixdog tracks the project dir in memory (no process.chdir).
36
+ // Anchor their cwd to the .mcp.json directory: default when absent, resolve
37
+ // relative values against it, keep absolute values as-is.
38
+ const isStdio = typeof cfg.command === 'string' && cfg.command !== '' && !cfg.url;
39
+ if (isStdio) {
40
+ out[key] = { ...cfg, cwd: typeof cfg.cwd === 'string' && cfg.cwd ? resolve(cwd, cfg.cwd) : resolve(cwd) };
41
+ } else {
42
+ out[key] = cfg;
43
+ }
44
+ }
45
+ return out;
46
+ }
47
+
48
+ export function countSkillFiles(root) {
49
+ const skillsDir = join(root, 'skills');
50
+ if (!existsSync(skillsDir)) return 0;
51
+ let count = 0;
52
+ const walk = (dir) => {
53
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
54
+ const full = join(dir, entry.name);
55
+ if (entry.isDirectory()) walk(full);
56
+ else if (/^(SKILL|skill)\.md$/i.test(entry.name) || entry.name.toLowerCase().endsWith('.md')) count += 1;
57
+ }
58
+ };
59
+ try { walk(skillsDir); } catch { return count; }
60
+ return count;
61
+ }
62
+
63
+ export function mcpScriptForPlugin(root) {
64
+ const candidates = [
65
+ '.mcp.json',
66
+ 'scripts/run-mcp.mjs',
67
+ 'mcp/server.mjs',
68
+ 'server.mjs',
69
+ ];
70
+ return candidates.find((rel) => existsSync(join(root, rel))) || null;
71
+ }
72
+
73
+ function substitutePluginRootTokens(value, root) {
74
+ if (typeof value !== 'string') return value;
75
+ return value
76
+ .replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, root)
77
+ .replace(/\$\{CODEX_PLUGIN_ROOT\}/g, root);
78
+ }
79
+
80
+ function substitutePluginRootTokensDeep(value, root) {
81
+ if (typeof value === 'string') return substitutePluginRootTokens(value, root);
82
+ if (Array.isArray(value)) return value.map((v) => substitutePluginRootTokensDeep(v, root));
83
+ if (value && typeof value === 'object') {
84
+ const out = {};
85
+ for (const [k, v] of Object.entries(value)) out[k] = substitutePluginRootTokensDeep(v, root);
86
+ return out;
87
+ }
88
+ return value;
89
+ }
90
+
91
+ export function normalizePluginMcpServerConfig(cfg, root) {
92
+ const substituted = substitutePluginRootTokensDeep(cfg, root) || {};
93
+ const out = { ...substituted };
94
+ if (typeof out.cwd === 'string' && out.cwd) {
95
+ out.cwd = isAbsolute(out.cwd) ? out.cwd : join(root, out.cwd);
96
+ } else {
97
+ out.cwd = root;
98
+ }
99
+ return out;
100
+ }
101
+
102
+ export function pluginManifest(root) {
103
+ return readJsonSafe(join(root, '.codex-plugin', 'plugin.json'))
104
+ || readJsonSafe(join(root, 'plugin.json'))
105
+ || {};
106
+ }
107
+
108
+ export function pluginMcpServerName(plugin = {}) {
109
+ const base = clean(plugin.name || plugin.title || 'plugin')
110
+ .toLowerCase()
111
+ .replace(/[^a-z0-9_.-]+/g, '-')
112
+ .replace(/^-+|-+$/g, '');
113
+ return base ? `plugin-${base}` : 'plugin-mcp';
114
+ }
@@ -0,0 +1,100 @@
1
+ // Session message/preview text helpers. Pure, no runtime-closure deps.
2
+
3
+ export function sessionMessageText(content) {
4
+ if (content == null) return '';
5
+ if (typeof content === 'string') return content;
6
+ const parts = Array.isArray(content)
7
+ ? content
8
+ : (content && typeof content === 'object' && Array.isArray(content.content) ? content.content : null);
9
+ if (parts) {
10
+ return parts.map((part) => {
11
+ if (typeof part === 'string') return part;
12
+ return part?.text ?? '';
13
+ }).filter(Boolean).join('\n');
14
+ }
15
+ if (typeof content === 'object' && typeof content.text === 'string') return content.text;
16
+ try { return JSON.stringify(content); } catch { return String(content); }
17
+ }
18
+
19
+ export function messageContextText(message) {
20
+ if (!message || typeof message !== 'object') return '';
21
+ let text = sessionMessageText(message.content);
22
+ if (message.role === 'assistant' && Array.isArray(message.toolCalls) && message.toolCalls.length) {
23
+ try { text += `\n${JSON.stringify(message.toolCalls)}`; }
24
+ catch { text += `\n[${message.toolCalls.length} tool calls]`; }
25
+ }
26
+ if (message.role === 'tool' && message.toolCallId) text += `\n${message.toolCallId}`;
27
+ return text;
28
+ }
29
+
30
+ export function isSessionPreviewNoise(text) {
31
+ const value = String(text || '').trim();
32
+ return !value
33
+ || value.startsWith('<system-reminder>')
34
+ || value.startsWith('</system-reminder>')
35
+ || /^#\s*permission\b/i.test(value)
36
+ || /^permission:\s*/i.test(value)
37
+ || /^cwd:\s*/i.test(value);
38
+ }
39
+
40
+ export function cleanSessionPreview(text) {
41
+ return String(text || '')
42
+ .replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi, ' ')
43
+ .replace(/\s+/g, ' ')
44
+ .trim()
45
+ .slice(0, 160);
46
+ }
47
+
48
+ export function clean(value) {
49
+ return String(value ?? '').trim();
50
+ }
51
+
52
+ export function hasOwn(obj, key) {
53
+ return Object.prototype.hasOwnProperty.call(obj || {}, key);
54
+ }
55
+
56
+ export function toolResponseText(result) {
57
+ if (result && typeof result === 'object' && Array.isArray(result.content)) {
58
+ return result.content
59
+ .map((part) => (part?.type === 'text' ? part.text || '' : JSON.stringify(part)))
60
+ .join('\n');
61
+ }
62
+ if (typeof result === 'string') return result;
63
+ return JSON.stringify(result, null, 2);
64
+ }
65
+
66
+ export function isEmptyRecallText(value) {
67
+ const text = String(value || '').trim();
68
+ return !text || /^\(?no results\)?$/i.test(text) || /^\(?empty memory result\)?$/i.test(text);
69
+ }
70
+
71
+ export function currentSessionRecallRows(session, query, { limit = 10 } = {}) {
72
+ const messages = Array.isArray(session?.messages) ? session.messages : [];
73
+ if (!messages.length) return '(no results)';
74
+ const terms = [...new Set(String(query || '').toLowerCase().match(/[\p{L}\p{N}_./:-]{2,}/gu) || [])]
75
+ .filter(Boolean)
76
+ .slice(0, 16);
77
+ const max = Math.max(1, Math.min(100, Number(limit) || 10));
78
+ const rows = [];
79
+ for (let i = messages.length - 1; i >= 0 && rows.length < max; i -= 1) {
80
+ const m = messages[i];
81
+ if (!m || (m.role !== 'user' && m.role !== 'assistant' && m.role !== 'tool')) continue;
82
+ const text = messageContextText(m).replace(/\s+/g, ' ').trim();
83
+ if (!text) continue;
84
+ if (terms.length && !terms.some((term) => text.toLowerCase().includes(term))) continue;
85
+ rows.push(`[session:${i + 1}] ${m.role}: ${text.slice(0, 1000)}`);
86
+ }
87
+ return rows.length ? rows.join('\n') : '(no results)';
88
+ }
89
+
90
+ export function sessionHasConversationMessages(activeSession) {
91
+ const messages = Array.isArray(activeSession?.messages) ? activeSession.messages : [];
92
+ return messages.some((message) => {
93
+ const role = message?.role;
94
+ if (role !== 'user' && role !== 'assistant' && role !== 'tool') return false;
95
+ const text = sessionMessageText(message.content).trim();
96
+ if (!text && role !== 'assistant') return false;
97
+ if (role === 'user' && isSessionPreviewNoise(text)) return false;
98
+ return true;
99
+ });
100
+ }
@@ -0,0 +1,35 @@
1
+ // Statusline route serialization. Pure helpers.
2
+ import { clean, hasOwn } from './session-text.mjs';
3
+
4
+ export function routeForStatusline(route) {
5
+ const out = {
6
+ mode: 'fixed',
7
+ defaultProvider: route.provider,
8
+ defaultModel: route.model,
9
+ };
10
+ const preset = route.preset || {};
11
+ if (preset.id) out.presetId = preset.id;
12
+ if (preset.name) out.presetName = preset.name;
13
+ // Prefer the preset's curated label, then the route's resolved model display
14
+ // (set by refreshRouteEffort from the live/offline catalog). Without the
15
+ // route fallback, a preset-less direct model (e.g. claude-fable-5) reaches
16
+ // the statusline with no display and renders as the raw id.
17
+ const modelDisplay = clean(preset.modelDisplay) || clean(route.modelDisplay);
18
+ if (modelDisplay) out.modelDisplay = modelDisplay;
19
+ if (route.fast === true || route.fast === false) out.fast = route.fast;
20
+ else if (preset.fast === true || preset.fast === false) out.fast = preset.fast;
21
+ if (route.effectiveEffort) {
22
+ out.effort = route.effectiveEffort;
23
+ out.displayEffort = route.effectiveEffort;
24
+ } else if (hasOwn(route, 'effort')) {
25
+ delete out.effort;
26
+ delete out.displayEffort;
27
+ }
28
+ return out;
29
+ }
30
+
31
+ export function writeStatuslineRoute(statusRoutes, session, route) {
32
+ if (!session?.id || !route) return;
33
+ const clientHostPid = session?.clientHostPid || process.pid;
34
+ statusRoutes?.writeGatewaySessionRoute?.(session.id, routeForStatusline(route), { clientHostPid });
35
+ }