@kinqs/brainrouter-cli 0.3.5 → 0.3.6

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 (49) hide show
  1. package/.env.example +55 -48
  2. package/bin/cli.cjs +71 -0
  3. package/dist/agent/agent.d.ts +212 -2
  4. package/dist/agent/agent.js +428 -38
  5. package/dist/cli/banner.d.ts +60 -0
  6. package/dist/cli/banner.js +199 -0
  7. package/dist/cli/cliPrompt.d.ts +69 -0
  8. package/dist/cli/cliPrompt.js +287 -0
  9. package/dist/cli/commands/_helpers.js +6 -6
  10. package/dist/cli/commands/guard.js +75 -10
  11. package/dist/cli/commands/mcp.d.ts +17 -0
  12. package/dist/cli/commands/mcp.js +121 -0
  13. package/dist/cli/commands/memory.js +2 -2
  14. package/dist/cli/commands/obs.js +22 -22
  15. package/dist/cli/commands/session.js +13 -5
  16. package/dist/cli/commands/ui.js +97 -45
  17. package/dist/cli/commands/workflow.d.ts +18 -0
  18. package/dist/cli/commands/workflow.js +314 -43
  19. package/dist/cli/repl.js +219 -132
  20. package/dist/cli/spinner.d.ts +34 -0
  21. package/dist/cli/spinner.js +36 -0
  22. package/dist/cli/statusline.d.ts +67 -0
  23. package/dist/cli/statusline.js +204 -0
  24. package/dist/cli/theme.d.ts +79 -0
  25. package/dist/cli/theme.js +106 -0
  26. package/dist/cli/whereView.d.ts +81 -0
  27. package/dist/cli/whereView.js +245 -0
  28. package/dist/config/config.d.ts +40 -0
  29. package/dist/config/config.js +45 -73
  30. package/dist/index.js +80 -13
  31. package/dist/memory/briefing.d.ts +10 -0
  32. package/dist/memory/briefing.js +69 -1
  33. package/dist/prompt/breadthHint.d.ts +5 -0
  34. package/dist/prompt/breadthHint.js +44 -0
  35. package/dist/prompt/systemPrompt.d.ts +34 -0
  36. package/dist/prompt/systemPrompt.js +124 -108
  37. package/dist/runtime/dangerousCommand.d.ts +53 -0
  38. package/dist/runtime/dangerousCommand.js +105 -0
  39. package/dist/runtime/mcpClient.d.ts +38 -1
  40. package/dist/runtime/mcpClient.js +90 -2
  41. package/dist/state/goalStore.d.ts +98 -17
  42. package/dist/state/goalStore.js +132 -42
  43. package/dist/state/preferencesStore.d.ts +67 -3
  44. package/dist/state/preferencesStore.js +84 -1
  45. package/dist/state/workflowArtifacts.d.ts +63 -2
  46. package/dist/state/workflowArtifacts.js +120 -8
  47. package/dist/tests/_helpers.d.ts +31 -0
  48. package/dist/tests/_helpers.js +91 -0
  49. package/package.json +5 -4
package/.env.example CHANGED
@@ -1,77 +1,81 @@
1
- # BrainRouter CLI agent — environment
1
+ # BrainRouter CLI agent — environment template
2
2
  #
3
- # Copy to brainrouter-cli/.env. Loaded by the CLI at startup.
3
+ # Copy to `brainrouter-cli/.env`. Loaded by the CLI at startup.
4
4
  #
5
5
  # This file is for CLI-AGENT concerns only:
6
- # - chat LLM the terminal agent talks to
7
- # - tool runtime knobs (loop limit, result clamp, MCP timeout)
8
- # - auto-compaction trigger
9
- # - sandbox configuration for run_command
10
- # - web search backend
11
- # - trace logging
12
- # - workspace override
6
+ # 1. Chat LLM (the model the terminal agent talks to)
7
+ # 2. Tool runtime (loop limit, result clamp, MCP timeout, auto-compact)
8
+ # 3. Sandbox (run_command wrapping)
9
+ # 4. Workspace (root override + shared state root)
10
+ # 5. Web search (custom backend for the web_search tool)
11
+ # 6. Observability (trace log path)
13
12
  #
