@blockrun/franklin 3.8.31 → 3.8.32
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/session/search.js +3 -3
- package/dist/session/storage.js +25 -22
- package/dist/ui/app.js +10 -1
- package/dist/ui/session-picker.d.ts +11 -0
- package/dist/ui/session-picker.js +45 -16
- package/package.json +1 -1
package/dist/session/search.js
CHANGED
|
@@ -11,9 +11,9 @@ import { listSessions, getSessionFilePath } from './storage.js';
|
|
|
11
11
|
function tokenize(text) {
|
|
12
12
|
return text
|
|
13
13
|
.toLowerCase()
|
|
14
|
-
.replace(/[^\
|
|
15
|
-
.split(/\s+/)
|
|
16
|
-
.filter(t => t.length > 1);
|
|
14
|
+
.replace(/[^\p{L}\p{N}_\s]/gu, ' ')
|
|
15
|
+
.split(/\s+/u)
|
|
16
|
+
.filter(t => t.length > 1 || /[^\x00-\x7F]/.test(t));
|
|
17
17
|
}
|
|
18
18
|
function parseQuery(query) {
|
|
19
19
|
const phrases = [];
|
package/dist/session/storage.js
CHANGED
|
@@ -158,10 +158,7 @@ export function loadSessionHistory(sessionId) {
|
|
|
158
158
|
return [];
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
|
-
|
|
162
|
-
* List all saved sessions, newest first.
|
|
163
|
-
*/
|
|
164
|
-
export function listSessions() {
|
|
161
|
+
function readSessionMetas(includeGhosts = false) {
|
|
165
162
|
const sessionsDir = getSessionsDir();
|
|
166
163
|
try {
|
|
167
164
|
const files = fs.readdirSync(sessionsDir)
|
|
@@ -174,14 +171,19 @@ export function listSessions() {
|
|
|
174
171
|
}
|
|
175
172
|
catch { /* skip corrupted */ }
|
|
176
173
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
return filtered.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
174
|
+
const visible = includeGhosts ? metas : metas.filter(m => m.messageCount > 0);
|
|
175
|
+
return visible.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
180
176
|
}
|
|
181
177
|
catch {
|
|
182
178
|
return [];
|
|
183
179
|
}
|
|
184
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* List all saved sessions, newest first.
|
|
183
|
+
*/
|
|
184
|
+
export function listSessions() {
|
|
185
|
+
return readSessionMetas(false);
|
|
186
|
+
}
|
|
185
187
|
/**
|
|
186
188
|
* Find the latest saved session tagged with a given channel (e.g.
|
|
187
189
|
* `telegram:12345`). Used by non-CLI drivers to resume across process
|
|
@@ -198,25 +200,26 @@ export function findLatestSessionByChannel(channel) {
|
|
|
198
200
|
* Accepts optional activeSessionId to protect from deletion.
|
|
199
201
|
*/
|
|
200
202
|
export function pruneOldSessions(activeSessionId) {
|
|
201
|
-
const sessions =
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
203
|
+
const sessions = readSessionMetas(false);
|
|
204
|
+
const allSessions = readSessionMetas(true);
|
|
205
|
+
if (sessions.length > MAX_SESSIONS) {
|
|
206
|
+
const toDelete = sessions
|
|
207
|
+
.slice(MAX_SESSIONS)
|
|
208
|
+
.filter(s => s.id !== activeSessionId); // Never delete active session
|
|
209
|
+
for (const s of toDelete) {
|
|
210
|
+
try {
|
|
211
|
+
fs.unlinkSync(sessionPath(s.id));
|
|
212
|
+
}
|
|
213
|
+
catch { /* ok */ }
|
|
214
|
+
try {
|
|
215
|
+
fs.unlinkSync(metaPath(s.id));
|
|
216
|
+
}
|
|
217
|
+
catch { /* ok */ }
|
|
214
218
|
}
|
|
215
|
-
catch { /* ok */ }
|
|
216
219
|
}
|
|
217
220
|
// Also clean up ghost sessions (0 messages, older than 5 minutes)
|
|
218
221
|
const fiveMinAgo = Date.now() - 5 * 60 * 1000;
|
|
219
|
-
for (const s of
|
|
222
|
+
for (const s of allSessions) {
|
|
220
223
|
if (s.id === activeSessionId)
|
|
221
224
|
continue;
|
|
222
225
|
if (s.messageCount === 0 && s.createdAt < fiveMinAgo) {
|
package/dist/ui/app.js
CHANGED
|
@@ -614,7 +614,16 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
614
614
|
break;
|
|
615
615
|
}
|
|
616
616
|
case 'usage': {
|
|
617
|
-
setCurrentModel(event.model)
|
|
617
|
+
// DO NOT setCurrentModel(event.model) here. currentModel
|
|
618
|
+
// represents the user's selection (e.g. 'blockrun/auto'),
|
|
619
|
+
// not what the router resolved for this specific turn. The
|
|
620
|
+
// per-turn resolved model is already captured in
|
|
621
|
+
// turnModelRef (rendered in the turn-summary line below
|
|
622
|
+
// each response) and in onModelChange('system') when the
|
|
623
|
+
// loop itself decides to swap (empty-response / 402 fallback).
|
|
624
|
+
// Overriding currentModel from every usage event made the
|
|
625
|
+
// status bar permanently show the last resolved model and
|
|
626
|
+
// create a false impression that auto mode was stuck.
|
|
618
627
|
setTurnTokens(prev => ({
|
|
619
628
|
input: prev.input + event.inputTokens,
|
|
620
629
|
output: prev.output + event.outputTokens,
|
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
* Lists recent sessions (newest first) and returns the selected ID.
|
|
4
4
|
*/
|
|
5
5
|
import { type SessionMeta } from '../session/storage.js';
|
|
6
|
+
type SessionPickerSelection = {
|
|
7
|
+
kind: 'cancel';
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'selected';
|
|
10
|
+
id: string;
|
|
11
|
+
} | {
|
|
12
|
+
kind: 'invalid';
|
|
13
|
+
message: string;
|
|
14
|
+
};
|
|
6
15
|
/**
|
|
7
16
|
* Resolve a user-provided session identifier to a full session ID.
|
|
8
17
|
* Supports exact match and unambiguous prefix match (minimum 8 chars).
|
|
@@ -16,6 +25,7 @@ export declare function resolveSessionIdInput(input: string): {
|
|
|
16
25
|
error: 'not-found' | 'ambiguous';
|
|
17
26
|
candidates: SessionMeta[];
|
|
18
27
|
};
|
|
28
|
+
export declare function resolvePickerSelection(input: string, shown: SessionMeta[], sessions: SessionMeta[]): SessionPickerSelection;
|
|
19
29
|
/**
|
|
20
30
|
* Find the most recent session for a given working directory.
|
|
21
31
|
* Returns null if none exists.
|
|
@@ -28,3 +38,4 @@ export declare function findLatestSessionForDir(workDir: string): SessionMeta |
|
|
|
28
38
|
export declare function pickSession(opts?: {
|
|
29
39
|
workDir?: string;
|
|
30
40
|
}): Promise<string | null>;
|
|
41
|
+
export {};
|
|
@@ -61,6 +61,33 @@ export function resolveSessionIdInput(input) {
|
|
|
61
61
|
}
|
|
62
62
|
return { ok: false, error: 'not-found', candidates: [] };
|
|
63
63
|
}
|
|
64
|
+
export function resolvePickerSelection(input, shown, sessions) {
|
|
65
|
+
const trimmed = input.trim();
|
|
66
|
+
if (!trimmed)
|
|
67
|
+
return { kind: 'cancel' };
|
|
68
|
+
const num = parseInt(trimmed, 10);
|
|
69
|
+
if (!isNaN(num) && num >= 1 && num <= shown.length) {
|
|
70
|
+
return { kind: 'selected', id: shown[num - 1].id };
|
|
71
|
+
}
|
|
72
|
+
const exact = sessions.find((s) => s.id === trimmed);
|
|
73
|
+
if (exact)
|
|
74
|
+
return { kind: 'selected', id: exact.id };
|
|
75
|
+
const resolved = resolveSessionIdInput(trimmed);
|
|
76
|
+
if (resolved.ok) {
|
|
77
|
+
return { kind: 'selected', id: resolved.id };
|
|
78
|
+
}
|
|
79
|
+
if (resolved.error === 'ambiguous') {
|
|
80
|
+
const preview = resolved.candidates
|
|
81
|
+
.slice(0, 3)
|
|
82
|
+
.map((candidate) => candidate.id)
|
|
83
|
+
.join(', ');
|
|
84
|
+
return {
|
|
85
|
+
kind: 'invalid',
|
|
86
|
+
message: `Ambiguous session prefix: ${trimmed}${preview ? ` (${preview}${resolved.candidates.length > 3 ? ', …' : ''})` : ''}`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return { kind: 'invalid', message: `No session found matching: ${trimmed}` };
|
|
90
|
+
}
|
|
64
91
|
/**
|
|
65
92
|
* Find the most recent session for a given working directory.
|
|
66
93
|
* Returns null if none exists.
|
|
@@ -109,21 +136,23 @@ export async function pickSession(opts = {}) {
|
|
|
109
136
|
terminal: process.stdin.isTTY ?? false,
|
|
110
137
|
});
|
|
111
138
|
return new Promise((resolve) => {
|
|
112
|
-
|
|
113
|
-
rl.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
139
|
+
const prompt = () => {
|
|
140
|
+
rl.question(chalk.bold(' session> '), (answer) => {
|
|
141
|
+
const selection = resolvePickerSelection(answer, shown, sessions);
|
|
142
|
+
if (selection.kind === 'cancel') {
|
|
143
|
+
rl.close();
|
|
144
|
+
resolve(null);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (selection.kind === 'selected') {
|
|
148
|
+
rl.close();
|
|
149
|
+
resolve(selection.id);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
console.error(chalk.yellow(` ${selection.message}`));
|
|
153
|
+
prompt();
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
prompt();
|
|
128
157
|
});
|
|
129
158
|
}
|
package/package.json
CHANGED