mixdog 0.9.17 → 0.9.19

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 (129) hide show
  1. package/package.json +3 -2
  2. package/scripts/build-runtime-windows.ps1 +242 -242
  3. package/scripts/output-style-smoke.mjs +2 -2
  4. package/scripts/recall-bench-cases.json +11 -0
  5. package/scripts/recall-bench.mjs +91 -2
  6. package/scripts/recall-usecase-cases.json +1 -1
  7. package/scripts/smoke-runtime-negative.ps1 +106 -106
  8. package/scripts/tool-efficiency-diag.mjs +5 -2
  9. package/scripts/tool-smoke.mjs +101 -27
  10. package/src/agents/debugger/AGENT.md +3 -3
  11. package/src/agents/heavy-worker/AGENT.md +7 -10
  12. package/src/agents/maintainer/AGENT.md +1 -2
  13. package/src/agents/reviewer/AGENT.md +1 -2
  14. package/src/agents/worker/AGENT.md +5 -8
  15. package/src/defaults/agents.json +3 -0
  16. package/src/mixdog-session-runtime.mjs +23 -6
  17. package/src/rules/agent/00-core.md +4 -3
  18. package/src/rules/agent/30-explorer.md +53 -22
  19. package/src/rules/lead/02-channels.md +3 -3
  20. package/src/rules/lead/lead-tool.md +3 -2
  21. package/src/rules/shared/01-tool.md +24 -29
  22. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +1 -1
  23. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -1
  24. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +24 -3
  25. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +3 -3
  26. package/src/runtime/agent/orchestrator/session/context-utils.mjs +2 -2
  27. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +250 -35
  28. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +6 -4
  29. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +198 -6
  30. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +10 -2
  31. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +13 -15
  32. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +6 -1
  33. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +45 -3
  34. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -2
  35. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +195 -3
  36. package/src/runtime/agent/orchestrator/tools/builtin.mjs +17 -1
  37. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +38 -0
  38. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -4
  39. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +1 -1
  40. package/src/runtime/channels/backends/discord-attachments.mjs +2 -2
  41. package/src/runtime/channels/backends/discord-gateway.mjs +26 -3
  42. package/src/runtime/channels/backends/discord.mjs +139 -7
  43. package/src/runtime/channels/index.mjs +290 -76
  44. package/src/runtime/channels/lib/crash-log.mjs +21 -3
  45. package/src/runtime/channels/lib/inbound-routing.mjs +19 -12
  46. package/src/runtime/channels/lib/memory-client.mjs +32 -14
  47. package/src/runtime/channels/lib/output-forwarder.mjs +156 -14
  48. package/src/runtime/channels/lib/owner-heartbeat.mjs +13 -4
  49. package/src/runtime/channels/lib/runtime-paths.mjs +48 -1
  50. package/src/runtime/channels/lib/tool-dispatch.mjs +17 -7
  51. package/src/runtime/channels/lib/voice-transcription.mjs +4 -2
  52. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +8 -2
  53. package/src/runtime/memory/lib/memory-recall-store.mjs +182 -9
  54. package/src/runtime/memory/lib/query-handlers.mjs +36 -4
  55. package/src/runtime/memory/lib/recall-format.mjs +106 -6
  56. package/src/runtime/memory/lib/session-ingest.mjs +1 -1
  57. package/src/runtime/shared/atomic-file.mjs +10 -4
  58. package/src/runtime/shared/background-tasks.mjs +4 -2
  59. package/src/runtime/shared/tool-execution-contract.mjs +1 -1
  60. package/src/runtime/shared/tool-result-summary.mjs +1 -1
  61. package/src/runtime/shared/tool-surface.mjs +30 -1
  62. package/src/session-runtime/config-lifecycle.mjs +1 -1
  63. package/src/session-runtime/cwd-plugins.mjs +46 -3
  64. package/src/session-runtime/mcp-glue.mjs +24 -3
  65. package/src/session-runtime/output-styles.mjs +44 -10
  66. package/src/session-runtime/workflow.mjs +16 -1
  67. package/src/standalone/channel-worker.mjs +88 -7
  68. package/src/standalone/explore-tool.mjs +1 -1
  69. package/src/tui/App.jsx +57 -77
  70. package/src/tui/app/channel-pickers.mjs +45 -0
  71. package/src/tui/app/slash-commands.mjs +0 -1
  72. package/src/tui/app/slash-dispatch.mjs +0 -16
  73. package/src/tui/app/transcript-window.mjs +66 -1
  74. package/src/tui/app/use-mouse-input.mjs +9 -2
  75. package/src/tui/app/use-prompt-handlers.mjs +7 -94
  76. package/src/tui/app/use-transcript-scroll.mjs +5 -1
  77. package/src/tui/app/use-transcript-window.mjs +74 -8
  78. package/src/tui/components/PromptInput.jsx +33 -64
  79. package/src/tui/components/ToolExecution.jsx +2 -2
  80. package/src/tui/dist/index.mjs +4744 -4806
  81. package/src/tui/engine.mjs +109 -20
  82. package/src/tui/lib/voice-setup.mjs +166 -0
  83. package/src/tui/paste-attachments.mjs +12 -5
  84. package/src/tui/prompt-history-store.mjs +125 -12
  85. package/scripts/bench/cache-probe-tasks.json +0 -8
  86. package/scripts/bench/lead-review-tasks-r3.json +0 -20
  87. package/scripts/bench/lead-review-tasks.json +0 -20
  88. package/scripts/bench/r4-mixed-tasks.json +0 -20
  89. package/scripts/bench/r5-orchestrated-task.json +0 -7
  90. package/scripts/bench/review-tasks.json +0 -20
  91. package/scripts/bench/round-codex.json +0 -114
  92. package/scripts/bench/round-mixdog-lead-r3.json +0 -269
  93. package/scripts/bench/round-mixdog-lead.json +0 -269
  94. package/scripts/bench/round-mixdog.json +0 -126
  95. package/scripts/bench/round-r10-bigsample.json +0 -679
  96. package/scripts/bench/round-r11-codexalign.json +0 -257
  97. package/scripts/bench/round-r13-clientmeta.json +0 -464
  98. package/scripts/bench/round-r14-betafeatures.json +0 -466
  99. package/scripts/bench/round-r15-fulldefault.json +0 -462
  100. package/scripts/bench/round-r16-sessionid.json +0 -466
  101. package/scripts/bench/round-r17-wirebytes.json +0 -456
  102. package/scripts/bench/round-r18-prewarm.json +0 -468
  103. package/scripts/bench/round-r19-clean.json +0 -472
  104. package/scripts/bench/round-r20-prewarm-clean.json +0 -475
  105. package/scripts/bench/round-r21-delta-retry.json +0 -473
  106. package/scripts/bench/round-r22-full-probe.json +0 -693
  107. package/scripts/bench/round-r23-itemprobe.json +0 -701
  108. package/scripts/bench/round-r24-shapefix.json +0 -677
  109. package/scripts/bench/round-r25-serial.json +0 -464
  110. package/scripts/bench/round-r26-parallel3.json +0 -671
  111. package/scripts/bench/round-r27-parallel10.json +0 -894
  112. package/scripts/bench/round-r28-parallel10-stagger.json +0 -882
  113. package/scripts/bench/round-r29-parallel10-stagger166.json +0 -886
  114. package/scripts/bench/round-r30-instid.json +0 -253
  115. package/scripts/bench/round-r31-upgradeprobe.json +0 -256
  116. package/scripts/bench/round-r32-vs-codex-lead.json +0 -254
  117. package/scripts/bench/round-r33-vs-codex-codex.json +0 -115
  118. package/scripts/bench/round-r34-orchestrated.json +0 -120
  119. package/scripts/bench/round-r35-orchestrated-codex.json +0 -61
  120. package/scripts/bench/round-r36-orchestrated-capped.json +0 -128
  121. package/scripts/bench/round-r4-codex.json +0 -114
  122. package/scripts/bench/round-r4-mixed.json +0 -225
  123. package/scripts/bench/round-r5-gpt-lead.json +0 -259
  124. package/scripts/bench/round-r6-codex.json +0 -114
  125. package/scripts/bench/round-r6-solo.json +0 -257
  126. package/scripts/bench/round-r7-full.json +0 -254
  127. package/scripts/bench/round-r8-fulldefault.json +0 -255
  128. package/src/tui/components/prompt-input/voice-indicator.mjs +0 -39
  129. package/src/tui/lib/voice-recorder.mjs +0 -469
