strom-research 1.13.2-beta.2 → 1.13.2-beta.5

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.
Files changed (89) hide show
  1. package/assets/lang/cs.json +23 -0
  2. package/assets/lang/de.json +23 -0
  3. package/assets/method/core.md +25 -3
  4. package/assets/method/intake.md +11 -8
  5. package/assets/method/link.md +2 -1
  6. package/assets/method/reading.md +50 -17
  7. package/assets/method/recording.md +9 -4
  8. package/assets/method/verify.md +2 -1
  9. package/dist/agents/files.js +14 -5
  10. package/dist/agents/images.js +59 -0
  11. package/dist/agents/models.js +248 -0
  12. package/dist/agents/profiles.js +23 -6
  13. package/dist/agents/scanreader.js +96 -0
  14. package/dist/brief/brief.js +628 -66
  15. package/dist/brief/sheet.js +133 -0
  16. package/dist/cli/context.js +3 -1
  17. package/dist/cli/execute.js +216 -11
  18. package/dist/cli/help.js +9 -1
  19. package/dist/cli/main.js +38 -5
  20. package/dist/cli/menu-links.js +3 -2
  21. package/dist/cli/menu-settings.js +40 -1
  22. package/dist/cli/model-choice.js +35 -0
  23. package/dist/cli/registry.js +20 -10
  24. package/dist/cli/ui.js +20 -0
  25. package/dist/cli/wizard.js +20 -10
  26. package/dist/commands/analysis.js +95 -195
  27. package/dist/commands/batch.js +96 -17
  28. package/dist/commands/browse.js +2 -1
  29. package/dist/commands/clips.js +7 -4
  30. package/dist/commands/connectors.js +66 -24
  31. package/dist/commands/guide.js +6 -3
  32. package/dist/commands/index.js +2 -0
  33. package/dist/commands/media.js +343 -53
  34. package/dist/commands/meta.js +1 -1
  35. package/dist/commands/people.js +9 -8
  36. package/dist/commands/read.js +92 -24
  37. package/dist/commands/readers.js +13 -3
  38. package/dist/commands/readings.js +272 -0
  39. package/dist/commands/research.js +14 -7
  40. package/dist/commands/session.js +119 -27
  41. package/dist/commands/setup.js +17 -2
  42. package/dist/commands/show.js +92 -0
  43. package/dist/commands/sources.js +41 -14
  44. package/dist/commands/start.js +5 -3
  45. package/dist/commands/tasks.js +85 -36
  46. package/dist/commands/transcripts.js +3 -0
  47. package/dist/core/actions.js +118 -11
  48. package/dist/core/age.js +4 -2
  49. package/dist/core/assets.js +47 -10
  50. package/dist/core/batches.js +3 -2
  51. package/dist/core/changelog.js +1 -1
  52. package/dist/core/check.js +10 -1
  53. package/dist/core/config.js +20 -1
  54. package/dist/core/connector.js +5 -0
  55. package/dist/core/edge.js +7 -8
  56. package/dist/core/errors.js +7 -0
  57. package/dist/core/frontier.js +3 -2
  58. package/dist/core/gdate.js +31 -0
  59. package/dist/core/git.js +10 -1
  60. package/dist/core/hypolinks.js +509 -0
  61. package/dist/core/integrity.js +15 -0
  62. package/dist/core/live.js +188 -47
  63. package/dist/core/mediaindex.js +107 -0
  64. package/dist/core/mode.js +11 -2
  65. package/dist/core/model.js +1 -0
  66. package/dist/core/net.js +62 -9
  67. package/dist/core/overview.js +13 -3
  68. package/dist/core/people.js +117 -3
  69. package/dist/core/phrases.js +3 -0
  70. package/dist/core/queue.js +3 -2
  71. package/dist/core/reader.js +98 -15
  72. package/dist/core/recent.js +95 -0
  73. package/dist/core/records.js +19 -4
  74. package/dist/core/review.js +2 -1
  75. package/dist/core/schema.js +2 -2
  76. package/dist/core/session.js +144 -28
  77. package/dist/core/text.js +31 -0
  78. package/dist/core/tidy.js +14 -0
  79. package/dist/core/tree.js +126 -15
  80. package/dist/core/views.js +56 -12
  81. package/dist/core/years.js +42 -0
  82. package/dist/gedcom/export.js +5 -4
  83. package/dist/runners/antigravity.js +119 -51
  84. package/dist/runners/claude.js +51 -5
  85. package/dist/runners/codex.js +9 -2
  86. package/dist/runners/jsonl.js +2 -1
  87. package/dist/runners/runner.js +8 -0
  88. package/dist/runners/script.js +8 -2
  89. package/package.json +1 -1
@@ -130,6 +130,13 @@
130
130
  "ui.setup.model.opus": "Opus – nejlépe čte staré rukopisy (doporučeno)",
131
131
  "ui.setup.model.sonnet": "Sonnet – rychlejší; tištěné dokumenty a běžná práce",
132
132
  "ui.setup.model.own": "Nechat na agentovi",
133
+ "ui.setup.model.agent": "Který model má v {agent} dělat výzkum? Nejsilnější nejlépe čtou staré rukopisy – rychleji spotřebují předplatné nebo stojí víc.",
134
+ "ui.setup.model.recommended": "{model} (doporučeno)",
135
+ "ui.setup.model.own.is": "Nechat na agentovi (teď {model})",
136
+ "ui.setup.model.now": "{model} (zvoleno teď)",
137
+ "ui.chat.app.model.chosen": "V aplikaci {app} zvolit pro výzkum model {model} (zvolený v nastavení).",
138
+ "ui.settings.model": "Model výzkumu ({agent}): {model}",
139
+ "ui.settings.model.own": "vlastní model agenta",
133
140
  "ui.setup.level": "Co smí agent dělat bez ptaní?",
134
141
  "ui.setup.level.ask": "Ptát se mě na všechno mimo výzkum",
135
142
  "ui.setup.level.auto": "Pracovat samostatně – ptát se jen u riskantních kroků (doporučeno)",
@@ -213,6 +220,7 @@
213
220
  "ui.run.stop.resets": " (obnoví se {at})",
214
221
  "ui.run.stop.auth": "agent není přihlášený – jednou spustit {command} a přihlásit se",
215
222
  "ui.run.stop.failed": "agent selhal: {detail}",
223
+ "ui.run.stop.model": "{agent} nepřijímá model {model} – zvolit jiný: strom config set model.lead <model> (nebo pro jeden běh: strom run --model <model>)",
216
224
  "ui.run.finishing": "■ Ctrl-C: sezení {session} dostalo žádost skončit – agent zapíše, co našel, a sezení uzavře, pak běh skončí (další sezení nezačne). Další Ctrl-C ho zastaví hned.",
