@blockrun/franklin 3.3.2 → 3.5.0
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 +58 -7
- 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/banner.js +27 -40
- 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 +16 -3
- 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 +3 -3
- 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/dist/agent/llm.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* LLM Client for
|
|
2
|
+
* LLM Client for Franklin
|
|
3
3
|
* Calls BlockRun API directly with x402 payment handling and streaming.
|
|
4
4
|
* Original implementation — not derived from any existing codebase.
|
|
5
5
|
*/
|
|
@@ -42,6 +42,13 @@ export declare class ModelClient {
|
|
|
42
42
|
* Yields parsed SSE chunks as they arrive.
|
|
43
43
|
* Handles x402 payment automatically on 402 responses.
|
|
44
44
|
*/
|
|
45
|
+
/**
|
|
46
|
+
* Resolve virtual routing profiles (blockrun/auto, blockrun/eco, etc.)
|
|
47
|
+
* to concrete models. This is the final safety net — if the router in
|
|
48
|
+
* loop.ts didn't resolve it (e.g. old global install without router),
|
|
49
|
+
* we resolve it here before hitting the API.
|
|
50
|
+
*/
|
|
51
|
+
private resolveVirtualModel;
|
|
45
52
|
streamCompletion(request: ModelRequest, signal?: AbortSignal): AsyncGenerator<StreamChunk>;
|
|
46
53
|
/**
|
|
47
54
|
* Non-streaming completion for simple requests.
|
package/dist/agent/llm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* LLM Client for
|
|
2
|
+
* LLM Client for Franklin
|
|
3
3
|
* Calls BlockRun API directly with x402 payment handling and streaming.
|
|
4
4
|
* Original implementation — not derived from any existing codebase.
|
|
5
5
|
*/
|
|
@@ -82,7 +82,44 @@ export class ModelClient {
|
|
|
82
82
|
* Yields parsed SSE chunks as they arrive.
|
|
83
83
|
* Handles x402 payment automatically on 402 responses.
|
|
84
84
|
*/
|
|
85
|
+
/**
|
|
86
|
+
* Resolve virtual routing profiles (blockrun/auto, blockrun/eco, etc.)
|
|
87
|
+
* to concrete models. This is the final safety net — if the router in
|
|
88
|
+
* loop.ts didn't resolve it (e.g. old global install without router),
|
|
89
|
+
* we resolve it here before hitting the API.
|
|
90
|
+
*/
|
|
91
|
+
resolveVirtualModel(model) {
|
|
92
|
+
if (!model.startsWith('blockrun/'))
|
|
93
|
+
return model;
|
|
94
|
+
// Import router dynamically to avoid circular deps
|
|
95
|
+
try {
|
|
96
|
+
const { routeRequest, parseRoutingProfile } = require('../router/index.js');
|
|
97
|
+
const profile = parseRoutingProfile(model);
|
|
98
|
+
if (profile) {
|
|
99
|
+
const result = routeRequest('', profile);
|
|
100
|
+
if (result?.model && !result.model.startsWith('blockrun/')) {
|
|
101
|
+
return result.model;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Router not available (e.g. old build) — use hardcoded fallback table
|
|
107
|
+
}
|
|
108
|
+
// Static fallback if router is unavailable
|
|
109
|
+
const FALLBACKS = {
|
|
110
|
+
'blockrun/auto': 'zai/glm-5.1',
|
|
111
|
+
'blockrun/eco': 'nvidia/nemotron-ultra-253b',
|
|
112
|
+
'blockrun/premium': 'anthropic/claude-sonnet-4.6',
|
|
113
|
+
'blockrun/free': 'nvidia/nemotron-ultra-253b',
|
|
114
|
+
};
|
|
115
|
+
return FALLBACKS[model] || 'zai/glm-5.1';
|
|
116
|
+
}
|
|
85
117
|
async *streamCompletion(request, signal) {
|
|
118
|
+
// Resolve virtual models before any API call
|
|
119
|
+
const resolvedModel = this.resolveVirtualModel(request.model);
|
|
120
|
+
if (resolvedModel !== request.model) {
|
|
121
|
+
request = { ...request, model: resolvedModel };
|
|
122
|
+
}
|
|
86
123
|
const isAnthropic = request.model.startsWith('anthropic/');
|
|
87
124
|
const isGLM = request.model.startsWith('zai/') || request.model.includes('glm');
|
|
88
125
|
// Build the request payload, injecting model-specific optimizations
|
|
@@ -100,6 +137,27 @@ export class ModelClient {
|
|
|
100
137
|
}
|
|
101
138
|
}
|
|
102
139
|
if (isAnthropic) {
|
|
140
|
+
// ─ Anthropic extended thinking ──────────────────────────────────────
|
|
141
|
+
// Enable thinking for Claude models that support it (Opus 4.6, Sonnet 4.6).
|
|
142
|
+
// This is the single biggest quality lever — Claude with thinking enabled
|
|
143
|
+
// is dramatically better at complex multi-step tasks, reasoning, and code.
|
|
144
|
+
//
|
|
145
|
+
// Uses adaptive thinking: the model decides how much to think per request.
|
|
146
|
+
// budget_tokens is the MAX it can use (not a minimum), so the model won't
|
|
147
|
+
// waste tokens on simple tasks. Set to 80% of max_tokens to leave room
|
|
148
|
+
// for the actual response.
|
|
149
|
+
const supportsThinking = request.model.includes('opus') ||
|
|
150
|
+
request.model.includes('sonnet-4') ||
|
|
151
|
+
request.model.includes('sonnet-3.7');
|
|
152
|
+
if (supportsThinking) {
|
|
153
|
+
const maxOut = (request.max_tokens ?? 16_384);
|
|
154
|
+
requestPayload['thinking'] = {
|
|
155
|
+
type: 'enabled',
|
|
156
|
+
budget_tokens: Math.min(maxOut, 16_384), // Cap thinking budget — most benefit comes from first few K tokens
|
|
157
|
+
};
|
|
158
|
+
// Extended thinking requires temperature=1 on Anthropic API
|
|
159
|
+
requestPayload['temperature'] = 1;
|
|
160
|
+
}
|
|
103
161
|
// ─ Anthropic prompt caching: `system_and_3` strategy ─────────────────
|
|
104
162
|
// 4 cache_control breakpoints (Anthropic max):
|
|
105
163
|
// 1. System prompt (stable across turns)
|
|
@@ -110,6 +168,20 @@ export class ModelClient {
|
|
|
110
168
|
// multi-turn conversations. Pattern adopted from nousresearch/hermes-agent.
|
|
111
169
|
requestPayload = applyAnthropicPromptCaching(requestPayload, request);
|
|
112
170
|
}
|
|
171
|
+
// ── GPT-5 / Codex: use "developer" role for system prompt ──────────────
|
|
172
|
+
// OpenAI GPT models give stronger instruction-following weight to the
|
|
173
|
+
// "developer" role. Move the top-level system prompt into messages[0]
|
|
174
|
+
// with role "developer" instead of the default "system".
|
|
175
|
+
const isGPT5OrCodex = request.model.includes('gpt-5') || request.model.includes('codex');
|
|
176
|
+
if (isGPT5OrCodex && typeof request.system === 'string' && request.system.length > 0) {
|
|
177
|
+
const systemRole = 'developer';
|
|
178
|
+
const existingMessages = requestPayload['messages'] || [];
|
|
179
|
+
requestPayload['messages'] = [
|
|
180
|
+
{ role: systemRole, content: request.system },
|
|
181
|
+
...existingMessages,
|
|
182
|
+
];
|
|
183
|
+
delete requestPayload['system'];
|
|
184
|
+
}
|
|
113
185
|
const body = JSON.stringify(requestPayload);
|
|
114
186
|
const endpoint = `${this.apiUrl}/v1/messages`;
|
|
115
187
|
const headers = {
|
|
@@ -118,12 +190,12 @@ export class ModelClient {
|
|
|
118
190
|
'x-api-key': 'x402-agent-handles-auth',
|
|
119
191
|
'User-Agent': USER_AGENT,
|
|
120
192
|
};
|
|
121
|
-
// Enable prompt caching
|
|
193
|
+
// Enable prompt caching + extended thinking betas for Anthropic models
|
|
122
194
|
if (isAnthropic) {
|
|
123
195
|
headers['anthropic-beta'] = 'prompt-caching-2024-07-31';
|
|
124
196
|
}
|
|
125
197
|
if (this.debug) {
|
|
126
|
-
console.error(`[
|
|
198
|
+
console.error(`[franklin] POST ${endpoint} model=${request.model}`);
|
|
127
199
|
}
|
|
128
200
|
let response = await fetch(endpoint, {
|
|
129
201
|
method: 'POST',
|
|
@@ -134,7 +206,7 @@ export class ModelClient {
|
|
|
134
206
|
// Handle x402 payment
|
|
135
207
|
if (response.status === 402) {
|
|
136
208
|
if (this.debug)
|
|
137
|
-
console.error('[
|
|
209
|
+
console.error('[franklin] Payment required — signing...');
|
|
138
210
|
const paymentHeader = await this.signPayment(response);
|
|
139
211
|
if (!paymentHeader) {
|
|
140
212
|
yield { kind: 'error', payload: { message: 'Payment signing failed' } };
|
|
@@ -175,6 +247,7 @@ export class ModelClient {
|
|
|
175
247
|
// Accumulate from stream
|
|
176
248
|
let currentText = '';
|
|
177
249
|
let currentThinking = '';
|
|
250
|
+
let currentThinkingSignature = '';
|
|
178
251
|
let currentToolId = '';
|
|
179
252
|
let currentToolName = '';
|
|
180
253
|
let currentToolInput = '';
|
|
@@ -190,6 +263,7 @@ export class ModelClient {
|
|
|
190
263
|
}
|
|
191
264
|
else if (cblock?.type === 'thinking') {
|
|
192
265
|
currentThinking = '';
|
|
266
|
+
currentThinkingSignature = '';
|
|
193
267
|
}
|
|
194
268
|
else if (cblock?.type === 'text') {
|
|
195
269
|
currentText = '';
|
|
@@ -212,6 +286,10 @@ export class ModelClient {
|
|
|
212
286
|
if (text)
|
|
213
287
|
onStreamDelta?.({ type: 'thinking', text });
|
|
214
288
|
}
|
|
289
|
+
else if (delta.type === 'signature_delta') {
|
|
290
|
+
// Accumulate signature for multi-turn thinking continuity
|
|
291
|
+
currentThinkingSignature += delta.signature || '';
|
|
292
|
+
}
|
|
215
293
|
else if (delta.type === 'input_json_delta') {
|
|
216
294
|
currentToolInput += delta.partial_json || '';
|
|
217
295
|
}
|
|
@@ -220,24 +298,39 @@ export class ModelClient {
|
|
|
220
298
|
case 'content_block_stop': {
|
|
221
299
|
if (currentToolId) {
|
|
222
300
|
let parsedInput = {};
|
|
301
|
+
let inputParseError = false;
|
|
223
302
|
try {
|
|
224
303
|
parsedInput = JSON.parse(currentToolInput || '{}');
|
|
225
304
|
}
|
|
226
305
|
catch (parseErr) {
|
|
227
|
-
//
|
|
306
|
+
// Incomplete JSON from stream abort or model error.
|
|
307
|
+
// Mark as error so the executor returns an error result
|
|
308
|
+
// instead of silently invoking the tool with empty/wrong params.
|
|
309
|
+
inputParseError = true;
|
|
228
310
|
if (this.debug) {
|
|
229
|
-
console.error(`[
|
|
311
|
+
console.error(`[franklin] Malformed tool input JSON for ${currentToolName}: ${parseErr.message}`);
|
|
312
|
+
console.error(`[franklin] Raw input was: ${currentToolInput.slice(0, 200)}`);
|
|
230
313
|
}
|
|
231
314
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
315
|
+
if (inputParseError) {
|
|
316
|
+
// Don't invoke the tool — add a text block explaining the error
|
|
317
|
+
// and skip the tool_use entirely. The model will see the error and retry.
|
|
318
|
+
collected.push({
|
|
319
|
+
type: 'text',
|
|
320
|
+
text: `[Tool call to ${currentToolName} failed: incomplete JSON input from stream. The request may have been interrupted.]`,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
const toolInvocation = {
|
|
325
|
+
type: 'tool_use',
|
|
326
|
+
id: currentToolId,
|
|
327
|
+
name: currentToolName,
|
|
328
|
+
input: parsedInput,
|
|
329
|
+
};
|
|
330
|
+
collected.push(toolInvocation);
|
|
331
|
+
// Notify caller so concurrent tools can start immediately
|
|
332
|
+
onToolReady?.(toolInvocation);
|
|
333
|
+
}
|
|
241
334
|
currentToolId = '';
|
|
242
335
|
currentToolName = '';
|
|
243
336
|
currentToolInput = '';
|
|
@@ -246,8 +339,10 @@ export class ModelClient {
|
|
|
246
339
|
collected.push({
|
|
247
340
|
type: 'thinking',
|
|
248
341
|
thinking: currentThinking,
|
|
342
|
+
...(currentThinkingSignature ? { signature: currentThinkingSignature } : {}),
|
|
249
343
|
});
|
|
250
344
|
currentThinking = '';
|
|
345
|
+
currentThinkingSignature = '';
|
|
251
346
|
}
|
|
252
347
|
else if (currentText) {
|
|
253
348
|
collected.push({
|
|
@@ -305,13 +400,13 @@ export class ModelClient {
|
|
|
305
400
|
catch (err) {
|
|
306
401
|
const msg = err.message || '';
|
|
307
402
|
if (msg.includes('insufficient') || msg.includes('balance')) {
|
|
308
|
-
console.error(`[
|
|
403
|
+
console.error(`[franklin] Insufficient USDC balance. Run 'franklin balance' to check.`);
|
|
309
404
|
}
|
|
310
405
|
else if (this.debug) {
|
|
311
|
-
console.error('[
|
|
406
|
+
console.error('[franklin] Payment error:', msg);
|
|
312
407
|
}
|
|
313
408
|
else {
|
|
314
|
-
console.error(`[
|
|
409
|
+
console.error(`[franklin] Payment failed: ${msg.slice(0, 100)}`);
|
|
315
410
|
}
|
|
316
411
|
return null;
|
|
317
412
|
}
|
|
@@ -398,7 +493,7 @@ export class ModelClient {
|
|
|
398
493
|
// Safety: if buffer grows too large without newlines, something is wrong
|
|
399
494
|
if (buffer.length > MAX_BUFFER) {
|
|
400
495
|
if (this.debug) {
|
|
401
|
-
console.error(`[
|
|
496
|
+
console.error(`[franklin] SSE buffer overflow (${(buffer.length / 1024).toFixed(0)}KB) — truncating to prevent OOM`);
|
|
402
497
|
}
|
|
403
498
|
buffer = buffer.slice(-MAX_BUFFER / 2);
|
|
404
499
|
}
|
package/dist/agent/loop.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Franklin Agent Loop
|
|
3
3
|
* The core reasoning-action cycle: prompt → model → extract capabilities → execute → repeat.
|
|
4
|
-
* Original implementation with different architecture from any reference codebase.
|
|
5
4
|
*/
|
|
6
5
|
import type { AgentConfig, Dialogue, StreamEvent } from './types.js';
|
|
7
6
|
/**
|