14
- # MCP-server concerns (cognitive extraction, embeddings, reranker, memory
15
- # engine knobs, server auth) live in brainrouter/.env.example.
13
+ # MCP-server concerns (cognitive extraction, embeddings, reranker, judge,
14
+ # memory engine knobs, server auth) live in `brainrouter/.env.example`.
16
15
  #
17
16
  # Why split: the MCP and the CLI are separate processes with different
18
17
  # concerns. The CLI's chat LLM can be a smart cloud model while the MCP's
19
18
  # cognitive extractor is a cheap local one. Their concurrency caps differ
20
19
  # too (CLI default 4, MCP default 2). Keep them independent.
20
+ #
21
+ # All values in this template are blank placeholders. Fill in only what
22
+ # you actually need — most settings have sensible defaults.
23
+
21
24
 
22
- # ==========================================
23
- # Chat LLM (for the agent's own conversation)
24
- # ==========================================
25
+ # =============================================================================
26
+ # 1. Chat LLM (for the agent's own conversation)
27
+ # =============================================================================
25
28
  # Same var names as the MCP, but a separate process — set them here for the
26
29
  # CLI's chat model. Falls back to OPENAI_API_KEY.
27
30
  #
28
31
  # If you don't set BRAINROUTER_LLM_API_KEY here, the CLI also reads it from
29
- # brainrouter/.env as a transitional fallback. Setting it here makes the
32
+ # `brainrouter/.env` as a transitional fallback. Setting it here makes the
30
33
  # CLI's choice explicit and lets you use a different chat model than the
31
34
  # MCP's extractor (e.g. gpt-4o for chat, gpt-4o-mini for extraction).
32
- BRAINROUTER_LLM_API_KEY=your_api_key_here
35
+ BRAINROUTER_LLM_API_KEY=
33
36
 
34
37
  # OpenAI-compatible chat-completions endpoint.
35
38
  # Examples:
36
- # OpenAI: https://api.openai.com/v1/chat/completions
37
- # OpenRouter: https://openrouter.ai/api/v1/chat/completions
38
- # Anthropic via OpenRouter: anthropic/claude-sonnet-4
39
- # LM Studio: http://localhost:1234/v1/chat/completions
39
+ # OpenAI: https://api.openai.com/v1/chat/completions
40
+ # OpenRouter: https://openrouter.ai/api/v1/chat/completions
41
+ # Anthropic via OpenRouter: model id "anthropic/claude-sonnet-4"
42
+ # LM Studio: http://localhost:1234/v1/chat/completions
40
43
  BRAINROUTER_LLM_ENDPOINT=https://api.openai.com/v1/chat/completions
41
-
42
44
  BRAINROUTER_LLM_MODEL=gpt-4o-mini
43
45
 
44
- # Per-call timeout for the CLI's chat LLM. Default: 120000.
46
+ # Per-call timeout for the CLI's chat LLM. Default 120000 (2 min).
45
47
  # BRAINROUTER_LLM_TIMEOUT_MS=120000
46
48
 
47
49
  # Cap on concurrent in-flight chat LLM calls FROM THE CLI PROCESS.
48
- # Default: 4 (separate from the MCP's own cap). Set to 1 for consumer-grade
49
- # local backends; crank to 16+ for cloud APIs.
50
+ # Default 4 (separate from the MCP's own cap). Set to 1 for consumer-grade
51
+ # local backends; raise to 16+ for cloud APIs.
50
52
  # BRAINROUTER_LLM_MAX_CONCURRENT=4
51
53
 
