mixdog 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -1
- package/scripts/_bench-cwc.json +20 -0
- package/scripts/agent-loop-policy-test.mjs +37 -0
- package/scripts/agent-parallel-smoke.mjs +54 -10
- package/scripts/background-task-meta-smoke.mjs +1 -1
- package/scripts/bench-run.mjs +262 -0
- package/scripts/compact-smoke.mjs +12 -0
- package/scripts/compact-trigger-migration-smoke.mjs +67 -1
- package/scripts/ingest-pure-conversation-smoke.mjs +148 -0
- package/scripts/internal-comms-bench.mjs +727 -0
- package/scripts/internal-comms-smoke.mjs +75 -0
- package/scripts/lead-workflow-smoke.mjs +4 -4
- package/scripts/live-worker-smoke.mjs +9 -9
- package/scripts/output-style-bench.mjs +285 -0
- package/scripts/output-style-smoke.mjs +13 -10
- package/scripts/patch-replay.mjs +90 -0
- package/scripts/provider-stream-stall-test.mjs +276 -0
- package/scripts/provider-toolcall-test.mjs +599 -1
- package/scripts/routing-corpus.mjs +281 -0
- package/scripts/session-bench.mjs +1526 -0
- package/scripts/session-diag.mjs +595 -0
- package/scripts/task-bench.mjs +207 -0
- package/scripts/tool-failures.mjs +6 -6
- package/scripts/tool-smoke.mjs +306 -66
- package/scripts/toolcall-args-test.mjs +81 -0
- package/src/agents/debugger/AGENT.md +4 -4
- package/src/agents/heavy-worker/AGENT.md +4 -2
- package/src/agents/reviewer/AGENT.md +4 -4
- package/src/agents/worker/AGENT.md +4 -2
- package/src/app.mjs +10 -6
- package/src/defaults/{hidden-roles.json → agents.json} +7 -7
- package/src/examples/schedules/SCHEDULE.example.md +32 -0
- package/src/examples/webhooks/WEBHOOK.example.md +40 -0
- package/src/headless-role.mjs +14 -14
- package/src/help.mjs +1 -0
- package/src/lib/rules-builder.cjs +32 -54
- package/src/mixdog-session-runtime.mjs +710 -318
- package/src/output-styles/default.md +12 -7
- package/src/output-styles/minimal.md +25 -0
- package/src/output-styles/oneline.md +21 -0
- package/src/output-styles/simple.md +10 -9
- package/src/repl.mjs +12 -2
- package/src/rules/agent/00-common.md +7 -5
- package/src/rules/agent/30-explorer.md +7 -8
- package/src/rules/lead/01-general.md +3 -1
- package/src/rules/lead/lead-tool.md +7 -0
- package/src/rules/shared/01-tool.md +17 -12
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +90 -32
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +32 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +18 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +23 -20
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +48 -14
- package/src/runtime/agent/orchestrator/agent-trace.mjs +87 -12
- package/src/runtime/agent/orchestrator/config.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +131 -67
- package/src/runtime/agent/orchestrator/{internal-roles.mjs → internal-agents.mjs} +72 -72
- package/src/runtime/agent/orchestrator/internal-tools.mjs +13 -26
- package/src/runtime/agent/orchestrator/mcp/client.mjs +94 -16
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +188 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +444 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +332 -57
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +59 -32
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +27 -20
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +184 -17
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +8 -1
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +18 -8
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +210 -21
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +78 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +202 -98
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +183 -20
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +11 -5
- package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +15 -9
- package/src/runtime/agent/orchestrator/session/compact.mjs +560 -51
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +250 -3
- package/src/runtime/agent/orchestrator/session/loop.mjs +394 -132
- package/src/runtime/agent/orchestrator/session/manager.mjs +217 -170
- package/src/runtime/agent/orchestrator/session/store.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -0
- package/src/runtime/agent/orchestrator/stall-policy.mjs +63 -15
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +194 -24
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +143 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +34 -18
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +0 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-formatting.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +15 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +9 -44
- package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +13 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +10 -17
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +18 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4076 -3985
- package/src/runtime/agent/orchestrator/tools/patch.mjs +116 -2
- package/src/runtime/channels/backends/discord.mjs +99 -9
- package/src/runtime/channels/backends/telegram.mjs +501 -0
- package/src/runtime/channels/index.mjs +441 -1224
- package/src/runtime/channels/lib/config.mjs +54 -2
- package/src/runtime/channels/lib/format.mjs +4 -2
- package/src/runtime/channels/lib/output-forwarder.mjs +80 -67
- package/src/runtime/channels/lib/runtime-paths.mjs +29 -0
- package/src/runtime/channels/lib/scheduler.mjs +1 -1
- package/src/runtime/channels/lib/telegram-format.mjs +283 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +19 -1
- package/src/runtime/channels/lib/webhook.mjs +59 -31
- package/src/runtime/channels/tool-defs.mjs +1 -1
- package/src/runtime/memory/index.mjs +184 -19
- package/src/runtime/memory/lib/agent-ipc.mjs +2 -2
- package/src/runtime/memory/lib/core-memory-store.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2.mjs +9 -6
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
- package/src/runtime/memory/lib/memory.mjs +101 -4
- package/src/runtime/memory/lib/pg/adapter.mjs +139 -15
- package/src/runtime/memory/lib/session-ingest.mjs +107 -0
- package/src/runtime/memory/lib/trace-store.mjs +69 -22
- package/src/runtime/memory/tool-defs.mjs +6 -3
- package/src/runtime/shared/channel-notification-routing.mjs +12 -0
- package/src/runtime/shared/channel-notification-routing.test.mjs +45 -0
- package/src/runtime/shared/config.mjs +9 -0
- package/src/runtime/shared/llm/http-agent.mjs +12 -5
- package/src/runtime/shared/schedules-store.mjs +21 -19
- package/src/runtime/shared/tool-surface.mjs +98 -13
- package/src/runtime/shared/transcript-writer.mjs +129 -0
- package/src/runtime/shared/update-checker.mjs +214 -0
- package/src/standalone/agent-tool.mjs +255 -109
- package/src/standalone/channel-admin.mjs +133 -40
- package/src/standalone/channel-worker.mjs +8 -291
- package/src/standalone/explore-tool.mjs +2 -2
- package/src/standalone/memory-runtime-proxy.mjs +3 -1
- package/src/standalone/provider-admin.mjs +11 -0
- package/src/standalone/usage-dashboard.mjs +1 -1
- package/src/tui/App.jsx +2096 -732
- package/src/tui/components/ConfirmBar.jsx +47 -0
- package/src/tui/components/ContextPanel.jsx +5 -3
- package/src/tui/components/ItemRightHintOverprint.jsx +54 -0
- package/src/tui/components/Markdown.jsx +22 -98
- package/src/tui/components/Message.jsx +14 -35
- package/src/tui/components/Picker.jsx +87 -12
- package/src/tui/components/PromptInput.jsx +83 -7
- package/src/tui/components/QueuedCommands.jsx +1 -1
- package/src/tui/components/SlashCommandPalette.jsx +8 -5
- package/src/tui/components/Spinner.jsx +7 -7
- package/src/tui/components/StatusLine.jsx +40 -21
- package/src/tui/components/TextEntryPanel.jsx +51 -7
- package/src/tui/components/ToolExecution.jsx +170 -98
- package/src/tui/components/TurnDone.jsx +4 -4
- package/src/tui/components/UsagePanel.jsx +1 -1
- package/src/tui/components/tool-output-format.mjs +159 -21
- package/src/tui/components/tool-output-format.test.mjs +87 -0
- package/src/tui/display-width.mjs +69 -0
- package/src/tui/display-width.test.mjs +35 -0
- package/src/tui/dist/index.mjs +6965 -2391
- package/src/tui/engine.mjs +287 -126
- package/src/tui/index.jsx +117 -7
- package/src/tui/keyboard-protocol.mjs +42 -0
- package/src/tui/lib/voice-recorder.mjs +453 -0
- package/src/tui/markdown/format-token.mjs +129 -76
- package/src/tui/markdown/format-token.test.mjs +61 -19
- package/src/tui/markdown/measure-rendered-rows.mjs +85 -0
- package/src/tui/markdown/render-ansi.test.mjs +1 -1
- package/src/tui/markdown/streaming-markdown.mjs +167 -0
- package/src/tui/markdown/streaming-markdown.test.mjs +70 -0
- package/src/tui/markdown/table-layout.mjs +9 -9
- package/src/tui/paste-attachments.mjs +0 -11
- package/src/tui/prompt-history-store.mjs +129 -0
- package/src/tui/prompt-history-store.test.mjs +52 -0
- package/src/tui/statusline-ansi-bridge.test.mjs +3 -3
- package/src/tui/theme.mjs +41 -657
- package/src/tui/themes/base.mjs +86 -0
- package/src/tui/themes/basic.mjs +85 -0
- package/src/tui/themes/catppuccin.mjs +72 -0
- package/src/tui/themes/dracula.mjs +70 -0
- package/src/tui/themes/everforest.mjs +71 -0
- package/src/tui/themes/gruvbox.mjs +71 -0
- package/src/tui/themes/index.mjs +71 -0
- package/src/tui/themes/indigo.mjs +78 -0
- package/src/tui/themes/kanagawa.mjs +80 -0
- package/src/tui/themes/light.mjs +81 -0
- package/src/tui/themes/nord.mjs +72 -0
- package/src/tui/themes/onedark.mjs +16 -0
- package/src/tui/themes/rosepine.mjs +70 -0
- package/src/tui/themes/teal.mjs +81 -0
- package/src/tui/themes/tokyonight.mjs +79 -0
- package/src/tui/themes/utils.mjs +106 -0
- package/src/tui/themes/warm.mjs +79 -0
- package/src/tui/transcript-tool-failures.mjs +13 -2
- package/src/ui/markdown.mjs +1 -1
- package/src/ui/model-display.mjs +2 -2
- package/src/ui/statusline.mjs +26 -27
- package/src/vendor/statusline/bin/statusline-route.mjs +5 -12
- package/src/vendor/statusline/src/gateway/claude-current.mjs +3 -3
- package/src/vendor/statusline/src/gateway/route-meta.mjs +30 -16
- package/src/workflows/default/WORKFLOW.md +39 -12
- package/src/workflows/sequential/WORKFLOW.md +46 -0
- package/src/workflows/solo/WORKFLOW.md +7 -0
- package/vendor/ink/build/display-width.js +62 -0
- package/vendor/ink/build/ink.js +100 -12
- package/vendor/ink/build/measure-text.js +4 -1
- package/vendor/ink/build/output.js +115 -9
- package/vendor/ink/build/render-node-to-output.js +4 -1
- package/vendor/ink/build/render.js +4 -0
- package/src/output-styles/extreme-simple.md +0 -20
- package/src/rules/lead/04-workflow.md +0 -51
- package/src/workflows/default/workflow.json +0 -13
- package/src/workflows/solo/workflow.json +0 -7
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: default
|
|
3
|
-
title:
|
|
3
|
+
title: Default
|
|
4
4
|
description: Concise engineering summaries
|
|
5
5
|
keep-coding-instructions: true
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Output Style
|
|
9
9
|
|
|
10
|
-
Mixdog default —
|
|
10
|
+
Mixdog default — the most detailed of the three styles, but only as long as the
|
|
11
|
+
task warrants.
|
|
11
12
|
|
|
12
|
-
- Lead with the outcome
|
|
13
|
-
|
|
13
|
+
- Lead with the outcome, then add the supporting detail that matters: what
|
|
14
|
+
changed, key evidence (paths, commands, errors, verification), and important
|
|
15
|
+
context. Include trade-offs or follow-up only when they actually matter.
|
|
16
|
+
- Use a few bullets or short paragraphs when they add signal; this style may run
|
|
17
|
+
longer than Simple, but match length to the work — do not pad a small change
|
|
18
|
+
into a full report. Cut anything that does not earn its place.
|
|
14
19
|
- Use labels such as `바뀐 점`, `확인한 것`, and `남은 리스크/다음 단계`
|
|
15
|
-
|
|
16
|
-
- Collapse trivial tasks to a
|
|
20
|
+
in final reports to structure the summary; skip labels on interim progress.
|
|
21
|
+
- Collapse trivial tasks to a couple of sentences instead of forcing sections.
|
|
17
22
|
- Synthesize agent or retrieval results; never forward raw reports, long file
|
|
18
23
|
lists, tool traces, or session metadata.
|
|
19
24
|
- Do not hide blockers, failed verification, or required follow-up; surface them
|
|
20
|
-
|
|
25
|
+
explicitly rather than omitting them.
|
|
21
26
|
- Keep paths, commands, symbols, API names, code, and exact errors verbatim.
|
|
22
27
|
- Never name this style unless asked.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: minimal
|
|
3
|
+
title: Minimal
|
|
4
|
+
description: One- or two-sentence summary
|
|
5
|
+
aliases: extreme, extreme-simple
|
|
6
|
+
keep-coding-instructions: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Output Style
|
|
10
|
+
|
|
11
|
+
Minimal — a very short summary: one or two sentences, nothing more.
|
|
12
|
+
|
|
13
|
+
- Summarize only the net result in one short sentence; add a second short
|
|
14
|
+
sentence only if a second fact (verification, blocker) genuinely needs it.
|
|
15
|
+
Never cram unrelated facts into one run-on line just to stay at one sentence.
|
|
16
|
+
- Summarize, never itemize: do not describe which files changed or how they were
|
|
17
|
+
edited. State only what the change accomplishes.
|
|
18
|
+
- No headings, bullets, numbered lists, labels, or sections — plain sentences
|
|
19
|
+
only. This holds even when the request says "report" or "summary"; keep it to
|
|
20
|
+
one or two sentences regardless.
|
|
21
|
+
- Preferred pattern: `<target> 변경되었습니다. <verification> 통과 완료입니다.`
|
|
22
|
+
- If verification was not run, say the change is done and verification was not
|
|
23
|
+
run.
|
|
24
|
+
- Preserve only the single decisive path, command, symbol, API name, code, or
|
|
25
|
+
error verbatim.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oneline
|
|
3
|
+
title: Oneline
|
|
4
|
+
description: Single sentence under 100 characters
|
|
5
|
+
aliases: one-line, one line, mono
|
|
6
|
+
keep-coding-instructions: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Output Style
|
|
10
|
+
|
|
11
|
+
Oneline — the most minimal style: exactly one sentence, under 100 characters.
|
|
12
|
+
|
|
13
|
+
- Reply with a SINGLE sentence, always under 100 characters. Never a second
|
|
14
|
+
sentence, clause pile-up, or run-on that smuggles in extra facts.
|
|
15
|
+
- State only the net result. Drop file lists, how-it-was-done, verification
|
|
16
|
+
detail, and follow-ups unless one is the single most decisive fact.
|
|
17
|
+
- No headings, bullets, numbered lists, labels, or sections — one plain sentence
|
|
18
|
+
only, even when the request says "report" or "summary".
|
|
19
|
+
- Preferred pattern: `<target> 변경 완료.`
|
|
20
|
+
- Preserve only the single decisive path, command, symbol, or error verbatim,
|
|
21
|
+
and only if it fits the limit.
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: simple
|
|
3
|
-
title:
|
|
3
|
+
title: Simple
|
|
4
4
|
description: Outcome-first concise handoffs for coding work
|
|
5
|
-
aliases:
|
|
5
|
+
aliases: concise, handoff
|
|
6
6
|
keep-coding-instructions: true
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Output Style
|
|
10
10
|
|
|
11
|
-
Practical concise — outcome-first handoffs for coding work
|
|
11
|
+
Practical concise — outcome-first handoffs for coding work: summarize the result,
|
|
12
|
+
do not narrate the change.
|
|
12
13
|
|
|
13
14
|
- Open with the outcome in one sentence: done, blocked, or awaiting a decision.
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
not
|
|
17
|
-
- Keep controlled detail: usually 1–3 short bullets or 2–4 sentences total.
|
|
18
|
-
|
|
19
|
-
next steps.
|
|
15
|
+
- Summarize what the change accomplishes rather than listing every file and how
|
|
16
|
+
each was edited. Name a path (`file_path:line_number`) only when the reader
|
|
17
|
+
truly needs it to navigate — not as a per-file changelog.
|
|
18
|
+
- Keep controlled detail: usually 1–3 short bullets or 2–4 sentences total. No
|
|
19
|
+
step-by-step narration, no exhaustive file/line inventory. Expand only when the
|
|
20
|
+
user asks, scope is ambiguous, or a blocker needs concrete next steps.
|
|
20
21
|
- On final handoffs, optional labels such as `바뀐 점`, `확인한 것`, and
|
|
21
22
|
`남은 리스크/다음 단계` fit Korean-facing profiles; use plain English labels
|
|
22
23
|
when the thread is English. Do not label interim progress.
|
package/src/repl.mjs
CHANGED
|
@@ -160,13 +160,17 @@ export async function runRepl({ provider: providerName, model, toolMode = 'full'
|
|
|
160
160
|
|
|
161
161
|
let streamedText = '';
|
|
162
162
|
let printedAny = false;
|
|
163
|
+
let printedToolCard = false;
|
|
163
164
|
try {
|
|
164
165
|
const runtime = await ensureRuntime();
|
|
165
166
|
const { result } = await runtime.ask(
|
|
166
167
|
line,
|
|
167
168
|
{
|
|
168
169
|
onToolCall: async (_iter, calls) => {
|
|
169
|
-
for (const c of calls || [])
|
|
170
|
+
for (const c of calls || []) {
|
|
171
|
+
printedToolCard = true;
|
|
172
|
+
stdout.write('\n' + (await renderToolCardLazy(c)) + '\n');
|
|
173
|
+
}
|
|
170
174
|
},
|
|
171
175
|
onTextDelta: (chunk) => {
|
|
172
176
|
printedAny = true;
|
|
@@ -184,12 +188,18 @@ export async function runRepl({ provider: providerName, model, toolMode = 'full'
|
|
|
184
188
|
// raw text already on screen is fine (and we must not emit cursor escapes
|
|
185
189
|
// into a pipe).
|
|
186
190
|
if (finalText) {
|
|
187
|
-
if (printedAny && colorEnabled()) {
|
|
191
|
+
if (printedAny && colorEnabled() && !printedToolCard) {
|
|
188
192
|
eraseStreamedBlock(streamedText);
|
|
189
193
|
stdout.write(await renderMarkdownLazy(finalText) + '\n');
|
|
190
194
|
} else if (!printedAny) {
|
|
191
195
|
// Nothing streamed live (provider without onTextDelta) — render once.
|
|
192
196
|
stdout.write(await renderMarkdownLazy(finalText) + '\n');
|
|
197
|
+
} else if (printedToolCard) {
|
|
198
|
+
// Tool cards are printed after the streamed text. Erasing only the
|
|
199
|
+
// streamed text from the current cursor position would clear/move
|
|
200
|
+
// through the card rows and make the terminal scroll jump. Keep the
|
|
201
|
+
// live transcript as-is for mixed text+tool turns.
|
|
202
|
+
stdout.write('\n');
|
|
193
203
|
} else {
|
|
194
204
|
// Non-TTY / NO_COLOR: leave the raw stream, just terminate the line.
|
|
195
205
|
stdout.write('\n');
|
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
text before tool calls.
|
|
9
9
|
- If tools are needed, call them immediately. Emit text only for the final
|
|
10
10
|
handoff after tool work is done.
|
|
11
|
-
- Final
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
- Final handoff: minimum characters, maximum information for Lead. Follow the
|
|
12
|
+
role's stricter output contract if defined; else emit fragments — outcome
|
|
13
|
+
(1 line), key `file:line`(s), verification result, material risks (only if
|
|
14
|
+
any).
|
|
15
|
+
- Banned as pure cost: report headings, markdown tables (unless requested),
|
|
16
|
+
prose narration, raw logs/tool traces, speculative next-checks, restated
|
|
17
|
+
brief, articles/politeness.
|
|
@@ -6,17 +6,16 @@ kind: retrieval
|
|
|
6
6
|
|
|
7
7
|
# Role: explorer
|
|
8
8
|
|
|
9
|
-
Locator only
|
|
10
|
-
|
|
9
|
+
Locator only: likely file/symbol/line anchors; no analysis, debugging, decisions,
|
|
10
|
+
or recommendations.
|
|
11
11
|
|
|
12
12
|
Output only:
|
|
13
13
|
- `path:line — symbol/name — short reason`
|
|
14
14
|
- or `EXPLORATION_FAILED`
|
|
15
15
|
|
|
16
|
-
No preambles
|
|
17
|
-
|
|
18
|
-
Prompt queries need exact function/prompt anchors. Weak anchors: `?`.
|
|
16
|
+
No preambles/tool-call preambles, bullets, headings, summaries, code quotes,
|
|
17
|
+
verdicts, or invented coordinates. Weak anchors: `?`.
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
One batched lookup turn; first plausible anchor wins. No verification loop,
|
|
20
|
+
synonym sweep, or proof-chasing. Hard stop after 5 tool calls; if uncertain,
|
|
21
|
+
return best weak anchors with `?`.
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
- Omit direct names, honorifics, headings, and labels in preambles.
|
|
4
4
|
- Preambles are optional: use them only when they add user-visible value, keep
|
|
5
5
|
them to one short sentence, and skip routine lookup narration.
|
|
6
|
-
- When you emit a preamble or other user-facing lead-in, it MUST be in the user's configured response language (see Profile Preferences).
|
|
7
6
|
- Destructive/hard-to-reverse actions require explicit confirmation.
|
|
8
7
|
- Never push, build, or deploy without an explicit user request.
|
|
9
8
|
Implementation approval is not deploy approval.
|
|
9
|
+
- Rather than deferring checks or work to the user, proactively handle whatever
|
|
10
|
+
you can first, and propose only the parts that need a decision in a
|
|
11
|
+
consultative tone ("Shall we proceed this way?").
|
|
@@ -4,3 +4,10 @@
|
|
|
4
4
|
- Use the current project/workspace selected by the session. Only change the work project when the user asks for a different project or a tool call explicitly needs another project root.
|
|
5
5
|
- Use `shell` directly for approved git/build/test/run work; do not delegate those commands to agents.
|
|
6
6
|
- Use `agent` for scoped implementation, research, review, and debugging, not for git commit/push/stash or Ship.
|
|
7
|
+
- Reuse the same agent tag/session for follow-up on the same scope (`send` or
|
|
8
|
+
`spawn` with the same tag reuses a live session). Spawn a new tag only for a
|
|
9
|
+
genuinely independent scope.
|
|
10
|
+
- Briefs: minimum characters, maximum information. Fixed one-line fragment
|
|
11
|
+
fields — `Goal:` `Anchors:` `Allow/Forbid:` `Deliver:` `Verify:` (+`Stop:` for
|
|
12
|
+
heavy-worker). Omit role-known rules (git/preamble bans, output format),
|
|
13
|
+
background, motivation; non-actionable tokens are wasted cost.
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
# Tool Use
|
|
2
2
|
|
|
3
|
-
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
`
|
|
7
|
-
`read`
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
- Independent lookups MUST batch in one turn; serialize only when a call needs a prior result.
|
|
4
|
+
- Target validity comes first: symbols/callers/deps → `code_graph`; exact text in
|
|
5
|
+
a verified scope → `grep`; unknown path/name → `find`; structure → `glob`;
|
|
6
|
+
dirs → `list`; verified file → `read`; broad unknown with no anchor →
|
|
7
|
+
`explore`. Never call `grep`/`read` on guessed paths.
|
|
8
|
+
- Concept normalization comes first: one concept gets one batched
|
|
9
|
+
`grep pattern:[...]` with `output_mode:"content_with_context"` (or one
|
|
10
|
+
`code_graph symbols[]`). Refine from returned paths; do not repeat equivalent
|
|
11
|
+
patterns or scopes.
|
|
12
|
+
- On miss/error, normalize the target once and switch tool; on a plausible hit,
|
|
13
|
+
stop searching and answer from the framed context. Do not follow
|
|
14
|
+
`content_with_context` with `read` unless the needed span is not shown.
|
|
15
|
+
- Avoid read fragmentation: `read` uses `offset`/`limit` only. If you need 2+
|
|
16
|
+
spans from one or more known files, make one batched `read` call with
|
|
17
|
+
`{path,offset,limit}` region objects instead of serial reads.
|
|
18
|
+
- `search`/`web_fetch` for external info; `recall` for history.
|
|
19
|
+
- Don't mix `apply_patch` with shell or other state-changing calls in one turn.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* The returned function uses the existing caller signature, so call sites
|
|
10
10
|
* do not need changes:
|
|
11
11
|
*
|
|
12
|
-
* const llm = makeAgentDispatch({
|
|
12
|
+
* const llm = makeAgentDispatch({ agent: 'maintenance', preset: 'haiku' });
|
|
13
13
|
* const text = await llm({ prompt });
|
|
14
14
|
*
|
|
15
15
|
* Internally it:
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
import { loadConfig } from '../config.mjs';
|
|
25
25
|
import { resolveRuntimeSpec } from '../config.mjs';
|
|
26
|
-
import {
|
|
26
|
+
import { getHiddenAgent, resolveAgentSessionPermission } from '../internal-agents.mjs';
|
|
27
27
|
import { prepareAgentSession } from './session-builder.mjs';
|
|
28
28
|
import {
|
|
29
29
|
askSession,
|
|
@@ -50,6 +50,41 @@ function applyBriefCap(text) {
|
|
|
50
50
|
return `${head}\n\n... [TRUNCATED — full answer was ~${approxTokens} tokens / ${Math.round(text.length / 1024)} KB. Re-run with brief:false for the complete synthesis]`;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
function formatCompactElapsedSeconds(ms) {
|
|
54
|
+
const value = Math.max(0, Number(ms) || 0);
|
|
55
|
+
if (value <= 0) return '';
|
|
56
|
+
return `${Math.max(1, Math.ceil(value / 1000))}s`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function agentCompactEventLabel(event = {}) {
|
|
60
|
+
const status = String(event.status || '').toLowerCase();
|
|
61
|
+
const reactive = String(event.trigger || '').toLowerCase() === 'reactive';
|
|
62
|
+
if (status === 'failed') return reactive ? 'Compact failed (overflow retry)' : 'Compact failed';
|
|
63
|
+
if (status === 'skipped') return 'Compact skipped';
|
|
64
|
+
if (status === 'no_change') return 'Compact checked';
|
|
65
|
+
return reactive ? 'Compact complete (overflow recovery)' : 'Compact complete';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function agentCompactEventDetail(event = {}) {
|
|
69
|
+
const parts = [];
|
|
70
|
+
const elapsed = formatCompactElapsedSeconds(Number(event.durationMs ?? event.elapsedMs ?? 0));
|
|
71
|
+
if (elapsed) parts.push(elapsed);
|
|
72
|
+
const type = String(event.compactType || event.type || '').trim();
|
|
73
|
+
if (type && type !== 'semantic') parts.push(type);
|
|
74
|
+
const trigger = String(event.trigger || '').toLowerCase();
|
|
75
|
+
if (trigger === 'reactive') parts.push('reactive');
|
|
76
|
+
else if (trigger === 'manual') parts.push('manual');
|
|
77
|
+
const before = Number(event.beforeTokens ?? event.pressureTokens ?? 0);
|
|
78
|
+
const after = Number(event.afterTokens ?? 0);
|
|
79
|
+
const fmtTok = (n) => {
|
|
80
|
+
const v = Number(n) || 0;
|
|
81
|
+
if (v >= 1000) return `${(v / 1000).toFixed(v >= 10_000 ? 0 : 1)}k`;
|
|
82
|
+
return `${Math.round(v)}`;
|
|
83
|
+
};
|
|
84
|
+
if (before > 0 && after > 0 && after !== before) parts.push(`${fmtTok(before)}→${fmtTok(after)}`);
|
|
85
|
+
return parts.join(' · ');
|
|
86
|
+
}
|
|
87
|
+
|
|
53
88
|
// Unified-shard policy — most agent sessions (Pool B + Pool C) share the
|
|
54
89
|
// same tool schema so BP_1 is bit-identical across roles and one provider-side
|
|
55
90
|
// cache shard serves every caller. Per-role behaviour is steered by:
|
|
@@ -57,9 +92,10 @@ function applyBriefCap(text) {
|
|
|
57
92
|
// rules/agent/<role>.md
|
|
58
93
|
// 2. call-time guards (loop.mjs write-block + ai-wrapped-dispatch
|
|
59
94
|
// recursion break)
|
|
60
|
-
// Hidden-
|
|
95
|
+
// Hidden-agent exceptions are declarative: defaults/agents.json may set
|
|
61
96
|
// toolSchemaProfile when first-turn routing quality is worth a separate tool
|
|
62
|
-
// prefix. Standard profiles are none/read/full; legacy names
|
|
97
|
+
// prefix. Standard profiles are none/read/full/read-write-search; legacy names
|
|
98
|
+
// stay as aliases.
|
|
63
99
|
// See manager.mjs resolveSessionTools for the single source of truth;
|
|
64
100
|
// agent visibility is declared via annotations.agentHidden on each tool def.
|
|
65
101
|
const HIDDEN_ROLE_TOOL_SCHEMA_PROFILES = Object.freeze({
|
|
@@ -73,6 +109,17 @@ const HIDDEN_ROLE_TOOL_SCHEMA_PROFILES = Object.freeze({
|
|
|
73
109
|
'grep',
|
|
74
110
|
'read',
|
|
75
111
|
]),
|
|
112
|
+
'read-write-search': Object.freeze([
|
|
113
|
+
'code_graph',
|
|
114
|
+
'find',
|
|
115
|
+
'glob',
|
|
116
|
+
'list',
|
|
117
|
+
'grep',
|
|
118
|
+
'read',
|
|
119
|
+
'apply_patch',
|
|
120
|
+
'search',
|
|
121
|
+
'web_fetch',
|
|
122
|
+
]),
|
|
76
123
|
// Backward-compatible aliases for older hidden-role definitions.
|
|
77
124
|
unified: null,
|
|
78
125
|
'llm-only': Object.freeze([]),
|
|
@@ -95,7 +142,7 @@ export function resolveHiddenRoleSchemaAllowedTools(hidden) {
|
|
|
95
142
|
if (Object.prototype.hasOwnProperty.call(HIDDEN_ROLE_TOOL_SCHEMA_PROFILES, profile)) {
|
|
96
143
|
return HIDDEN_ROLE_TOOL_SCHEMA_PROFILES[profile];
|
|
97
144
|
}
|
|
98
|
-
process.stderr.write(`[agent-dispatch] unknown hidden-
|
|
145
|
+
process.stderr.write(`[agent-dispatch] unknown hidden-agent toolSchemaProfile="${profile}" agent="${hidden.agent || 'unknown'}"; using full schema\n`);
|
|
99
146
|
return null;
|
|
100
147
|
}
|
|
101
148
|
|
|
@@ -114,11 +161,11 @@ export function resolveHiddenRoleSchemaAllowedTools(hidden) {
|
|
|
114
161
|
* Hidden roles read their slot from `maint[maintKey || slot]`; the cycle1/2/3
|
|
115
162
|
* agents share one knob via the `maintKey: 'memory'` override.
|
|
116
163
|
*/
|
|
117
|
-
export function resolveMaintenanceRoute({ preset, optsPreset,
|
|
164
|
+
export function resolveMaintenanceRoute({ preset, optsPreset, agent, config: cfgIn = null }) {
|
|
118
165
|
if (preset) return preset;
|
|
119
166
|
if (optsPreset) return optsPreset;
|
|
120
|
-
if (!
|
|
121
|
-
const hidden =
|
|
167
|
+
if (!agent) return null;
|
|
168
|
+
const hidden = getHiddenAgent(agent);
|
|
122
169
|
if (hidden) {
|
|
123
170
|
try {
|
|
124
171
|
const config = cfgIn || loadConfig({ secrets: false });
|
|
@@ -134,14 +181,14 @@ export function resolveMaintenanceRoute({ preset, optsPreset, role, config: cfgI
|
|
|
134
181
|
export const resolvePresetName = resolveMaintenanceRoute;
|
|
135
182
|
|
|
136
183
|
// A maintenance slot value is a direct route when it carries provider+model.
|
|
137
|
-
function maintenanceRouteToPreset(routeOrName,
|
|
184
|
+
function maintenanceRouteToPreset(routeOrName, agent) {
|
|
138
185
|
if (!routeOrName || typeof routeOrName !== 'object') return null;
|
|
139
186
|
const provider = String(routeOrName.provider || '').trim();
|
|
140
187
|
const model = String(routeOrName.model || '').trim();
|
|
141
188
|
if (!provider || !model) return null;
|
|
142
189
|
const out = {
|
|
143
|
-
id: `maint-${
|
|
144
|
-
name: `MAINT ${String(
|
|
190
|
+
id: `maint-${agent}`,
|
|
191
|
+
name: `MAINT ${String(agent || '').toUpperCase()}`,
|
|
145
192
|
type: 'agent',
|
|
146
193
|
provider,
|
|
147
194
|
model,
|
|
@@ -157,50 +204,50 @@ function maintenanceRouteToPreset(routeOrName, role) {
|
|
|
157
204
|
* Build an agent-backed dispatch callback.
|
|
158
205
|
*
|
|
159
206
|
* @param {object} opts
|
|
160
|
-
* @param {string} opts.
|
|
207
|
+
* @param {string} opts.agent — REQUIRED; canonical agent name (worker, cycle1-agent, scheduler-task, ...)
|
|
161
208
|
* @param {string} [opts.taskType] — optional internal classification stamped on the session
|
|
162
|
-
* @param {string} [opts.preset] — explicit preset override (bypasses
|
|
209
|
+
* @param {string} [opts.preset] — explicit preset override (bypasses agent → preset lookup)
|
|
163
210
|
* @param {string} [opts.parentSessionId] — parent agent session for trace aggregation
|
|
164
211
|
* @param {string|null} [opts.ownerSessionId] — owning Mixdog session for statusline isolation
|
|
165
212
|
* @param {AbortSignal} [opts.parentSignal] — optional AbortSignal from the fan-out coordinator;
|
|
166
|
-
* when aborted the agent
|
|
213
|
+
* when aborted the agent session's own controller is also aborted so the
|
|
167
214
|
* provider call tears down promptly (parent→child cascade).
|
|
168
215
|
* @returns {(args: { prompt, preset?, sourceName? }) => Promise<string>}
|
|
169
216
|
*/
|
|
170
217
|
export function makeAgentDispatch(opts = {}) {
|
|
171
|
-
if (!opts.
|
|
172
|
-
throw new Error('[agent-dispatch] opts.
|
|
218
|
+
if (!opts.agent || typeof opts.agent !== 'string') {
|
|
219
|
+
throw new Error('[agent-dispatch] opts.agent is required');
|
|
173
220
|
}
|
|
174
|
-
const
|
|
221
|
+
const agent = opts.agent;
|
|
175
222
|
|
|
176
223
|
return async function agentDispatch({ prompt, preset: presetArg, sourceName: sourceNameArg, parentSignal: callParentSignal, idleTimeoutMs: callIdleTimeoutMs }) {
|
|
177
224
|
if (typeof prompt !== 'string' || !prompt) {
|
|
178
|
-
throw new Error(`[agent-dispatch] prompt required for
|
|
225
|
+
throw new Error(`[agent-dispatch] prompt required for agent "${agent}"`);
|
|
179
226
|
}
|
|
180
227
|
|
|
181
228
|
const config = opts.config || loadConfig({ secrets: false });
|
|
182
229
|
const routeOrName = resolveMaintenanceRoute({
|
|
183
230
|
preset: presetArg,
|
|
184
231
|
optsPreset: opts.preset,
|
|
185
|
-
|
|
232
|
+
agent,
|
|
186
233
|
config,
|
|
187
234
|
});
|
|
188
235
|
if (!routeOrName) {
|
|
189
236
|
throw new Error(
|
|
190
|
-
`[agent-dispatch] maintenance route unresolved for
|
|
237
|
+
`[agent-dispatch] maintenance route unresolved for agent "${agent}" `
|
|
191
238
|
+ `(preset="${presetArg || opts.preset || ''}")`,
|
|
192
239
|
);
|
|
193
240
|
}
|
|
194
241
|
// Preferred path: a maintenance slot that stores its model directly
|
|
195
242
|
// (route object). Legacy path: a slot still holding a preset NAME —
|
|
196
243
|
// resolve it against config.presets for backward compatibility.
|
|
197
|
-
let preset = maintenanceRouteToPreset(routeOrName,
|
|
244
|
+
let preset = maintenanceRouteToPreset(routeOrName, agent);
|
|
198
245
|
if (!preset) {
|
|
199
246
|
const legacyName = String(routeOrName || '').trim();
|
|
200
247
|
preset = config.presets?.find((p) => p.id === legacyName || p.name === legacyName) || null;
|
|
201
248
|
if (!preset) {
|
|
202
249
|
throw new Error(
|
|
203
|
-
`[agent-dispatch] maintenance route for
|
|
250
|
+
`[agent-dispatch] maintenance route for agent "${agent}" is neither a `
|
|
204
251
|
+ `{provider,model} route nor a known preset name ("${legacyName}")`,
|
|
205
252
|
);
|
|
206
253
|
}
|
|
@@ -208,11 +255,11 @@ export function makeAgentDispatch(opts = {}) {
|
|
|
208
255
|
// Stable label for traces / session metadata, derived from the resolved
|
|
209
256
|
// preset object regardless of whether it came from a direct route or a
|
|
210
257
|
// legacy preset name.
|
|
211
|
-
const presetName = preset.id || preset.name || `maint-${
|
|
258
|
+
const presetName = preset.id || preset.name || `maint-${agent}`;
|
|
212
259
|
|
|
213
260
|
const runtimeSpec = resolveRuntimeSpec(preset, {
|
|
214
261
|
lane: 'agent',
|
|
215
|
-
agentId:
|
|
262
|
+
agentId: agent,
|
|
216
263
|
});
|
|
217
264
|
|
|
218
265
|
// Callers (e.g. aiWrapped explore dispatch) may pass an explicit
|
|
@@ -232,12 +279,12 @@ export function makeAgentDispatch(opts = {}) {
|
|
|
232
279
|
// Runtime permission enforcement was removed (every tool call is
|
|
233
280
|
// trusted); schema profiles remain a routing-efficiency layer that
|
|
234
281
|
// narrows the advertised tool list, not a runtime safety gate.
|
|
235
|
-
const hidden =
|
|
282
|
+
const hidden = getHiddenAgent(agent);
|
|
236
283
|
const isPoolC = Boolean(hidden);
|
|
237
284
|
// Permission: read-declared hidden roles are locked in
|
|
238
285
|
// resolveAgentSessionPermission (prepareAgentSession applies the same).
|
|
239
286
|
const permission = resolveAgentSessionPermission(
|
|
240
|
-
|
|
287
|
+
agent,
|
|
241
288
|
opts.permission ?? (isPoolC ? (hidden?.permission || 'read') : null),
|
|
242
289
|
);
|
|
243
290
|
// Pool C hidden-role instructions live in BP2 role-scoped context
|
|
@@ -252,7 +299,7 @@ export function makeAgentDispatch(opts = {}) {
|
|
|
252
299
|
// layer (account-level), not the session level.
|
|
253
300
|
const finalPrompt = prompt;
|
|
254
301
|
const { session } = prepareAgentSession({
|
|
255
|
-
|
|
302
|
+
agent,
|
|
256
303
|
presetName,
|
|
257
304
|
preset,
|
|
258
305
|
runtimeSpec,
|
|
@@ -273,7 +320,7 @@ export function makeAgentDispatch(opts = {}) {
|
|
|
273
320
|
// count-only "tools=N" line.
|
|
274
321
|
try {
|
|
275
322
|
const _toolNames = (session.tools || []).map((t) => t?.name).filter(Boolean);
|
|
276
|
-
process.stderr.write(`[agent-dispatch]
|
|
323
|
+
process.stderr.write(`[agent-dispatch] agent=${agent} tool-list (${_toolNames.length}): ${_toolNames.join(',')}\n`);
|
|
277
324
|
} catch { /* best-effort diagnostic */ }
|
|
278
325
|
|
|
279
326
|
await updateSessionStatus(session.id, 'running');
|
|
@@ -297,7 +344,7 @@ export function makeAgentDispatch(opts = {}) {
|
|
|
297
344
|
// - firstResponseTimeoutMs cuts quickly only when the model produces no
|
|
298
345
|
// first stream/tool activity at all.
|
|
299
346
|
// - idle/tool-running caps come from role stallCap (hidden roles) or env defaults.
|
|
300
|
-
const _watchdogPolicy = resolveAgentWatchdogPolicy(
|
|
347
|
+
const _watchdogPolicy = resolveAgentWatchdogPolicy(agent, {
|
|
301
348
|
idleTimeoutMs: Number.isFinite(callIdleTimeoutMs)
|
|
302
349
|
? callIdleTimeoutMs
|
|
303
350
|
: opts.idleTimeoutMs,
|
|
@@ -330,11 +377,22 @@ export function makeAgentDispatch(opts = {}) {
|
|
|
330
377
|
: null;
|
|
331
378
|
if (_idleTimer && typeof _idleTimer.unref === 'function') _idleTimer.unref();
|
|
332
379
|
let terminalStatus = 'idle';
|
|
333
|
-
process.stderr.write(`[agent-dispatch]
|
|
380
|
+
process.stderr.write(`[agent-dispatch] agent=${agent} preset=${presetName} model=${preset.model} provider=${preset.provider} session=${session.id}\n`);
|
|
334
381
|
const _agentDispatchT0 = Date.now();
|
|
335
382
|
try {
|
|
336
|
-
const result = await askSession(session.id, finalPrompt, null, null, cwd
|
|
337
|
-
|
|
383
|
+
const result = await askSession(session.id, finalPrompt, null, null, cwd, undefined, {
|
|
384
|
+
onCompactEvent: (event) => {
|
|
385
|
+
try {
|
|
386
|
+
const label = agentCompactEventLabel(event);
|
|
387
|
+
const detail = agentCompactEventDetail(event);
|
|
388
|
+
const suffix = detail ? ` (${detail})` : '';
|
|
389
|
+
process.stderr.write(
|
|
390
|
+
`[agent-dispatch] agent=${agent} session=${session.id} compact: ${label}${suffix}\n`,
|
|
391
|
+
);
|
|
392
|
+
} catch { /* best-effort compact visibility */ }
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
process.stderr.write(`[agent-dispatch] agent=${agent} session=${session.id} elapsed=${Date.now() - _agentDispatchT0}ms\n`);
|
|
338
396
|
const raw = result?.content || '';
|
|
339
397
|
// Brief cap. Agent role answers (explore/recall/search)
|
|
340
398
|
// occasionally balloon to 8-10k token walls that then ride in the
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent loop ceiling — a single high runaway-guard shared by every session
|
|
3
|
+
* (Lead and delegated sub-agents alike). There are intentionally NO low
|
|
4
|
+
* per-agent caps: a worker/heavy-worker must be free to run as many tool +
|
|
5
|
+
* synthesis turns as the task needs. This ceiling exists ONLY to stop a truly
|
|
6
|
+
* runaway loop; it is not a task-length budget.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
function envPositiveInt(name, fallback) {
|
|
10
|
+
const raw = process.env[name];
|
|
11
|
+
if (raw === undefined || raw === '') return fallback;
|
|
12
|
+
const n = Number(raw);
|
|
13
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Single runaway guard for ALL sessions. High by design; env-overridable only
|
|
17
|
+
// to raise/lower the safety ceiling, never used as a per-agent task budget.
|
|
18
|
+
export const LEAD_MAX_LOOP_ITERATIONS = envPositiveInt('MIXDOG_AGENT_MAX_LOOP', 200);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the hard cap used by agentLoop for this session.
|
|
22
|
+
*
|
|
23
|
+
* Order: explicit override → session-pinned value → shared runaway guard.
|
|
24
|
+
* No per-agent low caps are applied.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveSessionMaxLoopIterations(sessionRef, explicit) {
|
|
27
|
+
if (Number.isFinite(explicit) && explicit > 0) return Math.floor(explicit);
|
|
28
|
+
if (Number.isFinite(sessionRef?.maxLoopIterations) && sessionRef.maxLoopIterations > 0) {
|
|
29
|
+
return Math.floor(sessionRef.maxLoopIterations);
|
|
30
|
+
}
|
|
31
|
+
return LEAD_MAX_LOOP_ITERATIONS;
|
|
32
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* lastProgressAt during long tool work; this module decides when to abort.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { getHiddenAgent } from '../internal-agents.mjs';
|
|
8
8
|
import {
|
|
9
9
|
resolveAgentStallThresholds,
|
|
10
10
|
resolveAgentToolThresholdSeconds,
|
|
@@ -31,7 +31,7 @@ function resolveExplicitMs(value, fallback) {
|
|
|
31
31
|
return fallback;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export function resolveAgentWatchdogPolicy(
|
|
34
|
+
export function resolveAgentWatchdogPolicy(agent, overrides = {}) {
|
|
35
35
|
const firstResponseMs = resolveExplicitMs(
|
|
36
36
|
overrides.firstResponseTimeoutMs,
|
|
37
37
|
DEFAULT_FIRST_RESPONSE_TIMEOUT_MS,
|
|
@@ -40,15 +40,27 @@ export function resolveAgentWatchdogPolicy(role, overrides = {}) {
|
|
|
40
40
|
let idleStaleMs;
|
|
41
41
|
if (Number.isFinite(overrides.idleTimeoutMs) && overrides.idleTimeoutMs >= 0) {
|
|
42
42
|
idleStaleMs = Math.floor(overrides.idleTimeoutMs);
|
|
43
|
-
} else if (
|
|
44
|
-
const { abort } = resolveAgentStallThresholds(
|
|
43
|
+
} else if (getHiddenAgent(agent)) {
|
|
44
|
+
const { abort } = resolveAgentStallThresholds(agent);
|
|
45
45
|
idleStaleMs = abort * 1000;
|
|
46
46
|
} else {
|
|
47
|
-
|
|
47
|
+
// Part B: the primary mid-stream stall catch is now the provider-level
|
|
48
|
+
// SEMANTIC idle abort (~120s, ping-immune). This public-agent idle is a
|
|
49
|
+
// BACKSTOP only, so it must not exceed the stall abort (600s default) —
|
|
50
|
+
// the old 30-min value meant a ping-only wedge that slipped past the
|
|
51
|
+
// provider layer would still hang the owner for half an hour. Cap it at
|
|
52
|
+
// the stall abort while keeping 30 min as an absolute ceiling. The
|
|
53
|
+
// tool-running heartbeat exemption (toolRunningMs, below) is unchanged,
|
|
54
|
+
// so legitimately long tool calls still refresh progress and are safe.
|
|
55
|
+
const { abort } = resolveAgentStallThresholds(agent);
|
|
56
|
+
const backstopMs = Math.max(0, Math.floor(abort * 1000));
|
|
57
|
+
idleStaleMs = backstopMs > 0
|
|
58
|
+
? Math.min(DEFAULT_STALE_TIMEOUT_MS, backstopMs)
|
|
59
|
+
: DEFAULT_STALE_TIMEOUT_MS;
|
|
48
60
|
}
|
|
49
61
|
|
|
50
62
|
const idleSec = idleStaleMs / 1000;
|
|
51
|
-
const toolRunningSec = resolveAgentToolThresholdSeconds(
|
|
63
|
+
const toolRunningSec = resolveAgentToolThresholdSeconds(agent, idleSec);
|
|
52
64
|
const toolRunningMs = Math.max(0, Math.floor(toolRunningSec * 1000));
|
|
53
65
|
|
|
54
66
|
return {
|