@blockrun/franklin 3.3.3 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -25
- package/dist/agent/commands.d.ts +1 -1
- package/dist/agent/commands.js +128 -17
- package/dist/agent/compact.d.ts +2 -2
- package/dist/agent/compact.js +148 -22
- package/dist/agent/context.d.ts +8 -3
- package/dist/agent/context.js +301 -108
- package/dist/agent/error-classifier.d.ts +11 -2
- package/dist/agent/error-classifier.js +64 -10
- package/dist/agent/llm.d.ts +8 -1
- package/dist/agent/llm.js +114 -19
- package/dist/agent/loop.d.ts +1 -2
- package/dist/agent/loop.js +509 -61
- package/dist/agent/optimize.d.ts +2 -2
- package/dist/agent/optimize.js +9 -7
- package/dist/agent/permissions.d.ts +1 -1
- package/dist/agent/permissions.js +1 -1
- package/dist/agent/planner.d.ts +42 -0
- package/dist/agent/planner.js +110 -0
- package/dist/agent/reduce.d.ts +7 -1
- package/dist/agent/reduce.js +85 -3
- package/dist/agent/streaming-executor.d.ts +6 -1
- package/dist/agent/streaming-executor.js +83 -5
- package/dist/agent/tokens.d.ts +11 -2
- package/dist/agent/tokens.js +38 -5
- package/dist/agent/tool-guard.d.ts +27 -0
- package/dist/agent/tool-guard.js +324 -0
- package/dist/agent/types.d.ts +7 -1
- package/dist/agent/types.js +1 -1
- package/dist/brain/extract.d.ts +11 -0
- package/dist/brain/extract.js +154 -0
- package/dist/brain/index.d.ts +3 -0
- package/dist/brain/index.js +2 -0
- package/dist/brain/store.d.ts +42 -0
- package/dist/brain/store.js +225 -0
- package/dist/brain/types.d.ts +45 -0
- package/dist/brain/types.js +5 -0
- package/dist/commands/daemon.js +2 -1
- package/dist/commands/start.js +19 -7
- package/dist/config.js +1 -1
- package/dist/index.js +27 -2
- package/dist/learnings/extractor.d.ts +13 -0
- package/dist/learnings/extractor.js +69 -8
- package/dist/learnings/index.d.ts +1 -1
- package/dist/learnings/index.js +1 -1
- package/dist/learnings/store.js +42 -13
- package/dist/learnings/types.d.ts +1 -1
- package/dist/mcp/client.d.ts +1 -1
- package/dist/mcp/client.js +5 -5
- package/dist/mcp/config.d.ts +1 -1
- package/dist/mcp/config.js +1 -1
- package/dist/panel/html.d.ts +2 -0
- package/dist/panel/html.js +409 -146
- package/dist/panel/server.js +19 -0
- package/dist/pricing.js +3 -2
- package/dist/proxy/fallback.d.ts +3 -1
- package/dist/proxy/fallback.js +4 -4
- package/dist/proxy/server.js +29 -11
- package/dist/proxy/sse-translator.js +1 -1
- package/dist/router/categories.d.ts +21 -0
- package/dist/router/categories.js +96 -0
- package/dist/router/index.d.ts +9 -2
- package/dist/router/index.js +106 -27
- package/dist/router/local-elo.d.ts +32 -0
- package/dist/router/local-elo.js +107 -0
- package/dist/router/selector.d.ts +46 -0
- package/dist/router/selector.js +106 -0
- package/dist/session/storage.d.ts +5 -1
- package/dist/session/storage.js +24 -2
- package/dist/social/a11y.d.ts +1 -1
- package/dist/social/a11y.js +5 -1
- package/dist/social/browser.d.ts +5 -0
- package/dist/social/browser.js +22 -0
- package/dist/social/preflight.d.ts +4 -0
- package/dist/social/preflight.js +42 -3
- package/dist/stats/failures.d.ts +20 -0
- package/dist/stats/failures.js +63 -0
- package/dist/stats/format.d.ts +6 -0
- package/dist/stats/format.js +23 -0
- package/dist/stats/insights.js +1 -21
- package/dist/stats/session-tracker.d.ts +21 -0
- package/dist/stats/session-tracker.js +28 -0
- package/dist/stats/tracker.d.ts +1 -1
- package/dist/stats/tracker.js +1 -1
- package/dist/tools/bash.d.ts +14 -1
- package/dist/tools/bash.js +132 -7
- package/dist/tools/edit.js +77 -14
- package/dist/tools/glob.js +13 -3
- package/dist/tools/grep.js +30 -12
- package/dist/tools/imagegen.js +5 -5
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +5 -1
- package/dist/tools/read.d.ts +16 -2
- package/dist/tools/read.js +36 -8
- package/dist/tools/searchx.d.ts +6 -2
- package/dist/tools/searchx.js +221 -44
- package/dist/tools/subagent.js +37 -3
- package/dist/tools/task.js +43 -7
- package/dist/tools/validate.d.ts +11 -0
- package/dist/tools/validate.js +42 -0
- package/dist/tools/webfetch.js +18 -7
- package/dist/tools/websearch.js +41 -7
- package/dist/tools/write.js +26 -6
- package/dist/ui/app.js +31 -6
- package/dist/ui/model-picker.d.ts +1 -1
- package/dist/ui/model-picker.js +1 -1
- package/dist/ui/terminal.d.ts +1 -1
- package/dist/ui/terminal.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
<a href="#quick-start">Quick start</a> ·
|
|
30
30
|
<a href="#a-new-category">New category</a> ·
|
|
31
31
|
<a href="#what-franklin-can-execute">What it does</a> ·
|
|
32
|
+
<a href="#smart-router">Smart Router</a> ·
|
|
32
33
|
<a href="#the-comparison">Compare</a> ·
|
|
33
34
|
<a href="#features">Features</a> ·
|
|
34
35
|
<a href="#how-it-works">Architecture</a> ·
|
|
@@ -120,31 +121,20 @@ Franklin is **chat-first**. You do not wire a DAG, configure six API keys, or co
|
|
|
120
121
|
|
|
121
122
|
Live data from CoinGecko. RSI, MACD, Bollinger, and volatility computed locally. No API key needed.
|
|
122
123
|
|
|
123
|
-
###
|
|
124
|
+
### 🎨 Image generation
|
|
124
125
|
|
|
125
126
|
```text
|
|
126
|
-
>
|
|
127
|
+
> generate a logo for my AI startup — minimalist, dark background
|
|
127
128
|
|
|
128
|
-
✓
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
1. "Claude keeps throttling me in the middle of shipping..." — @buildermax (2h)
|
|
132
|
-
2. "I need an agent that can switch models automatically." — @indiedev (5h)
|
|
133
|
-
...
|
|
134
|
-
|
|
135
|
-
> write a reply to #2 — mention Franklin uses a wallet instead of subscriptions
|
|
129
|
+
✓ ImageGen "minimalist AI startup logo, dark background..."
|
|
130
|
+
Saved: generated-logo-1713052800.png (1024x1024)
|
|
131
|
+
```
|
|
136
132
|
|
|
137
|
-
|
|
138
|
-
"That was my pain too. Franklin routes across 55+ models,
|
|
139
|
-
pays per action from a USDC wallet, and doesn't trap you
|
|
140
|
-
inside a monthly seat. Better economics, better uptime."
|
|
133
|
+
Generates images via DALL-E / GPT Image directly from the CLI. Paid from your wallet — no OpenAI API key needed.
|
|
141
134
|
|
|
142
|
-
|
|
135
|
+
### 🎯 Social growth (with setup)
|
|
143
136
|
|
|
144
|
-
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Search X, generate contextual replies, and post with confirmation. Uses Playwright for browser automation, so there is no X API key, no OAuth maze, and no $100/month developer account.
|
|
137
|
+
After running `franklin social setup && franklin social login x`, Franklin can search X, draft replies, and post with your confirmation — no X API key or developer account needed.
|
|
148
138
|
|
|
149
139
|
### 🔎 Research, code, anything with a budget
|
|
150
140
|
|
|
@@ -179,6 +169,52 @@ Every tool call is itemized. Every token is priced. When the wallet hits zero, F
|
|
|
179
169
|
|
|
180
170
|
---
|
|
181
171
|
|
|
172
|
+
## Smart Router
|
|
173
|
+
|
|
174
|
+
**55+ models. One decision. Zero guesswork.**
|
|
175
|
+
|
|
176
|
+
You don't pick models. Franklin picks for you.
|
|
177
|
+
|
|
178
|
+
The Smart Router classifies every request — coding, trading, reasoning, research — and selects the model with the best quality-to-cost ratio. Trained on **2M+ real requests** from the BlockRun gateway, continuously updated.
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
> refactor this auth module to use JWT
|
|
182
|
+
|
|
183
|
+
CODING kimi-k2.5 · 12.4K in / 2.1K out · $0.0023 saved 84%
|
|
184
|
+
|
|
185
|
+
> what's the BTC outlook for the week?
|
|
186
|
+
|
|
187
|
+
TRADING grok-4-1-fast-reasoning · 8.2K in / 1.8K out · $0.0008 saved 95%
|
|
188
|
+
|
|
189
|
+
> prove that this algorithm is O(n log n)
|
|
190
|
+
|
|
191
|
+
REASONING claude-sonnet-4.6 · 15.1K in / 3.4K out · $0.0312
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Every response shows which model was chosen, why, and how much you saved vs. always using the most expensive option.
|
|
195
|
+
|
|
196
|
+
**Four profiles:**
|
|
197
|
+
|
|
198
|
+
| Profile | Strategy | Use case |
|
|
199
|
+
|---------|----------|----------|
|
|
200
|
+
| `auto` | Best quality-to-cost ratio | Default — smart spend |
|
|
201
|
+
| `eco` | Cheapest model with decent quality | Budget-conscious |
|
|
202
|
+
| `premium` | Highest quality regardless of cost | Mission-critical |
|
|
203
|
+
| `free` | Free NVIDIA models only | Zero wallet balance |
|
|
204
|
+
|
|
205
|
+
**Per-session breakdown** — run `/cost` to see exactly where your USDC went:
|
|
206
|
+
|
|
207
|
+
```text
|
|
208
|
+
Session Cost: $0.0847 (23 requests)
|
|
209
|
+
gemini-2.5-flash $0.0012 14 req CODING
|
|
210
|
+
kimi-k2.5 $0.0423 6 req CODING
|
|
211
|
+
claude-sonnet-4.6 $0.0412 3 req REASONING
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The router also learns from **your** usage. If you keep retrying a model for coding tasks, Franklin adapts and picks a better one next time. Your router gets smarter the more you use it.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
182
218
|
## Why Franklin
|
|
183
219
|
|
|
184
220
|
<table>
|
|
@@ -215,6 +251,7 @@ Marketing, trading, research, code, and anything else you can express as tools p
|
|
|
215
251
|
| ------------------------------------ | --------------- | ---------------- | ---------------- | ------------------------------- |
|
|
216
252
|
| Main unit of value | Answers | Code changes | Fixed automations| **Budgeted outcomes** |
|
|
217
253
|
| Has purchasing power | ❌ | ❌ | ❌ | ✅ **wallet-native** |
|
|
254
|
+
| Picks best model per task | ❌ | ❌ | ❌ | ✅ **learned router** |
|
|
218
255
|
| Can choose tools/models per step | ⚠️ limited | ✅ mostly coding | ❌ usually fixed | ✅ **yes** |
|
|
219
256
|
| Works across marketing/trading/code | ⚠️ | ❌ code-first | ⚠️ integration-bound | ✅ **cross-vertical** |
|
|
220
257
|
| Hard spend cap | ❌ | ❌ | ⚠️ external billing | ✅ **wallet balance** |
|
|
@@ -238,8 +275,8 @@ Franklin can decide what is worth paying for, route the call, sign the micropaym
|
|
|
238
275
|
**📈 Trading signals**
|
|
239
276
|
Ask "what's BTC looking like?" — Franklin fetches live price data, computes RSI/MACD/Bollinger/volatility, and synthesizes a signal.
|
|
240
277
|
|
|
241
|
-
|
|
242
|
-
Ask "
|
|
278
|
+
**🎨 AI image generation**
|
|
279
|
+
Ask "generate a logo" — Franklin calls DALL-E / GPT Image, saves the result locally, paid from your wallet.
|
|
243
280
|
|
|
244
281
|
**🧠 55+ models via one wallet**
|
|
245
282
|
Anthropic, OpenAI, Google, xAI, DeepSeek, GLM, Kimi, Minimax, NVIDIA free tier. One wallet, one interface, automatic fallback.
|
|
@@ -247,8 +284,8 @@ Anthropic, OpenAI, Google, xAI, DeepSeek, GLM, Kimi, Minimax, NVIDIA free tier.
|
|
|
247
284
|
**💳 x402 micropayments**
|
|
248
285
|
HTTP 402 native. Every paid action is a signed micropayment against your USDC balance. No subscriptions. No refund loop. No account lock-in.
|
|
249
286
|
|
|
250
|
-
|
|
251
|
-
|
|
287
|
+
**🧠 Learned model router**
|
|
288
|
+
Trained on 2M+ real requests. Classifies your task and picks the best model from 55+ LLMs. Four profiles (auto/eco/premium/free). Adapts to your usage over time.
|
|
252
289
|
|
|
253
290
|
</td>
|
|
254
291
|
<td width="50%" valign="top">
|
|
@@ -301,7 +338,10 @@ Core is workflow-agnostic. Add new verticals without touching the loop. Discover
|
|
|
301
338
|
```text
|
|
302
339
|
┌──────────────────────────────────────────────────────────────┐
|
|
303
340
|
│ Franklin Runtime │
|
|
304
|
-
│ Intent →
|
|
341
|
+
│ Intent → Smart Router → Tool Use → Spend Control → Result │
|
|
342
|
+
├──────────────────────────────────────────────────────────────┤
|
|
343
|
+
│ Learned Router │
|
|
344
|
+
│ 2M+ requests · 55+ models · category detection · Elo scores │
|
|
305
345
|
├──────────────────────────────────────────────────────────────┤
|
|
306
346
|
│ Agent Loop │
|
|
307
347
|
│ 16 tools · Sessions · Compaction · Pricing · Plugin SDK │
|
|
@@ -349,7 +389,7 @@ src/
|
|
|
349
389
|
├── stats/ Usage tracking + insights engine
|
|
350
390
|
├── ui/ Ink-based terminal UI
|
|
351
391
|
├── proxy/ Payment proxy for external tools
|
|
352
|
-
├── router/
|
|
392
|
+
├── router/ Learned model router (2M+ requests, Elo scoring)
|
|
353
393
|
├── wallet/ Wallet management (Base + Solana)
|
|
354
394
|
├── mcp/ MCP server auto-discovery
|
|
355
395
|
└── commands/ CLI subcommands
|
package/dist/agent/commands.d.ts
CHANGED
package/dist/agent/commands.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Slash command registry for
|
|
2
|
+
* Slash command registry for Franklin.
|
|
3
3
|
* Extracted from loop.ts for maintainability.
|
|
4
4
|
*
|
|
5
5
|
* Two types of commands:
|
|
@@ -104,8 +104,13 @@ function extractText(msg) {
|
|
|
104
104
|
// ─── Command Definitions ──────────────────────────────────────────────────
|
|
105
105
|
// Direct-handled commands (don't go to agent)
|
|
106
106
|
const DIRECT_COMMANDS = {
|
|
107
|
+
'/noplan': (ctx) => {
|
|
108
|
+
ctx.config.planDisabled = true;
|
|
109
|
+
ctx.onEvent({ kind: 'text_delta', text: 'Plan-then-execute disabled for this session. Complex tasks will use a single model.\n' });
|
|
110
|
+
emitDone(ctx);
|
|
111
|
+
},
|
|
107
112
|
'/stash': (ctx) => {
|
|
108
|
-
const r = gitCmd(ctx, 'git stash push -m "
|
|
113
|
+
const r = gitCmd(ctx, 'git stash push -m "franklin auto-stash"', 10000);
|
|
109
114
|
if (r !== null)
|
|
110
115
|
ctx.onEvent({ kind: 'text_delta', text: r ? `${r}\n` : 'No changes to stash.\n' });
|
|
111
116
|
emitDone(ctx);
|
|
@@ -197,8 +202,8 @@ const DIRECT_COMMANDS = {
|
|
|
197
202
|
` **Git:** /push /pr /undo /status /diff /log /branch /stash /unstash\n` +
|
|
198
203
|
` **Analysis:** /security /lint /optimize /todo /deps /clean /migrate /doc\n` +
|
|
199
204
|
` **Session:** /plan /ultraplan /execute /compact /retry /sessions /resume /session-search /context /tasks\n` +
|
|
200
|
-
` **Power:** /ultrathink [query] /ultraplan /dump\n` +
|
|
201
|
-
` **Info:** /model /wallet /cost /tokens /learnings /mcp /doctor /version /bug /help\n` +
|
|
205
|
+
` **Power:** /ultrathink [query] /ultraplan /noplan /dump\n` +
|
|
206
|
+
` **Info:** /model /wallet /cost /tokens /learnings /brain /mcp /doctor /version /bug /help\n` +
|
|
202
207
|
` **UI:** /clear /exit\n` +
|
|
203
208
|
(ultrathinkOn ? `\n Ultrathink: ON\n` : '')
|
|
204
209
|
});
|
|
@@ -225,7 +230,7 @@ const DIRECT_COMMANDS = {
|
|
|
225
230
|
emitDone(ctx);
|
|
226
231
|
},
|
|
227
232
|
'/bug': (ctx) => {
|
|
228
|
-
ctx.onEvent({ kind: 'text_delta', text: 'Report issues at: https://github.com/BlockRunAI/
|
|
233
|
+
ctx.onEvent({ kind: 'text_delta', text: 'Report issues at: https://github.com/BlockRunAI/Franklin/issues\n' });
|
|
229
234
|
emitDone(ctx);
|
|
230
235
|
},
|
|
231
236
|
'/version': (ctx) => {
|
|
@@ -340,34 +345,57 @@ const DIRECT_COMMANDS = {
|
|
|
340
345
|
}
|
|
341
346
|
emitDone(ctx);
|
|
342
347
|
},
|
|
343
|
-
'/sessions': (ctx) => {
|
|
348
|
+
'/sessions': async (ctx) => {
|
|
344
349
|
const sessions = listSessions();
|
|
345
350
|
if (sessions.length === 0) {
|
|
346
351
|
ctx.onEvent({ kind: 'text_delta', text: 'No saved sessions.\n' });
|
|
347
352
|
}
|
|
348
353
|
else {
|
|
354
|
+
const { formatTokens, formatUsd, shortModelName } = await import('../stats/format.js');
|
|
349
355
|
let text = `**${sessions.length} saved sessions:**\n\n`;
|
|
350
356
|
for (const s of sessions.slice(0, 10)) {
|
|
351
357
|
const date = new Date(s.updatedAt).toLocaleString();
|
|
352
|
-
const dir = s.workDir ?
|
|
353
|
-
const current = s.id === ctx.sessionId ? '
|
|
354
|
-
|
|
358
|
+
const dir = s.workDir ? path.basename(s.workDir) : '';
|
|
359
|
+
const current = s.id === ctx.sessionId ? ' (current)' : '';
|
|
360
|
+
const model = shortModelName(s.model);
|
|
361
|
+
const tokens = (s.inputTokens || s.outputTokens)
|
|
362
|
+
? ` ${formatTokens(s.inputTokens ?? 0)} in / ${formatTokens(s.outputTokens ?? 0)} out`
|
|
363
|
+
: '';
|
|
364
|
+
const cost = s.costUsd ? ` ${formatUsd(s.costUsd)}` : '';
|
|
365
|
+
const saved = s.savedVsOpusUsd && s.savedVsOpusUsd > 0.001
|
|
366
|
+
? ` saved ${formatUsd(s.savedVsOpusUsd)}`
|
|
367
|
+
: '';
|
|
368
|
+
text += ` ${model} — ${s.messageCount} messages${tokens}${cost}${saved}\n`;
|
|
369
|
+
text += ` ${date} · ${dir}${current}\n\n`;
|
|
355
370
|
}
|
|
356
371
|
if (sessions.length > 10)
|
|
357
372
|
text += ` ... and ${sessions.length - 10} more\n`;
|
|
358
|
-
text += '
|
|
373
|
+
text += 'Use /resume to restore the latest session, or /resume <session-id> for a specific one.\n';
|
|
359
374
|
ctx.onEvent({ kind: 'text_delta', text });
|
|
360
375
|
}
|
|
361
376
|
emitDone(ctx);
|
|
362
377
|
},
|
|
363
378
|
'/cost': async (ctx) => {
|
|
364
379
|
const { stats, saved } = getStatsSummary();
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
380
|
+
const { getSessionModelBreakdown } = await import('../stats/session-tracker.js');
|
|
381
|
+
const { formatTokens, formatUsd, shortModelName } = await import('../stats/format.js');
|
|
382
|
+
const breakdown = getSessionModelBreakdown();
|
|
383
|
+
let text = `**Session Cost**\n` +
|
|
384
|
+
` Requests: ${stats.totalRequests}\n` +
|
|
385
|
+
` Cost: $${stats.totalCostUsd.toFixed(4)} USDC\n` +
|
|
386
|
+
` Saved: $${saved.toFixed(2)} vs Claude Opus\n` +
|
|
387
|
+
` Tokens: ${formatTokens(stats.totalInputTokens)} in / ${formatTokens(stats.totalOutputTokens)} out\n`;
|
|
388
|
+
if (breakdown.length > 0) {
|
|
389
|
+
text += `\n **By model:**\n`;
|
|
390
|
+
for (const m of breakdown) {
|
|
391
|
+
const name = shortModelName(m.model).padEnd(28);
|
|
392
|
+
const cost = formatUsd(m.costUsd).padStart(8);
|
|
393
|
+
const reqs = `${m.requests} req`.padStart(6);
|
|
394
|
+
const tier = m.lastTier ? ` ${m.lastTier}` : '';
|
|
395
|
+
text += ` ${name} ${cost} ${reqs}${tier}\n`;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
ctx.onEvent({ kind: 'text_delta', text });
|
|
371
399
|
emitDone(ctx);
|
|
372
400
|
},
|
|
373
401
|
'/wallet': async (ctx) => {
|
|
@@ -419,6 +447,38 @@ const DIRECT_COMMANDS = {
|
|
|
419
447
|
ctx.onEvent({ kind: 'text_delta', text: 'Conversation history cleared.\n' });
|
|
420
448
|
emitDone(ctx);
|
|
421
449
|
},
|
|
450
|
+
'/failures': async (ctx) => {
|
|
451
|
+
const { getFailureStats } = await import('../stats/failures.js');
|
|
452
|
+
const stats = getFailureStats();
|
|
453
|
+
if (stats.total === 0) {
|
|
454
|
+
ctx.onEvent({ kind: 'text_delta', text: 'No failures recorded.\n' });
|
|
455
|
+
emitDone(ctx);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
let text = `**Failure Log** (${stats.total} total)\n\n`;
|
|
459
|
+
if (stats.byType.size > 0) {
|
|
460
|
+
text += ' **By type:**\n';
|
|
461
|
+
for (const [type, count] of [...stats.byType.entries()].sort((a, b) => b[1] - a[1])) {
|
|
462
|
+
text += ` ${type.padEnd(20)} ${count}\n`;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (stats.byTool.size > 0) {
|
|
466
|
+
text += '\n **By tool:**\n';
|
|
467
|
+
for (const [tool, count] of [...stats.byTool.entries()].sort((a, b) => b[1] - a[1])) {
|
|
468
|
+
text += ` ${tool.padEnd(20)} ${count}\n`;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if (stats.recentFailures.length > 0) {
|
|
472
|
+
text += '\n **Recent:**\n';
|
|
473
|
+
for (const f of stats.recentFailures.slice(-5)) {
|
|
474
|
+
const date = new Date(f.timestamp).toLocaleDateString();
|
|
475
|
+
const tool = f.toolName ? ` ${f.toolName}:` : '';
|
|
476
|
+
text += ` [${date}]${tool} ${f.errorMessage.slice(0, 80)}\n`;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
ctx.onEvent({ kind: 'text_delta', text });
|
|
480
|
+
emitDone(ctx);
|
|
481
|
+
},
|
|
422
482
|
'/compact': async (ctx) => {
|
|
423
483
|
const beforeTokens = estimateHistoryTokens(ctx.history);
|
|
424
484
|
const { history: compacted, compacted: didCompact } = await forceCompact(ctx.history, ctx.config.model, ctx.client, ctx.config.debug);
|
|
@@ -546,6 +606,57 @@ export async function handleSlashCommand(input, ctx) {
|
|
|
546
606
|
emitDone(ctx);
|
|
547
607
|
return { handled: true };
|
|
548
608
|
}
|
|
609
|
+
// /brain — view knowledge graph entities
|
|
610
|
+
if (input === '/brain' || input.startsWith('/brain ')) {
|
|
611
|
+
const { searchEntities, loadEntities, getEntityObservations, getEntityRelations, getBrainStats, loadObservations } = await import('../brain/store.js');
|
|
612
|
+
const arg = input.slice('/brain'.length).trim();
|
|
613
|
+
if (!arg) {
|
|
614
|
+
const stats = getBrainStats();
|
|
615
|
+
if (stats.entities === 0) {
|
|
616
|
+
ctx.onEvent({ kind: 'text_delta', text: 'Brain is empty. Franklin learns entities (people, projects, companies) from your conversations over time.\n' });
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
const entities = loadEntities().sort((a, b) => b.reference_count - a.reference_count);
|
|
620
|
+
let text = `**Franklin Brain** (${stats.entities} entities, ${stats.observations} facts, ${stats.relations} relations)\n\n`;
|
|
621
|
+
for (const e of entities.slice(0, 20)) {
|
|
622
|
+
text += ` ${e.type === 'person' ? '👤' : e.type === 'company' ? '🏢' : e.type === 'project' ? '📦' : '💡'} **${e.name}** (${e.type}, ×${e.reference_count})\n`;
|
|
623
|
+
}
|
|
624
|
+
if (entities.length > 20)
|
|
625
|
+
text += ` ... and ${entities.length - 20} more\n`;
|
|
626
|
+
text += '\nSearch: `/brain <name>` for details.\n';
|
|
627
|
+
ctx.onEvent({ kind: 'text_delta', text });
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
const results = searchEntities(arg, 5);
|
|
632
|
+
if (results.length === 0) {
|
|
633
|
+
ctx.onEvent({ kind: 'text_delta', text: `No entities matching "${arg}".\n` });
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
let text = '';
|
|
637
|
+
for (const e of results) {
|
|
638
|
+
text += `**${e.name}** (${e.type})\n`;
|
|
639
|
+
if (e.aliases.length > 0)
|
|
640
|
+
text += ` Aliases: ${e.aliases.join(', ')}\n`;
|
|
641
|
+
const obs = getEntityObservations(e.id).slice(0, 5);
|
|
642
|
+
for (const o of obs) {
|
|
643
|
+
text += ` - ${o.content}\n`;
|
|
644
|
+
}
|
|
645
|
+
const rels = getEntityRelations(e.id);
|
|
646
|
+
const allEntities = loadEntities();
|
|
647
|
+
for (const r of rels.slice(0, 3)) {
|
|
648
|
+
const other = allEntities.find(x => x.id === (r.from_id === e.id ? r.to_id : r.from_id));
|
|
649
|
+
if (other)
|
|
650
|
+
text += ` → ${r.type} ${other.name}\n`;
|
|
651
|
+
}
|
|
652
|
+
text += '\n';
|
|
653
|
+
}
|
|
654
|
+
ctx.onEvent({ kind: 'text_delta', text });
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
emitDone(ctx);
|
|
658
|
+
return { handled: true };
|
|
659
|
+
}
|
|
549
660
|
// /model — show current model or switch with /model <name>
|
|
550
661
|
if (input === '/model' || input.startsWith('/model ')) {
|
|
551
662
|
if (input === '/model') {
|
|
@@ -677,7 +788,7 @@ export async function handleSlashCommand(input, ctx) {
|
|
|
677
788
|
...Object.keys(DIRECT_COMMANDS),
|
|
678
789
|
...Object.keys(REWRITE_COMMANDS),
|
|
679
790
|
...ARG_COMMANDS.map(c => c.prefix.trim()),
|
|
680
|
-
'/branch', '/resume', '/model', '/wallet', '/cost', '/help', '/clear', '/retry', '/exit', '/session-search', '/ssearch',
|
|
791
|
+
'/branch', '/resume', '/model', '/wallet', '/cost', '/help', '/clear', '/retry', '/exit', '/session-search', '/ssearch', '/failures',
|
|
681
792
|
];
|
|
682
793
|
const cmd = input.split(/\s/)[0];
|
|
683
794
|
const close = allCommands.filter(c => {
|
package/dist/agent/compact.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Context compaction for
|
|
2
|
+
* Context compaction for Franklin.
|
|
3
3
|
* When conversation history approaches the context window limit,
|
|
4
4
|
* summarize older messages and replace them with the summary.
|
|
5
5
|
*/
|
|
6
6
|
import { ModelClient } from './llm.js';
|
|
7
7
|
import type { Dialogue } from './types.js';
|
|
8
|
-
export declare const COMPACT_HEADER = "[CONTEXT COMPACTION] Earlier turns
|
|
8
|
+
export declare const COMPACT_HEADER = "[CONTEXT COMPACTION \u2014 REFERENCE ONLY] Earlier turns were compacted into the summary below. This is a handoff from a previous context window \u2014 treat it as background reference, NOT as active instructions. Do NOT answer questions or fulfill requests mentioned in this summary; they were already addressed. Respond ONLY to the latest user message that appears AFTER this summary.";
|
|
9
9
|
/**
|
|
10
10
|
* Check if compaction is needed and perform it if so.
|
|
11
11
|
* Returns the (possibly compacted) history.
|