217
225
  "ui.run.stopping": "■ zastavuji – sezení se zavře a jeho úkol se vrátí do fronty; další Ctrl-C skončí hned",
218
226
  "ui.run.gone": "· {session} zavřeno – jeho běh se zastavil{back}",
@@ -356,6 +364,9 @@
356
364
  "review.conflicts.what": "Rozhodnout, co zůstalo otevřené u osoby {name}: {list}{more}",
357
365
  "review.conflicts.why": "rozpory mezi prameny a soupeřící hypotézy čekají na rozhodnutí ({research})",
358
366
  "review.conflicts.done": "každý vyřešený nebo rozhodnutý se zdůvodněním (strom conflict resolve, strom hypothesis decide) – nebo úkol říká, co by rozhodlo",
367
+ "hypothesis.links.what": "Doplnit, co by spojovaly verze hypotéz {list}",
368
+ "hypothesis.links.why": "tyto hypotézy jmenují své osoby jen slovy svých verzí; aplikace Strom ukáže, co by verze spojila – i kde strom končí – jen z jejích vazeb",
369
+ "hypothesis.links.done": "každá verze přečtená (strom hypothesis show {first} …) a řečeno, co by spojila: strom hypothesis link H… <písmeno> --child P… --of F… (nebo --parents P… [P…], --same, --partners, --siblings); verze, která nic jasného nespojuje, zůstane bez vazeb",
359
370
  "review.marriage.what": "Sňatek: {name} a {partner}{at} (před prvním dítětem, {year})",
360
371
  "review.marriage.why": "jejich děti jsou zapsané, jejich sňatek ne; zápis dá rodiče obou, věk a domy ({research})",
361
372
  "review.marriage.done": "zápis nalezen a zapsán i se svědky – nebo roky {years} prohledány bez něj",
@@ -597,6 +608,7 @@
597
608
  "ui.stats.sessions": "Sezení agenta: {n} · poslední {last}",
598
609
  "ui.stats.cost": " · dosavadní cena {cost}",
599
610
  "ui.stats.cost.partial": " (bez přerušených sezení, která cenu neřekla: {n})",
611
+ "ui.stats.cost.readers": ", z toho čtenáři skenů {cost}",
600
612
  "ui.stats.stories": "Vyprávění: {n}",
601
613
  "ui.status.data": "Osoby: {persons} · rodiny: {families}",
602
614
  "ui.status.directions": " · směry výzkumu: {n}",
@@ -995,6 +1007,7 @@
995
1007
  "ui.archives.agentbrowser.on": "Dát agentovi prohlížeč v každém sezení?",
996
1008
  "ui.archives.agentbrowser.off": "Vrátit prohlížeč jen pro archivy přes prohlížeč?",
997
1009
  "ui.consent.remote": "Spouštět sezení Claude Code, která strom otevírá, se vzdáleným ovládáním (Remote Control), aby je šlo sledovat a řídit z claude.ai nebo z aplikace Claude v telefonu?",
1010
+ "ui.consent.addons": "Dovolit agentovi při samostatné práci načíst osobní doplňky z jeho nastavení (skilly, pluginy, MCP servery – třeba i poštu nebo dokumenty) jako v chatu? Pracuje přitom bez dozoru.",
998
1011
  "ui.consent.edits.user": "Mají mít úpravy v aplikaci Strom přednost před tím, co říká záznam? Údaj ze záznamu se pak stáhne s důvodem (synchronizace jde vrátit).",
999
1012
  "ui.consent.review.off": "Zapisovat, co pošle aplikace Strom, hned, bez ptaní u každého poslání? Přidané se zapíše, vodítka se opraví, změna údaje doloženého záznamem bude rozpor k rozhodnutí; strom sync undo poslání vrátí.",
1000
1013
  "ui.consent.mode.archive": "Udělat z výzkumu „{name}“ archiv? Data přicházejí z aplikace Strom a zapisují se, jak přijdou; žádný agent na něm nepracuje, jeho úkoly počkají odložené. Práci s agentem jde zase zapnout.",
@@ -1336,6 +1349,13 @@
1336
1349
  "ui.dirs.active.now": "▶ „{name}“ zase běží: jeho úkoly ({n}) jsou zpátky ve frontě.",
1337
1350
  "ui.research.resume": "Takový výzkum už je, {state}: „{name}“. Spustit ho znovu?",
1338
1351
  "ui.alone.gate.bad": "Čekám počet bodů od 0 do 100 (třeba 10).",
1352
+ "ui.alone.addons": "Bez osobních doplňků agenta (skilly, pluginy, MCP servery): {state}",
1353
+ "ui.alone.addons.about": "Agent při samostatné práci načte jen nástroje výzkumu. Spojení s aplikací Strom a prohlížeč fungují dál. Chat s agentem doplňky načítá vždy.",
1354
+ "ui.alone.addons.on": "Zapnout?",
1355
+ "ui.alone.addons.off": "Vypnout? (agent při samostatné práci pak načte i osobní doplňky)",
1356
+ "ui.setup.addons": "Bez osobních doplňků agenta (skilly, pluginy, MCP servery)?",
1357
+ "ui.setup.addons.yes": "Ano (doporučeno)",
1358
+ "ui.setup.addons.no": "Ne – i při samostatné práci s doplňky",
1339
1359
  "ui.waiting.long": "Na odpověď sem je to dlouhé (nejvýš {n} znaků) – stačí odkaz nebo věta, zbytek stačí říct agentovi v rozhovoru.",
1340
1360
  "ui.settings.remote.chats": "rozhovory ano, samostatná práce ne",
1341
1361
  "ui.settings.remote.own": "Rozhovory s agentem už z telefonu sledovat jde – zapíná to sám Claude Code (jeho vlastní nastavení). Samostatnou práci agenta (volba 2 v menu) až po zapnutí i tohohle.",
@@ -1621,12 +1641,15 @@
1621
1641
  "ui.arg.year": "rok",
1622
1642
  "ui.error.arg.extra": "nečekaný argument „{arg}“",
1623
1643
  "ui.error.arg.extra.hint": "strom help {cmd} — hodnotu s mezerami dát do uvozovek",
1644
+ "ui.error.arg.extra.fix.hint": "správně: {fix}",
1645
+ "ui.error.arg.extra.several.hint": "více záznamů: {line}",
1624
1646
  "ui.error.option.is-arg": "{opt} se u {cmd} píše jako argument, ne jako volba",