52
- # ==========================================
53
- # Tool runtime
54
- # ==========================================
55
- # Per-tool timeout for CLI → MCP requests. Default: 60000.
54
+
55
+ # =============================================================================
56
+ # 2. Tool runtime
57
+ # =============================================================================
58
+ # Per-tool timeout for CLI → MCP requests. Default 60000.
56
59
  # BRAINROUTER_MCP_TIMEOUT_MS=60000
57
60
 
58
61
  # LLM-visible clamp on a single tool-result body (full text still recorded
59
- # in the transcript on disk). Default: 8000.
62
+ # in the transcript on disk). Default 8000.
60
63
  # BRAINROUTER_MAX_TOOL_RESULT_CHARS=8000
61
64
 
62
- # Hard ceiling on tool-call iterations per turn. Default: 60.
65
+ # Hard ceiling on tool-call iterations per turn. Default 60.
63
66
  # BRAINROUTER_MAX_TOOL_LOOPS=60
64
67
 
65
- # Estimated history-size trigger for auto-`/compact`. Default: 80000 tokens.
68
+ # Estimated history-size trigger for auto-`/compact`. Default 80000 tokens.
66
69
  # BRAINROUTER_AUTO_COMPACT_TOKENS=80000
67
70
 
68
- # ==========================================
69
- # Sandbox (run_command)
70
- # ==========================================
71
+
72
+ # =============================================================================
73
+ # 3. Sandbox (run_command)
74
+ # =============================================================================
71
75
  # Wrap shell commands in the platform sandbox:
72
76
  # macOS: sandbox-exec
73
77
  # Linux: bwrap (preferred) or firejail
74
- # Set 'on' to enable. Off by default.
78
+ # Set `on` to enable. Off by default.
75
79
  # BRAINROUTER_SANDBOX=on
76
80
 
77
81
  # Allow outbound network from sandboxed commands. Off by default.
@@ -81,29 +85,32 @@ BRAINROUTER_LLM_MODEL=gpt-4o-mini
81
85
  # BRAINROUTER_SANDBOX_READ_PATHS=/usr/local:/opt
82
86
  # BRAINROUTER_SANDBOX_WRITE_PATHS=/tmp
83
87
 
84
- # ==========================================
85
- # Workspace
86
- # ==========================================
87
- # Override workspace root the CLI uses for file tools + session key.
88
- # Most users let the CLI auto-detect via git/closest package.json.
88
+
89
+ # =============================================================================
90
+ # 4. Workspace
91
+ # =============================================================================
92
+ # Override the workspace root the CLI uses for file tools + session key.
93
+ # Most users let the CLI auto-detect via git / closest package.json.
89
94
  # BRAINROUTER_WORKSPACE=/path/to/project
90
95
 
91
96
  # Override per-user state root. Default: ~/.brainrouter.
92
97
  # Both the CLI and MCP honor this — set it once and both processes use it.
93
98
  # BRAINROUTER_HOME=/path/to/state
94
99
 
95
- # ==========================================
96
- # Web search
97
- # ==========================================
100
+
101
+ # =============================================================================
102
+ # 5. Web search
103
+ # =============================================================================
98
104
  # Custom search backend for the web_search tool. Must accept
99
- # POST { query, maxResults } → { results: [{ title, url, snippet }] }.
105
+ # POST { query, maxResults } → { results: [{ title, url, snippet }] }
100
106
  # Falls back to DuckDuckGo's Instant Answer API when unset.
101
- # Compatible with Brave Search API wrappers, Tavily, SerpAPI proxies.
107
+ # Compatible with Brave Search API wrappers, Tavily, SerpAPI proxies, etc.
102
108
  # BRAINROUTER_WEB_SEARCH_ENDPOINT=https://your-search-proxy.example.com/search
103
109
 
104
- # ==========================================
105
- # Observability
106
- # ==========================================
110
+
111
+ # =============================================================================
112
+ # 6. Observability
113
+ # =============================================================================
107
114
  # Path for OTEL-style JSONL turn traces. One line per turn/tool span.
108
115
  # Toggle at runtime with /trace on|off.
