mixdog 0.9.101 → 0.9.103

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 (40) hide show
  1. package/README.md +13 -5
  2. package/package.json +2 -2
  3. package/src/defaults/agents.json +0 -28
  4. package/src/defaults/skills/setup/SKILL.md +5 -5
  5. package/src/headless-command.mjs +0 -1
  6. package/src/help.mjs +1 -1
  7. package/src/lib/rules-builder.cjs +0 -96
  8. package/src/rules/shared/01-tool.md +28 -26
  9. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +2 -13
  10. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +1 -6
  11. package/src/runtime/agent/orchestrator/config.mjs +3 -6
  12. package/src/runtime/agent/orchestrator/context/collect.mjs +2 -23
  13. package/src/runtime/agent/orchestrator/internal-agents.mjs +4 -45
  14. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +3 -4
  15. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +0 -32
  16. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +3 -11
  17. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -3
  18. package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
  19. package/src/runtime/agent/orchestrator/stall-policy.mjs +0 -1
  20. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +1 -1
  21. package/src/runtime/channels/lib/webhook.mjs +1 -3
  22. package/src/runtime/channels/lib/worker-main.mjs +0 -19
  23. package/src/runtime/media/adapters/codex-image.mjs +10 -4
  24. package/src/runtime/media/adapters/codex-image.test.mjs +22 -0
  25. package/src/runtime/shared/tool-card-model.mjs +0 -1
  26. package/src/runtime/shared/tool-primitives.mjs +0 -1
  27. package/src/runtime/shared/webhooks-db.mjs +4 -4
  28. package/src/session-runtime/workflow-agents-api.mjs +33 -21
  29. package/src/session-runtime/workflow.mjs +14 -26
  30. package/src/standalone/agent-tool/helpers.mjs +2 -1
  31. package/src/tui/app/onboarding-steps.mjs +2 -3
  32. package/src/tui/dist/index.mjs +2 -5
  33. package/src/ui/statusline-agents.mjs +1 -3
  34. package/src/agents/debugger/AGENT.md +0 -14
  35. package/src/agents/debugger/agent.json +0 -6
  36. package/src/agents/scheduler-task/AGENT.md +0 -11
  37. package/src/agents/scheduler-task/agent.json +0 -6
  38. package/src/agents/webhook-handler/AGENT.md +0 -11
  39. package/src/agents/webhook-handler/agent.json +0 -6
  40. package/src/rules/agent/20-skip-protocol.md +0 -4
