strom-research 1.28.0-beta.1 → 1.28.0-beta.2

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 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.28.0
9
9
 
10
- *Připravuje se: bety do 1.28.0-beta.1. Oddíl bude úplný s vydáním.*
10
+ *Připravuje se: bety do 1.28.0-beta.2. Oddíl bude úplný s vydáním.*
11
11
 
12
12
  - **Jak Strom funguje je přímo ve stromu: `strom doc`.** Dokumentace aplikace a výzkumu v češtině, angličtině, němčině
13
13
  a polštině je součástí stromu a čte se i bez internetu: `strom doc` vypíše kapitoly, `strom doc R10.1` jedno pravidlo
@@ -26,6 +26,10 @@ Změny každé verze ve zkratce. Úplné poznámky a instalační řádky jsou n
26
26
  - **Překlad vedle přepisu.** Záznam v jiném jazyce nebo písmu, než je jazyk výzkumu – latina, kurent, polština,
27
27
  azbuka –, dostane vedle doslovného přepisu i překlad do jazyka výzkumu.
28
28
 
29
+ - **Kde agent pracuje, pro mapu aplikace Strom.** Během práce agenta strom aplikaci říká, ke kterému místu patří kniha,
30
+ kterou agent zkoumá – farnost nebo obec jejích zápisů, nikdy archiv –, aby ho aplikace při sledování práce mohla
31
+ ukázat na mapě. Když to výzkum neví, nic neříká a aplikace zůstane u míst osoby.
32
+
29
33
  - **DigiArchiv SOA v Třeboni:** podmínky, jak je archiv potvrdil – ručně jen vybrané snímky, nikdy celá kniha, žádné
30
34
  nástroje ke stahování – a jak citovat trvalý odkaz na snímek.
31
35
 
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.28.0
9
9
 
10
- *In Vorbereitung: die Betas bis 1.28.0-beta.1. Der Abschnitt ist mit dem Release vollständig.*
10
+ *In Vorbereitung: die Betas bis 1.28.0-beta.2. Der Abschnitt ist mit dem Release vollständig.*
11
11
 
12
12
  - **Wie Strom funktioniert gehört zu strom: `strom doc`.** Die Dokumentation der App und der Forschung auf Tschechisch,
13
13
  Englisch, Deutsch und Polnisch ist Teil von strom und liest sich auch ohne Internet: `strom doc` listet die Kapitel,
@@ -29,6 +29,11 @@ Die Änderungen jeder Version in Kürze. Die vollständigen Hinweise und die Ins
29
29
  Latein, Kurrent, Polnisch, Kyrillisch – bekommt neben der wörtlichen Abschrift eine Übersetzung in die Sprache der
30
30
  Forschung.
31
31
 
32
+ - **Wo der Agent arbeitet, für die Karte der Strom-App.** Während ein Agent arbeitet, sagt strom der App, zu welchem
33
+ Ort das Buch gehört, das er untersucht – die Pfarrei oder das Dorf seiner Einträge, nie das Archiv –, damit die App
34
+ ihn beim Verfolgen der Arbeit auf der Karte zeigen kann. Weiß die Forschung es nicht, sagt sie nichts, und die App
35
+ bleibt bei den Orten der Person.
36
+
32
37
  - **DigiArchiv des Staatlichen Gebietsarchivs Třeboň:** seine Bedingungen, wie das Archiv sie bestätigt hat – von Hand
33
38
  nur ausgewählte Bilder, nie ein ganzes Buch, keine Download-Werkzeuge – und wie der dauerhafte Link eines Bildes
34
39
  zitiert wird.
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.28.0
9
9
 
10
- *In preparation: the betas up to 1.28.0-beta.1. The section is complete with the release.*
10
+ *In preparation: the betas up to 1.28.0-beta.2. The section is complete with the release.*
11
11
 
12
12
  - **How Strom works comes with strom: `strom doc`.** The documentation of the app and the research, in Czech, English,
13
13
  German and Polish, is part of strom and reads offline: `strom doc` lists its chapters, `strom doc R10.1` shows one rule