109
116
  # BRAINROUTER_TRACE_LOG=/path/to/trace.jsonl
package/bin/cli.cjs ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Thin CommonJS shim that runs BEFORE the real ESM CLI entrypoint.
5
+ *
6
+ * Why CJS for the bin: ESM hoists all `import` statements above any
7
+ * top-level code in the module that owns them. The CLI imports
8
+ * `node:sqlite` transitively via `config/config.ts`, which triggers
9
+ * Node's `ExperimentalWarning` the FIRST time the module is touched —
10
+ * and that happens during import resolution, before any line of code
11
+ * in `src/index.ts` runs. So a warning filter installed inside that
12
+ * file always fires too late.
13
+ *
14
+ * This shim does three things synchronously, with zero ESM imports
15
+ * blocking it, and only then hands off:
16
+ *
17
+ * 1. Remove Node's default "warning" printer.
18
+ * 2. Install a filtered listener that drops `ExperimentalWarning`
19
+ * (sqlite, ESM in older Node) and dotenv self-promotion lines.
20
+ * 3. Override `process.emitWarning` so future direct callers also
21
+ * route through the same filter.
22
+ *
23
+ * Anything BrainRouter itself emits via `process.emitWarning('…',
24
+ * 'BrainRouterWarning')` (or any non-suppressible type) flows through
25
+ * unchanged. NODE_NO_WARNINGS=1 would silence those too, which is why
26
+ * we don't just set that env.
27
+ *
28
+ * The shim then dynamically imports the ESM entry. Dynamic `import()`
29
+ * is the only way to load ESM from CJS; it returns a promise we await
30
+ * so an unhandled rejection during boot still surfaces as an error.
31
+ */
32
+
33
+ function isSuppressibleWarning(message, type) {
34
+ const looksExperimental =
35
+ type === 'ExperimentalWarning' ||
36
+ /experimental feature|SQLite is an experimental/i.test(message);
37
+ const looksDotenvNoise = /dotenv@\d|dotenvx|dotenv\.org/i.test(message);
38
+ return looksExperimental || looksDotenvNoise;
39
+ }
40
+
41
+ for (const listener of process.listeners('warning')) {
42
+ process.removeListener('warning', listener);
43
+ }
44
+ process.on('warning', (warning) => {
45
+ const message = (warning && warning.message) || '';
46
+ const type = (warning && warning.name) || '';
47
+ if (isSuppressibleWarning(message, type)) return;
48
+ process.stderr.write(`(node:${process.pid}) ${type || 'Warning'}: ${message || warning}\n`);
49
+ });
50
+
51
+ const originalEmitWarning = process.emitWarning.bind(process);
52
+ process.emitWarning = function emitWarning(warning, ...rest) {
53
+ const message = typeof warning === 'string' ? warning : (warning && warning.message) || '';
54
+ const type =
55
+ typeof rest[0] === 'string' ? rest[0] :
56
+ (rest[0] && typeof rest[0] === 'object' && 'type' in rest[0]) ? rest[0].type :
57
+ (warning && warning.name) || '';
58
+ if (isSuppressibleWarning(message, type)) return;
59
+ return originalEmitWarning(warning, ...rest);
60
+ };
61
+
62
+ // Path to the compiled ESM entry, resolved relative to this shim.
63
+ const path = require('node:path');
64
+ const url = require('node:url');
65
+ const entry = path.resolve(__dirname, '..', 'dist', 'index.js');
66
+ import(url.pathToFileURL(entry).href).catch((err) => {
67
+ // Surface boot-time errors verbatim — a silent exit would just look like
68
+ // the CLI never started.
69
+ process.stderr.write(`brainrouter: failed to load CLI entrypoint: ${(err && err.stack) || err}\n`);
70
+ process.exit(1);
71
+ });
@@ -1,6 +1,8 @@
1
1
  import type { McpClientWrapper } from '../runtime/mcpClient.js';
2
2
  import type { LLMConfig } from '../config/config.js';