@@ -5,16 +5,13 @@ permission: read-write
5
5
  # Heavy Worker
6
6
  Broad implementation agent.
7
7
 
8
- Bounded slices; smallest coherent change, not rewrite. Stop: unclear scope,
9
- growing blast radius, or Lead-only verification.
8
+ Bounded slices; smallest coherent change, not rewrite. Stop when scope is
9
+ unclear or the blast radius grows.
10
10
 
11
- EDIT-FIRST DISCIPLINE. Survey the slice with ONE batched read/grep round, then
12
- patch the first bounded piece — edit incrementally, don't read exhaustively.
13
- NEVER "one more confirming read": a plausible anchor means the next call is
14
- `apply_patch`. Repeated read-only turns without an edit = stalling; on a
15
- runtime reminder, patch the piece you understand or report blocked — a valid
16
- completion. Self-check comes AFTER edits; deep verification is Lead's.
11
+ EDIT-FIRST DISCIPLINE. Survey the slice with one batched retrieval round,
12
+ then patch the first bounded piece — edit incrementally, don't read
13
+ exhaustively. Repeated read-only turns without an edit = stalling; on a
14
+ runtime reminder, patch the piece you understand or report blocked.
17
15
 
18
- Minimal checks + how-to-verify. Hand off outcome as fragments anchored to
19
- `file:line`; no narration or bloat.
16
+ Self-verify edits with shell (targeted test/build).
20
17
 