@@ -27,6 +27,11 @@ The changes of each version in short. The full notes and the install lines are o
27
27
  - **A translation beside the transcript.** A record in another language or script than the research's – Latin, German
28
28
  Kurrent, Polish, Cyrillic – gets a translation into the research's language beside its word-for-word transcript.
29
29
 
30
+ - **Where the agent works, for the map of the Strom app.** While an agent works, strom tells the app which place the
31
+ book it examines belongs to – the parish or village of its records, never the archive – so the app can show it on the
32
+ map when following the work. When the research does not know it, it says nothing, and the app keeps to the person's
33
+ own places.
34
+
30
35
  - **DigiArchiv of the regional archive in Třeboň:** its terms as the archive confirmed them – selected images saved by
31
36
  hand only, never a whole book, no download tools – and how to cite an image's permanent link.
32
37
 
package/CHANGELOG.pl.md CHANGED
@@ -7,7 +7,7 @@ Zmiany każdej wersji w skrócie. Pełne informacje o wydaniach i polecenia inst
7
7
 
8
8
  ## 1.28.0
9
9
 
10
- *W przygotowaniu: bety do 1.28.0-beta.1. Sekcja będzie kompletna z wydaniem.*
10
+ *W przygotowaniu: bety do 1.28.0-beta.2. Sekcja będzie kompletna z wydaniem.*
11
11
 
12
12
  - **Jak działa Strom – w samym stromie: `strom doc`.** Dokumentacja aplikacji i badań po czesku, angielsku, niemiecku i
13
13
  polsku jest częścią stromu i czyta się ją także bez internetu: `strom doc` wypisuje rozdziały, `strom doc R10.1` jedną
@@ -28,6 +28,11 @@ Zmiany każdej wersji w skrócie. Pełne informacje o wydaniach i polecenia inst
28
28
  - **Tłumaczenie obok transkrypcji.** Wpis w innym języku lub piśmie niż język badań – łacina, kurrenta, niemiecki,
29
29
  cyrylica – otrzymuje obok dosłownej transkrypcji tłumaczenie na język badań.
30
30
 
31
+ - **Gdzie pracuje agent – dla mapy aplikacji Strom.** Podczas pracy agenta strom podaje aplikacji, do którego miejsca
32
+ należy księga, którą agent bada – parafia lub wieś jej wpisów, nigdy archiwum – aby aplikacja mogła je pokazać na
33
+ mapie podczas śledzenia pracy. Gdy badania tego nie wiedzą, nic nie podają, a aplikacja pozostaje przy miejscach
34
+ osoby.
35
+
31
36
  - **DigiArchiv Archiwum Państwowego w Třeboniu:** jego warunki, tak jak potwierdziło je archiwum – ręcznie tylko wybrane
32
37
  skany, nigdy cała księga, żadnych narzędzi do pobierania – i jak cytować stały link do skanu.
33
38
 
package/dist/core/live.js CHANGED
@@ -12,7 +12,9 @@
12
12
  // Made once for a state of the research on a thread of its own (core/livestatus.ts) and
13
13
  // answered from it while the state lasts. ?parts=1 (feature status.parts): without the parts
14
14
  // that weigh (waiting; queue, queueMore, queueCounts; offLine; researches), with parts {name:
15
- // rev} instead — the same rev, the same part.
15
+ // rev} instead — the same rev, the same part. Each of working (who is at work; the event
16
+ // working too): the place record (L…) of the book the agent examines, when the research knows
17
+ // it (place, core/workplace.ts; none: not known) — feature working.place.
16
18
  // GET <token>/status/<part> one of those parts: {part, rev, head, …its fields as /status carries them}; 404
17
19
  // status.part for another name.
18
20
  // A queue item off the lines of a research with several (core/offline.ts) says offLine:
@@ -299,6 +301,7 @@
299
301
  // new secret. Nothing is replaced on a timer.
300
302
  import { browserState, treeBrowserArchives } from "./browserhelp.js";
301
303
  import { opsLogsOf } from "./opslog.js";