3
3
  import type { AccessMode } from '../orchestration/roles.js';
4
+ import { type RecalledRecord } from '../memory/briefing.js';
5
+ import { type EffortLevel } from '../state/preferencesStore.js';
4
6
  export interface RunTurnCallbacks {
5
7
  onStatusUpdate: (status: string) => void;
6
8
  onToolStart: (name: string, args: Record<string, any>) => void;
@@ -77,6 +79,12 @@ export interface ChatCompletionPayload {
77
79
  };
78
80
  }>;
79
81
  tool_choice?: 'auto';
82
+ /**
83
+ * OpenAI Chat Completions reasoning slot — accepted by gpt-5 / o-series.
84
+ * Only set when the user has chosen a non-default `/effort` AND the
85
+ * endpoint+model combo accepts the field (see `supportsReasoningEffortField`).
86
+ */
87
+ reasoning_effort?: EffortLevel;
80
88
  }
81
89
  export interface AgentOptions {
82
90
  workspaceRoot: string;
@@ -131,6 +139,10 @@ export declare const LOCAL_TOOLS: ({
131
139
  url?: undefined;
132
140
  maxResults?: undefined;
133
141
  patch?: undefined;
142
+ question?: undefined;
143
+ header?: undefined;
144
+ options?: undefined;
145
+ multiSelect?: undefined;
134
146
  explanation?: undefined;
135
147
  plan?: undefined;
136
148
  proof?: undefined;
@@ -163,6 +175,10 @@ export declare const LOCAL_TOOLS: ({
163
175
  url?: undefined;
164
176
  maxResults?: undefined;
165
177
  patch?: undefined;
178
+ question?: undefined;
179
+ header?: undefined;
180
+ options?: undefined;
181
+ multiSelect?: undefined;
166
182
  explanation?: undefined;
167
183
  plan?: undefined;
168
184
  proof?: undefined;
@@ -198,6 +214,10 @@ export declare const LOCAL_TOOLS: ({
198
214
  url?: undefined;
199
215
  maxResults?: undefined;
200
216
  patch?: undefined;
217
+ question?: undefined;
218
+ header?: undefined;
219
+ options?: undefined;
220
+ multiSelect?: undefined;
201
221
  explanation?: undefined;
202
222
  plan?: undefined;
203
223
  proof?: undefined;
@@ -227,6 +247,10 @@ export declare const LOCAL_TOOLS: ({
227
247
  url?: undefined;
228
248
  maxResults?: undefined;
229
249
  patch?: undefined;
250
+ question?: undefined;
251
+ header?: undefined;
252
+ options?: undefined;
253
+ multiSelect?: undefined;
230
254
  explanation?: undefined;
231
255
  plan?: undefined;
232
256
  proof?: undefined;
@@ -259,6 +283,10 @@ export declare const LOCAL_TOOLS: ({
259
283
  url?: undefined;
260
284
  maxResults?: undefined;
261
285
  patch?: undefined;
286
+ question?: undefined;
287
+ header?: undefined;
288
+ options?: undefined;
289
+ multiSelect?: undefined;
262
290
  explanation?: undefined;
263
291
  plan?: undefined;
264
292
  proof?: undefined;
@@ -288,6 +316,10 @@ export declare const LOCAL_TOOLS: ({
288
316
  url?: undefined;
289
317
  maxResults?: undefined;
290
318
  patch?: undefined;
319
+ question?: undefined;
320
+ header?: undefined;
321
+ options?: undefined;
322
+ multiSelect?: undefined;
291
323
  explanation?: undefined;
292
324
  plan?: undefined;
293
325
  proof?: undefined;
@@ -317,6 +349,10 @@ export declare const LOCAL_TOOLS: ({
317
349
  url?: undefined;
318
350
  maxResults?: undefined;
319
351
  patch?: undefined;
352
+ question?: undefined;
353
+ header?: undefined;
354
+ options?: undefined;
355
+ multiSelect?: undefined;
320
356
  explanation?: undefined;
321
357
  plan?: undefined;
322
358
  proof?: undefined;
@@ -346,6 +382,10 @@ export declare const LOCAL_TOOLS: ({
346
382
  command?: undefined;
347
383
  maxResults?: undefined;
348
384
  patch?: undefined;
385
+ question?: undefined;
386
+ header?: undefined;
387
+ options?: undefined;
388
+ multiSelect?: undefined;
349
389
  explanation?: undefined;
350
390
  plan?: undefined;
351
391
  proof?: undefined;
@@ -378,6 +418,10 @@ export declare const LOCAL_TOOLS: ({
378
418
  command?: undefined;
379
419
  url?: undefined;
380
420
  patch?: undefined;
421
+ question?: undefined;
422
+ header?: undefined;
423
+ options?: undefined;
424
+ multiSelect?: undefined;
381
425
  explanation?: undefined;
382
426
  plan?: undefined;
383
427
  proof?: undefined;
@@ -407,6 +451,68 @@ export declare const LOCAL_TOOLS: ({
407
451
  command?: undefined;
408
452
  url?: undefined;
409
453
  maxResults?: undefined;
454
+ question?: undefined;
455
+ header?: undefined;
456
+ options?: undefined;
457
+ multiSelect?: undefined;
458
+ explanation?: undefined;
459
+ plan?: undefined;
460
+ proof?: undefined;
461
+ reason?: undefined;
462
+ needed?: undefined;
463
+ };
464
+ required: string[];
465
+ };
466
+ } | {
467
+ name: string;
468
+ description: string;
469
+ inputSchema: {
470
+ type: string;
471
+ properties: {
472
+ question: {
473
+ type: string;
474
+ description: string;
475
+ };
476
+ header: {
477
+ type: string;
478
+ description: string;
479
+ };
480
+ options: {
481
+ type: string;
482
+ description: string;
483
+ minItems: number;
484
+ maxItems: number;
485
+ items: {
486
+ type: string;
487
+ properties: {
488
+ label: {
489
+ type: string;
490
+ description: string;
491
+ };
492
+ description: {
493
+ type: string;
494
+ description: string;
495
+ };
496
+ };
497
+ required: string[];
498
+ };
499
+ };
500
+ multiSelect: {
501
+ type: string;
502
+ description: string;
503
+ };
504
+ path?: undefined;
505
+ startLine?: undefined;
506
+ endLine?: undefined;
507
+ content?: undefined;
508
+ targetContent?: undefined;
509
+ replacementContent?: undefined;
510
+ query?: undefined;
511
+ pattern?: undefined;
512
+ command?: undefined;
513
+ url?: undefined;
514
+ maxResults?: undefined;
515
+ patch?: undefined;
410
516
  explanation?: undefined;
411
517
  plan?: undefined;
412
518
  proof?: undefined;
@@ -454,6 +560,10 @@ export declare const LOCAL_TOOLS: ({
454
560
  url?: undefined;
455
561
  maxResults?: undefined;
456
562
  patch?: undefined;
563
+ question?: undefined;
564
+ header?: undefined;
565
+ options?: undefined;
566
+ multiSelect?: undefined;
457
567
  proof?: undefined;
458
568
  reason?: undefined;
459
569
  needed?: undefined;
@@ -482,6 +592,10 @@ export declare const LOCAL_TOOLS: ({
482
592
  url?: undefined;
483
593
  maxResults?: undefined;
484
594
  patch?: undefined;
595
+ question?: undefined;
596
+ header?: undefined;
597
+ options?: undefined;
598
+ multiSelect?: undefined;
485
599
  explanation?: undefined;
486
600
  plan?: undefined;
487
601
  reason?: undefined;
@@ -515,6 +629,10 @@ export declare const LOCAL_TOOLS: ({
515
629
  url?: undefined;
516
630
  maxResults?: undefined;
517
631
  patch?: undefined;
632
+ question?: undefined;
633
+ header?: undefined;
634
+ options?: undefined;
635
+ multiSelect?: undefined;
518
636
  explanation?: undefined;
519
637
  plan?: undefined;
520
638
  proof?: undefined;
@@ -575,6 +693,28 @@ export declare class Agent {
575
693
  private recalledRecordIds;
576
694
  private recalledRecords;
577
695
  private lastBriefingSources;
696
+ /**
697
+ * 10b: latest MCP tool inventory captured by `listTools()` calls. Used by
698
+ * `createSystemMessage` to decide whether the BrainRouter memory section
699
+ * should render — when `memory_recall` is missing from this list (the
700
+ * cloud brain is offline), the prompt swaps to a brain-offline notice so
701
+ * the model doesn't try to call tools that aren't there. Undefined until
702
+ * the first successful list; treated as "assume online" by the prompt
703
+ * builder until then (back-compat for callers that don't list pre-turn).
704
+ */
705
+ private lastKnownMcpTools?;
706
+ /**
707
+ * 9b: gated recall state. `recallHasFiredThisSession` flips to true on the
708
+ * first successful briefing injection so subsequent turns can skip the
709
+ * fresh recall pull unless a gated trigger fires. `recallNextTurnIsPost-
710
+ * Compaction` is set by `compactHistory()` to force the next turn through
711
+ * the full briefing path (compaction just dropped the prior briefing as
712
+ * collateral; replay it once so the model isn't blind). Both are
713
+ * cleared on `loadHistory` / `fork` / `bootstrapSession` so a fresh
714
+ * session re-pulls.
715
+ */
716
+ private recallHasFiredThisSession;
717
+ private recallNextTurnIsPostCompaction;
578
718
  private roleOverlay?;
579
719
  private accessMode;
580
720
  private silent;
@@ -708,9 +848,30 @@ export declare class Agent {
708
848
  * Idempotent: calling this with a tag that isn't present is a no-op.
709
849
  */
710
850
  removeTaggedSystemMessage(tag: string): void;
851
+ /**
852
+ * Zero the in-process counters that back `/tokens`. Call this on any
853
+ * conceptual session boundary (`/resume`, `fork`) — otherwise the parent
854
+ * row keeps accumulating across the switch and "this session" no longer
855
+ * matches the displayed sessionKey.
856
+ */
857
+ resetSessionCounters(): void;
711
858
  /** Fork the current chat history into a fresh sessionKey. Returns the new key. */
712
859
  fork(newSessionKey: string): string;
713
860
  private bootstrapSession;
861
+ /**
862
+ * Public, callback-free wrapper around bootstrapSession for slash commands
863
+ * that mutate per-session state (notably `/goal`) BEFORE any runTurn has
864
+ * fired. Without this, the FIRST `/goal` of a session writes goal.json
865
+ * under the deterministic fallback sessionKey ("brainrouter-cli:<path>")
866
+ * because bootstrap hasn't happened yet, but every subsequent runTurn
867
+ * reads from the MCP-resolved UUID sessionKey — split-brain that left
868
+ * the agent reading a stale goal from a different directory.
869
+ *
870
+ * Idempotent: returns immediately if already initialized. Tolerates
871
+ * missing MCP — falls back to the deterministic key the same way
872
+ * bootstrapSession does.
873
+ */
874
+ ensureInitialized(): Promise<void>;
714
875
  private createSystemMessage;
715
876
  private injectRecallContext;
716
877
  /** Inspectable summary of the most recent memory briefing. Used by the `/briefing` slash command. */
@@ -718,6 +879,13 @@ export declare class Agent {
718
879
  sources: string[];
719
880
  recordIds: string[];
720
881
  };
882
+ /**
883
+ * Snapshot of the records produced by the most recent pre-turn briefing.
884
+ * `/where` surfaces a few of these to give the user a sense of what the
885
+ * agent is leaning on right now. Returns a shallow copy so callers can't
886
+ * mutate the agent's internal state.
887
+ */
888
+ getRecalledRecords(): RecalledRecord[];
721
889
  /** One-line summary of any new contradiction surfaced after the last capture, or undefined if none. */
722
890
  private lastContradictionWarning?;
723
891
  takeContradictionWarning(): string | undefined;
@@ -757,8 +925,50 @@ export declare function getToolSummary(name: string, args: Record<string, any>,
757
925
  * the terminal. Returns undefined when no useful preview is available.
758
926
  */
759
927
  export declare function getToolPreview(name: string, args: Record<string, any>, result: string): string | undefined;
760
- export declare function buildChatCompletionPayload(config: LLMConfig, messages: any[], tools: any[]): ChatCompletionPayload;
761
- export declare function callOpenAI(config: LLMConfig, messages: any[], tools: any[]): Promise<{
928
+ /**
929
+ * Heuristic for "does this model accept the OpenAI Chat Completions
930
+ * `reasoning_effort` field?". The signal that actually matters is the
931
+ * **model name**, not the endpoint hostname — modern OpenAI-compatible
932
+ * servers (LM Studio 0.3.29+, Ollama, vLLM, OpenRouter, OpenAI itself)
933
+ * all accept the field on /v1/chat/completions for the reasoning-capable
934
+ * model classes below, and silently ignore it for everything else. So a
935
+ * `gpt-oss-20b` served from localhost via LM Studio gets the same
936
+ * treatment as `gpt-5` on `api.openai.com`.
937
+ *
938
+ * Borrowed shape from openai-node's `ReasoningEffort` enum
939
+ * (openSrc/openai-node/src/resources/shared.ts) — `low|medium|high` map
940
+ * straight through to the provider field across OpenAI, DeepSeek,
941
+ * LM Studio, Ollama, and OpenRouter's pass-through. Anthropic models
942
+ * (`claude-*`) use a different field shape (`thinking: { budget_tokens }`)
943
+ * and a different endpoint (`/v1/messages`), so they're intentionally
944
+ * skipped here — brainrouter would need a separate provider adapter to
945
+ * forward into Anthropic's native API.
946
+ */
947
+ /**
948
+ * 9b: resolve the recall-gating mode for this process. `BRAINROUTER_RECALL_MODE`
949
+ * env var beats everything; unset defaults to `gated`. Anything outside the
950
+ * three valid values falls back to `gated` (defensive — better to be helpful
951
+ * than crash on a typo). Re-resolved each turn so users can flip with
952
+ * `export BRAINROUTER_RECALL_MODE=always` mid-session via a /run command.
953
+ */
954
+ export declare function resolveRecallMode(): 'always' | 'gated' | 'off';
955
+ /**
956
+ * 9b: cheap local heuristic for "the user message names something specific
957
+ * memory might have history on." Counts entity-shaped tokens: proper nouns
958
+ * (capitalized words that aren't sentence-starting), file paths (anything
959
+ * with `/` or `\\` or a `.<ext>` suffix), and identifier-shaped tokens (`camelCase`
960
+ * / `snake_case` / `PascalCase` longer than 4 chars). Crude but the bar is
961
+ * "is recall plausibly worth it?" — false positives waste a recall call,
962
+ * false negatives waste an ask. Tunable threshold via the caller.
963
+ */
964
+ export declare function countEntityTokens(text: string): number;
965
+ export declare function supportsReasoningEffortField(config: LLMConfig): boolean;
966
+ export interface BuildPayloadOptions {
967
+ /** Reasoning-depth preference, when provider supports it. `medium` is a no-op. */
968
+ effort?: EffortLevel;
969
+ }
970
+ export declare function buildChatCompletionPayload(config: LLMConfig, messages: any[], tools: any[], options?: BuildPayloadOptions): ChatCompletionPayload;
971
+ export declare function callOpenAI(config: LLMConfig, messages: any[], tools: any[], options?: BuildPayloadOptions): Promise<{
762
972
  content: any;
763
973
  toolCalls: any;
764
974
  usage: any;