@@ -7,5 +7,4 @@ permission: read-write
7
7
  Maintenance and cleanup agent.
8
8
 
9
9
  Smallest coherent change; no scope growth. Repeated read-only turns without
10
- an edit = stalling; patch or return blocked — a blocked report is a valid
11
- completion. Hand off as fragments (`file:line`); no narration.
10
+ an edit = stalling; patch or return blocked.
@@ -8,5 +8,4 @@ Regression/risk review agent.
8
8
 
9
9
  Find actionable correctness/regression/security/verification risks. Findings
10
10
  first, severity-ordered, one line with `file:line`; skip non-risky nits. If
11
- clean, one line + only material residual risk. Fragments only; no narration,
12
- no report bloat.
11
+ clean, one line + only material residual risk.
@@ -7,13 +7,10 @@ Scoped implementation agent.
7
7
 
8
8
  Smallest scoped change; no drive-by cleanup. Stop when done/blocked.
9
9
 
10
- EDIT-FIRST DISCIPLINE. Brief anchors (`file:line`) are pre-verified — trust and
11
- patch. No anchor: locate with AT MOST 1-2 reads, then edit — NEVER "one more
12
- confirming read"; if you know the file and the change, the next call is
13
- `apply_patch`. Repeated read-only turns without an edit = stalling; on a
14
- runtime reminder, patch now or return blocked with what's missing — a blocked
15
- report is a valid completion. Threshold is "plausible", not "proven";
16
- self-check comes AFTER the edit, and Lead/Reviewer own final verification.
10
+ EDIT-FIRST DISCIPLINE. Brief anchors (`file:line`) are pre-verified — trust
11
+ and patch. No anchor: one locating batch, then `apply_patch`. Repeated
12
+ read-only turns without an edit = stalling; on a runtime reminder, patch now
13
+ or return blocked. Threshold is "plausible", not "proven".
17
14
 
18
- Hand off outcome as fragments anchored to `file:line`; no narration or bloat.
15
+ Self-verify edits with shell (node --check/test).
19
16
 
@@ -7,6 +7,7 @@
7
7
  "description": "Filesystem navigation agent invoked by the `explore` MCP tool",
8
8
  "invokedBy": "explore",
9
9
  "toolSchemaProfile": "read",
10
+ "schemaAllowedTools": ["grep", "find", "glob", "code_graph"],
10
11
  "kind": "retrieval",
11
12
  "permission": "read",
12
13
  "stallCap": { "idleSeconds": 240, "toolRunningSeconds": 180 }
@@ -54,6 +55,7 @@
54
55
  "description": "Scheduled-task executor invoked by scheduler tick",
55
56
  "invokedBy": "scheduler",
56
57
  "toolSchemaProfile": "read-write-search",
58
+ "schemaAllowedTools": ["code_graph", "find", "glob", "list", "grep", "read", "apply_patch", "search", "web_fetch"],
57
59
  "kind": "maintenance",
58
60
  "permission": "read-write",
59
61
  "inboundEvent": true,
@@ -67,6 +69,7 @@
67
69
  "description": "Webhook payload handler invoked by inbound webhook events",
