strom-research 1.19.0-beta.3 → 1.19.0-beta.4
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/CHANGELOG.cs.md +1 -1
- package/CHANGELOG.de.md +1 -1
- package/CHANGELOG.md +1 -1
- package/dist/commands/document.js +1 -1
- package/dist/commands/readers.js +1 -1
- package/dist/core/live-read.js +60 -5
- package/dist/core/live.js +1 -1
- package/dist/core/tree.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.cs.md
CHANGED
|
@@ -7,7 +7,7 @@ Změny každé verze ve zkratce. Úplné poznámky a instalační řádky jsou n
|
|
|
7
7
|
|
|
8
8
|
## 1.19.0
|
|
9
9
|
|
|
10
|
-
*Připravuje se: bety do 1.19.0-beta.
|
|
10
|
+
*Připravuje se: bety do 1.19.0-beta.4. Oddíl bude úplný s vydáním.*
|
|
11
11
|
|
|
12
12
|
- **Na archiv, který zmlkl, se čeká, místo opakovaných dotazů.** Když archiv přestane odpovídat, strom ho nechá na
|
|
13
13
|
pokoji hodinu — a když týž den mlčí dál, dvě a pak čtyři hodiny — a agent se dozví, do kdy: mezitím dělá
|
package/CHANGELOG.de.md
CHANGED
|
@@ -7,7 +7,7 @@ Die Änderungen jeder Version in Kürze. Die vollständigen Hinweise und die Ins
|
|
|
7
7
|
|
|
8
8
|
## 1.19.0
|
|
9
9
|
|
|
10
|
-
*In Vorbereitung: die Betas bis 1.19.0-beta.
|
|
10
|
+
*In Vorbereitung: die Betas bis 1.19.0-beta.4. Der Abschnitt ist mit dem Release vollständig.*
|
|
11
11
|
|
|
12
12
|
- **Auf ein verstummtes Archiv wird gewartet, statt es immer wieder zu fragen.** Antwortet ein Archiv nicht mehr,
|
|
13
13
|
lässt strom es eine Stunde in Ruhe — zwei, dann vier, wenn es am selben Tag still bleibt — und der Agent erfährt, bis
|
package/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,7 @@ The changes of each version in short. The full notes and the install lines are o
|
|
|
7
7
|
|
|
8
8
|
## 1.19.0
|
|
9
9
|
|
|
10
|
-
*In preparation: the betas up to 1.19.0-beta.
|
|
10
|
+
*In preparation: the betas up to 1.19.0-beta.4. The section is complete with the release.*
|
|
11
11
|
|
|
12
12
|
- **An archive that went silent is waited for, not asked again and again.** When an archive stops answering, strom
|
|
13
13
|
leaves it alone for an hour — two, then four when it stays silent the same day — and the agent hears until when:
|
|
@@ -246,7 +246,7 @@ async function readWith(ctx, tree, s, opts, only) {
|
|
|
246
246
|
}
|
|
247
247
|
const team = readers(ctx, tree, shared, "document", "1", opts, undefined, { stem: s.id });
|
|
248
248
|
const views = s.pages.map((p) => viewsOf(tree, shared, p, team.viewMax));
|
|
249
|
-
s = { ...s, groups: groupsOf(views), state: "reading", since: now(), pid: process.pid, ...(team.model ? { model: team.model } : {}) };
|
|
249
|
+
s = { ...s, groups: groupsOf(views), state: "reading", since: now(), pid: process.pid, agent: team.agent, ...(team.model ? { model: team.model } : {}) };
|
|
250
250
|
saveState(tree.root, s);
|
|
251
251
|
const stop = new AbortController();
|
|
252
252
|
const look = setInterval(() => {
|
package/dist/commands/readers.js
CHANGED
|
@@ -189,5 +189,5 @@ export function readers(ctx, tree, shared, kind, first, opts, reportsAt, own = {
|
|
|
189
189
|
fs.appendFileSync(report, r.text.trim() + "\n");
|
|
190
190
|
return { report, text: fs.readFileSync(report, "utf8"), outcome: r.outcome, ...(r.metrics.costUsd !== undefined ? { costUsd: r.metrics.costUsd } : {}), ...(r.metrics.costPartial ? { costPartial: true } : {}), ...(r.resumeAt ? { resumeAt: r.resumeAt } : {}) };
|
|
191
191
|
};
|
|
192
|
-
return { model, viewMax: sizes.read, parallel, stem, reportsDir, read, cost: () => cost, runs: () => runs, partial: () => partial, failed, progress };
|
|
192
|
+
return { agent: agentId, model, viewMax: sizes.read, parallel, stem, reportsDir, read, cost: () => cost, runs: () => runs, partial: () => partial, failed, progress };
|
|
193
193
|
}
|
package/dist/core/live-read.js
CHANGED
|
@@ -27,6 +27,8 @@ import { EXIT } from "./errors.js";
|
|
|
27
27
|
import { forApp, refusal } from "./receive.js";
|
|
28
28
|
import { stromLauncher } from "./self.js";
|
|
29
29
|
import { normId } from "./records.js";
|
|
30
|
+
import { metricsDir } from "./metrics.js";
|
|
31
|
+
import { readJournal } from "./readstats.js";
|
|
30
32
|
/** The readings the app may ask, unless the person set others (read.app). */
|
|
31
33
|
export const APP_READ_DEFAULT = { hour: 20, day: 60 };
|
|
32
34
|
/** Readings of the app waiting beside the one being read. */
|
|
@@ -213,12 +215,12 @@ export class ReadingsSeen {
|
|
|
213
215
|
};
|
|
214
216
|
}
|
|
215
217
|
}
|
|
216
|
-
/** /status reads: those at work, and those ended in the last week. */
|
|
217
|
-
export function statusReads(seen, now = Date.now()) {
|
|
218
|
+
/** /status reads: those at work (with the seconds each is expected to take: expectSecs), and those ended in the last week. */
|
|
219
|
+
export function statusReads(seen, agentNow, now = Date.now()) {
|
|
218
220
|
return seen
|
|
219
221
|
.all()
|
|
220
|
-
.
|
|
221
|
-
.
|
|
222
|
+
.filter((r) => !FINAL_STATES.includes(r.read.state) || Date.parse(r.read.since) > now - STATUS_DAYS * DAY_MS)
|
|
223
|
+
.map((r) => (r.state && !FINAL_STATES.includes(r.read.state) ? { ...r.read, expectSecs: expectOf(seen.root, r.state, agentNow) } : r.read));
|
|
222
224
|
}
|
|
223
225
|
/**
|
|
224
226
|
* GET /reads: the last 90 days, and every reading no source of the research is of. inSource: a source of this tree —
|
|
@@ -268,6 +270,7 @@ export function readAccepts(tree, settings, env, seen, now = Date.now()) {
|
|
|
268
270
|
max: READ_MAX_BYTES,
|
|
269
271
|
pages: DOC_PAGES,
|
|
270
272
|
langs: readLangs(),
|
|
273
|
+
...(not ? {} : { expectSecs: expectSecs(tree.root, settings.agent(tree.config).value) }),
|
|
271
274
|
};
|
|
272
275
|
}
|
|
273
276
|
function notReady(tree, settings, env, seen, now) {
|
|
@@ -296,6 +299,55 @@ function notReady(tree, settings, env, seen, now) {
|
|
|
296
299
|
}
|
|
297
300
|
return undefined;
|
|
298
301
|
}
|
|
302
|
+
// ── how long a reading takes ────────────────────────────────────────────────────────────────────────────────────
|
|
303
|
+
/** A reading's seconds before any of this agent's readers ended here (measured 2026-10-20 and 21): Grok's take minutes. */
|
|
304
|
+
export const READ_EXPECT_DEFAULT = { grok: 240 };
|
|
305
|
+
export const READ_EXPECT_OTHERS = 60;
|
|
306
|
+
/** The readers of documents a reading's time is learned from: the last so many of the agent that read to an end. */
|
|
307
|
+
const EXPECT_LAST = 9;
|
|
308
|
+
const expectSeen = new Map();
|
|
309
|
+
/** How long this agent's readers of documents took here (.strom/metrics/readers.jsonl, nothing of the tree), oldest first. */
|
|
310
|
+
function readerSecs(root, agent) {
|
|
311
|
+
const file = path.join(metricsDir(root), "readers.jsonl");
|
|
312
|
+
let key = "-";
|
|
313
|
+
try {
|
|
314
|
+
const st = fs.statSync(file);
|
|
315
|
+
key = `${st.mtimeMs}:${st.size}`;
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
return [];
|
|
319
|
+
}
|
|
320
|
+
let had = expectSeen.get(file);
|
|
321
|
+
if (had?.key !== key) {
|
|
322
|
+
const secs = new Map();
|
|
323
|
+
for (const r of readJournal(file)) {
|
|
324
|
+
const ms = typeof r.wallMs === "number" ? r.wallMs : typeof r.ms === "number" ? r.ms : NaN;
|
|
325
|
+
if (r.kind !== "document" || r.outcome !== "ok" || typeof r.agent !== "string" || !(ms >= 0))
|
|
326
|
+
continue;
|
|
327
|
+
secs.set(r.agent, [...(secs.get(r.agent) ?? []), ms / 1000].slice(-EXPECT_LAST));
|
|
328
|
+
}
|
|
329
|
+
had = { key, secs };
|
|
330
|
+
expectSeen.set(file, had);
|
|
331
|
+
}
|
|
332
|
+
return had.secs.get(agent) ?? [];
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* The seconds one reading is expected to take with this agent (`rounds`: its readers one after another, a long
|
|
336
|
+
* document's parts beyond the readers at work at once): the median of its last readers of documents here, else what
|
|
337
|
+
* the agent's readers took when measured — for the app to say a reading takes minutes, never a promise.
|
|
338
|
+
*/
|
|
339
|
+
export function expectSecs(root, agent, rounds = 1) {
|
|
340
|
+
const secs = readerSecs(root, agent).sort((a, b) => a - b);
|
|
341
|
+
const mid = secs.length >> 1;
|
|
342
|
+
const one = secs.length ? (secs.length % 2 ? secs[mid] : (secs[mid - 1] + secs[mid]) / 2) : (READ_EXPECT_DEFAULT[agent] ?? READ_EXPECT_OTHERS);
|
|
343
|
+
return Math.max(1, Math.round(one * Math.max(1, rounds)));
|
|
344
|
+
}
|
|
345
|
+
/** A reading at work or queued: how long it is expected to take from its start (since, when it is reading). */
|
|
346
|
+
function expectOf(root, s, agentNow) {
|
|
347
|
+
// each part of the document read by the first reader and the blind one, as many at once as the readers asked, three at least
|
|
348
|
+
const jobs = Math.max(1, s.groups.length) * ((s.only === "blind" ? 0 : 1) + (s.readers > 1 ? 1 : 0));
|
|
349
|
+
return expectSecs(root, s.agent ?? agentNow, Math.ceil(jobs / Math.max(3, s.readers)));
|
|
350
|
+
}
|
|
299
351
|
const BODY_MAX = 16 * 1024;
|
|
300
352
|
/** The names of files sent to be read kept until they are asked (the last ones). */
|
|
301
353
|
const NAMES_KEPT = 200;
|
|
@@ -394,7 +446,10 @@ export class AppReadings {
|
|
|
394
446
|
return this.json(res, n.code, n.body);
|
|
395
447
|
}
|
|
396
448
|
const r = resultForApp(got.result);
|
|
397
|
-
|
|
449
|
+
// at work or queued: how long it is expected to take from its start
|
|
450
|
+
const s = FINAL_STATES.includes(got.result.state) ? undefined : loadState(this.b.root, id);
|
|
451
|
+
const expect = s ? { expectSecs: expectOf(this.b.root, s, this.b.settings().agent(got.tree.config).value) } : {};
|
|
452
|
+
this.json(res, 200, { ...r, ...expect, pages: r.pages.map((p) => ({ ...p, image: `read/${id}/page/${p.n}.jpg` })) });
|
|
398
453
|
}
|
|
399
454
|
/** GET /read/<id>/page/<n>.jpg: the page upright as a JPEG of 2000 px at most (a HEIC's, a PDF's page too). */
|
|
400
455
|
page(res, id, rest) {
|
package/dist/core/live.js
CHANGED
|
@@ -1211,7 +1211,7 @@ function status(root, env, version, seen) {
|
|
|
1211
1211
|
...(intake ? { lastIntake: intake } : {}),
|
|
1212
1212
|
accepts: accepts(tree, settings, env, seen),
|
|
1213
1213
|
// the documents the app asked to be read and the research's own readings: at work, and ended in the last week
|
|
1214
|
-
...(archive || !seen ? {} : { reads: statusReads(seen) }),
|
|
1214
|
+
...(archive || !seen ? {} : { reads: statusReads(seen, settings.agent(tree.config).value) }),
|
|
1215
1215
|
inbox: { trees: inboxTrees(root, env), material: materialWaiting(tree) },
|
|
1216
1216
|
sends: recentSends(root, tree),
|
|
1217
1217
|
batches: batchStatus(tree),
|
package/dist/core/tree.js
CHANGED
|
@@ -27,7 +27,7 @@ import { lastBackup } from "./backup.js";
|
|
|
27
27
|
export const TREE_FILE = "strom.json";
|
|
28
28
|
// between releases the candidate being made (a bridge run from the sources says what it is: the Strom app goes by it);
|
|
29
29
|
// at a release the release's own (package.json)
|
|
30
|
-
export const VERSION = "1.19.0-beta.
|
|
30
|
+
export const VERSION = "1.19.0-beta.4";
|
|
31
31
|
const GITIGNORE = `# Strom Research — local state and large files are not versioned
|
|
32
32
|
.strom/
|
|
33
33
|
/media/
|