1625
1647
  "ui.error.option.is-arg.hint": "{cmd} <{arg}>",
1626
1648
  "ui.error.option.unknown": "neznámá volba {opt} u {cmd}",
1627
1649
  "ui.error.option.unknown.hint": "volby: {options} · strom help {path}",
1628
1650
  "ui.error.option.unknown.none": "neznámá volba {opt} u {cmd}",
1629
1651
  "ui.error.option.unknown.none.hint": "{help}",
1652
+ "ui.error.option.unknown.status.hint": "jistota (--status) patří jen k údaji (E…), nikdy ke jménu ani k rodině: strom cite E… S… --status probable|proven, strom event edit E… --status …",
1630
1653
  "ui.error.option.no-value": "{opt} se píše bez hodnoty",
1631
1654
  "ui.error.option.needs-value": "{opt} potřebuje hodnotu {value}",
1632
1655
  "ui.error.option.needs-value.hint": "strom help {path}",
@@ -130,6 +130,13 @@
130
130
  "ui.setup.model.opus": "Opus – liest alte Handschriften am besten (empfohlen)",
131
131
  "ui.setup.model.sonnet": "Sonnet – schneller; gedruckte Dokumente und alltägliche Arbeit",
132
132
  "ui.setup.model.own": "Dem Agenten überlassen",
133
+ "ui.setup.model.agent": "Welches Modell soll in {agent} die Forschung machen? Die stärksten lesen alte Handschriften am besten – sie verbrauchen ein Abo schneller oder kosten mehr.",
134
+ "ui.setup.model.recommended": "{model} (empfohlen)",
135
+ "ui.setup.model.own.is": "Dem Agenten überlassen (jetzt {model})",
136
+ "ui.setup.model.now": "{model} (jetzt gewählt)",
137
+ "ui.chat.app.model.chosen": "In {app} für die Forschung das Modell {model} wählen (das in den Einstellungen gewählte).",
138
+ "ui.settings.model": "Modell der Forschung ({agent}): {model}",
139
+ "ui.settings.model.own": "das eigene des Agenten",
133
140
  "ui.setup.level": "Was darf der Agent tun, ohne nachzufragen?",
134
141
  "ui.setup.level.ask": "Mich bei allem außerhalb der Forschung fragen",
135
142
  "ui.setup.level.auto": "Selbstständig arbeiten – nur bei riskanten Schritten fragen (empfohlen)",
@@ -213,6 +220,7 @@
213
220
  "ui.run.stop.resets": " (erneuert sich {at})",
214
221
  "ui.run.stop.auth": "der Agent ist nicht angemeldet – einmal {command} starten und anmelden",
215
222
  "ui.run.stop.failed": "der Agent ist gescheitert: {detail}",
223
+ "ui.run.stop.model": "{agent} nimmt das Modell {model} nicht an – ein anderes wählen: strom config set model.lead <model> (oder für einen Lauf: strom run --model <model>)",
216
224
  "ui.run.finishing": "■ Strg-C: Sitzung {session} ist gebeten aufzuhören – der Agent schreibt auf, was er gefunden hat, und schließt sie, dann endet der Lauf (keine nächste Sitzung). Noch einmal Strg-C stoppt ihn sofort.",
217
225
  "ui.run.stopping": "■ wird angehalten – die Sitzung wird geschlossen und ihre Aufgabe kehrt in die Warteschlange zurück; nochmals Strg-C beendet sofort",
218
226
  "ui.run.gone": "· {session} geschlossen – ihr Lauf war abgebrochen{back}",
@@ -356,6 +364,9 @@
356
364
  "review.conflicts.what": "Entscheiden, was bei {name} offen geblieben ist: {list}{more}",
357
365
  "review.conflicts.why": "Widersprüche zwischen Quellen und konkurrierende Hypothesen warten auf eine Entscheidung ({research})",
358
366
  "review.conflicts.done": "jeder gelöst oder entschieden mit Begründung (strom conflict resolve, strom hypothesis decide) – oder eine Aufgabe sagt, was entscheiden würde",
367
+ "hypothesis.links.what": "Angeben, was die Varianten von {list} verbinden würden",
368
+ "hypothesis.links.why": "diese Hypothesen nennen ihre Personen nur in den Worten ihrer Varianten; die Strom-App zeigt, was eine Variante verbinden würde – und wo der Stammbaum endet – nur aus ihren Verknüpfungen",
369
+ "hypothesis.links.done": "jede Variante gelesen (strom hypothesis show {first} …) und angegeben, was sie verbinden würde: strom hypothesis link H… <Buchstabe> --child P… --of F… (oder --parents P… [P…], --same, --partners, --siblings); eine Variante, die nichts Klares verbindet, bleibt ohne Verknüpfungen",
359
370
  "review.marriage.what": "Heirat: {name} und {partner}{at} (vor dem ersten Kind, {year})",
360
371
  "review.marriage.why": "ihre Kinder sind erfasst, ihre Heirat nicht; der Eintrag nennt die Eltern beider, Alter und Häuser ({research})",
361
372
  "review.marriage.done": "der Eintrag gefunden und mit den Zeugen erfasst – oder {years} ohne ihn durchsucht",
@@ -597,6 +608,7 @@
597
608
  "ui.stats.sessions": "Sitzungen des Agenten: {n} · die letzte {last}",
598
609
  "ui.stats.cost": " · Kosten bisher {cost}",
599
610
  "ui.stats.cost.partial": " (ohne abgebrochene Sitzungen, die ihre Kosten nicht nannten: {n})",
611
+ "ui.stats.cost.readers": ", davon Leser der Scans {cost}",
600
612
  "ui.stats.stories": "Geschichten: {n}",
601
613
  "ui.status.data": "Personen: {persons} · Familien: {families}",
602
614
  "ui.status.directions": " · Richtungen der Forschung: {n}",
@@ -995,6 +1007,7 @@
995
1007
  "ui.archives.agentbrowser.on": "Dem Agenten den Browser in jeder Sitzung geben?",
996
1008
  "ui.archives.agentbrowser.off": "Zurück zum Browser nur für Archive über den Browser?",
997
1009
  "ui.consent.remote": "Die Claude-Code-Sitzungen, die strom öffnet, mit Remote Control starten, damit sie sich auf claude.ai oder in der Claude-App auf dem Telefon verfolgen und steuern lassen?",
