strom-research 1.22.0-beta.2 → 1.22.0-beta.3
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 +13 -1
- package/CHANGELOG.de.md +16 -1
- package/CHANGELOG.md +15 -1
- package/assets/lang/cs.json +7 -1
- package/assets/lang/de.json +7 -1
- package/assets/plugins/gates/README.md +11 -0
- package/assets/plugins/gates/claude-usage/gate.json +1 -1
- package/assets/plugins/gates/claude-usage/gate.ts +57 -8
- package/dist/brief/brief.js +4 -2
- package/dist/cli/menu.js +8 -6
- package/dist/cli/ui.js +7 -1
- package/dist/commands/browse.js +18 -7
- package/dist/commands/connectors.js +65 -11
- package/dist/commands/guide.js +2 -1
- package/dist/commands/read.js +10 -5
- package/dist/commands/readers.js +7 -2
- package/dist/commands/session.js +31 -1
- package/dist/commands/tasks.js +43 -5
- package/dist/commands/trees.js +3 -1
- package/dist/core/errors.js +5 -0
- package/dist/core/leftalone.js +125 -0
- package/dist/core/logimages.js +203 -0
- package/dist/core/queue.js +33 -3
- package/dist/core/reader.js +17 -0
- package/dist/core/readstats.js +84 -9
- package/dist/core/session.js +2 -0
- package/dist/core/tidy.js +156 -24
- package/dist/core/tree.js +1 -1
- package/dist/runners/claude.js +86 -16
- package/dist/runners/jsonl.js +13 -4
- package/package.json +1 -1
package/CHANGELOG.cs.md
CHANGED
|
@@ -7,13 +7,25 @@ Změny každé verze ve zkratce. Úplné poznámky a instalační řádky jsou n
|
|
|
7
7
|
|
|
8
8
|
## 1.22.0
|
|
9
9
|
|
|
10
|
-
*Připravuje se: bety do 1.22.0-beta.
|
|
10
|
+
*Připravuje se: bety do 1.22.0-beta.3. Oddíl bude úplný s vydáním.*
|
|
11
11
|
|
|
12
12
|
- **Jméno jednou v tom, co čeká.** Úkol k materiálu poslanému z aplikace Strom jmenoval ve widgetu a na stránce
|
|
13
13
|
telefonu každého člověka dvakrát (podruhé v hranatých závorkách) a jeho text bez jmen nechal prázdné závorky; menu,
|
|
14
14
|
`strom plan`, aplikace Strom i soubor GEDCOM ukazovaly jméno s jeho ID zopakované. Každé teď jméno řekne jednou.
|
|
15
15
|
- **Widget otevře správný výzkum.** Při několika stromech stejného jména v aplikaci Strom mohl widget na ploše Macu
|
|
16
16
|
otevřít jiný z nich; teď otevře strom svého výzkumu.
|
|
17
|
+
- **Mnohem menší logy sezení.** Logy sezení a čtenářů už neuchovávají obrázky, na které se agent díval (jen jejich
|
|
18
|
+
velikost a cestu k náhledu), takže jsou o 95–99 % menší.
|
|
19
|
+
- **Úklid, který stačí.** `strom tidy` zvládne logy jakékoli velikosti. Náhledy skenů, přerušená stahování a další,
|
|
20
|
+
co strom umí vyrobit znovu, se uklízejí i bez souhlasu; logy se pak jen zmenší, nikdy nesmažou. Po odmítnutí přijde
|
|
21
|
+
otázka ještě jednou, když `.strom` výrazně přeroste samotný výzkum.
|
|
22
|
+
- **Archiv, který neodpovídá, zastaví smyčku agenta.** `strom fetch` řekne, kdy to zkusit znovu a co dělat mezitím;
|
|
23
|
+
stejný dotaz ve stejném sezení skončí hned (exit 8) a nic neodešle. Úkol, jehož archiv má pauzu, počká mimo frontu
|
|
24
|
+
(plán ho ukáže odložený do té doby) a `strom run --loop` do té doby počká, místo aby pouštěl sezení bez práce.
|
|
25
|
+
- **Čtenáři nečekají na umlklou službu.** Čtenář, jehož agent po opakovaném pokusu služby zmlkne, se jednou obnoví
|
|
26
|
+
tam, kde skončil; když mlčí dál, hned skončí. Takový výpadek už nezmenšuje dávky čtení.
|
|
27
|
+
- **Brána týdne předplatného pustí víc sezení za sebou.** Když jednou čeká na náskok, pustí znovu až se dvěma body navíc
|
|
28
|
+
(při 15: 17), místo jednoho sezení po každém čekání.
|
|
17
29
|
|
|
18
30
|
## 1.21.0
|
|
19
31
|
|
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.22.0
|
|
9
9
|
|
|
10
|
-
*In Vorbereitung: die Betas bis 1.22.0-beta.
|
|
10
|
+
*In Vorbereitung: die Betas bis 1.22.0-beta.3. Der Abschnitt ist mit dem Release vollständig.*
|
|
11
11
|
|
|
12
12
|
- **Jeder Name einmal in dem, was wartet.** Eine Aufgabe zu Material, das aus der Strom-App gesendet wurde, nannte im
|
|
13
13
|
Widget und auf der Seite des Telefons jede Person zweimal (das zweite Mal in eckigen Klammern), und ihr Text ohne
|
|
@@ -15,6 +15,21 @@ Die Änderungen jeder Version in Kürze. Die vollständigen Hinweise und die Ins
|
|
|
15
15
|
seiner ID wiederholt. Jedes nennt den Namen jetzt einmal.
|
|
16
16
|
- **Das Widget öffnet die richtige Forschung.** Bei mehreren Stammbäumen gleichen Namens in der Strom-App konnte das
|
|
17
17
|
Widget auf dem Mac-Schreibtisch einen der anderen öffnen; jetzt öffnet es den Stammbaum seiner eigenen Forschung.
|
|
18
|
+
- **Viel kleinere Sitzungsprotokolle.** Die Protokolle der Sitzungen und Leser behalten die Bilder, die der Agent
|
|
19
|
+
angesehen hat, nicht mehr (nur ihre Größe und den Pfad der Ansicht) und sind dadurch 95–99 % kleiner.
|
|
20
|
+
- **Aufräumen, das mithält.** `strom tidy` bewältigt Protokolle jeder Größe. Ansichten von Scans, abgebrochene
|
|
21
|
+
Downloads und anderes, was strom neu erstellen kann, werden auch ohne Zustimmung aufgeräumt; Protokolle werden dann
|
|
22
|
+
nur verkleinert, nie gelöscht. Nach einem Nein kommt die Frage noch einmal, wenn `.strom` weit über die Forschung
|
|
23
|
+
hinauswächst.
|
|
24
|
+
- **Ein Archiv, das nicht antwortet, beendet die Schleife des Agenten.** `strom fetch` sagt, wann es wieder zu
|
|
25
|
+
versuchen ist und was bis dahin zu tun ist; dieselbe Anfrage in derselben Sitzung endet sofort (Exit 8) und sendet
|
|
26
|
+
nichts. Eine Aufgabe, deren Archiv pausiert, wartet bis dahin außerhalb der Warteschlange (der Plan zeigt sie bis zu
|
|
27
|
+
dieser Zeit zurückgestellt), und `strom run --loop` wartet bis dahin, statt Sitzungen ohne Arbeit zu starten.
|
|
28
|
+
- **Leser warten nicht mehr auf einen verstummten Dienst.** Ein Leser, dessen Agent nach einem erneuten Versuch seines
|
|
29
|
+
Dienstes verstummt, wird einmal dort fortgesetzt, wo er aufhörte; bleibt er still, endet er sofort. Ein solcher
|
|
30
|
+
Ausfall verkleinert die Lesestapel nicht mehr.
|
|
31
|
+
- **Das Tor der Abo-Woche lässt mehrere Sitzungen hintereinander laufen.** Wartet es einmal auf Vorsprung, lässt es
|
|
32
|
+
erst mit zwei Punkten mehr wieder los (bei 15: 17), statt einer Sitzung nach jedem Warten.
|
|
18
33
|
|
|
19
34
|
## 1.21.0
|
|
20
35
|
|
package/CHANGELOG.md
CHANGED
|
@@ -7,13 +7,27 @@ The changes of each version in short. The full notes and the install lines are o
|
|
|
7
7
|
|
|
8
8
|
## 1.22.0
|
|
9
9
|
|
|
10
|
-
*In preparation: the betas up to 1.22.0-beta.
|
|
10
|
+
*In preparation: the betas up to 1.22.0-beta.3. The section is complete with the release.*
|
|
11
11
|
|
|
12
12
|
- **Each name once in what waits.** A task for material sent from the Strom app named each person twice in the
|
|
13
13
|
widget and on the phone's page (the second time in brackets), and its text without names kept empty brackets; the
|
|
14
14
|
menu, `strom plan`, the Strom app and the GEDCOM file showed the name with its ID repeated. Each says the name once.
|
|
15
15
|
- **The widget opens the right research.** With several trees of one name in the Strom app, the widget on the Mac's
|
|
16
16
|
desktop could open one of the others; it now opens the tree of its own research.
|
|
17
|
+
- **Much smaller session logs.** The logs of sessions and readers no longer keep the images the agent looked at
|
|
18
|
+
(only their size and the view's path), so they are 95–99 % smaller.
|
|
19
|
+
- **Tidying that keeps up.** `strom tidy` handles logs of any size. Views of scans, downloads cut short and other
|
|
20
|
+
things strom makes again are kept in order even without a yes; logs are only made smaller then, never removed. After
|
|
21
|
+
a no, the question comes once more when `.strom` grows far beyond the research.
|
|
22
|
+
- **An archive that stops answering stops the agent's loop.** `strom fetch` says when to try again and what to do
|
|
23
|
+
meanwhile; asked again in the same session it ends at once (exit 8) and sends nothing. A task whose archive is left
|
|
24
|
+
alone waits out of the queue until then (the plan shows it put off until that time), and `strom run --loop` waits
|
|
25
|
+
for that time instead of starting sessions with nothing to do.
|
|
26
|
+
- **Readers no longer wait out a silent service.** A reader whose agent goes silent after a retry of its service is
|
|
27
|
+
resumed once where it stopped; still silent, it ends at once. Such a failure no longer makes the reading batches
|
|
28
|
+
smaller.
|
|
29
|
+
- **The gate of the subscription's week lets several sessions run in a row.** Once it waits for points in hand, it goes again only
|
|
30
|
+
with two more (at 15: 17), instead of one session after every wait.
|
|
17
31
|
|
|
18
32
|
## 1.21.0
|
|
19
33
|
|
package/assets/lang/cs.json
CHANGED
|
@@ -250,7 +250,7 @@
|
|
|
250
250
|
"ui.tune.context.none": "Kontext: nevyčistil se (čtenáři: {of})",
|
|
251
251
|
"ui.tune.context.unknown": "Vyčištění kontextu: neznámé (spotřeba se hlásí až na konci)",
|
|
252
252
|
"ui.tune.readers": "Čtenáři bez výsledku: {bad} z {n}",
|
|
253
|
-
"ui.tune.readers.halted": "Čtenáři zastavení přihlášením, limitem plánu nebo
|
|
253
|
+
"ui.tune.readers.halted": "Čtenáři zastavení přihlášením, limitem plánu, chybou nebo výpadkem služby: {n} – nepočítají se, o čtení neříkají nic",
|
|
254
254
|
"ui.tune.books": "Knihy:",
|
|
255
255
|
"ui.tune.more": " … další: {n} (všechny: --json)",
|
|
256
256
|
"ui.tune.book": " {id} {title}: skeny {scans} · pohledy na sken {vps}",
|
|
@@ -544,6 +544,7 @@
|
|
|
544
544
|
"ui.run.stop.user": "zastaveno ručně",
|
|
545
545
|
"ui.run.stop.time": "časový limit",
|
|
546
546
|
"ui.run.stop.empty": "žádné otevřené úkoly",
|
|
547
|
+
"ui.run.stop.asleep": "do {at} není co dělat – zbylé úkoly potřebují {host}, který teď neodpovídá",
|
|
547
548
|
"ui.run.stop.problems": "doklady mají problém – strom check",
|
|
548
549
|
"ui.run.stop.denied": "agent nesměl spouštět strom – nepustila ho jeho oprávnění nebo sandbox; nic dalšího se nespustilo",
|
|
549
550
|
"ui.run.stop.limit": "limit předplatného{resets}",
|
|
@@ -1124,6 +1125,7 @@
|
|
|
1124
1125
|
"ui.tidy.no": "Nic se nesmazalo.",
|
|
1125
1126
|
"ui.consent.tidy": "Uvolnit asi {size} vedle výzkumu {name} (staré záznamy sezení, náhledy skenů, dávno zapsaná poslání)? Výzkum i jeho skeny zůstanou.",
|
|
1126
1127
|
"ui.tidy.first": "Vedle výzkumu se nastřádalo, co jde uvolnit, asi {size} (záznamy práce, náhledy snímků a podobně – nikdy výzkum). Ukazuje se tu jednou; později v menu Nastavení → Údržba → Místo na disku.",
|
|
1128
|
+
"ui.tidy.again": "Co si strom drží vedle výzkumu, narostlo na {strom} (samotný výzkum má {research}); uvolnit jde asi {size} (hlavně záznamy práce – nikdy výzkum). Ukazuje se tu ještě jednou, naposledy; později v menu Nastavení → Údržba → Místo na disku.",
|
|
1127
1129
|
"ui.tidy.hint": "Vedle výzkumu si strom drží {strom}, z toho jde uvolnit {size}: strom tidy (v menu Nastavení → Údržba → Místo na disku)",
|
|
1128
1130
|
"ui.error.consent": "tady je potřeba souhlas – {question}",
|
|
1129
1131
|
"ui.error.consent.hint": "spustit ve vlastním terminálu: {set}",
|
|
@@ -1642,6 +1644,8 @@
|
|
|
1642
1644
|
"ui.plan.title": "Co agent udělá dál – {name}:",
|
|
1643
1645
|
"ui.plan.now": "právě teď",
|
|
1644
1646
|
"ui.plan.images": " (nejdřív potřebuje snímky)",
|
|
1647
|
+
"ui.plan.later": "později",
|
|
1648
|
+
"ui.plan.asleep": " (odloženo do {at}: {host} teď neodpovídá)",
|
|
1645
1649
|
"ui.plan.more": "… a další úkoly: {n}",
|
|
1646
1650
|
"ui.plan.stories": "Potom vyprávění: {n} ({names})",
|
|
1647
1651
|
"ui.plan.waiting": "Čeká: {count} · v menu: Co čeká",
|
|
@@ -1670,6 +1674,8 @@
|
|
|
1670
1674
|
"ui.run.beside.sure": "Spustit další vedle něj?",
|
|
1671
1675
|
"ui.run.gate": "· podmínka: {name} — ptá se před každým sezením; běh může nechat čekat",
|
|
1672
1676
|
"ui.run.gate.wait": "⏸ čekám do {at} — {reason} (Ctrl-C běh ukončí)",
|
|
1677
|
+
"ui.run.asleep.wait": "⏸ čekám do {at} – zbylé úkoly potřebují {host}, který teď neodpovídá (Ctrl-C běh ukončí)",
|
|
1678
|
+
"ui.run.asleep.why": "{host} teď neodpovídá",
|
|
1673
1679
|
"ui.run.stop.gate": "podmínka „{name}“ řekla dost — {reason}",
|
|
1674
1680
|
"ui.run.stop.gate.error": "podmínka „{name}“ neodpověděla, jak má — {reason} (strom gate test)",
|
|
1675
1681
|
"ui.run.gate.once": "· podmínka: {name} — tyto úkoly jsou spuštěné ručně: zeptám se jí jednou, a kdyby nespouštěla, přijde otázka, zda přesto začít",
|
package/assets/lang/de.json
CHANGED
|
@@ -250,7 +250,7 @@
|
|
|
250
250
|
"ui.tune.context.none": "Kontext: nicht geleert (Leser: {of})",
|
|
251
251
|
"ui.tune.context.unknown": "Leeren des Kontexts: unbekannt (der Verbrauch wird erst am Ende genannt)",
|
|
252
252
|
"ui.tune.readers": "Leser ohne Ergebnis: {bad} von {n}",
|
|
253
|
-
"ui.tune.readers.halted": "Leser, die eine Anmeldung, das Limit des Plans oder ein
|
|
253
|
+
"ui.tune.readers.halted": "Leser, die eine Anmeldung, das Limit des Plans, ein Fehler oder ein Ausfall des Dienstes gestoppt hat: {n} – nicht gezählt, nichts über das Lesen",
|
|
254
254
|
"ui.tune.books": "Bücher:",
|
|
255
255
|
"ui.tune.more": " … weitere: {n} (alle: --json)",
|
|
256
256
|
"ui.tune.book": " {id} {title}: Scans {scans} · Ansichten pro Scan {vps}",
|
|
@@ -544,6 +544,7 @@
|
|
|
544
544
|
"ui.run.stop.user": "von Hand angehalten",
|
|
545
545
|
"ui.run.stop.time": "Zeitlimit",
|
|
546
546
|
"ui.run.stop.empty": "keine offenen Aufgaben",
|
|
547
|
+
"ui.run.stop.asleep": "bis {at} nichts zu tun – die übrigen Aufgaben brauchen {host}, der gerade nicht antwortet",
|
|
547
548
|
"ui.run.stop.problems": "die Belege haben Probleme – strom check",
|
|
548
549
|
"ui.run.stop.denied": "der Agent durfte strom nicht starten – seine Berechtigungen oder seine Sandbox ließen es nicht zu; nichts weiter wurde gestartet",
|
|
549
550
|
"ui.run.stop.limit": "Limit des Abonnements{resets}",
|
|
@@ -1124,6 +1125,7 @@
|
|
|
1124
1125
|
"ui.tidy.no": "Nichts wurde gelöscht.",
|
|
1125
1126
|
"ui.consent.tidy": "Etwa {size} neben der Forschung {name} freigeben (alte Sitzungsprotokolle, Ansichten von Scans, längst geschriebene Sendungen)? Die Forschung und ihre Scans bleiben.",
|
|
1126
1127
|
"ui.tidy.first": "Neben der Forschung hat sich angesammelt, was freigegeben werden kann, etwa {size} (Protokolle, Ansichten von Scans und Ähnliches – nie die Forschung). Wird hier einmal gezeigt; später im Menü Einstellungen → Wartung → Speicherplatz.",
|
|
1128
|
+
"ui.tidy.again": "Was strom neben der Forschung hält, ist auf {strom} angewachsen (die Forschung selbst: {research}); davon lassen sich etwa {size} freigeben (vor allem Protokolle der Arbeit – nie die Forschung). Wird hier noch ein letztes Mal gezeigt; später im Menü Einstellungen → Wartung → Speicherplatz.",
|
|
1127
1129
|
"ui.tidy.hint": "Neben der Forschung hält strom {strom}, davon lassen sich {size} freigeben: strom tidy (im Menü Einstellungen → Wartung → Speicherplatz)",
|
|
1128
1130
|
"ui.error.consent": "dafür braucht es eine Zustimmung – {question}",
|
|
1129
1131
|
"ui.error.consent.hint": "im eigenen Terminal ausführen: {set}",
|
|
@@ -1642,6 +1644,8 @@
|
|
|
1642
1644
|
"ui.plan.title": "Was der Agent als Nächstes tut – {name}:",
|
|
1643
1645
|
"ui.plan.now": "gerade jetzt",
|
|
1644
1646
|
"ui.plan.images": " (braucht zuerst die Scans)",
|
|
1647
|
+
"ui.plan.later": "später",
|
|
1648
|
+
"ui.plan.asleep": " (zurückgestellt bis {at}: {host} antwortet gerade nicht)",
|
|
1645
1649
|
"ui.plan.more": "… und {n, plural, one {# weitere Aufgabe} other {# weitere Aufgaben}}",
|
|
1646
1650
|
"ui.plan.stories": "Danach die Geschichten: {n} ({names})",
|
|
1647
1651
|
"ui.plan.waiting": "Wartet: {count} · im Menü: Was wartet",
|
|
@@ -1670,6 +1674,8 @@
|
|
|
1670
1674
|
"ui.run.beside.sure": "Einen weiteren daneben starten?",
|
|
1671
1675
|
"ui.run.gate": "· Bedingung: {name} — vor jeder Sitzung gefragt; sie kann den Lauf warten lassen",
|
|
1672
1676
|
"ui.run.gate.wait": "⏸ warte bis {at} — {reason} (Strg-C beendet den Lauf)",
|
|
1677
|
+
"ui.run.asleep.wait": "⏸ warte bis {at} — die übrigen Aufgaben brauchen {host}, der gerade nicht antwortet (Strg-C beendet den Lauf)",
|
|
1678
|
+
"ui.run.asleep.why": "{host} antwortet gerade nicht",
|
|
1673
1679
|
"ui.run.stop.gate": "die Bedingung „{name}“ sagte Halt — {reason}",
|
|
1674
1680
|
"ui.run.stop.gate.error": "die Bedingung „{name}“ gab keine gültige Antwort — {reason} (strom gate test)",
|
|
1675
1681
|
"ui.run.gate.once": "· Bedingung: {name} — diese Aufgaben sind von Hand gestartet: sie wird einmal gefragt, und würde sie nicht starten, wird nachgefragt",
|
|
@@ -29,6 +29,10 @@ the gates here. `strom config unset run.gate`: no gate.
|
|
|
29
29
|
is strom's own Node (TypeScript runs as it is) — best on every system. What
|
|
30
30
|
the user gives the gate after its name (`run.gate "my-gate 20 night"`) is
|
|
31
31
|
added to these arguments.
|
|
32
|
+
|
|
33
|
+
What a gate remembers between askings it keeps in its own folder (strom asks it
|
|
34
|
+
there): strom never touches a file there but those of the gates it ships
|
|
35
|
+
(`gate.json`, `gate.ts`).
|
|
32
36
|
- `timeout`: seconds it may take (default 120). No answer in time: an error.
|
|
33
37
|
|
|
34
38
|
## The answer
|
|
@@ -124,6 +128,13 @@ starts at once); never past the reset. Every answer says when the week resets
|
|
|
124
128
|
("weekly reset in 1.7 h"). A used-up session: until it resets. Another agent
|
|
125
129
|
than Claude Code: it lets the work go on.
|
|
126
130
|
|
|
131
|
+
Once it waited for want of points in hand, it goes again only with 2 more (at
|
|
132
|
+
15: 17 in hand; early in the week its share, as the number's), and then goes
|
|
133
|
+
until fewer than the number are in hand: several sessions in a row instead of
|
|
134
|
+
one after every wait ("until 17 in hand, then several sessions in a row"). It
|
|
135
|
+
remembers the week it waits in (`hold.json` in its folder); a new week starts
|
|
136
|
+
free.
|
|
137
|
+
|
|
127
138
|
**A cap** — your own hard stop for the week:
|
|
128
139
|
|
|
129
140
|
strom config set run.gate "claude-usage 10 --cap 95"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"interface": 1,
|
|
3
3
|
"title": "Claude usage",
|
|
4
|
-
"description": "Keeps working alone to the daily ration of your Claude subscription: a session starts only while the week's usage is at least n points behind the share of the week gone (a day ≈ 14.3; early in the week half the share gone, so a new week starts at once; in its last 2 hours what is left goes when it holds a session): strom config set run.gate \"claude-usage 10\" (without a number: 0). A cap: \"claude-usage 10 --cap 95\" — at 95 % of the week no session starts, in any run, and no \"start anyway\" goes past it.",
|
|
4
|
+
"description": "Keeps working alone to the daily ration of your Claude subscription: a session starts only while the week's usage is at least n points behind the share of the week gone (a day ≈ 14.3; early in the week half the share gone, so a new week starts at once; in its last 2 hours what is left goes when it holds a session; once it waited, it goes again only with 2 more in hand, then for several sessions in a row): strom config set run.gate \"claude-usage 10\" (without a number: 0). A cap: \"claude-usage 10 --cap 95\" — at 95 % of the week no session starts, in any run, and no \"start anyway\" goes past it.",
|
|
5
5
|
"command": [
|
|
6
6
|
"node",
|
|
7
7
|
"gate.ts"
|
|
@@ -22,6 +22,12 @@
|
|
|
22
22
|
// END_WINDOW (with room for a session), its reset (a new week starts at once) —
|
|
23
23
|
// never past the reset. A full session: until it resets.
|
|
24
24
|
//
|
|
25
|
+
// Once it waited for want of points in hand, it goes again only with RESERVE
|
|
26
|
+
// more (at 15: 17), and then goes until fewer than the number are in hand — so
|
|
27
|
+
// several sessions run in a row, not one after each wait. The reserve grows
|
|
28
|
+
// with the week as the number does. Run by strom (STROM_GATE), the gate keeps
|
|
29
|
+
// the week it waits in in its own folder (hold.json); a new week starts free.
|
|
30
|
+
//
|
|
25
31
|
// A cap (strom config set run.gate "claude-usage 15 --cap 95"): at 95 % of the
|
|
26
32
|
// week used no session starts — the user's own hard stop, asked before every
|
|
27
33
|
// session of every run, and no "start anyway" goes past it; a session at work
|
|
@@ -31,6 +37,7 @@
|
|
|
31
37
|
//
|
|
32
38
|
// strom writes this file; a copy of your own goes into a folder of its own name.
|
|
33
39
|
|
|
40
|
+
import fs from "node:fs";
|
|
34
41
|
import { spawnSync } from "node:child_process";
|
|
35
42
|
|
|
36
43
|
const DAY = 86_400_000;
|
|
@@ -49,6 +56,14 @@ const END_WINDOW = 2 * 3_600_000;
|
|
|
49
56
|
const SESSION_POINTS = 10;
|
|
50
57
|
/** After the reset the gate asks again this much later, so the new week is what Claude Code says. */
|
|
51
58
|
const AFTER_RESET = 60_000;
|
|
59
|
+
/**
|
|
60
|
+
* Once it waited for want of points in hand, this many more are needed before it goes again (with the full number;
|
|
61
|
+
* early in the week its share, as the number's): a session takes about a point of the week, so the work goes on in
|
|
62
|
+
* runs of a few sessions instead of one after every wait — each session reads its brief anew. With no number too.
|
|
63
|
+
*/
|
|
64
|
+
const RESERVE = 2;
|
|
65
|
+
/** The week it waits in for the reserve, in the gate's own folder — only when strom asks it (STROM_GATE). */
|
|
66
|
+
const HOLD = process.env.STROM_GATE ? "hold.json" : undefined;
|
|
52
67
|
const lang = (process.env.STROM_LANG ?? "en").slice(0, 2);
|
|
53
68
|
|
|
54
69
|
const T: Record<string, Record<string, string>> = {
|
|
@@ -58,7 +73,7 @@ const T: Record<string, Record<string, string>> = {
|
|
|
58
73
|
head: "week {used} % used, {gone} % gone, {behind} in hand (at least {min} needed{early})",
|
|
59
74
|
early: " — half the week gone, early in the week",
|
|
60
75
|
resetIn: "; weekly reset in {reset}",
|
|
61
|
-
|
|
76
|
+
hold: " — until {target} in hand, then several sessions in a row; weekly reset in {reset}",
|
|
62
77
|
last: " — until the last {window} before the weekly reset in {reset}",
|
|
63
78
|
reset: " — until the weekly reset in {reset}",
|
|
64
79
|
end: "week {used} % used, weekly reset in {reset} — the {room} % left is enough for a session",
|
|
@@ -78,7 +93,7 @@ const T: Record<string, Record<string, string>> = {
|
|
|
78
93
|
head: "týden: využito {used} %, uplynulo {gone} %, náskok {behind} (potřeba aspoň {min}{early})",
|
|
79
94
|
early: " – na začátku týdne polovina uplynulého",
|
|
80
95
|
resetIn: "; týden se obnoví za {reset}",
|
|
81
|
-
|
|
96
|
+
hold: " — čeká na náskok {target}, pak víc sezení za sebou; týden se obnoví za {reset}",
|
|
82
97
|
last: " — do posledních {window} před obnovením týdne za {reset}",
|
|
83
98
|
reset: " — do obnovení týdne za {reset}",
|
|
84
99
|
end: "týden: využito {used} %, obnoví se za {reset} — zbylých {room} % stačí na sezení",
|
|
@@ -98,7 +113,7 @@ const T: Record<string, Record<string, string>> = {
|
|
|
98
113
|
head: "Woche: {used} % genutzt, {gone} % vergangen, Vorsprung {behind} (mindestens {min} nötig{early})",
|
|
99
114
|
early: " – am Wochenanfang die Hälfte des Vergangenen",
|
|
100
115
|
resetIn: "; Wochen-Reset in {reset}",
|
|
101
|
-
|
|
116
|
+
hold: " — wartet auf Vorsprung {target}, dann mehrere Sitzungen am Stück; Wochen-Reset in {reset}",
|
|
102
117
|
last: " — bis zu den letzten {window} vor dem Wochen-Reset in {reset}",
|
|
103
118
|
reset: " — bis zum Wochen-Reset in {reset}",
|
|
104
119
|
end: "Woche: {used} % genutzt, Wochen-Reset in {reset} — die übrigen {room} % reichen für eine Sitzung",
|
|
@@ -188,22 +203,56 @@ const reset = inTime(resets - now);
|
|
|
188
203
|
// shown rounded down: never "15 in hand (at least 15 needed)" for 14.6 — and gone − used stays what it says; the
|
|
189
204
|
// need early in the week is a fraction: rounded down when it goes, up when it waits, so the two never seem to disagree
|
|
190
205
|
const v = { used: Math.floor(week.used), gone: Math.floor(gone), behind: Math.floor(behind), early, reset };
|
|
206
|
+
// the reserve once it waited: grown with the week as the number is (its share of the need), whole with no number
|
|
207
|
+
const reserve = min > 0 ? (RESERVE * need) / min : RESERVE;
|
|
208
|
+
const held = holding(resets);
|
|
191
209
|
if (session && session.used >= 100) answer(1, say("session", { reset }), session.resets ? new Date(session.resets.getTime() + AFTER_RESET) : undefined);
|
|
192
|
-
if (behind >= need
|
|
210
|
+
if (behind >= need + (held ? reserve : 0)) {
|
|
211
|
+
hold(undefined);
|
|
212
|
+
answer(0, say("head", { ...v, min: Math.floor(need) }) + say("resetIn", { reset }));
|
|
213
|
+
}
|
|
193
214
|
// what is left before the reset: (to the cap, else to 100 %) enough for a session — in the week's last stretch it goes
|
|
194
215
|
const room = (cap ?? 100) - week.used;
|
|
195
216
|
const fits = room >= SESSION_POINTS;
|
|
196
217
|
if (fits && resets - now <= END_WINDOW) answer(0, say("end", { used: v.used, reset, room: Math.floor(room) }));
|
|
197
|
-
//
|
|
198
|
-
|
|
218
|
+
// it waits for want of points in hand: from now on it goes again only with the reserve too
|
|
219
|
+
if (!held) hold(resets);
|
|
220
|
+
// else it waits for the first of: the week caught up with the reserve (gone − used ≥ need + reserve = k·need,
|
|
221
|
+
// k = 1 + RESERVE/n: with the full number gone = used + n + RESERVE, early in the week gone·(1 − k/2) = used — never
|
|
222
|
+
// when k ≥ 2), the week's last stretch (with room for a session), the reset — a new week at 0 % goes at once, so
|
|
199
223
|
// never past it
|
|
200
|
-
const
|
|
224
|
+
const k = 1 + RESERVE / min;
|
|
225
|
+
const byHalf = min > 0 && k < 2 ? week.used / (1 - k / 2) : Infinity;
|
|
226
|
+
const caught = start + (Math.min(week.used + min + RESERVE, byHalf) / 100) * WEEK;
|
|
201
227
|
const last = fits ? resets - END_WINDOW : Infinity;
|
|
202
228
|
const after = resets + AFTER_RESET;
|
|
203
229
|
const at = Math.min(caught, last, after);
|
|
204
|
-
const tail =
|
|
230
|
+
const tail =
|
|
231
|
+
at === caught ? say("hold", { reset, target: Math.ceil(need + reserve) }) : at === last ? say("last", { reset, window: say("hours", { n: END_WINDOW / 3_600_000 }) }) : say("reset", { reset });
|
|
205
232
|
answer(1, say("head", { ...v, min: Math.ceil(need) }) + tail, new Date(Math.max(at, now + 5 * 60_000)));
|
|
206
233
|
|
|
234
|
+
/** It waits for the reserve in this week (the week it remembers ends within a day of this one's reset). */
|
|
235
|
+
function holding(resets: number): boolean {
|
|
236
|
+
if (!HOLD) return false;
|
|
237
|
+
try {
|
|
238
|
+
const week = Date.parse((JSON.parse(fs.readFileSync(HOLD, "utf8")) as { week?: string }).week ?? "");
|
|
239
|
+
return Math.abs(week - resets) < DAY;
|
|
240
|
+
} catch {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** The week it now waits in for the reserve, or none (gone). Never fails: without it the gate only forgets. */
|
|
246
|
+
function hold(resets: number | undefined): void {
|
|
247
|
+
if (!HOLD) return;
|
|
248
|
+
try {
|
|
249
|
+
if (resets === undefined) fs.rmSync(HOLD, { force: true });
|
|
250
|
+
else fs.writeFileSync(HOLD, JSON.stringify({ week: new Date(resets).toISOString(), since: new Date().toISOString() }) + "\n");
|
|
251
|
+
} catch {
|
|
252
|
+
// a folder it may not write here: it asks as if for the first time
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
207
256
|
/** A stretch of time as a person reads it: hours to two days, then days — one decimal, in the research language. */
|
|
208
257
|
function inTime(ms: number): string {
|
|
209
258
|
const h = Math.max(0, ms) / 3_600_000;
|
package/dist/brief/brief.js
CHANGED
|
@@ -18,7 +18,7 @@ import { calibrationLine } from "../core/calibration.js";
|
|
|
18
18
|
import { taskRecordsets } from "../core/frontier.js";
|
|
19
19
|
import { reviewItems } from "../core/review.js";
|
|
20
20
|
import { byHandOnly, listConnectors, missingConsents, readyConnectors, routeOf } from "../core/connector.js";
|
|
21
|
-
import { clock, hostAllowed, hostState, REFUSED_FOREVER } from "../core/net.js";
|
|
21
|
+
import { clock, clockUp, hostAllowed, hostState, leftAloneFor, REFUSED_FOREVER } from "../core/net.js";
|
|
22
22
|
import { archiveLevel } from "../core/terms.js";
|
|
23
23
|
import { onHost, permissionQuestions, setHosts } from "../core/archivewait.js";
|
|
24
24
|
import { runs, runsArg, shellArg } from "../cli/format.js";
|
|
@@ -762,7 +762,9 @@ function archiveLine(tree, b, repo, shared, installed, qs = []) {
|
|
|
762
762
|
try {
|
|
763
763
|
const st = hostState(net, host);
|
|
764
764
|
if (st.blockedUntil && st.blockedUntil > Date.now())
|
|
765
|
-
parts.push(`${host} left alone until ${clock(st.blockedUntil)}${st.reason ? ` (${st.reason})` : ""} — nothing to it before then
|
|
765
|
+
parts.push(`${host} left alone until ${clock(st.blockedUntil)}${st.reason ? ` (${st.reason})` : ""} — nothing to it before then${
|
|
766
|
+
// it went silent, or asked to slow down: the work goes elsewhere meanwhile, never a fetch asked again and again
|
|
767
|
+
leftAloneFor(st, Date.now()) ? `: work on what needs nothing from it, else back to the queue for later (strom session close --continue) and on after ${clockUp(st.blockedUntil)}` : ""}`);
|
|
766
768
|
}
|
|
767
769
|
catch {
|
|
768
770
|
// the host's state unreadable: the level says enough
|
package/dist/cli/menu.js
CHANGED
|
@@ -11,7 +11,7 @@ import path from "node:path";
|
|
|
11
11
|
import { ui } from "./ui.js";
|
|
12
12
|
import { isAgent } from "../core/which.js";
|
|
13
13
|
import { offerAdopt } from "./seal.js";
|
|
14
|
-
import { mb, setTidyAsked, tidyAsked, tidyOn, tidyPlan, TIDY_SAID } from "../core/tidy.js";
|
|
14
|
+
import { mb, setTidyAsked, tidyAskAgain, tidyAsked, tidyOn, tidyPlan, TIDY_SAID } from "../core/tidy.js";
|
|
15
15
|
import { calibrationLabel, calibrationOffer } from "../core/viewsizes.js";
|
|
16
16
|
/** A line as a sentence: its first letter capital (a catalog's text that also stands inside a line). */
|
|
17
17
|
const sentence = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
@@ -214,11 +214,13 @@ export async function runMenu(ctx, run) {
|
|
|
214
214
|
const disk = tidyPlan(tree);
|
|
215
215
|
if (disk.frees >= TIDY_SAID) {
|
|
216
216
|
// never tidied (an older strom gathered it): its first tidy offered once, shown and asked as strom tidy asks
|
|
217
|
-
// (Enter says no) — nothing goes unasked; then kept in order by itself.
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
217
|
+
// (Enter says no) — nothing but what strom makes again goes unasked; then kept in order by itself. Said no and
|
|
218
|
+
// grown far beyond the research since (tidyAskAgain): offered once more, never again. Nobody to ask (no
|
|
219
|
+
// terminal, STROM_NONINTERACTIVE): only said, the offer kept for the person
|
|
220
|
+
const again = tidyAskAgain(root, disk);
|
|
221
|
+
if (!tidyOn(root) && (!tidyAsked(root) || again) && !isAgent(ctx.env) && ctx.interactive) {
|
|
222
|
+
setTidyAsked(root, again);
|
|
223
|
+
out(again ? t("ui.tidy.again", { strom: mb(disk.size.strom, lang), research: mb(disk.size.research, lang), size: mb(disk.frees, lang) }) : t("ui.tidy.first", { size: mb(disk.frees, lang) }));
|
|
222
224
|
out();
|
|
223
225
|
await run(["tidy"]);
|
|
224
226
|
out();
|
package/dist/cli/ui.js
CHANGED
|
@@ -224,7 +224,7 @@ export const UI = {
|
|
|
224
224
|
"ui.tune.context.none": "Context: not cleared (readers: {of})",
|
|
225
225
|
"ui.tune.context.unknown": "Context clears: not known (the use is said only at the end)",
|
|
226
226
|
"ui.tune.readers": "Readers without a result: {bad} of {n}",
|
|
227
|
-
"ui.tune.readers.halted": "Readers stopped by a login, a plan's limit or
|
|
227
|
+
"ui.tune.readers.halted": "Readers stopped by a login, a plan's limit, a failure or their service gone silent: {n} — not counted, nothing of the reading",
|
|
228
228
|
"ui.tune.books": "Books:",
|
|
229
229
|
"ui.tune.more": " … {n} more (all of them: --json)",
|
|
230
230
|
"ui.tune.book": " {id} {title}: scans {scans} · views per scan {vps}",
|
|
@@ -521,6 +521,7 @@ export const UI = {
|
|
|
521
521
|
"ui.run.stop.user": "by hand",
|
|
522
522
|
"ui.run.stop.time": "time limit",
|
|
523
523
|
"ui.run.stop.empty": "no open tasks",
|
|
524
|
+
"ui.run.stop.asleep": "nothing to do until {at} — the tasks left need {host}, which is not answering now",
|
|
524
525
|
"ui.run.stop.problems": "the evidence has problems — strom check",
|
|
525
526
|
"ui.run.stop.denied": "the agent was not allowed to run strom — its permissions or its sandbox kept it away; nothing more was started",
|
|
526
527
|
"ui.run.stop.limit": "subscription limit{resets}",
|
|
@@ -1015,6 +1016,7 @@ export const UI = {
|
|
|
1015
1016
|
"ui.tidy.no": "Nothing was removed.",
|
|
1016
1017
|
"ui.consent.tidy": "Free about {size} beside the research {name} (old logs, views of scans, sends written long ago)? The research and its images stay.",
|
|
1017
1018
|
"ui.tidy.first": "Beside the research strom has gathered what can go, about {size} (logs, views of scans and the like — never the research). Shown once here; later in the menu: Settings → Maintenance → Disk space.",
|
|
1019
|
+
"ui.tidy.again": "What strom keeps beside the research has grown to {strom} (the research itself: {research}); about {size} of it can go (mostly logs of work — never the research). Shown here once more, the last time; later in the menu: Settings → Maintenance → Disk space.",
|
|
1018
1020
|
"ui.tidy.hint": "Beside the research strom keeps {strom}, {size} of it can go: strom tidy (the menu: Settings → Maintenance → Disk space)",
|
|
1019
1021
|
"ui.fixes.title": "Fixes: what earlier versions of strom, or earlier models, left behind in this research",
|
|
1020
1022
|
"ui.fixes.none": "Nothing waits: nothing in this research to set right or to read again.",
|
|
@@ -1631,6 +1633,8 @@ export const UI = {
|
|
|
1631
1633
|
"ui.plan.title": "What the agent will do next – {name}:",
|
|
1632
1634
|
"ui.plan.now": "now",
|
|
1633
1635
|
"ui.plan.images": " (needs the scans first)",
|
|
1636
|
+
"ui.plan.later": "later",
|
|
1637
|
+
"ui.plan.asleep": " (put off until {at}: {host} is not answering now)",
|
|
1634
1638
|
"ui.plan.more": "… and {n, plural, one {# more task} other {# more tasks}}",
|
|
1635
1639
|
"ui.plan.stories": "Then the stories: {n} ({names})",
|
|
1636
1640
|
"ui.plan.waiting": "Waiting: {count} · in the menu: What waits",
|
|
@@ -1660,6 +1664,8 @@ export const UI = {
|
|
|
1660
1664
|
"ui.run.others": "⚠ Another run works in this family tree already (since {since}): this one takes other tasks — the costs add up.",
|
|
1661
1665
|
"ui.run.gate": "· condition: {name} — asked before each session; it may make the run wait",
|
|
1662
1666
|
"ui.run.gate.wait": "⏸ waiting until {at} — {reason} (Ctrl-C ends the run)",
|
|
1667
|
+
"ui.run.asleep.wait": "⏸ waiting until {at} — the tasks left need {host}, which is not answering now (Ctrl-C ends the run)",
|
|
1668
|
+
"ui.run.asleep.why": "{host} is not answering now",
|
|
1663
1669
|
"ui.run.stop.gate": "the condition “{name}” said stop — {reason}",
|
|
1664
1670
|
"ui.run.stop.gate.error": "the condition “{name}” gave no proper answer — {reason} (strom gate test)",
|
|
1665
1671
|
"ui.run.gate.once": "· condition: {name} — these tasks are started by hand: it is asked once, and if it would not start, \"start anyway?\" is asked",
|
package/dist/commands/browse.js
CHANGED
|
@@ -13,7 +13,8 @@ import { knownGenerations, ownerName, pedigree, personCard, recent, treeStats }
|
|
|
13
13
|
import { displayName } from "../core/people.js";
|
|
14
14
|
import { humanTask } from "../cli/human.js";
|
|
15
15
|
export { humanTask };
|
|
16
|
-
import { lacksImages } from "../core/queue.js";
|
|
16
|
+
import { lacksImages, waitsForArchive } from "../core/queue.js";
|
|
17
|
+
import { clockUp } from "../core/net.js";
|
|
17
18
|
import { forStory } from "../core/stories.js";
|
|
18
19
|
import { isArchive } from "../core/mode.js";
|
|
19
20
|
import { actsAsArchive, stateOf } from "../core/state.js";
|
|
@@ -212,13 +213,20 @@ register({
|
|
|
212
213
|
const waiting = waits.length;
|
|
213
214
|
const shown = work.slice(0, count);
|
|
214
215
|
let n = 0;
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
216
|
+
// put off for a while: their archive is not answering now (core/leftalone.ts) — back in the queue by themselves then
|
|
217
|
+
const held = waitsForArchive(tree);
|
|
218
|
+
const inQueue = new Set(queue.map((t) => t.id));
|
|
219
|
+
const later = rankedQueue(tree, { strategy: ctx.settings.strategy(tree.config), awake: true })
|
|
220
|
+
.map((r) => r.task)
|
|
221
|
+
.filter((t) => !inQueue.has(t.id) && !forStory(t))
|
|
222
|
+
.flatMap((t) => ((a) => (a ? [{ t, a }] : []))(held(t)));
|
|
223
|
+
const rows = [
|
|
224
|
+
...shown.map((t) => [` ${t.state === "doing" ? ui(lang, "ui.plan.now") : `${++n}.`}`, `${truncate(humanTask(tree, t.what, lang), 110)}${lacksImages(tree, t) ? ui(lang, "ui.plan.images") : ""}`]),
|
|
225
|
+
...later.slice(0, 5).map(({ t, a }) => [` ${ui(lang, "ui.plan.later")}`, `${truncate(humanTask(tree, t.what, lang), 110)}${ui(lang, "ui.plan.asleep", { at: clockUp(a.until), host: a.host })}`]),
|
|
226
|
+
];
|
|
219
227
|
const storyNames = [...new Set(stories.map((t) => tree.get(t.subject[0] ?? "")).filter((p) => !!p).map((p) => `${displayName(p)} [${p.id}]`))];
|
|
220
228
|
const out = [ui(lang, "ui.plan.title", { name: tree.config.name })];
|
|
221
|
-
if (!queue.length)
|
|
229
|
+
if (!queue.length && !later.length)
|
|
222
230
|
out.push(` ${ui(lang, "ui.plan.none")}`);
|
|
223
231
|
else {
|
|
224
232
|
if (rows.length)
|
|
@@ -233,7 +241,10 @@ register({
|
|
|
233
241
|
if (queue.length)
|
|
234
242
|
out.push("", ui(lang, "ui.plan.steer"));
|
|
235
243
|
const item = (t) => ({ id: t.id, what: humanTask(tree, t.what, lang), level: t.level, state: t.state, needsImages: lacksImages(tree, t) });
|
|
236
|
-
return {
|
|
244
|
+
return {
|
|
245
|
+
text: lines(...out),
|
|
246
|
+
data: { tasks: work.map(item), stories: stories.map(item), waiting, ...(later.length ? { later: later.map(({ t, a }) => ({ ...item(t), until: new Date(a.until).toISOString(), host: a.host })) } : {}) },
|
|
247
|
+
};
|
|
237
248
|
},
|
|
238
249
|
}, {
|
|
239
250
|
path: ["recent"],
|