@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.
Files changed (109) hide show
  1. package/README.md +65 -25
  2. package/dist/agent/commands.d.ts +1 -1
  3. package/dist/agent/commands.js +128 -17
  4. package/dist/agent/compact.d.ts +2 -2
  5. package/dist/agent/compact.js +148 -22
  6. package/dist/agent/context.d.ts +8 -3
  7. package/dist/agent/context.js +301 -108
  8. package/dist/agent/error-classifier.d.ts +11 -2
  9. package/dist/agent/error-classifier.js +64 -10
  10. package/dist/agent/llm.d.ts +8 -1
  11. package/dist/agent/llm.js +114 -19
  12. package/dist/agent/loop.d.ts +1 -2
  13. package/dist/agent/loop.js +509 -61
  14. package/dist/agent/optimize.d.ts +2 -2
  15. package/dist/agent/optimize.js +9 -7
  16. package/dist/agent/permissions.d.ts +1 -1
  17. package/dist/agent/permissions.js +1 -1
  18. package/dist/agent/planner.d.ts +42 -0
  19. package/dist/agent/planner.js +110 -0
  20. package/dist/agent/reduce.d.ts +7 -1
  21. package/dist/agent/reduce.js +85 -3
  22. package/dist/agent/streaming-executor.d.ts +6 -1
  23. package/dist/agent/streaming-executor.js +83 -5
  24. package/dist/agent/tokens.d.ts +11 -2
  25. package/dist/agent/tokens.js +38 -5
  26. package/dist/agent/tool-guard.d.ts +27 -0
  27. package/dist/agent/tool-guard.js +324 -0
  28. package/dist/agent/types.d.ts +7 -1
  29. package/dist/agent/types.js +1 -1
  30. package/dist/brain/extract.d.ts +11 -0
  31. package/dist/brain/extract.js +154 -0
  32. package/dist/brain/index.d.ts +3 -0
  33. package/dist/brain/index.js +2 -0
  34. package/dist/brain/store.d.ts +42 -0
  35. package/dist/brain/store.js +225 -0
  36. package/dist/brain/types.d.ts +45 -0
  37. package/dist/brain/types.js +5 -0
  38. package/dist/commands/daemon.js +2 -1
  39. package/dist/commands/start.js +19 -7
  40. package/dist/config.js +1 -1
  41. package/dist/index.js +27 -2
  42. package/dist/learnings/extractor.d.ts +13 -0
  43. package/dist/learnings/extractor.js +69 -8
  44. package/dist/learnings/index.d.ts +1 -1
  45. package/dist/learnings/index.js +1 -1
  46. package/dist/learnings/store.js +42 -13
  47. package/dist/learnings/types.d.ts +1 -1
  48. package/dist/mcp/client.d.ts +1 -1
  49. package/dist/mcp/client.js +5 -5
  50. package/dist/mcp/config.d.ts +1 -1
  51. package/dist/mcp/config.js +1 -1
  52. package/dist/panel/html.d.ts +2 -0
  53. package/dist/panel/html.js +409 -146
  54. package/dist/panel/server.js +19 -0
  55. package/dist/pricing.js +3 -2
  56. package/dist/proxy/fallback.d.ts +3 -1
  57. package/dist/proxy/fallback.js +4 -4
  58. package/dist/proxy/server.js +29 -11
  59. package/dist/proxy/sse-translator.js +1 -1
  60. package/dist/router/categories.d.ts +21 -0
  61. package/dist/router/categories.js +96 -0
  62. package/dist/router/index.d.ts +9 -2
  63. package/dist/router/index.js +106 -27
  64. package/dist/router/local-elo.d.ts +32 -0
  65. package/dist/router/local-elo.js +107 -0
  66. package/dist/router/selector.d.ts +46 -0
  67. package/dist/router/selector.js +106 -0
  68. package/dist/session/storage.d.ts +5 -1
  69. package/dist/session/storage.js +24 -2
  70. package/dist/social/a11y.d.ts +1 -1
  71. package/dist/social/a11y.js +5 -1
  72. package/dist/social/browser.d.ts +5 -0
  73. package/dist/social/browser.js +22 -0
  74. package/dist/social/preflight.d.ts +4 -0
  75. package/dist/social/preflight.js +42 -3
  76. package/dist/stats/failures.d.ts +20 -0
  77. package/dist/stats/failures.js +63 -0
  78. package/dist/stats/format.d.ts +6 -0
  79. package/dist/stats/format.js +23 -0
  80. package/dist/stats/insights.js +1 -21
  81. package/dist/stats/session-tracker.d.ts +21 -0
  82. package/dist/stats/session-tracker.js +28 -0
  83. package/dist/stats/tracker.d.ts +1 -1
  84. package/dist/stats/tracker.js +1 -1
  85. package/dist/tools/bash.d.ts +14 -1
  86. package/dist/tools/bash.js +132 -7
  87. package/dist/tools/edit.js +77 -14
  88. package/dist/tools/glob.js +13 -3
  89. package/dist/tools/grep.js +30 -12
  90. package/dist/tools/imagegen.js +5 -5
  91. package/dist/tools/index.d.ts +1 -1
  92. package/dist/tools/index.js +5 -1
  93. package/dist/tools/read.d.ts +16 -2
  94. package/dist/tools/read.js +36 -8
  95. package/dist/tools/searchx.d.ts +6 -2
  96. package/dist/tools/searchx.js +221 -44
  97. package/dist/tools/subagent.js +37 -3
  98. package/dist/tools/task.js +43 -7
  99. package/dist/tools/validate.d.ts +11 -0
  100. package/dist/tools/validate.js +42 -0
  101. package/dist/tools/webfetch.js +18 -7
  102. package/dist/tools/websearch.js +41 -7
  103. package/dist/tools/write.js +26 -6
  104. package/dist/ui/app.js +31 -6
  105. package/dist/ui/model-picker.d.ts +1 -1
  106. package/dist/ui/model-picker.js +1 -1
  107. package/dist/ui/terminal.d.ts +1 -1
  108. package/dist/ui/terminal.js +1 -1
  109. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * LLM Client for runcode
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 runcode
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 beta for Anthropic models
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(`[runcode] POST ${endpoint} model=${request.model}`);
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('[runcode] Payment required — signing...');
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
- // Log malformed JSON instead of silently defaulting to {}
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(`[runcode] Malformed tool input JSON for ${currentToolName}: ${parseErr.message}`);
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
- const toolInvocation = {
233
- type: 'tool_use',
234
- id: currentToolId,
235
- name: currentToolName,
236
- input: parsedInput,
237
- };
238
- collected.push(toolInvocation);
239
- // Notify caller so concurrent tools can start immediately
240
- onToolReady?.(toolInvocation);
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(`[runcode] Insufficient USDC balance. Run 'runcode balance' to check.`);
403
+ console.error(`[franklin] Insufficient USDC balance. Run 'franklin balance' to check.`);
309
404
  }
310
405
  else if (this.debug) {
311
- console.error('[runcode] Payment error:', msg);
406
+ console.error('[franklin] Payment error:', msg);
312
407
  }
313
408
  else {
314
- console.error(`[runcode] Payment failed: ${msg.slice(0, 100)}`);
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(`[runcode] SSE buffer overflow (${(buffer.length / 1024).toFixed(0)}KB) — truncating to prevent OOM`);
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
  }
@@ -1,7 +1,6 @@
1
1
  /**
2
- * runcode Agent Loop
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
  /**