1010
+ "ui.consent.addons": "Dem allein arbeitenden Agenten erlauben, die persönlichen Erweiterungen aus seinen Einstellungen zu laden (Skills, Plugins, MCP-Server – auch E-Mail oder Dokumente), wie im Chat? Er arbeitet dabei unbeaufsichtigt.",
998
1011
  "ui.consent.edits.user": "Sollen eigene Änderungen in der Strom-App vor dem gelten, was ein Eintrag sagt? Die Angabe aus dem Eintrag wird dann mit Begründung zurückgezogen (ein Abgleich lässt sich zurücknehmen).",
999
1012
  "ui.consent.review.off": "Was die Strom-App sendet, gleich übernehmen, ohne bei jeder Sendung zu fragen? Hinzugefügtes wird übernommen, Hinweise korrigiert, eine Änderung einer durch einen Eintrag belegten Angabe wird ein Widerspruch zur Entscheidung; strom sync undo nimmt eine Sendung zurück.",
1000
1013
  "ui.consent.mode.archive": "Die Forschung „{name}“ zu einem Archiv machen? Die Daten kommen aus der Strom-App und werden übernommen, wie sie gesendet werden; kein Agent arbeitet daran, ihre Aufgaben warten zurückgestellt. Die Arbeit mit einem Agenten lässt sich wieder einschalten.",
@@ -1336,6 +1349,13 @@
1336
1349
  "ui.dirs.active.now": "▶ „{name}“ läuft wieder: ihre Aufgaben ({n}) sind zurück in der Warteschlange.",
1337
1350
  "ui.research.resume": "Diese Forschung gibt es schon, {state}: „{name}“. Wieder aufnehmen?",
1338
1351
  "ui.alone.gate.bad": "Bitte eine Zahl von Punkten zwischen 0 und 100 (z. B. 10).",
1352
+ "ui.alone.addons": "Ohne die persönlichen Erweiterungen des Agenten (Skills, Plugins, MCP-Server): {state}",
1353
+ "ui.alone.addons.about": "Allein arbeitend lädt der Agent nur die Werkzeuge der Forschung. Die Verbindung zur Strom-App und der Browser funktionieren weiter. Im Chat mit dem Agenten werden die Erweiterungen immer geladen.",
1354
+ "ui.alone.addons.on": "Einschalten?",
1355
+ "ui.alone.addons.off": "Ausschalten? (der allein arbeitende Agent lädt dann auch die persönlichen Erweiterungen)",
1356
+ "ui.setup.addons": "Ohne die persönlichen Erweiterungen des Agenten (Skills, Plugins, MCP-Server)?",
1357
+ "ui.setup.addons.yes": "Ja (empfohlen)",
1358
+ "ui.setup.addons.no": "Nein – auch allein arbeitend mit den Erweiterungen",
1339
1359
  "ui.waiting.long": "Das ist für eine Antwort hier zu lang (höchstens {n} Zeichen) – ein Link oder ein Satz; das Übrige dem Agenten im Gespräch sagen.",
1340
1360
  "ui.settings.remote.chats": "Gespräche ja, allein arbeiten nein",
1341
1361
  "ui.settings.remote.own": "Gespräche mit dem Agenten lassen sich schon vom Telefon verfolgen – das macht Claude Code selbst (seine eigene Einstellung). Die Arbeit des Agenten allein (Punkt 2 im Menü) erst, wenn auch dies eingeschaltet ist.",
@@ -1621,12 +1641,15 @@
1621
1641
  "ui.arg.year": "Jahr",
1622
1642
  "ui.error.arg.extra": "unerwartetes Argument „{arg}“",
1623
1643
  "ui.error.arg.extra.hint": "strom help {cmd} — Werte mit Leerzeichen in Anführungszeichen",
1644
+ "ui.error.arg.extra.fix.hint": "richtig: {fix}",
1645
+ "ui.error.arg.extra.several.hint": "mehrere Datensätze: {line}",
1624
1646
  "ui.error.option.is-arg": "{opt} ist bei {cmd} ein Argument, keine Option",
1625
1647
  "ui.error.option.is-arg.hint": "{cmd} <{arg}>",
1626
1648
  "ui.error.option.unknown": "unbekannte Option {opt} für {cmd}",
1627
1649
  "ui.error.option.unknown.hint": "Optionen: {options} · strom help {path}",
1628
1650
  "ui.error.option.unknown.none": "unbekannte Option {opt} für {cmd}",
1629
1651
  "ui.error.option.unknown.none.hint": "{help}",
1652
+ "ui.error.option.unknown.status.hint": "eine Sicherheit (--status) gehört nur zu einer Angabe (E…), nie zu einem Namen oder einer Familie: strom cite E… S… --status probable|proven, strom event edit E… --status …",
1630
1653
  "ui.error.option.no-value": "{opt} hat keinen Wert",
1631
1654
  "ui.error.option.needs-value": "{opt} braucht einen Wert {value}",
1632
1655
  "ui.error.option.needs-value.hint": "strom help {path}",
@@ -4,11 +4,14 @@ You work to the Genealogical Proof Standard: search thoroughly, cite every
4
4
  fact, analyse and correlate the evidence, resolve conflicts, and write the
5
5
  conclusion so the next researcher can follow it.
6
6
 
7
+ <!-- for link verify enrich intake locate narrate -->
7
8
  - **Certainty is explicit.** `proven` — you read the original record yourself
8
9
  and it states the fact directly. `probable` — good evidence, but indirect or a
9
10
  single reading of hard handwriting. `possible` — weak evidence. `lead` — a
10
11
  family story, a family tree, an index or a guess: something to check, never a
11
12
  fact. Family trees and memories are leads, always.
13
+ <!-- end -->
14
+ <!-- for link verify enrich intake locate -->
12
15
  - **A namesake is not your person.** Identify by year, place, house, occupation,
13
16
  spouse and parents — the mother's maiden name decides most cases. Two entries
14
17
  that give one name different parents (another mother) are **two people** —
@@ -20,23 +23,36 @@ conclusion so the next researcher can follow it.
20
23
  - **Negative results are results.** Every search is recorded (`strom search add
21
24
  … --result negative`), with exactly what was covered. The most expensive
22
25
  mistake is to search the same book twice.
23
- - **Check the premise first.** Before opening anything: `strom searched <where>`.
26
+ <!-- end -->
27
+ - **Check the premise first.** Before opening anything, read the brief's
28
+ *Already known* — the searches of the task's books, people and surnames;
29
+ `strom searched <where>` only for a book, place or years it does not list.
30
+ Work that answers another open task as well closes it with yours:
31
+ `strom session close --done "…" --next "…" --also-done T…`.
32
+ <!-- for link verify enrich intake -->
24
33
  - **Extract everything the first time** you open a record: names, ages, house
25
34
  numbers, occupations, godparents, witnesses, midwife, remarks in the margin.
26
35
  Opening the same page again later costs more than writing it down now.
36
+ <!-- end -->
37
+ <!-- for link verify enrich intake locate -->
27
38
  - **Nothing is deleted.** A wrong fact is retracted with a reason; a changed
28
39
  fact is edited with a reason. Contradictions become conflicts, competing
29
40
  explanations become hypotheses — never silently pick one.
41
+ <!-- end -->
30
42
  - **Write as you go.** Record each finding as soon as you have it. Your context
31
43
  can be cut or summarised at any moment, and a session working alone is
32
44
  stopped at its time limit (the brief says when; near the end strom's output
33
45
  counts down): what is only in the conversation is lost, what is in strom is
34
- not. Going through many images, record each stretch when it is done (every
46
+ not.
47
+ <!-- for link verify enrich intake -->
48
+ Going through many images, record each stretch when it is done (every
35
49
  ten images, each district or year): the finds, and `strom search add …
36
50
  --pages` for what was searched in vain — never all of it at the end.
37
51
  A fact of a person goes on the person (`event add`); a note or the diary is
38
52
  for what is not a fact yet. What you held back while who it was stayed a
39
53
  hypothesis goes on the person when you decide it.
54
+ <!-- end -->
55
+ <!-- for link verify enrich intake locate -->
40
56
  - **Lessons belong where they apply.** A quirk of a register (its calibration,
41
57
  two years per page, a hand that writes 7 like 1) goes to `strom lesson add
42
58
  --on B…`, so whoever opens that book next sees it.
@@ -48,6 +64,8 @@ conclusion so the next researcher can follow it.
48
64
  queue holds back every other task about people off the tree
49
65
  (`strom task list --off-tree`); they come back by themselves once the link
50
66
  is recorded.
67
+ <!-- end -->
68
+ <!-- for link verify enrich intake -->
51
69
  - **Say what a hypothesis would connect.** A hypothesis about a connection —
52
70
  whose child, the same person, a couple, siblings — gets each variant's links
53
71
  when you record or argue it: `strom hypothesis link H… B --child P… --of F…`
@@ -61,11 +79,15 @@ conclusion so the next researcher can follow it.
61
79
  (`strom hypothesis variant H… "<claim>"`). Deciding one whose variants have
62
80
  links names the variant it is decided for:
63
81
  `strom hypothesis decide H… --variant B --decision "…"`.
82
+ <!-- end -->
64
83
  - **Stay within the task.** New questions become new tasks (`strom task add`,
65
- with where and done-when), not detours. A new task whose images are not here
84
+ with where and done-when), not detours.
85
+ <!-- for link verify enrich intake locate -->
86
+ A new task whose images are not here
66
87
  gets them now: through the archive's connector (built first when it has
67
88
  none), or — where the archive allows only that — it waits at once for what
68
89
  the user should download (`strom task wait T… --images B…:<numbers> --on
69
90
  "…"`); a later session would only find that out.
91
+ <!-- end -->
70
92
  - **Hand over cleanly.** Close with a summary of what was proven, what was
71
93
  searched in vain, and the next cheapest step.
@@ -3,8 +3,9 @@
3
3
  An input is material the user gave you: scans, photos, documents, notes, a
4
4
  family tree. Your job is to turn it into evidence without inventing anything.
5
5
 
6
- 1. `strom input show I…` — read the file (images and PDFs directly, text is
7
- shown). Identify who it is about and what kind of document it is.
6
+ 1. The brief's *The material* says what each input is, where its file is, and
7
+ shows its text: read the file (images and PDFs directly). Identify who it
8
+ is about and what kind of document it is.
8
9
  2. **What is it?** An original civil or church document (birth, marriage or
9
10
  death certificate, extract from a register, military papers) is a **source**
10
11
  and can prove facts: `strom source add … --input I… --form original
@@ -14,7 +15,7 @@ family tree. Your job is to turn it into evidence without inventing anything.
14
15
  `strom source add "<what it is>" --kind family-memory --form authored
15
16
  --information secondary --input I…`; a family tree is `--kind family-tree`.
16
17
  Many facts from one input: one `strom batch --file notes/<input>.txt`
17
- (try it with `--dry-run` first).
18
+ (all or nothing: it says every failing line at once, nothing written).
18
19
  3. Record every person and every fact it states, with a citation to the source
19
20
  (`--cite S… --locator "…"`). Use the words of the record in `--quote`.
20
21
  4. Put names and places as they are written; normalise only dates.
@@ -26,8 +27,9 @@ family tree. Your job is to turn it into evidence without inventing anything.
26
27
  the tree says differently stays a lead or becomes a conflict, never a fact.
27
28
  6. Create the next tasks: where would the records that prove these leads be?
28
29
  (`locate` if the archive or book is unknown, `link` if it is known.)
29
- 7. Close the task with what came out of it (`strom task done T… --result "…"`;
30
- its input is marked processed with it).
30
+ 7. Close the task with what came out of it (`strom session close --done "…"
31
+ --next "…"`: the task and the session in one call; its input is marked
32
+ processed with it).
31
33
 
32
34
  ## A batch from the Strom app (many files at once)
33
35
 
@@ -36,9 +38,10 @@ lists about 25 of its files, with the path each had there
36
38
  (`Babička/Dopisy/1946.jpg` — the folders say much). They are kept unchanged
37
39
  outside the tree's history: what you do not record of them stays private.
38
40
 
39
- 1. **Look over all of them first, cheaply**: `strom input show I…` for each —
40
- the name, the path, the type; images in a small view (`strom media view
41
- I… --grid`), not read whole. Sort each at once: `strom input sort I… [I…]
41
+ 1. **Look over all of them first, cheaply**: the brief lists each — the
42
+ name, the path, the type; images in a small view (`strom media view
43
+ I… I… I… --grid`: several in one call, opened together), not read
44
+ whole. Sort each at once: `strom input sort I… [I…]
42
45
  --as source|document|photo|unrelated --person P…`. Read in depth only what
43
46
  belongs to the people of the research.
