@blockrun/franklin 3.15.31 → 3.15.33
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/agent/streaming-executor.js +24 -2
- package/dist/tasks/spawn.d.ts +7 -2
- package/dist/tasks/spawn.js +30 -6
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ import { mkdirSync, writeFileSync } from 'node:fs';
|
|
|
7
7
|
import { join } from 'node:path';
|
|
8
8
|
import { recordFailure } from '../stats/failures.js';
|
|
9
9
|
import { BLOCKRUN_DIR } from '../config.js';
|
|
10
|
+
import { logger } from '../logger.js';
|
|
10
11
|
/** Persist a large tool result to disk and return a preview string. */
|
|
11
12
|
const PERSIST_THRESHOLD = 50_000;
|
|
12
13
|
const PREVIEW_SIZE = 2_000;
|
|
@@ -205,8 +206,22 @@ export class StreamingExecutor {
|
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
}
|
|
209
|
+
// Track elapsed for slow-tool forensics. Verified 2026-05-04
|
|
210
|
+
// from a real session: a 337.6s Bash error left no trace in
|
|
211
|
+
// franklin-debug.log — the user could see the ✗ in the UI but
|
|
212
|
+
// had no post-hoc way to ask "which Bash took 5+ minutes
|
|
213
|
+
// yesterday and what did it run". 30s threshold is conservative
|
|
214
|
+
// (Read/Glob/Grep finish in <1s; only network or shell work
|
|
215
|
+
// crosses).
|
|
216
|
+
const execStart = Date.now();
|
|
208
217
|
let result = await handler.execute(invocation.input, progressScope);
|
|
209
218
|
this.guard?.afterExecute(invocation, result);
|
|
219
|
+
const execElapsed = Date.now() - execStart;
|
|
220
|
+
if (execElapsed >= 30_000) {
|
|
221
|
+
const status = result.isError ? 'error' : 'ok';
|
|
222
|
+
const preview = this.inputPreview(invocation) || '';
|
|
223
|
+
logger.info(`[franklin] Slow tool: ${invocation.name} ${status} after ${(execElapsed / 1000).toFixed(1)}s${preview ? ` — ${preview.slice(0, 80)}` : ''}`);
|
|
224
|
+
}
|
|
210
225
|
// Persist large results to disk with preview.
|
|
211
226
|
// Instead of just truncating, save the full result to disk so it can be re-read later.
|
|
212
227
|
if (result.output.length > PERSIST_THRESHOLD) {
|
|
@@ -219,15 +234,22 @@ export class StreamingExecutor {
|
|
|
219
234
|
}
|
|
220
235
|
catch (err) {
|
|
221
236
|
this.guard?.cancelInvocation(invocation.id);
|
|
237
|
+
const errMsg = err.message;
|
|
222
238
|
recordFailure({
|
|
223
239
|
timestamp: Date.now(),
|
|
224
240
|
model: '', // not available at tool level
|
|
225
241
|
failureType: 'tool_error',
|
|
226
242
|
toolName: invocation.name,
|
|
227
|
-
errorMessage:
|
|
243
|
+
errorMessage: errMsg,
|
|
228
244
|
});
|
|
245
|
+
// Always log thrown tool errors. Pre-3.15.32 these went only to
|
|
246
|
+
// failures.jsonl (which the user rarely opens) and were absent
|
|
247
|
+
// from franklin-debug.log entirely. Now `franklin logs` shows
|
|
248
|
+
// them alongside everything else.
|
|
249
|
+
const preview = this.inputPreview(invocation) || '';
|
|
250
|
+
logger.warn(`[franklin] Tool error: ${invocation.name} threw "${errMsg.slice(0, 120)}"${preview ? ` — ${preview.slice(0, 80)}` : ''}`);
|
|
229
251
|
return {
|
|
230
|
-
output: `Error executing ${invocation.name}: ${
|
|
252
|
+
output: `Error executing ${invocation.name}: ${errMsg}`,
|
|
231
253
|
isError: true,
|
|
232
254
|
};
|
|
233
255
|
}
|
package/dist/tasks/spawn.d.ts
CHANGED
|
@@ -16,8 +16,13 @@
|
|
|
16
16
|
*
|
|
17
17
|
* CLI path resolution (in priority order):
|
|
18
18
|
* 1. process.env.FRANKLIN_CLI_PATH — escape hatch for tests / dev.
|
|
19
|
-
* 2.
|
|
20
|
-
*
|
|
19
|
+
* 2. STARTUP_CLI_PATH (captured at module load) — absolute path of
|
|
20
|
+
* the script Node is currently executing. Captured early so it
|
|
21
|
+
* survives any later chdir; resolved to absolute so it survives
|
|
22
|
+
* the spawn's `cwd:` override (the bug it fixes — verified
|
|
23
|
+
* 2026-05-04 from a real session: dev-mode `node dist/index.js`
|
|
24
|
+
* run, then Detach with workingDir=other-repo, child fails with
|
|
25
|
+
* MODULE_NOT_FOUND on `<other-repo>/dist/index.js`).
|
|
21
26
|
*/
|
|
22
27
|
export interface StartDetachedTaskInput {
|
|
23
28
|
label: string;
|
package/dist/tasks/spawn.js
CHANGED
|
@@ -16,22 +16,46 @@
|
|
|
16
16
|
*
|
|
17
17
|
* CLI path resolution (in priority order):
|
|
18
18
|
* 1. process.env.FRANKLIN_CLI_PATH — escape hatch for tests / dev.
|
|
19
|
-
* 2.
|
|
20
|
-
*
|
|
19
|
+
* 2. STARTUP_CLI_PATH (captured at module load) — absolute path of
|
|
20
|
+
* the script Node is currently executing. Captured early so it
|
|
21
|
+
* survives any later chdir; resolved to absolute so it survives
|
|
22
|
+
* the spawn's `cwd:` override (the bug it fixes — verified
|
|
23
|
+
* 2026-05-04 from a real session: dev-mode `node dist/index.js`
|
|
24
|
+
* run, then Detach with workingDir=other-repo, child fails with
|
|
25
|
+
* MODULE_NOT_FOUND on `<other-repo>/dist/index.js`).
|
|
21
26
|
*/
|
|
22
27
|
import { spawn } from 'node:child_process';
|
|
23
28
|
import fs from 'node:fs';
|
|
29
|
+
import path from 'node:path';
|
|
24
30
|
import { randomUUID } from 'node:crypto';
|
|
25
31
|
import { writeTaskMeta } from './store.js';
|
|
26
32
|
import { taskLogPath, ensureTaskDir } from './paths.js';
|
|
33
|
+
// Captured at module load so it survives later chdir / argv mutation.
|
|
34
|
+
// `process.argv[1]` may be relative (`dist/index.js` in dev mode); we
|
|
35
|
+
// resolve against process.cwd() at startup which is when the user's
|
|
36
|
+
// shell exec'd the bundle. Doing this at call time would be wrong if
|
|
37
|
+
// any code chdir'd between startup and the Detach call.
|
|
38
|
+
const STARTUP_CLI_PATH = (() => {
|
|
39
|
+
const argv1 = process.argv[1];
|
|
40
|
+
if (!argv1)
|
|
41
|
+
return undefined;
|
|
42
|
+
try {
|
|
43
|
+
return path.resolve(argv1);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return argv1;
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
27
49
|
function resolveCliPath() {
|
|
28
50
|
const fromEnv = process.env.FRANKLIN_CLI_PATH;
|
|
29
51
|
if (fromEnv && fromEnv.length > 0)
|
|
30
52
|
return fromEnv;
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
53
|
+
// STARTUP_CLI_PATH is the absolute path resolved at module load —
|
|
54
|
+
// safe to use after `cwd: input.workingDir` redirects the child.
|
|
55
|
+
// npm global installs already give an absolute path; this only
|
|
56
|
+
// matters in dev mode where `node dist/index.js` puts a relative
|
|
57
|
+
// path into argv[1].
|
|
58
|
+
return STARTUP_CLI_PATH || process.argv[1];
|
|
35
59
|
}
|
|
36
60
|
function generateRunId() {
|
|
37
61
|
return `t_${Date.now().toString(36)}_${randomUUID().slice(0, 8)}`;
|
package/package.json
CHANGED