304
+ import { placeOfWork } from "./workplace.js";
302
305
  import { notDownloaded } from "./integrity.js";
303
306
  import fs from "node:fs";
304
307
  import os from "node:os";
@@ -1122,7 +1125,7 @@ function idsOf(applied, known) {
1122
1125
  return Object.keys(ids.persons).length || Object.keys(ids.sources).length ? { ids } : {};
1123
1126
  }
1124
1127
  /** What the bridge does that an app may ask about (each added once, never taken away). */
1125
- export const BRIDGE_FEATURES = ["sync.again", "sync.undoneSince", "sync.takenBack", "sync.conflictEdit", "sync.since", "sync.ids", "family.noCouple", "family.alone", "adopt.transfer", "adopt.empty", "material.list", "person.titles", "media.codes", "hypothesis.links", "status.recent", "spend.readers", "hypothesis.intree", "archive.permission", "task.check", "waiting.groups", "waiting.sent", "waiting.note", "batch.kept", "live.end", "whats.new", "terminal.choice", "person.marriedSurname", "phone", "read.now", "read.sync", "excerpt.get", "log.sources", "material.assign", "material.thumb", "seal.foreign", "tree.state", "disk.full", "tree.notDownloaded", "sync.removed", "tree.kind", "experiments", "experiments.off", "experiments.link", "experiments.split", "history.events", "history.hide", "experiments.pace", "history.first", "history.regions", "history.hide.all", "history.skip", "history.map", "history.delete", "history.delete.restore", "stories.history", "history.place", "surnames.suggest", "surnames.read", "lineage.read", "duplicates.judge", "quality.dismiss", "quality.read", "person.primary", "fixes.list", "fixes.towns", "history.back", "places.identify", "places.answer", "places.undo", "tasks.offLine", "status.busy", "history.kinds", "history.align", "places.nameKinds", "directions.edit", "directions.order", "directions.found", "directions.people", "offLine.branches", "queue.own", "story.sources", "queue.page", "status.parts", "waiting.answer", "browser.state"];
1128
+ export const BRIDGE_FEATURES = ["sync.again", "sync.undoneSince", "sync.takenBack", "sync.conflictEdit", "sync.since", "sync.ids", "family.noCouple", "family.alone", "adopt.transfer", "adopt.empty", "material.list", "person.titles", "media.codes", "hypothesis.links", "status.recent", "spend.readers", "hypothesis.intree", "archive.permission", "task.check", "waiting.groups", "waiting.sent", "waiting.note", "batch.kept", "live.end", "whats.new", "terminal.choice", "person.marriedSurname", "phone", "read.now", "read.sync", "excerpt.get", "log.sources", "material.assign", "material.thumb", "seal.foreign", "tree.state", "disk.full", "tree.notDownloaded", "sync.removed", "tree.kind", "experiments", "experiments.off", "experiments.link", "experiments.split", "history.events", "history.hide", "experiments.pace", "history.first", "history.regions", "history.hide.all", "history.skip", "history.map", "history.delete", "history.delete.restore", "stories.history", "history.place", "surnames.suggest", "surnames.read", "lineage.read", "duplicates.judge", "quality.dismiss", "quality.read", "person.primary", "fixes.list", "fixes.towns", "history.back", "places.identify", "places.answer", "places.undo", "tasks.offLine", "status.busy", "history.kinds", "history.align", "places.nameKinds", "directions.edit", "directions.order", "directions.found", "directions.people", "offLine.branches", "queue.own", "story.sources", "queue.page", "status.parts", "waiting.answer", "browser.state", "working.place"];
1126
1129
  /** /status busy: how many sessions and runs work now. */