44
47
  2. **Not of the family** (a tax form, a holiday photo of strangers, the same
@@ -1,6 +1,7 @@
1
1
  # Method: proving a link (parents, marriage)
2
2
 
3
- 1. **Premise:** `strom searched B… --years …` and `strom task show T…` — if the
3
+ 1. **Premise:** the brief's *Already known* and the task's notes (`strom
4
+ searched B… --years …` only for a book or years it does not list) — if the
4
5
  range was already covered completely, do not repeat it; say so and close.
5
6
  2. **Index before book.** Use an index to find the page, then read the entry in
6
7
  the register itself. An index entry alone is a lead — and so is its absence:
@@ -7,48 +7,76 @@ search depends on** (a surname, a birthplace, a parish) is read again on a
7
7
  crop at full resolution before it is recorded; what stays unsure is recorded
8
8
  as unsure and said so.
9
9
 
10
- An image you open stays in your context and is paid for again on every turn.
11
- Look at as few pixels as the question needs, and write down what you saw at once.
10
+ Every image you open stays in your context to the end of the session: look at
11
+ as few pixels as the question needs, write down what you saw at once, and after
12
+ about 80 views write down what you found before you open more.
12
13
 
13
14
  - **Look only through views**: `strom media view B0001:57` (record set and
14
15
  image number) makes a file in `.strom/views/` — open that file. Whole images
15
- come reduced: good for "is our surname on this page?", not for reading.
16
+ (and `--grid`) come reduced: for finding the entry, never for reading it.
17
+ **Transcribe only from a crop** at full resolution (`--crop`, or `--half`
18
+ where the entry fills the page). A whole view unclear, or the entry not where
19
+ expected: `--half both` before calling it not found — a negative search only
20
+ after the pages were seen at full resolution.
21
+ - **The views of a scan, or of a batch, in one call**, then open all the files
22
+ it lists together — not one call and one look per view:
23
+ `strom media view B0001:57-60 --half both` (the two pages of each double
24
+ page, where they are sharper than one view of it), `--crop … --crop …`
25
+ (several entries of one image), `--split 2x2` (a page in overlapping parts),
26
+ `strom media view B0001 --page 112 113`. Each file comes with its image, page
27
+ and `--clip`. At most 12 images and 24 views in one call.
16
28
  - **Find, then crop.** `--grid` overlays tenths with labels; read off where the
17
29
  entry is and ask for exactly that part: `--crop 0.05,0.40,0.45,0.18` (x, y,
18
30
  width, height as fractions) or `--half left|right` for one page of a spread.
19
31
  A crop comes at full resolution (small ones enlarged). `--contrast` for faded
20
32
  ink, `--rotate 90` for sideways pages.
33
+ <!-- for link verify enrich part -->
21
34
  - **Too small to read?** When a crop says it is enlarged, the scan has no more
22
35
  detail there. A connector that can fetch a part of an image sharper says so
23
36
  in that line: `strom fetch <connector> --recordset B0001 --images 57 --crop
24
37
  …` (one request). Then view the same crop again: it comes from the sharper
25
38
  part by itself.
39
+ <!-- end -->
26
40
  - **Browsing a book is a reader's job**: `strom read B0001 --images 40-69
27
- --question "…"` — readers in batches of ten, each with the full question;
28
- their reports stay in notes/readings/ for the next session, you get the finds.
41
+ --question "…"` — readers in batches of ten, each with the full question.
42
+ Their reports stay in notes/readings/; `strom read` ends with what they
43
+ found, compact: the found and unclear entries whole, what was illegible
44
+ where nothing was found (a possible match often hides there — look at it),
45
+ and the gaps in the book. Later, or in the next session: `strom readings`
46
+ (`--match <surname>` across all readings, `--list`); one image's whole
47
+ block: `strom readings B0001 --image 57`. Do not open the reports whole —
48
+ they are many times longer. Images read before are said before they are
49
+ read again: another question or a blind check is fine, the same one is not.
29
50
  It waits for its readers (often ten minutes or more): run it in the
30
51
  foreground and let it finish — your session ends with your turn, and
31
52
  whatever is left running in the background ends with it.
32
- (Your own subagents can read too — on your own model, never a faster one — in batches of at most twelve — but what they
33
- report is lost with your session unless you write it down: record each
34
- report as it comes back, before you send the next readers.) Only the entries
53
+ (A scan-reader subagent where your agent has one, strom-scan-reader, or your
54
+ own subagents — on your own model, never a faster one — read too, in batches
55
+ of at most twelve with the whole question; what they report is lost with
56
+ your session unless you write it down: record each report as it comes back,
57
+ before you send the next readers.) Only the entries
35
58
  that will be cited need your own eyes, at full resolution.
36
59
  - Old handwriting is decoded, not copied: never a weaker model for handwriting,
37
60
  never a guess. "Illegible" is a valid and valuable answer.
38
- - **Extract everything the first time**: names, ages, house numbers,
39
- occupations, godparents, witnesses, midwife, remarks in the margin.
40
61
  - **Report image by image, as you go**: the image and page, what was found (or
41
62
  nothing), what was illegible and where, the hand, how sure each name is.
42
63
  - **Cite the image, and where the entry is on it**: `strom source add …
43
64
  --clip B0001:57@0.05,0.40,0.45,0.18 --locator "pag. 112, 2nd entry"` — the
44
65
  crop you read it in, as its view prints it; a searched range goes in
45
- `strom search add … --pages 40-69`.
66
+ `strom search add … --pages 40-69`. Pages missing from the book (the
67
+ numbering jumps) were not searched: say so in `--note` (strom read puts the
68
+ gaps its readers saw there), or leave them out of the range
69
+ (`--pages 40-52,55-69`).
70
+ <!-- for verify enrich intake -->
46
71
  - Page ↔ image: `strom recordset calibrate B0001 --point 57=112` (measured on
47
72
  the image, never guessed); then `strom media view B0001 --page 112` works.
48
- - **No images here yet:** a connector for that archive
49
- (`strom connector list`) fetches them through strom:
50
- `strom fetch <connector> <book> --images 40-69 --recordset B0001` — only the
51
- images you need, never a whole book "just in case".
73
+ <!-- end -->
74
+ <!-- for link verify enrich connector -->
75
+ - **No images here yet:** the book's connector fetches them through strom
76
+ (the brief's record set gives the exact `strom fetch`) — only the images you
77
+ need, never a whole book "just in case".
78
+ <!-- end -->
79
+ <!-- for link verify enrich no-connector -->
52
80
  - **The archive has no connector yet: build one — now, you.** The user does
53
81
  not know connectors exist and will not ask for one. Tell them in a sentence
54
82
  ("for this archive I am preparing a downloader; it will fetch only the images
@@ -56,10 +84,14 @@ Look at as few pixels as the question needs, and write down what you saw at once
56
84
  follow its DISCOVERY.md (the portal's terms and robots.txt first, official
57
85
  exports preferred), test it with `strom connector test`, then fetch. It is
58
86
  built once per archive and serves every later task.
87
+ <!-- end -->
59
88
  - You never download from an archive yourself (curl, a script, your browser
60
- tools) — only through a connector, paced by strom. The user saves images by
89
+ tools) — only through a connector, paced by strom.
90
+ <!-- for link verify enrich no-connector by-hand -->
91
+ The user saves images by
61
92
  hand only where the archive does not allow automation (the connector then
62
- finds books and gives links only) or where a check stops it. Ask with
93
+ finds books and gives links only), where its connector only finds books, or
94
+ where a check stops it. Ask with
63
95
  `strom task wait T… --images B0001:40-69 --on "…"`: strom makes the folder
64
96
  of the inbox they go into, shows it to the user with the book's link, and
65
97
  checks the numbers of what arrives. Write `--on` for the user, in their
@@ -70,3 +102,4 @@ Look at as few pixels as the question needs, and write down what you saw at once
70
102
  the entry and save that view too (40a.jpg); a full-resolution scan can be
71
103
  ordered from the archive. Then take the next task; when the images are
72
104
  registered the task comes back by itself.
105
+ <!-- end -->
@@ -1,8 +1,12 @@
1
1
  # Method: recording an entry
2
2
 
3
- Record a found entry in one batch: write the lines to a file in notes/, run
4
- `strom batch --file notes/<file> --dry-run`, fix what it reports, then run it
5
- without --dry-run. `#name` labels what a line creates, `@name` uses it later.
3
+ Record a found entry in one batch: write the lines to a file in notes/ and run
4
+ `strom batch --file notes/<file>` — all or nothing: when a line fails nothing is
5
+ written and every failing line is said at once; fix them in the file with your
6
+ file-editing tool, not with sed or another shell command (the tree's rules let
7
+ the shell run strom, not edit files), run it again. `#name` labels what a line
8
+ creates, `@name` uses it later; a found search comes after the source it found
9
+ (`--found @s`).
6
10
 
7
11
  source add "Baptism of Jan Novák 1885" --kind baptism --recordset B0001 --clip B0001:57@0.05,0.40,0.45,0.18 --locator "pag. 112, entry 2" --language la --information primary --transcript @notes/entry.txt #s
8
12
  event add P0001 CHR --date "25 JUN 1885" --place "Týnec" --house 13 --cite @s --quote "baptizatus est" --with "godparent:Marie Dvořáková" --with "midwife:Anna Nová" --with "officiant:P. Josef Kříž" --status proven
@@ -21,7 +25,8 @@ without --dry-run. `#name` labels what a line creates, `@name` uses it later.
21
25
  - `--transcript` is the entry's words as they stand in it — its own language,
22
26
  spelling and abbreviations, line by line, unread letters `[?]`; the Strom app
23
27
  shows them next to the entry's image. Every entry read gets them.
24
- - A fact already in the tree gets the citation (`cite E…`), not a second fact —
28
+ - A fact already in the tree gets the citation (`cite E…`, `--status` is a
29
+ fact's only — a name or a family cited takes none), not a second fact —
25
30
  and what the record adds to it: `event edit E… --age husband:27 --age wife:17
26
31
  --house 21 --with "witness:…" --with "officiant:…"` (filling in needs no reason).
27
32
  - The names a record gives go on the person: `name add` (a maiden name completes
@@ -6,7 +6,8 @@ first name) needs a second, independent reading.
6
6
  - The second reader must be **blind**: do not tell them the expected value, the
7
7
  name, the place or why it matters. Ask only what is written in that cell.
8
8
  - One cell, one second reader — not the whole page again.
9
- - Before asking, check whether two readings already exist (`strom find`).
9
+ - Before asking, check whether two readings already exist (`strom find`; what
10
+ readers wrote of an image: `strom readings B0001 --image 57`).
10
11
  - Certainty above "probable" only when two independent readings agree. When
11
12
  they disagree, record a conflict with both readings.
12
13
  - An independent reading of an entry you cited: `strom read M… --blind --crop
@@ -16,6 +16,7 @@ import { agentAppUrl, appSite } from "../core/stromapp.js";
16
16
  import { configDir } from "../core/paths.js";
17
17
  import { agentBrowser } from "../core/connector.js";
18
18
  import { CHROME_ALLOW, CHROME_DENY, chromeDomain } from "../core/browser.js";
19
+ import { claudeScanReader, opencodeScanReader, SCAN_READER } from "./scanreader.js";
19
20
  export const MARKER = "<!-- strom: generated above this line (strom agents sync); your own notes below are kept -->";
20
21
  function agentsMd(tree) {
21
22
  const lang = langName(tree.config.lang);
@@ -30,9 +31,10 @@ the researcher; \`strom\` is your only way to read and change the research.
30
31
  1. Start with \`strom\` — it says where things stand and what to do next.
31
32
  \`strom guide\` explains the work; \`strom help <command>\` any command.
32
33
  2. Work in sessions: \`strom session start\` gives you the brief for the next
33
- task; finish with \`strom session close --summary "…" --next "…"\`. The
34
- next step is strom's queue (\`strom\` names it, \`strom plan\` lists it):
35
- what the user wants instead becomes a task first — never search outside a
34
+ task; finish with \`strom session close --done "<result>" --next "…"\`
35
+ (the task and the session in one call). The next step is strom's queue
36
+ (\`strom\` names it, \`strom plan\` lists it): what the user wants
37
+ instead becomes a task first — never search outside a
36
38
  session and its task.
37
39
  3. Never create, edit or delete anything in \`data/\`, \`strom.json\` or \`.git\`,
38
40
  never read the files in \`data/\` (strom shows them better and shorter), and
@@ -45,7 +47,8 @@ the researcher; \`strom\` is your only way to read and change the research.
45
47
  terminal — never do it yourself.
46
48
  6. You may read \`inputs/\`, \`output/\`, \`notes/\` and \`.strom/views/\`, and write
47
49
  your own working notes in \`notes/\`. Scans are looked at through views:
48
- \`strom media view B0001:57 --half left\` writes one to \`.strom/views/\`.
50
+ \`strom media view B0001:57 --half left\` writes one to \`.strom/views/\`;
51
+ the views of a scan or a batch come in one call (\`B0001:57-60 --half both\`).
49
52
  7. Run strom commands on their own — no pipes (\`| head\`, \`| grep\`): output is
50
53
  already short, listings take \`--limit\` and \`--page\`, and piped commands may
51
54
  be refused by your permissions.
@@ -311,6 +314,8 @@ export function opencodeConfig(tree) {
311
314
  description: "strom run: the agent working alone, nobody to ask — what the rules do not allow is refused",
312
315
  permission: refused(permission),
313
316
  },
317
+ // reading scans: the shell for strom and the file reader, nothing else
318
+ [SCAN_READER]: opencodeScanReader(tree, permission, refused),
314
319
  },
315
320
  };
316
321
  }
@@ -341,6 +346,7 @@ function dropAgentFiles(tree) {
341
346
  ["AGENTS.md", agentsMd(tree)],
342
347
  ["CLAUDE.md", claudeMd(tree)],
343
348
  [path.join(".claude", "settings.json"), JSON.stringify(claudeSettings(tree), null, 2) + "\n"],
349
+ [CLAUDE_SCAN_READER, claudeScanReader(tree)],
344
350
  ["opencode.json", JSON.stringify(opencodeConfig(tree), null, 2) + "\n"],
345
351
  [path.join(".grok", "config.toml"), grokConfig(tree)],
346
352
  [path.join(".grok", "rules", "strom.md"), GROK_RULES],
@@ -368,7 +374,9 @@ function dropAgentFiles(tree) {
368
374
  }
369
375
  return gone;
370
376
  }
371
- export const AGENT_FILES = ["AGENTS.md", "CLAUDE.md", path.join(".claude", "settings.json"), "opencode.json", path.join(".grok", "config.toml"), path.join(".grok", "rules", "strom.md")];
377
+ /** Claude Code's subagent for reading scans, in the tree. */
378
+ export const CLAUDE_SCAN_READER = path.join(".claude", "agents", `${SCAN_READER}.md`);
379
+ export const AGENT_FILES = ["AGENTS.md", "CLAUDE.md", path.join(".claude", "settings.json"), CLAUDE_SCAN_READER, "opencode.json", path.join(".grok", "config.toml"), path.join(".grok", "rules", "strom.md")];
372
380
  export function syncAgentFiles(tree) {
373
381
  // an archive: no agent works on it, and nothing of one shows in its folder (Milan's decision, 2026-10-03) — strom's own
374
382
  // files of the agents go (what a person wrote in them stays); research switched on writes them again
@@ -394,6 +402,7 @@ export function syncAgentFiles(tree) {
394
402
  write("AGENTS.md", agentsMd(tree));
395
403
  write("CLAUDE.md", claudeMd(tree));
396
404
  write(path.join(".claude", "settings.json"), JSON.stringify(claudeSettings(tree), null, 2) + "\n");
405
+ write(CLAUDE_SCAN_READER, claudeScanReader(tree));
397
406
  write("opencode.json", JSON.stringify(opencodeConfig(tree), null, 2) + "\n");
398
407
  write(path.join(".grok", "config.toml"), grokConfig(tree));
399
408
  write(path.join(".grok", "rules", "strom.md"), GROK_RULES);
@@ -0,0 +1,59 @@
1
+ // How big an image each agent's model takes in whole. An agent shrinks a bigger image itself before its model sees
2
+ // it (and pays for the pixels it threw away in a slower read), so strom makes views no bigger than that:
3
+ // Claude Code ≤ 2000 px for Opus 4.7+, Sonnet 5+, Fable and Mythos (the image limit of their model catalogue;
4
+ // older models — Opus/Sonnet 4.6, Haiku — take 1568 on the API side)
5
+ // Codex ≤ 2048 px and about 2.5 megapixels at its default detail
6
+ // OpenCode ≤ 2000×2000 by default (attachment.image), the provider's model limit beyond it
7
+ // Grok Build shrinks to a size it does not say; Antigravity counts tokens per image, not pixels
8
+ // Unknown: 1568 px, what every vision model takes without shrinking it again.
9
+ /** The long side of a view every vision model takes in whole. */
10
+ export const IMAGE_MAX = 1568;
11
+ /** What the newer models take in whole, and Claude Code, Codex and OpenCode pass on unshrunk. */
12
+ export const IMAGE_MAX_LARGE = 2000;
13
+ /**
14
+ * The long side of a whole image shown to find an entry on it (strom media view without --half, --crop or --split):
15
+ * the entry is found as surely as at 2000 px for half the tokens; it is read from a crop or a half at the model's size.
16
+ */
17
+ export const OVERVIEW_MAX = 1400;
18
+ /** A Claude model name or alias ("opus", "claude-opus-4-7", "claude-sonnet-5-5[1m]", "us.anthropic.claude-…"): its family and version. */
19
+ export function claudeModel(model) {
20
+ const m = /(opus|sonnet|haiku|fable|mythos)(?:[-.]?(\d{1,2})(?:[-.](\d{1,2})(?!\d))?(?!\d))?/.exec(model.toLowerCase());
21
+ if (!m)
22
+ return undefined;
23
+ // "claude-3-5-sonnet-…": the version before the family (an old model)
24
+ const before = /(\d{1,2})(?:[-.](\d{1,2}))?[-.](?:opus|sonnet|haiku)/.exec(model.toLowerCase());
25
+ if (!m[2] && before)
26
+ return { family: m[1], version: Number(before[1]) + Number(before[2] ?? 0) / 10 };
27
+ return { family: m[1], ...(m[2] ? { version: Number(m[2]) + Number(m[3] ?? 0) / 10 } : {}) };
28
+ }
29
+ /**
30
+ * The longest side of a view for this agent and model (the model strom starts it with; undefined = the agent's own
31
+ * default). Claude Code's own default is a model of the current line (Opus or Sonnet 5): 2000. A model given as a
32
+ * family alone ("opus", "sonnet", "fable") is that family's newest — haiku stays at 1568 (not among the larger ones).
33
+ */
34
+ export function imageMax(agent, model) {
35
+ const m = model?.trim() || undefined;
36
+ switch (agent) {
37
+ case "claude": {
38
+ if (!m || m === "default" || m === "best")
39
+ return IMAGE_MAX_LARGE;
40
+ const c = claudeModel(m);
41
+ if (!c)
42
+ return IMAGE_MAX;
43
+ if (c.family === "fable" || c.family === "mythos")
44
+ return IMAGE_MAX_LARGE;
45
+ if (c.version === undefined)
46
+ return c.family === "haiku" ? IMAGE_MAX : IMAGE_MAX_LARGE;
47
+ if (c.family === "opus")
48
+ return c.version >= 4.7 ? IMAGE_MAX_LARGE : IMAGE_MAX;
49
+ if (c.family === "sonnet")
50
+ return c.version >= 5 ? IMAGE_MAX_LARGE : IMAGE_MAX;
51
+ return IMAGE_MAX;
52
+ }
53
+ case "codex":
54
+ case "opencode":
55
+ return IMAGE_MAX_LARGE;
56
+ default:
57
+ return IMAGE_MAX;
58
+ }
59
+ }