68
70
  "invokedBy": "webhook",
69
71
  "toolSchemaProfile": "read-write-search",
72
+ "schemaAllowedTools": ["code_graph", "find", "glob", "list", "grep", "read", "apply_patch", "search", "web_fetch"],
70
73
  "kind": "maintenance",
71
74
  "permission": "read-write",
72
75
  "inboundEvent": true,
@@ -315,7 +315,7 @@ async function profiledImport(label, spec, { optional = false } = {}) {
315
315
  throw error;
316
316
  }
317
317
  }
318
- const outputStyleStatus = (dataDir = STANDALONE_DATA_DIR) => outputStyleStatusRaw(STANDALONE_ROOT, dataDir || STANDALONE_DATA_DIR);
318
+ const outputStyleStatus = (dataDir = STANDALONE_DATA_DIR, opts = {}) => outputStyleStatusRaw(STANDALONE_ROOT, dataDir || STANDALONE_DATA_DIR, opts);
319
319
  // Workflow/agent pack loaders bound to this runtime's root/data layout.
320
320
  const {
321
321
  listWorkflowPacks,
@@ -325,6 +325,7 @@ const {
325
325
  activeWorkflowSummary,
326
326
  loadAgentDefinition,
327
327
  workflowContextBlock,
328
+ activeWorkflowContext,
328
329
  } = createWorkflowHelpers({
329
330
  rootDir: STANDALONE_ROOT,
330
331
  dataDir: STANDALONE_DATA_DIR,
@@ -918,6 +919,16 @@ export async function createMixdogSessionRuntime({
918
919
  stopRemote('superseded-by-newer-remote-session');
919
920
  emitRemoteStateChange(false, 'superseded');
920
921
  }
922
+ // Symmetric acquire: the worker took the bridge seat (boot make-before-
923
+ // break or activate). Flip remote ON via the same side-state a user
924
+ // /remote toggles — remoteEnabled + transcript writer — but WITHOUT
925
+ // re-invoking channel start (the worker is already running; startRemote
926
+ // would re-fork/activate). Idempotent: no-op when already enabled.
927
+ if (msg?.params?.state === 'acquired' && !remoteEnabled) {
928
+ remoteEnabled = true;
929
+ ensureRemoteTranscriptWriter();
930
+ emitRemoteStateChange(true, 'acquired');
931
+ }
921
932
  return;
922
933
  }
923
934
  if (msg?.method !== 'notifications/claude/channel') return;
@@ -1339,18 +1350,24 @@ export async function createMixdogSessionRuntime({
1339
1350
  await resolveMissingRouteModelForFirstTurn();
1340
1351
  requireModelRoute();
1341
1352
  bootProfile('session:create:route-ready', { ms: (performance.now() - startedAt).toFixed(1) });
1342
- await refreshRouteEffort();
1353
+ // refreshRouteEffort (effort/model-meta) and loadCoreMemoryContext (memory
1354
+ // files) are independent — refreshRouteEffort only touches route effort/
1355
+ // display fields, never provider/model that the memory load reads — so run
1356
+ // them concurrently instead of serially on the boot path.
1357
+ const [, coreMemoryContext] = await Promise.all([
1358
+ refreshRouteEffort(),
1359
+ loadCoreMemoryContext(),
1360
+ ]);
1343
1361
  bootProfile('session:create:effort-ready', { ms: (performance.now() - startedAt).toFixed(1) });
1344
1362
  const providerImpl = reg.getProvider(route.provider);
1345
1363
  if (!providerImpl) {
1346
1364
  throw new Error(`Provider "${route.provider}" is not configured.`);
1347
1365
  }
1348
1366
  bootProfile('session:create:provider-ready', { ms: (performance.now() - startedAt).toFixed(1) });
1349
- const coreMemoryContext = await loadCoreMemoryContext();
1350
1367
  if (closeRequested) throw new Error('runtime is closing');
1351
1368
  const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
1352
- const workflow = activeWorkflowSummary(config, dataDir);
1353
- const workflowContext = workflowContextBlock(config, dataDir);
1369
+ // Load the active WORKFLOW.md pack once for both summary + context block.
1370
+ const { summary: workflow, context: workflowContext } = activeWorkflowContext(config, dataDir);
1354
1371
  const sessionOpts = {
1355
1372
  provider: route.provider,
1356
1373
  model: route.model,
@@ -2991,7 +3008,7 @@ export async function createMixdogSessionRuntime({
2991
3008
  let selectedRoute = resolveRoute(config, requested);
2992
3009
  await reg.initProviders(ensureProviderEnabled(config, selectedRoute.provider));
2993
3010
  const modelMeta = await lookupModelMeta(selectedRoute.provider, selectedRoute.model);
2994
- // Guard (A / strict reject): a free-text string must never become the
3011
+ // Guard (option A / strict reject): a free-text string must never become the
2995
3012
  // lead model. When the caller did not pin a provider, the route did not
2996
3013
  // match a preset, and the model is unknown to both the live provider
2997
3014
  // catalog (modelMeta is still the {id,provider} placeholder) and the
@@ -1,9 +1,8 @@
1
1
  # Agent Constraints
2
2
 
3
3
  - Use English for agent task communication.
4
- - ONE TURN = ONE BATCH: emit all independent tool calls of a step in the same
5
- turn; a lone read-only call that could have ridden the previous turn is a
6
- wasted round-trip.
4
+ - ONE TURN = ONE BATCH: a read-only call that could have ridden the prior
5
+ turn is a wasted round-trip.
7
6
  - NEVER PREAMBLE: no status/progress narration, "I will..." setup, or any text
8
7
  before tool calls — call needed tools immediately; emit text only for the
9
8
  final handoff after tool work is done.
@@ -15,6 +14,8 @@
15
14
  searched); state only what changed and where to look. Verification =
16
15
  command + result in one line. Same fact twice = delete one.
17
16
  - Handoff cap ~30 lines unless `Deliver:` raises it — a ceiling, not a target.
17
+ - A blocked or partial report is a valid completion: state done/missing/
18
+ blocker in fragments — never keep retrieving to avoid reporting.
18
19
  - Banned as pure cost: report headings, markdown tables (unless requested),
19
20
  prose narration, raw logs/tool traces, speculative next-checks, restated
20
21
  brief, articles/politeness.
@@ -8,27 +8,58 @@ kind: retrieval
8
8
 
9
9
  Coordinate locator. Deliverable = WHERE as `path:line`, never WHY — no
10
10
  explaining or tracing. You ARE the `explore` tool: never call it; shared
11
- rules pointing at `explore` don't apply — use grep/code_graph/find/glob
12
- directly.
11
+ rules pointing at `explore` don't apply.
12
+
13
+ Tools: grep/find/glob/code_graph ONLY. `read` and `list` are FORBIDDEN —
14
+ grep match lines already carry every `path:line` you may output; opening a
15
+ file to "confirm" or "understand" a hit is the WHY you must not answer.
16
+ This ban has NO exception: not after a hit, not for the reason field, not
17
+ "just one read" — a `read`/`list` call anywhere in the session is a defect.
18
+
19
+ Turn 1 is the whole search, ONE message: one grep whose `pattern[]` packs
20
+ 3-6 token variants (camelCase/kebab-case/snake_case/SCREAMING_SNAKE casings,
21
+ synonyms, library/domain names), plus `find` with name fragments, plus `code_graph`
22
+ symbol_search when the query names an identifier. A single-pattern,
23
+ single-tool first turn is a defect.
24
+
25
+ Search tokens are CODE tokens: first translate natural-language or
26
+ non-English queries into probable English identifiers (e.g. "최대 루프 반복
27
+ 횟수" → maxLoop, loop-policy, iterations). Grep non-ASCII text only when
28
+ the query quotes a literal string.
29
+
30
+ Search scope is ALWAYS the session working directory: omit `path` (default
31
+ scope) or pass ONLY a path that appeared in an earlier result this session.
32
+ Inventing a directory (`/workspace/...`, guessed `src/lib`, another repo's
33
+ layout) is a defect — those results are always empty; on zero hits change
34
+ TOKENS, never guess paths.
13
35
 
14
36
  Credibility is mechanical: hit = line/path contains ANY query token or
15
- obvious synonym. No other standard "is this the real implementation" is
16
- the caller's question. Sole exception: a generic-word-only match (schema,
17
- handler, config, resolver…) with no SPECIFIC query token (product/library/
18
- domain name) = zero, not a hit.
19
-
20
- Rule zero — after EVERY tool result: ≥1 `path:line` matching a query
21
- token answer NOW with those coordinates; no verify, no re-grep, no
22
- ranking; mark weak anchors `?`. Zero → one more batch if budget remains.
23
- No third branch: "hits exist but I want better ones" IS branch one.
24
-
25
- Turns: 3 tool turns HARD max; start each tool message with `turn N/3`.
26
- Turn 1 → answer; turns 2-3 are miss-recovery only, and must change tokens
27
- OR scope never reword. One turn = ONE batch of AT MOST 5 calls: a single
28
- grep packing ALL variants in pattern[] plus code_graph/find/glob; a 6th
29
- call = packing failure. Flow/how questions: return first matching entry
30
- anchors (≤5); the caller traces the chain.
31
-
32
- Answer, nothing else: up to 5 lines `path:line symbol short reason`
33
- (`?` if weak), or `EXPLORATION_FAILED`only after 3 turns of zero
34
- anchors, or when every anchor matches only generic words.
37
+ obvious synonym never judge "is this the real implementation". Sole
38
+ exception: a generic-word-only match (schema, handler, config, resolver…)
39
+ with no SPECIFIC query token (product/library/domain name) = zero, not a hit.
40
+ A `code_graph` symbol hit (find_symbol/symbol_search returning `path:line`)
41
+ IS an anchor — emit it directly; never re-locate it with grep.
42
+
43
+ Rule zero after EVERY tool result: ≥1 `path:line` matching a query token
44
+ answer NOW with those coordinates, mark weak anchors `?`; zero → one more
45
+ batch if budget remains. No third branch: "hits exist but I want better
46
+ ones" IS branch one. Once ANY turn yields a matching line, the only legal
47
+ next output is the final answer text every further tool call is a defect.
48
+
49
+ Turns: hard max 3, expected 1; start each tool message with `turn N/3`.
50
+ Turns 2-3 are miss-recovery only (prior turn had ZERO matching lines) and
51
+ must change tokens OR scope, never reword.
52
+ Flow/how questions: first matching entry/definition anchors ARE the answer,
53
+ one anchor per concept — never trace the chain.
54
+ Compound queries ("where is X and what value/default does it use"): the
55
+ definition anchor answers BOTH partsnever launch extra searches for the
56
+ value, threshold, or content; one anchor per concept, all from the same batch.
57
+
58
+ Answer, nothing else: anchor lines `path:line — symbol — short reason`
59
+ (`?` if weak), max 3 lines — extra anchors are cost, not quality; pick the
60
+ 3 with the most specific token match. Or `EXPLORATION_FAILED` — only after
61
+ the turn budget is
62
+ spent with zero anchors, or when every anchor matches only generic words.
63
+ Before emitting `EXPLORATION_FAILED`, re-scan ALL earlier tool results: if
64
+ any line anywhere matched a specific query token, answer with that anchor
65
+ (`?` if weak) instead — a weak anchor beats a false miss.
@@ -1,3 +1,3 @@
1
- # Channels
2
-
3
- - Channel features are handled by the runtime.
1
+ # Channels
2
+
3
+ - Channel features are handled by the runtime.
@@ -1,7 +1,8 @@
1
1
  # Lead Tool Use
2
2
 
3
- - Lead owns repo-local shell work: run git/build/test/verification via `shell`
4
- directly; do not delegate to agents.
3
+ - Write-role agents run their own build/test verification via `shell`; Lead
4
+ runs cross-scope verification, benches, and everything git via `shell`
5
+ directly.
5
6
  - Use the session's current project/workspace. Change the work project only
6
7
  when the user asks for another project or a tool call needs another root.
7
8
  - Use `agent` for scoped implementation, research, review, and debugging — not
@@ -1,32 +1,27 @@
1
1
  # Tool Use
2
2
 
3
- - Batch independent lookups in one turn; serialize only when a call needs a
4
- prior result including edit loops: batch the post-edit verification read
5
- with the next target's lookup.
6
- - Pick by target: symbols/callers/deps → `code_graph`; exact text in a verified
7
- scope `grep`; dirs → `list`; verified file `read`.
8
- Locating files: concept-level or cross-file questions ("where is X handled",
9
- "how does Y flow") → `explore`, even when you could guess a keyword — its
10
- fan-out spends explorer context, not yours, and guess-and-retry keywords
11
- belong to it, not grep. Confident name fragment `find`; exact name
12
- pattern → `glob`; exact content literal `grep`. Flow/trace with a known
13
- symbol `code_graph`, never `explore` it returns entry-point anchors
14
- only; follow the chain yourself. Never `grep`/`read` guessed paths.
15
- - One concept ONE search call: all synonyms/variants in one
16
- `grep pattern:[...]` (or `code_graph symbols[]`), scopes as `path[]`;
17
- single-pattern bursts = packing failure. A second call on the same concept
18
- is a defect unless the first returned zero then change tokens or scope,
19
- never reword.
20
- - Content-mode `grep` always carries `-C 5`+ ; re-reading the same file after
21
- a content grep is a wasted round-trip. Bare match lines = existence checks
22
- only.
23
- - Any path not returned by a tool this session is a guess: find first, then
24
- grep/read the returned path. On ENOENT the next call is find on the
25
- basename never a second guess or the same path with changed args.
26
- - Retrieval serves the NEXT action (edit, answer, handoff), not certainty:
27
- the FIRST anchor ends the search — no verifying, ranking, or re-checking a
28
- hit already on screen. When acting and looking are both possible, act.
29
- - `read` uses `offset`/`limit` only; 2+ spans from known file(s) = ONE batched
30
- `read` with `{path,offset,limit}` regions. Adjacent spans in one file
31
- (within a few hundred lines) are ONE window, not repeated reads.
3
+ - Batch independent calls in one turn; serialize only when a call needs a
4
+ prior result. Merge equivalent variants/scopes into ONE call wherever the
5
+ schema accepts arrays (`pattern[]`, `path[]`, `symbols[]`, `query[]`).
6
+ - Route by what is already known: known symbol/relation → `code_graph`;
7
+ exact text in a known scope → `grep`; unknown location or concept-level
8
+ question `explore`; name fragment `find`; exact name pattern → `glob`;
9
+ known directory → `list`; known file/region `read`.
10
+ - Valid anchors come from user input or tool output in this session; locate
11
+ anything else with `find` before `grep`/`read`. On ENOENT the next call is
12
+ `find` on the basename never a retried guess.
13
+ - Retrieval stops when evidence covers the deliverable: single-answer tasks
14
+ end at the first sufficient anchor; enumeration tasks (review, audit) end
15
+ when the stated scope is covered. Never re-verify a hit already on screen;
16
+ a plausible anchor means the next call is the action itself
17
+ (patch/answer/handoff) never "one more confirming read".
18
+ - Content-mode `grep` requests enough context (`-C`) to act without
19
+ re-reading the same file; bare match lines are existence checks only.
20
+ - Repeat a search concept only after a zero-result call, changing tokens or
21
+ scope never wording alone.
22
+ - `read` windows use `offset`/`limit`; multiple spans or files = ONE call
23
+ with `{path,offset,limit}[]` regions. Size the window to the whole logical
24
+ unit (function/section) — over-read once instead of paging the same file
25
+ in small windows across turns; a 3rd window into one file means the first
26
+ should have been wider.
32
27
  - Don't mix `apply_patch` with shell or other state-changing calls in one turn.
@@ -376,7 +376,7 @@ function toAnthropicMessages(messages) {
376
376
  continue;
377
377
  }
378
378
 
379
- // Claude Code parity: fold a user text turn that directly follows a
379
+ // First-party client parity: fold a user text turn that directly follows a
380
380
  // tool_result turn into that tool_result's content. A sibling text
381
381
  // turn after tool_result renders as `</function_results>\n\nHuman:`
382
382
  // on the wire and trains the model toward 3-token empty end_turn
@@ -378,7 +378,7 @@ function toAnthropicMessages(messages) {
378
378
  }
379
379
  continue;
380
380
  }
381
- // Claude Code parity: fold a user text turn that directly follows a
381
+ // First-party client parity: fold a user text turn that directly follows a
382
382
  // tool_result turn into that tool_result's content (empty end_turn
383
383
  // livelock prevention; see foldUserTextIntoToolResultTail).
384
384
  if (m.role === 'user' && foldUserTextIntoToolResultTail(result, normalizeContentForAnthropic(m.content))) {
@@ -241,14 +241,35 @@ function windowFromPercent(label, value, source) {
241
241
  return Object.keys(out).length > 2 ? out : null;
242
242
  }
243
243
 
244
+ // Grok Build billing periods are migrating from monthly to a shared weekly
245
+ // pool (xAI rollout is per-account). Derive the window cadence from the
246
+ // actual billing period length instead of hardcoding monthly: <=10 days
247
+ // means a weekly cycle, anything longer (or unknown) stays monthly.
248
+ function grokBillingCadence(config) {
249
+ if (config?.weeklyLimit !== undefined || config?.weekly_limit !== undefined) {
250
+ return { label: 'W', period: 'weekly' };
251
+ }
252
+ const start = resetAtMs(config?.billingPeriodStart ?? config?.billing_period_start);
253
+ const end = resetAtMs(config?.billingPeriodEnd ?? config?.billing_period_end);
254
+ if (start && end && end > start && (end - start) <= 10 * 24 * 60 * 60_000) {
255
+ return { label: 'W', period: 'weekly' };
256
+ }
257
+ return { label: 'M', period: 'monthly' };
258
+ }
259
+
244
260
  function creditWindowFromBilling(config) {
245
261
  if (!config || typeof config !== 'object') return null;
246
- const limit = num(config.monthlyLimit?.val ?? config.monthlyLimit ?? config.includedLimit?.val, null);
262
+ const limit = num(
263
+ config.weeklyLimit?.val ?? config.weeklyLimit
264
+ ?? config.monthlyLimit?.val ?? config.monthlyLimit
265
+ ?? config.includedLimit?.val,
266
+ null,
267
+ );
247
268
  const used = num(config.used?.val ?? config.includedUsed?.val ?? config.used, null);
248
269
  if (limit === null || used === null || !(limit > 0)) return null;
249
270
  const resetAt = resetAtMs(config.billingPeriodEnd ?? config.billing_period_end);
250
271
  return {
251
- label: 'M',
272
+ label: grokBillingCadence(config).label,
252
273
  source: 'grok-build-billing',
253
274
  usedPct: round(Math.min(100, used * 100 / limit), 2),
254
275
  usedCredits: round(used, 2),
@@ -265,7 +286,7 @@ function balanceFromGrokBilling(config) {
265
286
  if (cap === null || !(cap > 0)) return null;
266
287
  return {
267
288
  source: 'grok-build-on-demand',
268
- period: 'monthly',
289
+ period: grokBillingCadence(config).period,
269
290
  budgetCredits: round(cap, 2),
270
291
  spentCredits: round(used, 2),
271
292
  remainingCredits: round(Math.max(0, cap - used), 2),
@@ -29,15 +29,15 @@ import {
29
29
  // 408 — request timeout
30
30
  // 500/502/503/504 — server errors (overload / bad gateway / timeout)
31
31
  // 429 is excluded from blanket transient retry, but withRetry() honors a
32
- // SHORT Retry-After header (< SHORT_RETRY_AFTER_MAX_MS) the way Claude Code
33
- // does (refs/claude-code withRetry.ts SHORT_RETRY_THRESHOLD_MS = 20s):
32
+ // SHORT Retry-After header (< SHORT_RETRY_AFTER_MAX_MS): a short-retry
33
+ // threshold of 20s matches first-party client behavior:
34
34
  // brief throttle windows retry once the server-stated wait elapses; long or
35
35
  // headerless 429s still surface immediately (quota windows are
36
36
  // deterministic — sleeping into an outer watchdog just relabels the error).
37
37
  const TRANSIENT_STATUSES = new Set([408, 500, 502, 503, 504])
38
38
 
39
39
  // Max server-stated Retry-After we are willing to sleep on a 429 before
40
- // surfacing it. Mirrors Claude Code's 20s short-retry threshold.
40
+ // surfacing it. Mirrors the first-party client's 20s short-retry threshold.
41
41
  const SHORT_RETRY_AFTER_MAX_MS = 20_000
42
42
 
43
43
  // HTTP statuses that mean "permanent: stop retrying, surface to caller".
@@ -651,8 +651,8 @@ export function sanitizeAnthropicContentPairs(messages) {
651
651
 
652
652
  /**
653
653
  * Fold a plain user text turn into the trailing tool_result block of the
654
- * previous user message (Claude Code parity: utils/messages.ts
655
- * mergeUserContentBlocks / smooshIntoToolResult). Any sibling text after a
654
+ * previous user message (first-party client parity: merge user content
655
+ * blocks into the trailing tool_result). Any sibling text after a
656
656
  * tool_result renders as `</function_results>\n\nHuman:<...>` on the
657
657
  * Anthropic wire; repeated mid-conversation this teaches the model to emit
658
658
  * 3-token empty end_turn completions (upstream A/B sai-20260310-161901: