@blockrun/franklin 3.15.85 → 3.15.87
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/dist/commands/start.js +57 -22
- package/dist/ui/app.d.ts +4 -0
- package/dist/ui/app.js +10 -3
- package/package.json +1 -1
package/dist/commands/start.js
CHANGED
|
@@ -311,6 +311,7 @@ export async function startCommand(options) {
|
|
|
311
311
|
}
|
|
312
312
|
// Resolve resume target, if requested.
|
|
313
313
|
let resumeSessionId;
|
|
314
|
+
let resumeTranscript;
|
|
314
315
|
if (options.resume || options.continue) {
|
|
315
316
|
const { pickSession } = await import('../ui/session-picker.js');
|
|
316
317
|
const { loadSessionMeta, loadSessionHistory } = await import('../session/storage.js');
|
|
@@ -338,10 +339,11 @@ export async function startCommand(options) {
|
|
|
338
339
|
}
|
|
339
340
|
if (resumeSessionId) {
|
|
340
341
|
const meta = loadSessionMeta(resumeSessionId);
|
|
341
|
-
const
|
|
342
|
+
const history = loadSessionHistory(resumeSessionId);
|
|
342
343
|
const when = meta ? new Date(meta.updatedAt).toLocaleString() : 'unknown';
|
|
343
344
|
console.log(chalk.green(` Resuming session ${resumeSessionId.slice(0, 24)}…`));
|
|
344
|
-
console.log(chalk.dim(` ${
|
|
345
|
+
console.log(chalk.dim(` ${history.length} messages · last active ${when}\n`));
|
|
346
|
+
resumeTranscript = buildResumeTranscript(history);
|
|
345
347
|
}
|
|
346
348
|
}
|
|
347
349
|
// Agent config
|
|
@@ -376,7 +378,7 @@ export async function startCommand(options) {
|
|
|
376
378
|
if (process.stdin.isTTY) {
|
|
377
379
|
await runWithInkUI(agentConfig, model, workDir, version, walletInfo, (cb) => {
|
|
378
380
|
onBalanceFetched = cb;
|
|
379
|
-
}, fetchBalance, importedKickoffPrompt);
|
|
381
|
+
}, fetchBalance, importedKickoffPrompt, resumeTranscript);
|
|
380
382
|
}
|
|
381
383
|
else {
|
|
382
384
|
await runWithBasicUI(agentConfig, model, workDir, importedKickoffPrompt);
|
|
@@ -409,8 +411,40 @@ async function runOneShot(agentConfig, prompt) {
|
|
|
409
411
|
});
|
|
410
412
|
return exitCode;
|
|
411
413
|
}
|
|
414
|
+
function buildResumeTranscript(history) {
|
|
415
|
+
const entries = history
|
|
416
|
+
.map((msg) => {
|
|
417
|
+
const text = extractVisibleText(msg).replace(/\s+/g, ' ').trim();
|
|
418
|
+
if (!text)
|
|
419
|
+
return null;
|
|
420
|
+
return { role: msg.role, text: text.length > 180 ? `${text.slice(0, 177)}...` : text };
|
|
421
|
+
})
|
|
422
|
+
.filter((entry) => entry !== null);
|
|
423
|
+
if (entries.length === 0)
|
|
424
|
+
return [];
|
|
425
|
+
const started = entries.slice(0, 4);
|
|
426
|
+
const recentStart = entries.length > 10 ? -6 : 4;
|
|
427
|
+
const recent = entries.slice(recentStart);
|
|
428
|
+
return entries.length > 10
|
|
429
|
+
? [...started, { role: 'assistant', text: '...' }, ...recent]
|
|
430
|
+
: [...started, ...recent];
|
|
431
|
+
}
|
|
432
|
+
function extractVisibleText(msg) {
|
|
433
|
+
if (typeof msg.content === 'string')
|
|
434
|
+
return msg.content;
|
|
435
|
+
if (!Array.isArray(msg.content))
|
|
436
|
+
return '';
|
|
437
|
+
return msg.content
|
|
438
|
+
.map((part) => {
|
|
439
|
+
if ('type' in part && part.type === 'text')
|
|
440
|
+
return part.text;
|
|
441
|
+
return '';
|
|
442
|
+
})
|
|
443
|
+
.filter(Boolean)
|
|
444
|
+
.join('\n');
|
|
445
|
+
}
|
|
412
446
|
// ─── Ink UI (interactive terminal) ─────────────────────────────────────────
|
|
413
|
-
async function runWithInkUI(agentConfig, model, workDir, version, walletInfo, onBalanceReady, fetchBalance, initialInput) {
|
|
447
|
+
async function runWithInkUI(agentConfig, model, workDir, version, walletInfo, onBalanceReady, fetchBalance, initialInput, initialTranscript) {
|
|
414
448
|
const startSnapshot = snapshotStats();
|
|
415
449
|
const ui = launchInkUI({
|
|
416
450
|
model,
|
|
@@ -418,6 +452,7 @@ async function runWithInkUI(agentConfig, model, workDir, version, walletInfo, on
|
|
|
418
452
|
version,
|
|
419
453
|
walletAddress: walletInfo?.address,
|
|
420
454
|
walletBalance: walletInfo?.balance,
|
|
455
|
+
initialTranscript,
|
|
421
456
|
chain: walletInfo?.chain,
|
|
422
457
|
onModelChange: (newModel, reason) => {
|
|
423
458
|
agentConfig.model = newModel;
|
|
@@ -474,25 +509,12 @@ async function runWithInkUI(agentConfig, model, workDir, version, walletInfo, on
|
|
|
474
509
|
recordLatestSessionIfEnabled(process.cwd(), agentConfig.chain);
|
|
475
510
|
}
|
|
476
511
|
catch { /* telemetry is best-effort */ }
|
|
477
|
-
//
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const { extractBrainEntities } = await import('../brain/extract.js');
|
|
482
|
-
const { ModelClient } = await import('../agent/llm.js');
|
|
483
|
-
const client = new ModelClient({ apiUrl: agentConfig.apiUrl, chain: agentConfig.chain });
|
|
484
|
-
const sid = `session-${new Date().toISOString()}`;
|
|
485
|
-
await Promise.race([
|
|
486
|
-
Promise.all([
|
|
487
|
-
extractLearnings(sessionHistory, sid, client),
|
|
488
|
-
extractBrainEntities(sessionHistory, sid, client),
|
|
489
|
-
]),
|
|
490
|
-
new Promise(resolve => setTimeout(resolve, 15_000)),
|
|
491
|
-
]);
|
|
492
|
-
}
|
|
493
|
-
catch { /* extraction is best-effort */ }
|
|
512
|
+
// Optional post-session learning extraction. Disabled by default because any
|
|
513
|
+
// network-backed background promise can keep Node alive after the UI exits.
|
|
514
|
+
if (process.env.FRANKLIN_EXTRACT_ON_EXIT === '1') {
|
|
515
|
+
runExitBackgroundTasks(sessionHistory, agentConfig).catch(() => { });
|
|
494
516
|
}
|
|
495
|
-
|
|
517
|
+
disconnectMcpServers().catch(() => { });
|
|
496
518
|
// Session summary — delta vs. snapshot at session start
|
|
497
519
|
try {
|
|
498
520
|
const delta = statsDelta(startSnapshot);
|
|
@@ -524,6 +546,19 @@ async function runWithInkUI(agentConfig, model, workDir, version, walletInfo, on
|
|
|
524
546
|
}
|
|
525
547
|
console.log(chalk.dim('\nGoodbye.\n'));
|
|
526
548
|
}
|
|
549
|
+
async function runExitBackgroundTasks(sessionHistory, agentConfig) {
|
|
550
|
+
if (!sessionHistory || sessionHistory.length < 4)
|
|
551
|
+
return;
|
|
552
|
+
const { extractLearnings } = await import('../learnings/extractor.js');
|
|
553
|
+
const { extractBrainEntities } = await import('../brain/extract.js');
|
|
554
|
+
const { ModelClient } = await import('../agent/llm.js');
|
|
555
|
+
const client = new ModelClient({ apiUrl: agentConfig.apiUrl, chain: agentConfig.chain });
|
|
556
|
+
const sid = `session-${new Date().toISOString()}`;
|
|
557
|
+
await Promise.all([
|
|
558
|
+
extractLearnings(sessionHistory, sid, client),
|
|
559
|
+
extractBrainEntities(sessionHistory, sid, client),
|
|
560
|
+
]);
|
|
561
|
+
}
|
|
527
562
|
// ─── Basic readline UI (piped input) ───────────────────────────────────────
|
|
528
563
|
async function runWithBasicUI(agentConfig, model, workDir, initialInput) {
|
|
529
564
|
const { TerminalUI } = await import('../ui/terminal.js');
|
package/dist/ui/app.d.ts
CHANGED
|
@@ -20,6 +20,10 @@ export declare function launchInkUI(opts: {
|
|
|
20
20
|
version: string;
|
|
21
21
|
walletAddress?: string;
|
|
22
22
|
walletBalance?: string;
|
|
23
|
+
initialTranscript?: Array<{
|
|
24
|
+
role: 'user' | 'assistant';
|
|
25
|
+
text: string;
|
|
26
|
+
}>;
|
|
23
27
|
chain?: string;
|
|
24
28
|
showPicker?: boolean;
|
|
25
29
|
onModelChange?: (model: string, reason?: 'user' | 'system') => void;
|
package/dist/ui/app.js
CHANGED
|
@@ -345,7 +345,7 @@ function formatAgentErrorForDisplay(error) {
|
|
|
345
345
|
out.push(`- Tip: ${tip}`);
|
|
346
346
|
return out.join('\n');
|
|
347
347
|
}
|
|
348
|
-
function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain, startWithPicker, onSubmit, onModelChange, onAbort, onExit, }) {
|
|
348
|
+
function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain, initialTranscript, startWithPicker, onSubmit, onModelChange, onAbort, onExit, }) {
|
|
349
349
|
const { exit } = useApp();
|
|
350
350
|
// Track terminal rows so we can cap the dynamic-region height. Ink wipes the
|
|
351
351
|
// terminal scrollback (via ansiEscapes.clearTerminal → \x1b[3J) whenever the
|
|
@@ -362,7 +362,14 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
362
362
|
// Last completed tool — shown in dynamic area so it can be expanded/collapsed with Tab
|
|
363
363
|
const [expandableTool, setExpandableTool] = useState(null);
|
|
364
364
|
// Full responses committed to Static immediately — goes into terminal scrollback
|
|
365
|
-
const [committedResponses, setCommittedResponses] = useState([])
|
|
365
|
+
const [committedResponses, setCommittedResponses] = useState(() => (initialTranscript ?? []).map((entry, idx) => ({
|
|
366
|
+
key: `${entry.role === 'user' ? 'user' : 'resume'}-${idx}`,
|
|
367
|
+
text: entry.role === 'user'
|
|
368
|
+
? chalk.hex('#FFD700').bold('❯ ') + chalk.hex('#FFD700').bold(entry.text)
|
|
369
|
+
: entry.text,
|
|
370
|
+
tokens: { input: 0, output: 0, calls: 0 },
|
|
371
|
+
cost: 0,
|
|
372
|
+
})));
|
|
366
373
|
// Short preview of latest response shown in dynamic area (last ~5 lines, cleared on next turn)
|
|
367
374
|
const [responsePreview, setResponsePreview] = useState('');
|
|
368
375
|
const [currentModel, setCurrentModel] = useState(initialModel || PICKER_MODELS_FLAT[0].id);
|
|
@@ -1138,7 +1145,7 @@ export function launchInkUI(opts) {
|
|
|
1138
1145
|
restoreTerminalAutoWrap?.();
|
|
1139
1146
|
instance?.unmount();
|
|
1140
1147
|
};
|
|
1141
|
-
instance = render(_jsx(RunCodeApp, { initialModel: opts.model, workDir: opts.workDir, walletAddress: opts.walletAddress || 'not set — run: franklin setup', walletBalance: opts.walletBalance || 'unknown', chain: opts.chain || 'base', startWithPicker: opts.showPicker, onSubmit: (value) => {
|
|
1148
|
+
instance = render(_jsx(RunCodeApp, { initialModel: opts.model, workDir: opts.workDir, walletAddress: opts.walletAddress || 'not set — run: franklin setup', walletBalance: opts.walletBalance || 'unknown', initialTranscript: opts.initialTranscript, chain: opts.chain || 'base', startWithPicker: opts.showPicker, onSubmit: (value) => {
|
|
1142
1149
|
if (resolveInput) {
|
|
1143
1150
|
resolveInput(value);
|
|
1144
1151
|
resolveInput = null;
|
package/package.json
CHANGED