package/README.md CHANGED
@@ -4,6 +4,14 @@
4
4
  ![node](https://img.shields.io/badge/node-%3E%3D22-brightgreen)
5
5
  ![license](https://img.shields.io/badge/license-MIT-blue)
6
6
 
7
+ **Mixdog Desktop:** [Download for Windows (x64)](https://github.com/tribgames/mixdog/releases/latest/download/mixdog-desktop-win-x64.exe)
8
+ · [macOS / Linux preview builds](https://github.com/tribgames/mixdog/releases/latest)
9
+
10
+ Windows is the primary manually tested desktop platform. macOS and Linux
11
+ packages are built on native CI runners and pass packaged-runtime smoke tests,
12
+ but remain preview builds. Desktop packages are currently unsigned, so Windows
13
+ SmartScreen or macOS Gatekeeper may show a security warning.
14
+
7
15
  Standalone coding agent that runs an orchestrated, multi-provider agent
8
16
  workflow from one terminal — or from a full desktop workbench — built to
9
17
  get the same quality out of the same models with less time, cost, and
@@ -118,9 +126,9 @@ every number above live under `benchmarks/terminal-bench-2.1/`.
118
126
 
119
127
  - Skills, MCP servers, hooks, and plugins load through standard-compatible
120
128
  interfaces.
121
- - Workflow delegation through the `agent` tool and `/agents`: worker,
122
- heavy-worker, reviewer, debugger, maintainer, explorer, and
123
- web-researcher roles.
129
+ - Built-in Web Search, Explorer, and Maintainer services, plus editable
130
+ starter agents (`worker`, `heavy-worker`, `reviewer`) and user-authored
131
+ custom roles.
124
132
 
125
133
  ## Run
126
134
 
@@ -160,8 +168,8 @@ mixdog --provider anthropic-oauth --model claude-opus-5 worker "fix the failing
160
168
  mixdog --provider openai-oauth --model gpt-5.6-sol reviewer "review the current diff"
161
169
  ```
162
170
 
163
- Roles: `explore`, `worker`, `heavy-worker`, `reviewer`, `debugger`,
164
- `maintainer`, `web-researcher`.
171
+ Roles: `explore`, `worker`, `heavy-worker`, `reviewer`, `maintainer`,
172
+ `web-researcher`.
165
173
 
166
174
  ## TUI basics
167
175
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixdog",
3
- "version": "0.9.101",
3
+ "version": "0.9.103",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone mixdog coding-agent CLI/TUI workspace.",
@@ -112,7 +112,7 @@
112
112
  "test:workflow-editor": "node --test scripts/workflow-id-test.mjs scripts/workflow-pack-editor-test.mjs",
113
113
  "test:route-scope": "node --test scripts/route-scope-isolation-test.mjs",
114
114
  "test:schedule-reload": "node --test scripts/schedule-reload-arm-test.mjs",
115
- "test:media": "node --test src/runtime/media/store.test.mjs src/runtime/media/renditions.test.mjs",
115
+ "test:media": "node --test src/runtime/media/store.test.mjs src/runtime/media/renditions.test.mjs src/runtime/media/adapters/codex-image.test.mjs",
116
116
  "test:shell-harness": "node --test scripts/shell-harness-regression-test.mjs",
117
117
  "failures": "node scripts/tool-failures.mjs",
118
118
  "trace:llm": "node scripts/llm-trace-summary.mjs",
@@ -59,34 +59,6 @@
59
59
  "kind": "maintenance",
60
60
  "permission": "read",
61
61
  "stallCap": { "idleSeconds": 90, "toolRunningSeconds": 90 }
62
- },
63
- {
64
- "agent": "scheduler-task",
65
- "slot": "scheduler",
66
- "systemFile": "agents/scheduler-task/AGENT.md",
67
- "description": "Scheduled-task executor invoked by scheduler tick",
68
- "invokedBy": "scheduler",
69
- "toolSchemaProfile": "read-write-search",
70
- "schemaAllowedTools": ["code_graph", "find", "glob", "list", "grep", "read", "apply_patch", "search", "web_fetch"],
71
- "kind": "maintenance",
72
- "permission": "read-write",
73
- "inboundEvent": true,
74
- "instructionDir": "schedules",
75
- "stallCap": { "idleSeconds": 600, "toolRunningSeconds": 600 }
76
- },
77
- {
78
- "agent": "webhook-handler",
79
- "slot": "webhook",
80
- "systemFile": "agents/webhook-handler/AGENT.md",
81
- "description": "Webhook payload handler invoked by inbound webhook events",
82
- "invokedBy": "webhook",
83
- "toolSchemaProfile": "read-write-search",
84
- "schemaAllowedTools": ["code_graph", "find", "glob", "list", "grep", "read", "apply_patch", "search", "web_fetch"],
85
- "kind": "maintenance",
86
- "permission": "read-write",
87
- "inboundEvent": true,
88
- "instructionDir": "webhooks",
89
- "stallCap": { "idleSeconds": 600, "toolRunningSeconds": 600 }
90
62
  }
91
63
  ]
92
64
  }
@@ -72,13 +72,13 @@ apply it, and verify the result.
72
72
 
73
73
  ### Workflow / agent definitions
74
74
 
75
- 1. Workflows and agents are Markdown packs. Built-ins ship with mixdog
76
- (workflows `default`, `solo`; agents worker, heavy-worker, reviewer,
77
- debugger, maintainer); custom packs live at
75
+ 1. Workflows and agents are Markdown packs. Built-in services are Web Search,
76
+ Explore, and Maintainer. Mixdog ships editable starter agents `worker`,
77
+ `heavy-worker`, and `reviewer`; custom packs live at
78
78
  `<mixdogData>/workflows/<id>/WORKFLOW.md` and
79
79
  `<mixdogData>/agents/<id>/AGENT.md`.
80
- 2. Saving a built-in id writes a user override; deleting the override
81
- reverts to the built-in.
80
+ 2. Starter and user-authored agents can be edited or deleted. Built-in service
81
+ definitions stay protected while their model routes remain configurable.
82
82
  3. The desktop app (Workflows page) is the full editor. The TUI only
83
83
  switches the active workflow (`/workflow`) and per-agent model routes
84
84
  (`/agents`).
@@ -9,7 +9,6 @@ const HEADLESS_ROLE_ALIASES = new Map([
9
9
  ['worker', 'worker'],
10
10
  ['heavy', 'heavy-worker'], ['heavyworker', 'heavy-worker'], ['heavy-worker', 'heavy-worker'],
11
11
  ['review', 'reviewer'], ['reviewer', 'reviewer'],
12
- ['debug', 'debugger'], ['debugger', 'debugger'],
13
12
  ['web', 'web-researcher'], ['web-researcher', 'web-researcher'],
14
13
  ]);
15
14
  const HEADLESS_WORKFLOW_ERROR = 'option --workflow is not supported for headless role commands';
package/src/help.mjs CHANGED
@@ -21,7 +21,7 @@ export const HELP_LINES = [
21
21
  '',
22
22
  'Headless role commands require an explicit provider/model pair and run with',
23
23
  'ephemeral config/data; host behavioral config and personal state are not loaded.',
24
- 'Roles: explore, worker, heavy-worker, reviewer, debugger, maintainer, web-researcher.',
24
+ 'Roles: explore, worker, heavy-worker, reviewer, maintainer, web-researcher.',
25
25
  '',
26
26
  'Slash commands (inside mixdog):',
27
27
  ' /clear start a fresh chat (alias: /new)',
@@ -9,7 +9,6 @@
9
9
  * - buildAgentRoleContent — BP2: agent role/system rules
10
10
  * - buildAgentRetrievalInjectionContent — BP2: narrow read-only retrieval role
11
11
  * - buildLeadMetaContent — BP3: Lead memory/meta context
12
- * - buildAgentRoleSpecificContent — BP4-adjacent user/task data
13
12
  * - buildInjectionContent — legacy joined Lead session content
14
13
  *
15
14
  * 4-BP cache layout (composeSystemPrompt):
@@ -186,61 +185,6 @@ function loadOutputStyle({ PLUGIN_ROOT, DATA_DIR }) {
186
185
  return '';
187
186
  }
188
187
 
189
- /**
190
- * Resolve the DATA_DIR subdir whose *.md instruction tree is sent as
191
- * BP4-adjacent user/task data, from the agent's `instructionDir` metadata in
192
- * defaults/agents.json. Returns null when the agent declares none.
193
- *
194
- * Mirrors internal-agents.mjs getAgentInstructionDir — rules-builder is CommonJS
195
- * and cannot import the ESM module, so it reads the same source of truth
196
- * directly. Keeps the webhook-handler→webhooks / scheduler-task→schedules
197
- * mapping declarative instead of a hard-coded agent-name ternary.
198
- *
199
- * @param {string} mixdogRoot
200
- * @param {string} agent
201
- * @returns {string|null}
202
- */
203
- function resolveAgentInstructionDir(mixdogRoot, agent) {
204
- if (!mixdogRoot || !agent) return null;
205
- const metaPath = path.join(mixdogRoot, 'defaults', 'agents.json');
206
- let raw;
207
- try {
208
- raw = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
209
- } catch (e) {
210
- throw new Error(`[rules-builder] failed to read/parse agents.json at ${metaPath}: ${e.message}`);
211
- }
212
- for (const entry of (raw.agents || [])) {
213
- if (entry && entry.agent === agent) {
214
- const dir = typeof entry.instructionDir === 'string' ? entry.instructionDir.trim() : '';
215
- return dir || null;
216
- }
217
- }
218
- return null;
219
- }
220
-
221
- /**
222
- * Recursively collect all `.md` files under `dir`. Returns absolute paths
223
- * in stack-DFS order (callers sort before use). Missing/unreadable `dir`
224
- * yields an empty array — matches the previous inline `try {} catch {}`
225
- * behavior at every call site.
226
- */
227
- function collectMarkdownFilesRecursive(dir) {
228
- const collected = [];
229
- try {
230
- const stack = [dir];
231
- while (stack.length) {
232
- const current = stack.pop();
233
- const entries = fs.readdirSync(current, { withFileTypes: true });
234
- for (const entry of entries) {
235
- const full = path.join(current, entry.name);
236
- if (entry.isDirectory()) stack.push(full);
237
- else if (entry.isFile() && entry.name.endsWith('.md')) collected.push(full);
238
- }
239
- }
240
- } catch {}
241
- return collected;
242
- }
243
-
244
188
  function buildSharedToolContent({ PLUGIN_ROOT }) {
245
189
  const SHARED_DIR = path.join(PLUGIN_ROOT, 'rules', 'shared');
246
190
  return readOptional(path.join(SHARED_DIR, '01-tool.md'));
@@ -365,45 +309,6 @@ function buildAgentRetrievalInjectionContent({ PLUGIN_ROOT }) {
365
309
  return parts.join('\n');
366
310
  }
367
311
 
368
- /**
369
- * BP4-adjacent role-specific data. Only the calling role's own task / tool
370
- * detail body emits — webhook-handler gets webhooks/<all-events>/ and
371
- * scheduler gets schedules/<all-tasks>/. Other roles return ''.
372
- *
373
- * NOTE: webhook-event narrowing (one event per call) requires the inbound
374
- * payload's event id at compose time; not implemented yet, so all webhook
375
- * instruction files still ride with the webhook-handler user/task context.
376
- *
377
- * @param {object} opts
378
- * @param {string} opts.PLUGIN_ROOT
379
- * @param {string} opts.DATA_DIR
380
- * @param {string|null} opts.currentAgent
381
- * @returns {string}
382
- */
383
- function buildAgentRoleSpecificContent({ PLUGIN_ROOT, DATA_DIR, currentAgent }) {
384
- if (!currentAgent) return '';
385
- const parts = [];
386
-
387
- // The agent's instruction subdir (webhook-handler → webhooks, scheduler-task
388
- // → schedules) is declared via `instructionDir` in defaults/agents.json
389
- // rather than hard-coded here, so adding a new inbound-event agent needs only
390
- // a metadata entry.
391
- const subdirForAgent = resolveAgentInstructionDir(PLUGIN_ROOT, currentAgent);
392
- if (subdirForAgent) {
393
- const dir = path.join(DATA_DIR, subdirForAgent);
394
- const collected = collectMarkdownFilesRecursive(dir);
395
- if (collected.length > 0) {
396
- collected.sort();
397
- const blocks = collected.map(f => stripFrontmatter(readOptional(f))).filter(Boolean);
398
- if (blocks.length > 0) {
399
- parts.push([`# Agent ${subdirForAgent}`, '', blocks.join('\n\n')].join('\n'));
400
- }
401
- }
402
- }
403
-
404
- return parts.join('\n\n');
405
- }
406
-
407
312
  module.exports = {
408
313
  buildSharedToolContent,
409
314
  buildLeadRoleContent,
@@ -412,5 +317,4 @@ module.exports = {
412
317
  buildInjectionContent,
413
318
  buildAgentInjectionContent,
414
319
  buildAgentRetrievalInjectionContent,
415
- buildAgentRoleSpecificContent,
416
320
  };
@@ -1,35 +1,37 @@
1
1
  # Tool Use
2
2
 
3
- - Call `explore`, when exposed, only to locate unknown coordinates in
4
- repository source it is plain search over source trees and files; it
5
- returns locations, not analysis or solutions; never batch another
6
- exploration tool onto a facet already sent to `explore` in the same turn —
7
- narrow first, then route. Then route each anchored facet
8
- exactly once by the evidence required to determine the complete edit:
9
- path/name only→`find`; exact directory entries→`list`; wildcard paths→`glob`;
10
- source content/value/`path:line`→`grep`; known file/range→`read`; exact
11
- symbol/relation→`code_graph`;
12
- web/current→`search` when exposed; returned URL body→`web_fetch` when exposed;
13
- prior work→`recall` when exposed; durable compact English memory→`memory`
14
- when exposed;
3
+ - Retrieval narrows one way, repository→path→content; enter at the deepest
4
+ anchored tier and never widen back. Call `explore`, when exposed, only to
5
+ locate unknown coordinates in repository source plain search over source
6
+ trees and files; it returns locations, not analysis or solutions. Then
7
+ route each anchored facet exactly once by the evidence required to
8
+ determine the complete edit:
9
+ path/name only→`find`; wildcard paths→`glob`; exact directory entries→`list`;
10
+ source content/value/`path:line`→`grep`; exact symbol/relation→`code_graph`;
11
+ known file/range→`read`;
12
+ web/current→`search`, returned URL body→`web_fetch`, prior work→`recall`,
13
+ durable compact English memory→`memory`, each when exposed;
15
14
  explicit project change→`cwd`; explicit user-requested conversation reset→
16
- `session_manage`. Use only named tools present in the current tool surface.
15
+ `session_manage`. `shell` is never an exploration or editing tool. Use only
16
+ named tools present in the current tool surface.
17
17
  - Use verified paths (cwd/project/user/tool); explicit paths may be outside cwd;
18
- guessed path/name fragments use `find`. Before each tool batch, extract
19
- every independent facet, deduplicate overlap, assign exactly ONE routed tool
20
- per facet, and launch all independent calls, whatever the tool, together in
21
- one maximum-fanout turn independence alone decides batching. Never send
22
- one facet to alternative tools, reserve known work, serialize independent
23
- calls, or cap facet count. Take the cheapest sufficient evidence per facet:
18
+ before every tool batch, extract every independent facet, deduplicate
19
+ overlap, assign exactly ONE routed tool per facet, and launch all
20
+ independent calls, whatever the tool, together in one maximum-fanout turn —
21
+ every turn, widest probe to last; independence alone decides batching.
22
+ Never send one facet to alternative tools, reserve known work, serialize
23
+ independent calls, or cap facet count. Take the cheapest sufficient
24
+ evidence per facet:
24
25
  symbol relations end at `code_graph`, values/locations end at the context
25
26
  grep returns; `read` covers only what returned spans cannot, as an anchored
26
- offset/limit window — never a full-file read when a window suffices.
27
- Adjacent context around an edit point counts as needed evidence. A batch
28
- carries only the evidence needed to determine the edit; the moment evidence
29
- determines it, stop retrieving and patch. Known state is never re-acquired —
30
- neither content already read nor the effect of your own successful call.
31
- - Once the edit is determined, finish in one assistant turn with one
32
- `apply_patch` for all edits.
27
+ offset/limit window — never a full-file read when a window suffices;
28
+ adjacent context around an edit point counts as needed evidence. The moment
29
+ evidence determines the edit, stop retrieving and patch. Known state is
30
+ never re-acquired or reconfirmed a credible result is final: never
31
+ re-read, re-verify, or cross-check it.
32
+ - Once the edit is determined, finish in one assistant turn: submit every
33
+ edit as one `apply_patch` envelope per file or cohesive unit — never one
34
+ envelope for all edits; on failure re-send only the failed envelope.
33
35
  - After a call returns a background `task_id`, end the turn; its completion
34
36
  notification resumes work. Never poll; use task control only for recovery or
35
37
  a required blocking result.
@@ -110,7 +110,7 @@ export function agentCompactEventDetail(event = {}) {
110
110
  // recursion break)
111
111
  // Hidden-agent exceptions are declarative: defaults/agents.json may set
112
112
  // toolSchemaProfile when first-turn routing quality is worth a separate tool
113
- // prefix. Standard profiles are none/read/full/read-write-search.
113
+ // prefix. Standard profiles are none/read/full.
114
114
  // See manager.mjs resolveSessionTools for the single source of truth;
115
115
  // agent visibility is declared via annotations.agentHidden on each tool def.
116
116
  const HIDDEN_ROLE_TOOL_SCHEMA_PROFILES = Object.freeze({
@@ -124,17 +124,6 @@ const HIDDEN_ROLE_TOOL_SCHEMA_PROFILES = Object.freeze({
124
124
  'grep',
125
125
  'read',
126
126
  ]),
127
- 'read-write-search': Object.freeze([
128
- 'code_graph',
129
- 'find',
130
- 'glob',
131
- 'list',
132
- 'grep',
133
- 'read',
134
- 'apply_patch',
135
- 'search',
136
- 'web_fetch',
137
- ]),
138
127
  });
139
128
 
140
129
  export function resolveHiddenRoleSchemaAllowedTools(hidden) {
@@ -191,7 +180,7 @@ function maintenanceRouteToPreset(routeOrName, agent) {
191
180
  * Build an agent-backed dispatch callback.
192
181
  *
193
182
  * @param {object} opts
194
- * @param {string} opts.agent — REQUIRED; canonical agent name (worker, cycle1-agent, scheduler-task, ...)
183
+ * @param {string} opts.agent — REQUIRED; canonical agent name (worker, cycle1-agent, ...)
195
184
  * @param {string} [opts.taskType] — optional internal classification stamped on the session
196
185
  * @param {string} [opts.preset] — explicit preset override (bypasses agent → preset lookup)
197
186
  * @param {string} [opts.parentSessionId] — parent agent session for trace aggregation
@@ -47,15 +47,10 @@ import { getHiddenAgent } from '../internal-agents.mjs';
47
47
  * premium for content read back 0 times. Identified by the declarative
48
48
  * (kind:'maintenance' + toolSchemaProfile:'none') pair rather than
49
49
  * hardcoded names, so new roles sharing the pattern are covered for free.
50
- * Multi-turn maintenance roles (scheduler-task / webhook-handler) are
51
- * read-write-search and therefore excluded — they run a tool loop whose tail
52
- * caches legitimately reuse across iterations.
53
50
  */
54
51
  function isOneShotMaintenanceAgent(agent) {
55
52
  const hidden = getHiddenAgent(agent);
56
- // Shipped cycle1/2/3 declare toolSchemaProfile:'none' (no tool schema at
57
- // all). Multi-turn maintenance roles (scheduler-task/webhook-handler) are
58
- // 'read-write-search' and stay excluded.
53
+ // Shipped one-shot maintenance roles declare toolSchemaProfile:'none'.
59
54
  return Boolean(
60
55
  hidden
61
56
  && hidden.kind === 'maintenance'
@@ -26,9 +26,7 @@ export function getPluginData() {
26
26
  // Main route. Their explicit routes live canonically in `agents.explore` and
27
27
  // `agents.maintainer`; load-time migration still accepts the older workflow /
28
28
  // maintenance aliases.
29
- // scheduler/webhook still let a per-entry config.json model win first (the
30
- // caller passes it explicitly via opts.preset); the haiku default below only
31
- // applies when an entry omits its own model.
29
+ // Webhook endpoints may omit a model and use the fallback route below.
32
30
  // Legacy route slots accepted only at config ingress for migration.
33
31
  const MAINTENANCE_SLOTS = Object.freeze(['explore', 'memory']);
34
32
 
@@ -124,7 +122,6 @@ const _HAIKU_ROUTE = Object.freeze({
124
122
  model: resolveAnthropicFamilyModel('haiku'),
125
123
  });
126
124
  export const DEFAULT_MAINTENANCE = Object.freeze({
127
- scheduler: { ..._HAIKU_ROUTE },
128
125
  webhook: { ..._HAIKU_ROUTE },
129
126
  });
130
127
 
@@ -484,8 +481,8 @@ export function loadConfig(options = {}) {
484
481
  }
485
482
  }
486
483
  // Drop unknown maintenance keys (e.g. truly legacy slot names from
487
- // pre-removal installs). Every valid slot incl. scheduler/webhook —
488
- // lives in DEFAULT_MAINTENANCE, so the allow-list below is the single
484
+ // pre-removal installs). Every valid fallback slot lives in
485
+ // DEFAULT_MAINTENANCE, so the allow-list below is the single
489
486
  // ingress gate and unknown keys are dropped here.
490
487
  const allowedMaintKeys = new Set([...Object.keys(DEFAULT_MAINTENANCE), ...MAINTENANCE_SLOTS]);
491
488
  const rawMaint = {};
@@ -555,7 +555,6 @@ import {
555
555
  listHiddenAgentsByKind,
556
556
  isHiddenAgent,
557
557
  getAgentCatalogShareAgents,
558
- isInboundEventAgent,
559
558
  } from '../internal-agents.mjs';
560
559
 
561
560
  function loadAgentClassification() {
@@ -617,9 +616,8 @@ function agentSectionDirs(pluginRoot) {
617
616
  }
618
617
 
619
618
  function loadAgentSections(pluginRoot) {
620
- // agents/ holds two layouts that must BOTH be loaded:
621
- // - flat: agents/<agent>.md (legacy: scheduler-task, webhook-handler)
622
- // - nested: agents/<agent>/AGENT.md (current: worker, heavy-worker, reviewer, …)
619
+ // agents/ accepts both the compatibility flat layout and the current
620
+ // nested agents/<agent>/AGENT.md layout.
623
621
  // The previous flat-only readdir silently dropped every nested agent, so a
624
622
  // public agent like heavy-worker produced an EMPTY scoped instruction block
625
623
  // (BP2) — the model lost its agent contract and the tool smoke's
@@ -659,14 +657,6 @@ function loadAgentSections(pluginRoot) {
659
657
  // provider-specific experiment without changing today's cache layout.
660
658
  const EXPLICIT_CACHE_PROVIDERS = new Set();
661
659
 
662
- // Inbound-event maintenance agents that report results back to Lead are
663
- // declared via `inboundEvent: true` in defaults/agents.json and read
664
- // through isInboundEventAgent(). Such agents must follow
665
- // rules/agent/20-skip-protocol.md so genuine no-ops (label-only events,
666
- // dedup, "nothing to report") prefix their output with `[meta:silent]` and the
667
- // dispatch layer suppresses the Lead inject. Other agents (cycle1/cycle2 memory
668
- // maintenance, retrieval agents) never emit toward Lead.
669
-
670
660
  export function loadScopedRoleInstructions(agent, provider = null) {
671
661
  const useUnified = !!(provider && EXPLICIT_CACHE_PROVIDERS.has(provider));
672
662
  const cacheKey = useUnified ? '__unified__' : (agent || '__all__');
@@ -704,10 +694,6 @@ export function loadScopedRoleInstructions(agent, provider = null) {
704
694
  const agentSharesCatalog = agent && classification.retrieval.has(agent)
705
695
  ? new Set(getAgentCatalogShareAgents(agent))
706
696
  : new Set();
707
- const agentIsInboundEvent = agent && classification.maintenance.has(agent)
708
- ? isInboundEventAgent(agent)
709
- : false;
710
-
711
697
  try {
712
698
  const agentSections = loadAgentSections(pluginRoot);
713
699
  const hiddenPairs = loadHiddenAgentSnippets(pluginRoot);
@@ -743,13 +729,6 @@ export function loadScopedRoleInstructions(agent, provider = null) {
743
729
  const fromAgent = agentSections.find(s => s.startsWith(`## ${agent}\n`));
744
730
  if (fromAgent) agentRuleSectionsToEmit.push(fromAgent);
745
731
  }
746
- // Inbound-event agents also need the skip-protocol rule so they
747
- // can opt their no-op outputs out of the Lead inject (BP1 would
748
- // waste the bytes on unrelated agents).
749
- if (agentIsInboundEvent) {
750
- const skip = hiddenPairs.find(p => p.name === 'skip-protocol');
751
- if (skip) agentRuleSectionsToEmit.push(`## ${skip.name}\n\n${skip.body}`);
752
- }
753
732
  agentSectionsToEmit = [];
754
733
  } else if (agent) {
755
734
  // Public/custom agent — self-only agents/<agent>.md when present,
@@ -21,31 +21,18 @@
21
21
  *
22
22
  * Kind classification:
23
23
  * - 'retrieval' : short-lived hidden retrieval agents (explore).
24
- * - 'maintenance' : background-trigger hidden agents (memory cycle, scheduler,
25
- * webhook). Receive only their own self section in BP2.
24
+ * - 'maintenance' : background-trigger hidden agents (memory cycle and
25
+ * title generation). Receive only their own self section.
26
26
  *
27
27
  * Permission classification:
28
28
  * - 'read' : read-only — apply_patch/shell blocked at loop.mjs runtime
29
29
  * guard with the same error string public read-only agents see.
30
- * - 'read-write' : full tool surface used by hidden agents that legitimately
31
- * mutate state (scheduler-task launches commands,
32
- * webhook-handler persists payloads). agent-dispatch honors this
33
- * declaratively instead of forcing every hidden agent to 'read'.
30
+ * - 'read-write' : full tool surface for any future mutating hidden role.
34
31
  *
35
32
  * Tool schema profile:
36
33
  * - 'none' : no tools exposed; pure transform/classifier agents.
37
34
  * - 'read' : read/search/code-navigation tools only.
38
35
  * - 'full' : shared agent tool schema for provider cache reuse.
39
- *
40
- * Agent-specific instruction metadata (consumed by rules-builder.cjs
41
- * buildAgentRoleSpecificContent + collect.mjs):
42
- * - inboundEvent : agent reports results back to Lead and must carry the
43
- * skip-protocol rule (rules/agent/20-skip-protocol.md)
44
- * in its BP2 agent-rule block so no-op outputs opt out of the Lead
45
- * inject.
46
- * - instructionDir : DATA_DIR subdir whose *.md tree is folded into the
47
- * agent's BP4-adjacent user/task data (webhook-handler →
48
- * webhooks, scheduler-task → schedules).
49
36
  */
50
37
 
51
38
  import { readFileSync, statSync } from 'fs'
@@ -97,16 +84,9 @@ function _mergeHiddenAgentFrontmatter(entry) {
97
84
  const merged = { ...entry }
98
85
  const permission = normalizeAgentPermissionOrNone(fm.permission)
99
86
  if (permission) merged.permission = permission
100
- for (const key of ['toolSchemaProfile', 'kind', 'slot', 'maintKey', 'instructionDir']) {
87
+ for (const key of ['toolSchemaProfile', 'kind', 'slot', 'maintKey']) {
101
88
  if (typeof fm[key] === 'string' && fm[key].trim()) merged[key] = fm[key].trim()
102
89
  }
103
- for (const key of ['inboundEvent']) {
104
- if (typeof fm[key] === 'string' && fm[key].trim()) {
105
- const raw = fm[key].trim().toLowerCase()
106
- if (raw === 'true') merged[key] = true
107
- else if (raw === 'false') merged[key] = false
108
- }
109
- }
110
90
  return merged
111
91
  }
112
92
 
@@ -213,24 +193,3 @@ export function getAgentCatalogShareAgents(name) {
213
193
  if (!hidden || !Array.isArray(hidden.catalogShareAgents)) return []
214
194
  return hidden.catalogShareAgents.map((n) => String(n || '').trim()).filter(Boolean)
215
195
  }
216
-
217
- /**
218
- * True when a hidden agent reports results back to Lead and must carry the
219
- * skip-protocol rule in its BP2 agent-rule block. Replaces the hard-coded
220
- * INBOUND_EVENT_ROLES set in collect.mjs.
221
- */
222
- export function isInboundEventAgent(name) {
223
- const hidden = getHiddenAgent(name)
224
- return !!(hidden && hidden.inboundEvent === true)
225
- }
226
-
227
- /**
228
- * Return the DATA_DIR subdir whose *.md tree rides as BP4-adjacent
229
- * user/task data, or null when the agent declares none. Replaces the
230
- * webhook/scheduler ternary in rules-builder.cjs buildAgentRoleSpecificContent.
231
- */
232
- export function getAgentInstructionDir(name) {
233
- const hidden = getHiddenAgent(name)
234
- const dir = hidden && typeof hidden.instructionDir === 'string' ? hidden.instructionDir.trim() : ''
235
- return dir || null
236
- }
@@ -211,7 +211,7 @@ export async function agentLoop(provider, messages, model, tools, onToolCall, cw
211
211
  const isExplorerSession = sessionAgent === 'explorer' || sessionAgent === 'explore';
212
212
  const loopUsageMetricsEpoch = () => Number(sessionRef?.usageMetricsEpoch) || 0;
213
213
  const loopUsageMetricsTurnId = () => Number(sessionRef?.usageMetricsTurnId) || 0;
214
- // Sub-agent (worker/heavy-worker/reviewer/debugger/explore/…) sessions
214
+ // Sub-agent (worker/heavy-worker/reviewer/explore/…) sessions
215
215
  // drop mid-turn assistant preamble text outright. Only the final
216
216
  // <final-answer> reply is consumed by Lead, so any "Now let me…" prose
217
217
  // that precedes a tool call is pure noise — both for live surfacing AND
@@ -798,9 +798,8 @@ export async function agentLoop(provider, messages, model, tools, onToolCall, cw
798
798
  // Opus 4.6 emits 'Now I'll polish…' preamble before its first tool
799
799
  // call) and used to leave the session idle until the idle sweep
800
800
  // collected it. Re-prompt the worker with a contract reminder on each
801
- // empty turn (hard iteration cap bounds total turns). Hidden roles
802
- // (cycle1-agent / cycle2-agent / explorer /
803
- // scheduler-task / webhook-handler) are exempt:
801
+ // empty turn (hard iteration cap bounds total turns). Hidden roles are
802
+ // exempt:
804
803
  // their own role rules define a different output contract (pipe-
805
804
  // separated chunker output, structured pipe-format, etc.) and a
806
805
  // text-only terminal turn is the correct shape — nudging them
@@ -5,7 +5,6 @@ import { createRequire } from 'module';
5
5
  import { join } from 'path';
6
6
  import { resolvePluginData, mixdogRoot } from '../../../../shared/plugin-paths.mjs';
7
7
  import { maxMtimeRecursive } from '../../cache-mtime.mjs';
8
- import { getAgentInstructionDir } from '../../internal-agents.mjs';
9
8
 
10
9
  // Phase B: Pool B Tier 2 content builder (common rules only).
11
10
  // Loaded once per process via createRequire so the CJS module reaches us.
@@ -122,34 +121,3 @@ export function _buildLeadMetaContext() {
122
121
  throw new Error(`[session] lead meta context build failed: ${e.message}`);
123
122
  }
124
123
  }
125
-
126
- // BP4-adjacent agent-specific data cache — keyed by agent. webhook / schedule
127
- // agents each have their own scoped instruction set; other agents return ''.
128
- const _roleSpecificCache = new Map(); // agent → { value, mtime }
129
- export function _buildAgentSpecific(currentAgent) {
130
- if (!_rulesBuilder || typeof _rulesBuilder.buildAgentRoleSpecificContent !== 'function') return '';
131
- if (!currentAgent) return '';
132
- const PLUGIN_ROOT = mixdogRoot();
133
- const DATA_DIR = resolvePluginData();
134
- const RULES_DIR = join(PLUGIN_ROOT, 'rules');
135
- const roleInstructionDir = getAgentInstructionDir(currentAgent);
136
- const mtime = maxMtimeRecursive([
137
- join(RULES_DIR, 'shared'),
138
- join(DATA_DIR, 'mixdog-config.json'),
139
- join(DATA_DIR, 'webhooks'),
140
- join(DATA_DIR, 'schedules'),
141
- ...(roleInstructionDir ? [join(DATA_DIR, roleInstructionDir)] : []),
142
- join(PLUGIN_ROOT, 'defaults', 'agents.json'),
143
- ]);
144
- const entry = _roleSpecificCache.get(currentAgent);
145
- if (entry && mtime <= entry.mtime) {
146
- return entry.value;
147
- }
148
- try {
149
- const built = _rulesBuilder.buildAgentRoleSpecificContent({ PLUGIN_ROOT, DATA_DIR, currentAgent });
150
- _roleSpecificCache.set(currentAgent, { mtime, value: built });
151
- return built;
152
- } catch (e) {
153
- throw new Error(`[session] agent-specific rules build failed (agent: ${currentAgent}): ${e.message}`);
154
- }
155
- }