1127
1130
  function busyOf(tree) {
1128
1131
  const b = busyIn(tree);
@@ -1339,31 +1342,38 @@ export function whoAtWork(label, lang) {
1339
1342
  * The person an agent works on, for the app to follow it in the tree: the first person its task is about (a marriage
1340
1343
  * names both, the one it asks about first), else the last one its session wrote about.
1341
1344
  */
1342
- function workedOn(root, tree, s, task) {
1345
+ function workedOn(tree, ops, task) {
1343
1346
  const known = (id) => /^P\d{1,7}$/.test(id) && tree.get(id) !== undefined;
1344
1347
  const first = (task?.subject ?? []).find(known);
1345
1348
  if (first)
1346
1349
  return { person: first };
1350
+ for (let i = ops.length - 1; i >= 0; i--) {
1351
+ const person = (ops[i].targets ?? []).findLast(known);
1352
+ if (person)
1353
+ return { person };
1354
+ }
1355
+ return {};
1356
+ }
1357
+ /** What a session wrote, in its order (a line being written, or a log not readable: left out). */
1358
+ function sessionOps(root, s) {
1347
1359
  let lines = [];
1348
1360
  try {
1349
1361
  lines = opsLogsOf(path.join(root, "data"), s.id).flatMap((file) => fs.readFileSync(file, "utf8").split("\n"));
1350
1362
  }
1351
1363
  catch {
1352
- return {};
1364
+ return [];
1353
1365
  }
1354
- for (let i = lines.length - 1; i >= 0; i--) {
1355
- let op;
1366
+ const ops = [];
1367
+ for (const line of lines) {
1356
1368
  try {
1357
- op = JSON.parse(lines[i]);
1369
+ if (line.trim())
1370
+ ops.push(JSON.parse(line));
1358
1371
  }
1359
1372
  catch {
1360
- continue; // empty, or a line being written
1373
+ // a line being written
1361
1374
  }
1362
- const person = (op.targets ?? []).findLast(known);
1363
- if (person)
1364
- return { person };
1365
1375
  }
1366
- return {};
1376
+ return ops;
1367
1377
  }
1368
1378
  /** Who is at work now, on which task (an agent strom started, and its open session). */
1369
1379
  function working(root, tree, all) {
@@ -1378,11 +1388,16 @@ function working(root, tree, all) {
1378
1388
  who: whoAtWork(w.label, tree.lang),
1379
1389
  since: w.since,
1380
1390
  ...(w.paused ? { paused: w.paused } : {}),
1381
- ...(s ? { session: s.id, ...(s.task ? { task: task ? taskTitle(tree, task) : s.task } : {}), ...workedOn(root, tree, s, task) } : {}),
1391
+ ...(s ? { session: s.id, ...(s.task ? { task: task ? taskTitle(tree, task) : s.task } : {}), ...worked(tree, sessionOps(root, s), task) } : {}),
1382
1392
  ...(research ? { research } : {}),
1383
1393
  };
1384
1394
  });
1385
1395
  }
1396
+ /** The person and the place (L…, core/workplace.ts) an agent works on, from what its session wrote and its task. */
1397
+ function worked(tree, ops, task) {
1398
+ const place = placeOfWork(tree, ops, task);
1399
+ return { ...workedOn(tree, ops, task), ...(place ? { place } : {}) };
1400
+ }
1386
1401
  /** The direction a session worked on: its own, else its task's. */
1387
1402
  function sessionResearch(tree, s, all) {
1388
1403
  if (s.research)
package/dist/core/tree.js CHANGED
@@ -31,7 +31,7 @@ export const TREE_FILE = "strom.json";
31
31
  export const SEAL_FOREIGN = "seal.foreign";
32
32
  // between releases the candidate being made (a bridge run from the sources says what it is: the Strom app goes by it);
33
33
  // at a release the release's own (package.json)
34
- export const VERSION = "1.28.0-beta.1";
34
+ export const VERSION = "1.28.0-beta.2";
35
35
  const GITIGNORE = `# Strom Research — local state and large files are not versioned
36
36
  .strom/
37
37
  /media/
@@ -0,0 +1,69 @@
1
+ // Where an agent works now, for the Strom app's map while it follows the work (the bridge's working[].place, feature
2
+ // working.place): the place record (L…) of the book the agent examines — the parish or the village of its records,
3
+ // never an archive. Only what the agent wrote through strom tells it, nothing guessed: the newest record of its work the
4
+ // session wrote (an image fetched, a source, a search, a book) decides alone — its book's place, none when its book is
5
+ // unsure (a search in several books of several places, a source of no book: the web); before any, the books its task
6
+ // names. What it writes of people from a record (a fact, a citation) keeps the place of that record.
7
+ // A book is a place's when the place lists it among its records (strom place jurisdiction --recordset), else when one
8
+ // place record alone bears the name of the first place the book covers. Unsure (no such place, several of one name,
9
+ // the task's books in several places): none — the app shows the person's places then.
10
+ import { foldText } from "./text.js";
11
+ /** The place record (L…) of the book the agent examines now, when the research knows it. */
12
+ export function placeOfWork(tree, ops, task) {
13
+ // the newest record of its work the session wrote decides alone: its book's place, or none — never an older book's
14
+ for (let i = ops.length - 1; i >= 0; i--) {
15
+ const main = ops[i].targets?.[0];
16
+ if (main && /^[BMSQ]\d+$/.test(main))
17
+ return placeOfRecord(tree, main);
18
+ }
19
+ // nothing written yet: the books its task names, when they are all of one place
20
+ return placeOfBooks(tree, (task?.where ?? []).map((w) => w.trim()));
21
+ }
22
+ /** The place of the books named, when they are all of one known place. */
23
+ function placeOfBooks(tree, ids) {
24
+ const books = ids.map((id) => tree.get(id)).filter((b) => b?.type === "recordset" && !b.retracted);
25
+ const places = new Set(books.map((b) => placeOfBook(tree, b)));
26
+ return places.size === 1 ? [...places][0] : undefined;
27
+ }
28
+ /** The place of a record of work: of its book, of the books of a search when all of one place; none when unsure. */
29
+ function placeOfRecord(tree, id) {
30
+ if (/^Q\d+$/.test(id)) {
31
+ const q = tree.get(id);
32
+ return q && !q.retracted ? placeOfBooks(tree, q.recordsets) : undefined;
33
+ }
34
+ const book = bookOf(tree, id);
35
+ return book ? placeOfBook(tree, book) : undefined;
36
+ }
37
+ /** The book a record is of: a book itself, an image of it, a source from it. */
38
+ function bookOf(tree, id) {
39
+ const book = (b) => {
40
+ const r = b ? tree.get(b) : undefined;
41
+ return r?.type === "recordset" && !r.retracted ? r : undefined;
42
+ };
43
+ if (/^B\d+$/.test(id))
44
+ return book(id);
45
+ if (/^M\d+$/.test(id)) {
46
+ const m = tree.get(id);
47
+ return m && !m.retracted ? book(m.recordset) : undefined;
48
+ }
49
+ if (/^S\d+$/.test(id)) {
50
+ const s = tree.get(id);
51
+ if (!s || s.retracted)
52
+ return undefined;
53
+ return book(s.recordset) ?? book((s.media ?? []).map((m) => tree.get(m)?.recordset).find(Boolean));
54
+ }
55
+ return undefined;
56
+ }
57
+ /** The place record of a book: the one listing it among its records, else the one alone of the name of its first place. */
58
+ function placeOfBook(tree, book) {
59
+ const places = tree.list("place").filter((p) => !p.retracted);
60
+ const listing = places.filter((p) => p.jurisdictions.some((j) => (j.recordsets ?? []).includes(book.id)));
61
+ if (listing.length === 1)
62
+ return listing[0].id;
63
+ const first = book.places[0]?.trim();
64
+ if (!first)
65
+ return undefined;
66
+ const name = foldText(first);
67
+ const named = (listing.length ? listing : places).filter((p) => p.names.some((n) => foldText(n.name) === name));
68
+ return named.length === 1 ? named[0].id : undefined;
69
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strom-research",
3
- "version": "1.28.0-beta.1",
3
+ "version": "1.28.0-beta.2",
4
4
  "stromDoc": "885249f43ea2f346c667b3f0ead9f8bba950f23f",
5
5
  "description": "Genealogical research toolkit for AI coding agents",
6
6
  "type": "module",