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.
- package/README.md +13 -5
- package/package.json +2 -2
- package/src/defaults/agents.json +0 -28
- package/src/defaults/skills/setup/SKILL.md +5 -5
- package/src/headless-command.mjs +0 -1
- package/src/help.mjs +1 -1
- package/src/lib/rules-builder.cjs +0 -96
- package/src/rules/shared/01-tool.md +28 -26
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +2 -13
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +1 -6
- package/src/runtime/agent/orchestrator/config.mjs +3 -6
- package/src/runtime/agent/orchestrator/context/collect.mjs +2 -23
- package/src/runtime/agent/orchestrator/internal-agents.mjs +4 -45
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +3 -4
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +0 -32
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +3 -11
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
- package/src/runtime/agent/orchestrator/stall-policy.mjs +0 -1
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +1 -1
- package/src/runtime/channels/lib/webhook.mjs +1 -3
- package/src/runtime/channels/lib/worker-main.mjs +0 -19
- package/src/runtime/media/adapters/codex-image.mjs +10 -4
- package/src/runtime/media/adapters/codex-image.test.mjs +22 -0
- package/src/runtime/shared/tool-card-model.mjs +0 -1
- package/src/runtime/shared/tool-primitives.mjs +0 -1
- package/src/runtime/shared/webhooks-db.mjs +4 -4
- package/src/session-runtime/workflow-agents-api.mjs +33 -21
- package/src/session-runtime/workflow.mjs +14 -26
- package/src/standalone/agent-tool/helpers.mjs +2 -1
- package/src/tui/app/onboarding-steps.mjs +2 -3
- package/src/tui/dist/index.mjs +2 -5
- package/src/ui/statusline-agents.mjs +1 -3
- package/src/agents/debugger/AGENT.md +0 -14
- package/src/agents/debugger/agent.json +0 -6
- package/src/agents/scheduler-task/AGENT.md +0 -11
- package/src/agents/scheduler-task/agent.json +0 -6
- package/src/agents/webhook-handler/AGENT.md +0 -11
- package/src/agents/webhook-handler/agent.json +0 -6
- package/src/rules/agent/20-skip-protocol.md +0 -4
package/README.md
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|

|
|
5
5
|

|
|
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
|
-
-
|
|
122
|
-
heavy-worker
|
|
123
|
-
|
|
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`, `
|
|
164
|
-
`
|
|
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.
|
|
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",
|
package/src/defaults/agents.json
CHANGED
|
@@ -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-
|
|
76
|
-
|
|
77
|
-
|
|
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.
|
|
81
|
-
|
|
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`).
|
package/src/headless-command.mjs
CHANGED
|
@@ -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,
|
|
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
|
-
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
path/name only→`find`; exact directory entries→`list`;
|
|
10
|
-
source content/value/`path:line`→`grep`;
|
|
11
|
-
|
|
12
|
-
web/current→`search
|
|
13
|
-
|
|
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`.
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
one facet to alternative tools, reserve known work, serialize
|
|
23
|
-
calls, or cap facet count. Take the cheapest sufficient
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- Once the edit is determined, finish in one assistant turn
|
|
32
|
-
`apply_patch`
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
//
|
|
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
|
|
488
|
-
//
|
|
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/
|
|
621
|
-
//
|
|
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
|
|
25
|
-
*
|
|
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
|
|
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'
|
|
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/
|
|
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
|
-
//
|
|
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
|
-
}
|