isorite 0.11.0 → 0.12.1

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 (57) hide show
  1. package/README.md +2 -5
  2. package/dist/core.js +91 -7
  3. package/dist/main.js +163 -179
  4. package/docs/setup-for-agents.md +11 -36
  5. package/docs/setup.md +20 -61
  6. package/docs/terminal.md +28 -80
  7. package/package.json +1 -1
  8. package/web/_app/immutable/assets/0.DqoJ_jEe.css +1 -0
  9. package/web/_app/immutable/assets/{5.DbDKXCoO.css → 5.rBvyRuST.css} +1 -1
  10. package/web/_app/immutable/assets/{7.ljiu9bn5.css → 7.C00vyVPo.css} +1 -1
  11. package/web/_app/immutable/assets/Drawing.Q7J9MyIo.css +1 -0
  12. package/web/_app/immutable/assets/{NoteList.F579Zvj_.css → NoteList.E6jC-AtZ.css} +1 -1
  13. package/web/_app/immutable/assets/Timeline.LsRXYIBv.css +1 -0
  14. package/web/_app/immutable/assets/ground.hxi5N5fC.css +1 -0
  15. package/web/_app/immutable/chunks/{BgT6ZdzK.js → 94XMxKlw.js} +1 -1
  16. package/web/_app/immutable/chunks/{DGmSCGD3.js → 9TpKhtgt.js} +1 -1
  17. package/web/_app/immutable/chunks/{CfL6O_ia.js → BBHd0LvJ.js} +1 -1
  18. package/web/_app/immutable/chunks/BMcDEv5V.js +1 -0
  19. package/web/_app/immutable/chunks/CFEI5lV9.js +1 -0
  20. package/web/_app/immutable/chunks/ChbZenhA.js +7 -0
  21. package/web/_app/immutable/chunks/{mLFVM17H.js → CmExTt6_.js} +1 -1
  22. package/web/_app/immutable/chunks/{DuLysVkb.js → CnR51TY0.js} +1 -1
  23. package/web/_app/immutable/chunks/{DktGnzDy.js → CxAq1aod.js} +80 -79
  24. package/web/_app/immutable/chunks/{CODXTnUW.js → DMzXaGgk.js} +4 -4
  25. package/web/_app/immutable/chunks/{DNjLZcwq.js → DSYLdqdc.js} +1 -1
  26. package/web/_app/immutable/chunks/{DaulBP5z.js → DT2w6KyY.js} +1 -1
  27. package/web/_app/immutable/chunks/{CMeq8te_.js → rq7JUohF.js} +1 -1
  28. package/web/_app/immutable/chunks/wQPxmN7w.js +1 -0
  29. package/web/_app/immutable/entry/app.Z7pwf_yI.js +2 -0
  30. package/web/_app/immutable/entry/start.B_WWh0TE.js +1 -0
  31. package/web/_app/immutable/nodes/0.zgVqViQf.js +26 -0
  32. package/web/_app/immutable/nodes/{1.C-RxYNx3.js → 1.D06ogNcd.js} +1 -1
  33. package/web/_app/immutable/nodes/2.Xq8_UpCY.js +1 -0
  34. package/web/_app/immutable/nodes/3.CgpZ3cDm.js +1 -0
  35. package/web/_app/immutable/nodes/4.DQfZbhoQ.js +2 -0
  36. package/web/_app/immutable/nodes/5.BqLxxB44.js +9 -0
  37. package/web/_app/immutable/nodes/6.Dg2_pK7R.js +1 -0
  38. package/web/_app/immutable/nodes/7.BpxHVHoY.js +1 -0
  39. package/web/_app/version.json +1 -1
  40. package/web/index.html +20 -19
  41. package/web/_app/immutable/assets/0.DcsDj0O1.css +0 -1
  42. package/web/_app/immutable/assets/Timeline.KTc3LKuK.css +0 -1
  43. package/web/_app/immutable/assets/drawn.CHbcoujQ.css +0 -1
  44. package/web/_app/immutable/chunks/BYSDTLqw.js +0 -1
  45. package/web/_app/immutable/chunks/DDVefAkE.js +0 -7
  46. package/web/_app/immutable/chunks/Db_gdTNI.js +0 -1
  47. package/web/_app/immutable/chunks/F7HCRtmu.js +0 -1
  48. package/web/_app/immutable/chunks/wxBqjpRy.js +0 -1
  49. package/web/_app/immutable/entry/app.wp4GfiBT.js +0 -2
  50. package/web/_app/immutable/entry/start.PPXyd7_0.js +0 -1
  51. package/web/_app/immutable/nodes/0.JerS9yOA.js +0 -26
  52. package/web/_app/immutable/nodes/2.Bbkruoyi.js +0 -1
  53. package/web/_app/immutable/nodes/3.BkpGKa9k.js +0 -1
  54. package/web/_app/immutable/nodes/4.Bj25nsxG.js +0 -2
  55. package/web/_app/immutable/nodes/5.D9sfzyBX.js +0 -9
  56. package/web/_app/immutable/nodes/6.Di1re0aR.js +0 -1
  57. package/web/_app/immutable/nodes/7.MWEn-qPC.js +0 -1
package/dist/main.js CHANGED
@@ -346,7 +346,7 @@ var init_data = __esmMin((() => {
346
346
  //#region packages/kit/src/context.gen.ts
347
347
  var REFERENCE, RECIPES;
348
348
  var init_context_gen = __esmMin((() => {
349
- REFERENCE = "# The kit (isorite kit 1)\n\nAn artifact is one HTML file: markup, perhaps a script, and a manifest in its `<head>`. isorite gives it the kit:\nthe look (every colour, font and corner), the elements below, and `window.iso`. Write what it shows and does, never\nhow it looks: no colour, font family or border-radius of your own.\n\n## The file\n\n```html\n<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Title</title>\n<meta name=\"description\" content=\"One sentence: what it shows, and what can be done with it.\">\n<meta name=\"isorite:kit\" content=\"1\">\n<meta name=\"isorite:uses\" content=\"runs.csv other.csv\">\n<style>/* layout only, if needed */</style>\n</head>\n<body>\n<iso-page>\n <header><h1>Title</h1><p>The question it answers, in a sentence or two.</p></header>\n <iso-grid>\n <iso-panel class=\"wide\"><h2>Name <span>a short reading</span></h2> … </iso-panel>\n <iso-panel><h2>…</h2> … </iso-panel>\n </iso-grid>\n</iso-page>\n</body>\n</html>\n```\n\n`write_artifact` fills in the title, description and manifest from its arguments, so they are always right.\n\n## Layout\n\n- `<iso-grid>`: panels in two columns (`style=\"--cols: 3\"` for three); `<iso-panel class=\"wide\">` spans the row.\n- `<iso-panel>`: its first child is `<h2>`; a `<span>` in the `h2` is a soft aside. A `<p>` in a panel is a\n caption: what the panel shows, in a sentence.\n- `<iso-row>`: controls side by side. `<iso-stats>`: big numbers side by side.\n\n## Data, with no script\n\nEvery element with `data=\"file.csv\"` reads it itself. Several files are one table:\n`data=\"euler.csv leapfrog.csv\"` adds a column `file` (`euler`, `leapfrog`) to compare them with `by=\"file\"`.\n`where=\"integrator=leapfrog\"` keeps some rows; `where=\"t<=$t\"` compares with a control's value. `abs(col)` is a\ncolumn's size.\n\n- `<iso-chart data=\"f.csv\" x=\"t\" y=\"energy\" by=\"body\">`: a chart.\n - `type`: `line` (default), `dot`, `bar`, `area`, `step`.\n - `y` may name several columns: `y=\"kinetic potential total\"`, each its own line.\n - `by`: a column whose values are the series.\n - `x-scale`, `y-scale`: `linear` or `log`, or `$name` to follow a control (`y-scale=\"$scale\"`).\n - `x-label`, `y-label`: the axis with its unit (`\"time (s)\"`). `height` in pixels (260).\n - `at=\"t\"`: follows the control `t`. A rule where x is its value; or, when the data has a column `t` and x is\n another column (a path in x and y over time), a dot on each series at the row whose `t` is nearest it.\n- `<iso-table data=\"f.csv\" columns=\"a b c\" limit=\"50\">`: the rows.\n- `<iso-stat data=\"f.csv\" column=\"err\" of=\"max\" label=\"worst error\" tone=\"gold\">`: one number. `of`: `min`, `max`,\n `mean`, `sum`, `count`, `first`, `last`. Tones: `gold`, `blue`, `green`, `rose`. Without `data`, set it from a\n script: `el.value = 0.0023`.\n- `<iso-chart3d data=\"f.csv\" x=\"x\" y=\"y\" z=\"z\" by=\"body\" at=\"t\">`: points or paths in 3D, turned by dragging.\n- `<iso-model src=\"part.glb\">`: a 3D model (glTF, GLB, STL, OBJ), turned by dragging. List it in isorite:uses.\n\n## Text and maths\n\n- A `<p>` in a panel is its caption. Maths is written as in a note: `$E = \\tfrac12 m v^2$` inline, `$$…$$` on a\n line of its own; the kit sets it. `<iso-math>\\frac{a}{b}</iso-math>` is a formula alone.\n- Never name the theme's colours in text (\"the lattice line\"): a chart's legend says which series is which.\n\n## Controls\n\nEach writes its value to `iso.state[name]`; elements that name it (`at=\"t\"`, `$t`) follow it.\n\n- `<iso-slider name=\"dt\" label=\"time step\" min=\"0.001\" max=\"0.05\" step=\"0.001\" value=\"0.01\" digits=\"3\">`\n- `<iso-select name=\"scale\" label=\"scale\" options=\"linear log\">`: one of several.\n- `<iso-toggle name=\"softened\" label=\"softened\" value=\"true\">`: on or off.\n- `<iso-play name=\"t\" min=\"0\" max=\"12\" speed=\"1.5\">`: plays `iso.state.t` from min to max, and pauses.\n- `<iso-note href=\"physics/softening\">softening</iso-note>`: opens a note in isorite.\n\n## Script, when markup isn't enough\n\nFor a simulation or anything computed. Put it at the end of the body.\n\n- `const rows = await iso.data(\"f.csv\")`: the rows as objects, numbers as numbers.\n- `iso.state.name`, `iso.watch((name) => …)`: the shared state; `watch` runs after any change.\n- `iso.loop((dt, t) => …)`: runs every frame (dt in seconds), paused when hidden. Returns a stop function.\n- `<iso-canvas height=\"320\" id=\"sim\">` and `document.getElementById(\"sim\").draw = (ctx, w, h, t) => { … }`: draw on\n a canvas sized to its panel; call `.redraw()` after a change. `t` is the theme: `t.text`, `t.soft`, `t.faint`,\n `t.line`, `t.lattice` (blue), `t.growth` (gold), `t.malachite` (green), `t.rose`, `t.series[i]`, `t.ui` (the font).\n- A chart of what the script computes: `chart.rows = [{ t: 0, energy: -1.3 }, …]` on an `<iso-chart>` with no\n `data`.\n- `iso.format(x)`: a number to read (3 figures, 2.3×10⁻³). `iso.tokens()`: the theme, as `t` above.\n- In CSS: `var(--text)`, `var(--text-soft)`, `var(--text-faint)`, `var(--line)`, `var(--lattice)`, `var(--growth)`,\n `var(--malachite)`, `var(--rose)`, `var(--font-ui)`, `var(--font-text)`.\n\n## Rules (write_artifact checks them, and refuses a file that breaks one)\n\n1. Every file read (`data=\"…\"`, `src=\"…\"`, `iso.data(\"…\")`) is in `uses`, and exists: call `read_data` first.\n2. Every column named is in its file, spelt as `read_data` gives it.\n3. No colour, font family or border-radius of its own: no `#hex`, `rgb(…)`, `white`, `Arial`. Use the tokens.\n4. No network: no `http…` address, no `<script src>`, no `fetch`. The kit is given; everything else is in the file.\n5. Under 200 kB: data stays in data files.\n6. No text that tells how to use it (\"drag to rotate\", \"click here\"): a label says what a thing is.\n\n## What good is\n\nEvery panel answers a question about the data, with a caption saying what to see. Numbers are read out. Axes say\nwhat they measure, with units. Controls change what is drawn at once. Nothing is decoration.\n";
349
+ REFERENCE = "# The kit (isorite kit 1)\n\nAn artifact is one HTML file: markup, perhaps a script, and a manifest in its `<head>`. isorite gives it the kit: the look (every colour, font and corner), the elements below, and `window.iso`. Write what it shows and does, never how it looks: no colour, font family or border-radius of your own.\n\n## The file\n\n```html\n<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Title</title>\n<meta name=\"description\" content=\"One sentence: what it shows, and what can be done with it.\">\n<meta name=\"isorite:kit\" content=\"1\">\n<meta name=\"isorite:uses\" content=\"runs.csv other.csv\">\n<style>/* layout only, if needed */</style>\n</head>\n<body>\n<iso-page>\n <header><h1>Title</h1><p>The question it answers, in a sentence or two.</p></header>\n <iso-grid>\n <iso-panel class=\"wide\"><h2>Name <span>a short reading</span></h2> … </iso-panel>\n <iso-panel><h2>…</h2> … </iso-panel>\n </iso-grid>\n</iso-page>\n</body>\n</html>\n```\n\n`write_artifact` fills in the title, description and manifest from its arguments, so they are always right.\n\n## Layout\n\n- `<iso-grid>`: panels in two columns (`style=\"--cols: 3\"` for three); `<iso-panel class=\"wide\">` spans the row.\n- `<iso-panel>`: its first child is `<h2>`; a `<span>` in the `h2` is a soft aside. A `<p>` in a panel is a caption: what the panel shows, in a sentence.\n- `<iso-row>`: controls side by side. `<iso-stats>`: big numbers side by side.\n\n## Data, with no script\n\nEvery element with `data=\"file.csv\"` reads it itself. Several files are one table: `data=\"euler.csv leapfrog.csv\"` adds a column `file` (`euler`, `leapfrog`) to compare them with `by=\"file\"`. `where=\"integrator=leapfrog\"` keeps some rows; `where=\"t<=$t\"` compares with a control's value. `abs(col)` is a column's size.\n\n- `<iso-chart data=\"f.csv\" x=\"t\" y=\"energy\" by=\"body\">`: a chart.\n - `type`: `line` (default), `dot`, `bar`, `area`, `step`.\n - `y` may name several columns: `y=\"kinetic potential total\"`, each its own line.\n - `by`: a column whose values are the series.\n - `x-scale`, `y-scale`: `linear` or `log`, or `$name` to follow a control (`y-scale=\"$scale\"`).\n - `x-label`, `y-label`: the axis with its unit (`\"time (s)\"`). `height` in pixels (260).\n - `at=\"t\"`: follows the control `t`. A rule where x is its value; or, when the data has a column `t` and x is another column (a path in x and y over time), a dot on each series at the row whose `t` is nearest it.\n- `<iso-table data=\"f.csv\" columns=\"a b c\" limit=\"50\">`: the rows.\n- `<iso-stat data=\"f.csv\" column=\"err\" of=\"max\" label=\"worst error\" tone=\"gold\">`: one number. `of`: `min`, `max`, `mean`, `sum`, `count`, `first`, `last`. Tones: `gold`, `blue`, `green`, `rose`. Without `data`, set it from a script: `el.value = 0.0023`.\n- `<iso-chart3d data=\"f.csv\" x=\"x\" y=\"y\" z=\"z\" by=\"body\" at=\"t\">`: points or paths in 3D, turned by dragging.\n- `<iso-model src=\"part.glb\">`: a 3D model (glTF, GLB, STL, OBJ), turned by dragging. List it in isorite:uses.\n\n## Text and maths\n\n- A `<p>` in a panel is its caption. Maths is written as in a note: `$E = \\tfrac12 m v^2$` inline, `$$…$$` on a line of its own; the kit sets it. `<iso-math>\\frac{a}{b}</iso-math>` is a formula alone.\n- Never name the theme's colours in text (\"the lattice line\"): a chart's legend says which series is which.\n\n## Controls\n\nEach writes its value to `iso.state[name]`; elements that name it (`at=\"t\"`, `$t`) follow it.\n\n- `<iso-slider name=\"dt\" label=\"time step\" min=\"0.001\" max=\"0.05\" step=\"0.001\" value=\"0.01\" digits=\"3\">`\n- `<iso-select name=\"scale\" label=\"scale\" options=\"linear log\">`: one of several.\n- `<iso-toggle name=\"softened\" label=\"softened\" value=\"true\">`: on or off.\n- `<iso-play name=\"t\" min=\"0\" max=\"12\" speed=\"1.5\">`: plays `iso.state.t` from min to max, and pauses.\n- `<iso-note href=\"physics/softening\">softening</iso-note>`: opens a note in isorite.\n\n## Script, when markup isn't enough\n\nFor a simulation or anything computed. Put it at the end of the body.\n\n- `const rows = await iso.data(\"f.csv\")`: the rows as objects, numbers as numbers.\n- `iso.state.name`, `iso.watch((name) => …)`: the shared state; `watch` runs after any change.\n- `iso.loop((dt, t) => …)`: runs every frame (dt in seconds), paused when hidden. Returns a stop function.\n- `<iso-canvas height=\"320\" id=\"sim\">` and `document.getElementById(\"sim\").draw = (ctx, w, h, t) => { … }`: draw on a canvas sized to its panel; call `.redraw()` after a change. `t` is the theme: `t.text`, `t.soft`, `t.faint`, `t.line`, `t.lattice` (blue), `t.growth` (gold), `t.malachite` (green), `t.rose`, `t.series[i]`, `t.ui` (the font).\n- A chart of what the script computes: `chart.rows = [{ t: 0, energy: -1.3 }, …]` on an `<iso-chart>` with no `data`.\n- `iso.format(x)`: a number to read (3 figures, 2.3×10⁻³). `iso.tokens()`: the theme, as `t` above.\n- In CSS: `var(--text)`, `var(--text-soft)`, `var(--text-faint)`, `var(--line)`, `var(--lattice)`, `var(--growth)`, `var(--malachite)`, `var(--rose)`, `var(--font-ui)`, `var(--font-text)`.\n\n## Rules (write_artifact checks them, and refuses a file that breaks one)\n\n1. Every file read (`data=\"…\"`, `src=\"…\"`, `iso.data(\"…\")`) is in `uses`, and exists: call `read_data` first.\n2. Every column named is in its file, spelt as `read_data` gives it.\n3. No colour, font family or border-radius of its own: no `#hex`, `rgb(…)`, `white`, `Arial`. Use the tokens.\n4. No network: no `http…` address, no `<script src>`, no `fetch`. The kit is given; everything else is in the file.\n5. Under 200 kB: data stays in data files.\n6. No text that tells how to use it (\"drag to rotate\", \"click here\"): a label says what a thing is.\n\n## What good is\n\nEvery panel answers a question about the data, with a caption saying what to see. Numbers are read out. Axes say what they measure, with units. Controls change what is drawn at once. Nothing is decoration.\n";
350
350
  RECIPES = {
351
351
  "dashboard": {
352
352
  "about": "a dashboard of one data file: numbers, charts and a table, in markup only",
@@ -1570,6 +1570,79 @@ var init_source = __esmMin((() => {
1570
1570
  };
1571
1571
  }));
1572
1572
  //#endregion
1573
+ //#region packages/core/src/unwrap.ts
1574
+ function unwrap(text) {
1575
+ const lines = text.split("\n"), out = [];
1576
+ let i = 0, fence = "", maths = false;
1577
+ /** What the last line written may be continued by: prose, a quoted line, or nothing. */
1578
+ let open = "";
1579
+ if (lines[0] === "---") {
1580
+ const end = lines.indexOf("---", 1);
1581
+ if (end > 0) {
1582
+ out.push(...lines.slice(0, end + 1));
1583
+ i = end + 1;
1584
+ }
1585
+ }
1586
+ for (; i < lines.length; i++) {
1587
+ const l = lines[i], t = l.trim();
1588
+ if (fence) {
1589
+ out.push(l);
1590
+ if (t.startsWith(fence)) fence = "";
1591
+ continue;
1592
+ }
1593
+ if (maths) {
1594
+ out.push(l);
1595
+ if (t.endsWith("$$")) maths = false;
1596
+ continue;
1597
+ }
1598
+ if (t.startsWith("```") || t.startsWith("~~~")) {
1599
+ fence = t.slice(0, 3);
1600
+ out.push(l);
1601
+ open = "";
1602
+ continue;
1603
+ }
1604
+ if (t.startsWith("$$") && !t.slice(2).includes("$$")) {
1605
+ maths = true;
1606
+ out.push(l);
1607
+ open = "";
1608
+ continue;
1609
+ }
1610
+ if (!t) {
1611
+ out.push(l);
1612
+ open = "";
1613
+ continue;
1614
+ }
1615
+ const hard = /( {2}|\\)$/.test(out.at(-1) ?? "");
1616
+ if (APART.test(l)) {
1617
+ if (open === "quote" && QUOTED.test(l) && !/^\s*>\s*([-*+]|\d+[.)]|#)/.test(l) && !hard) {
1618
+ out[out.length - 1] += " " + l.replace(/^\s*>\s?/, "").trim();
1619
+ continue;
1620
+ }
1621
+ out.push(l);
1622
+ open = QUOTED.test(l) ? "quote" : "";
1623
+ continue;
1624
+ }
1625
+ if (ITEM.test(l)) {
1626
+ out.push(l);
1627
+ open = "prose";
1628
+ continue;
1629
+ }
1630
+ if (open === "prose" && !hard) {
1631
+ out[out.length - 1] += " " + t;
1632
+ continue;
1633
+ }
1634
+ out.push(l);
1635
+ open = open === "" && /^( {4}|\t)/.test(l) ? "" : "prose";
1636
+ }
1637
+ return out.join("\n");
1638
+ }
1639
+ var ITEM, APART, QUOTED;
1640
+ var init_unwrap = __esmMin((() => {
1641
+ ITEM = /^\s*([-*+]|\d+[.)])\s+/;
1642
+ APART = /^\s*(#{1,6}\s|\||<|```|~~~|\$\$|>|---\s*$|\*\*\*\s*$|___\s*$|\[[^\]]+\]:\s)/;
1643
+ QUOTED = /^\s*>\s?\S/;
1644
+ }));
1645
+ //#endregion
1573
1646
  //#region packages/core/src/search.ts
1574
1647
  function stem(token) {
1575
1648
  for (const suffix of ["ness", "ing"]) if (token.endsWith(suffix) && token.length - suffix.length >= 4) return token.slice(0, -suffix.length);
@@ -2093,16 +2166,21 @@ function written$1(note, p, region) {
2093
2166
  lines.splice(at, 0, ...blank(at - 1) ? [] : [""], ...ins, ...blank(at) ? [] : [""]);
2094
2167
  return lines.join("\n");
2095
2168
  }
2096
- /** The journal as the timeline shows it. Your own edits in a row within a session are one moment; a proposal is
2097
- * one moment whose `what` says how it ended. Revisions are not moments. */
2169
+ /** The journal as the timeline shows it. Your own edits in a row within a session are one moment, until ten minutes
2170
+ * pass with none (LAYER_IDLE_MS); a proposal is one moment whose `what` says how it ended. Revisions are not
2171
+ * moments. */
2098
2172
  function moments(entries) {
2099
2173
  const out = [], offered = /* @__PURE__ */ new Map(), asked = /* @__PURE__ */ new Map();
2174
+ /** Lines a change put in and took out: a note's first entry, all of its text. */
2175
+ const size = (c) => c.why === "start" || !c.hunks.length && c.before === null ? [(c.text ?? "").split("\n").length, 0] : [c.hunks.reduce((n, h) => n + h.ins.length, 0), c.hunks.reduce((n, h) => n + h.del.length, 0)];
2100
2176
  for (const e of entries) {
2101
2177
  const base = {
2102
2178
  ids: [e.id],
2103
2179
  at: e.at,
2104
2180
  session: e.session,
2105
- who: e.who
2181
+ who: e.who,
2182
+ added: 0,
2183
+ removed: 0
2106
2184
  };
2107
2185
  if (e.kind === "proposal") {
2108
2186
  const m = {
@@ -2121,22 +2199,29 @@ function moments(entries) {
2121
2199
  }
2122
2200
  } else if (e.kind === "change") {
2123
2201
  const m = e.proposal ? offered.get(e.proposal) : void 0;
2202
+ const [added, removed] = size(e);
2124
2203
  if (e.why === "keep" && m) {
2125
2204
  m.what = "kept";
2126
2205
  m.ids.push(e.id);
2127
2206
  m.at = e.at;
2207
+ m.added += added;
2208
+ m.removed += removed;
2128
2209
  continue;
2129
2210
  }
2130
2211
  const last = out[out.length - 1];
2131
- if (e.why === "edit" && last && last.what === "edited" && last.who === e.who && last.session === e.session) {
2212
+ if (e.why === "edit" && last && last.what === "edited" && last.who === e.who && last.session === e.session && Date.parse(e.at) - Date.parse(last.at) <= 6e5) {
2132
2213
  last.ids.push(e.id);
2133
2214
  last.at = e.at;
2215
+ last.added += added;
2216
+ last.removed += removed;
2134
2217
  continue;
2135
2218
  }
2136
2219
  const what = e.why === "start" ? e.who === "external" ? "found" : "wrote" : e.why === "edit" ? "edited" : e.why === "restore" ? "restored" : e.why === "keep" ? "kept" : e.why === "move" ? "moved" : "changed elsewhere";
2137
2220
  out.push({
2138
2221
  ...base,
2139
2222
  what,
2223
+ added,
2224
+ removed,
2140
2225
  ...e.asked ? { asked: e.asked } : {}
2141
2226
  });
2142
2227
  }
@@ -2330,6 +2415,7 @@ var init_src$1 = __esmMin((() => {
2330
2415
  init_frontmatter();
2331
2416
  init_source();
2332
2417
  init_markdown();
2418
+ init_unwrap();
2333
2419
  init_text();
2334
2420
  init_search();
2335
2421
  init_record();
@@ -3053,7 +3139,7 @@ function parseRewrite(reply, count) {
3053
3139
  /** A passage's new text, set between the same neighbours as the old: it keeps the old one's space and line
3054
3140
  * breaks at either end, whatever the model put round what it wrote. An empty one takes the passage out. */
3055
3141
  function fitted(old, text) {
3056
- const core = text.trim();
3142
+ const core = unwrap(text.trim());
3057
3143
  if (!core) return "";
3058
3144
  return /^\s*/.exec(old)[0] + core + /\s*$/.exec(old)[0];
3059
3145
  }
@@ -3112,7 +3198,7 @@ function replyText(reply) {
3112
3198
  let text = reply.replaceAll(HERE, "").replace(/^\s*\n/, "").trimEnd();
3113
3199
  const fenced = /^```(?:markdown|md)?\n([\s\S]*)\n```$/.exec(text.trim());
3114
3200
  if (fenced) text = fenced[1];
3115
- return text.trim();
3201
+ return unwrap(text.trim());
3116
3202
  }
3117
3203
  var HERE, WHO$1, INSERT, OPEN, CLOSE, SAY_OPEN, SAY_CLOSE, REWRITE, NOTE, FORMAT, FORMAT_RULES, LOOKUP, estimate, CONVERSATION_BUDGET, NOTE_BUDGET, OLD_EXCHANGE, NO_LOOKUP, looked, LINKED_BUDGET;
3118
3204
  var init_iso = __esmMin((() => {
@@ -3207,6 +3293,8 @@ something at one place in the note they have open.`;
3207
3293
  - A picture is shown as ![what it shows](its/path.png). You can't see it: what
3208
3294
  you know of it is the words in the brackets. Keep a picture's line as it is
3209
3295
  when you rewrite around it, and never make up a picture's path.
3296
+ - A paragraph or a list item is one line, however long: do not break lines at
3297
+ a width. The editor wraps them to the pane.
3210
3298
  - Ordinary Markdown otherwise. No HTML.`;
3211
3299
  FORMAT_RULES = FORMAT;
3212
3300
  LOOKUP = (rounds, meaning) => `# Looking things up
@@ -3524,38 +3612,6 @@ var init_lookup = __esmMin((() => {
3524
3612
  };
3525
3613
  }));
3526
3614
  //#endregion
3527
- //#region packages/engine/src/understand.ts
3528
- /** The two parts of an Understanding note's file. A file in another shape is read as all scratchpad. */
3529
- function parseUnderstanding(text) {
3530
- if (!text) return {
3531
- reading: "",
3532
- scratchpad: ""
3533
- };
3534
- const body = text.replace(/^---\n[\s\S]*?\n---\n/, "");
3535
- const r = body.indexOf(READING$1), p = body.indexOf(PAD);
3536
- if (r < 0 && p < 0) return {
3537
- reading: "",
3538
- scratchpad: body.trim()
3539
- };
3540
- return {
3541
- reading: r < 0 ? "" : body.slice(r + 16, p > r ? p : void 0).trim(),
3542
- scratchpad: p < 0 ? "" : body.slice(p + 13, r > p ? r : void 0).trim()
3543
- };
3544
- }
3545
- /** An Understanding note's file, from its parts. */
3546
- function understandingText(note, title, parts) {
3547
- return `---\ntype: Understanding\ntitle: ${JSON.stringify(`Understanding · ${title}`)}\nof: ${note}\n---\n\n${READING$1}\n\n${parts.reading.trim()}\n\n${PAD}\n\n${parts.scratchpad.trim()}\n`;
3548
- }
3549
- var UNDERSTANDING_PREFIX, UNDERSTANDING_DIR, isUnderstandingId, understandingId, READING$1, PAD;
3550
- var init_understand = __esmMin((() => {
3551
- UNDERSTANDING_PREFIX = "@understanding/";
3552
- UNDERSTANDING_DIR = "understanding";
3553
- isUnderstandingId = (id) => id.startsWith(UNDERSTANDING_PREFIX);
3554
- understandingId = (note) => UNDERSTANDING_PREFIX + note;
3555
- READING$1 = "## Iso's reading";
3556
- PAD = "## Scratchpad";
3557
- }));
3558
- //#endregion
3559
3615
  //#region packages/engine/src/hand.ts
3560
3616
  /** The lines about the working set in a conversation's file, in order. Turns' own lines are passed over. */
3561
3617
  function readHand(text) {
@@ -3676,6 +3732,7 @@ function readTurns(text) {
3676
3732
  turns.set(t.id, {
3677
3733
  ...t,
3678
3734
  mode: MODES.includes(t.mode) ? t.mode : "ask",
3735
+ given: (t.given ?? []).filter((g) => GIVEN.includes(g.kind)),
3679
3736
  items: [],
3680
3737
  state: "running"
3681
3738
  });
@@ -3684,7 +3741,7 @@ function readTurns(text) {
3684
3741
  const t = turns.get(line.turn);
3685
3742
  if (!t) continue;
3686
3743
  if (line.kind === "item") {
3687
- if (ITEMS.includes(line.item.kind)) t.items.push(line.item);
3744
+ if (ITEMS.includes(line.item.kind) && !(line.item.kind === "change" && line.item.note.startsWith("@understanding/"))) t.items.push(line.item);
3688
3745
  } else if (line.kind === "answer") {
3689
3746
  for (const i of t.items) if (i.kind === "permission" && i.id === line.permission) i.answer = line.decision;
3690
3747
  } else if (line.kind === "taken") {
@@ -3701,9 +3758,9 @@ function readTurns(text) {
3701
3758
  state: "stopped"
3702
3759
  } : t).sort((a, b) => a.id < b.id ? -1 : 1);
3703
3760
  }
3704
- /** A turn's own tools: its mode's, and with an Understanding note given, the one that rewrites Iso's reading. */
3705
- function turnTools(mode, sent) {
3706
- return [...MODE_TOOLS[mode], ...sent.some((s) => s.kind === "understanding") ? [WRITE_UNDERSTANDING] : []];
3761
+ /** A turn's own tools: its mode's. */
3762
+ function turnTools(mode) {
3763
+ return MODE_TOOLS[mode];
3707
3764
  }
3708
3765
  function sentText(s) {
3709
3766
  if (s.kind === "note") {
@@ -3712,15 +3769,14 @@ function sentText(s) {
3712
3769
  }
3713
3770
  if (s.kind === "passage") return `## A passage they chose in ${s.title} (/${s.id}.md)\n\n${s.text.split("\n").map((l) => `> ${l}`).join("\n")}`;
3714
3771
  if (s.kind === "read") return `## ${s.title} (/${s.id}.md)${s.sections === "whole" ? "" : `, ${s.sections.length === 1 ? "the section" : "the sections"} ${s.sections.map((x) => `"${x}"`).join(", ")}`}\n\n${s.text}`;
3715
- if (s.kind === "folder") return `## The folder ${s.id || "at the top of the base"}: its notes\n\n${s.notes.map((n) => `- ${n.title} (/${n.id}.md)${n.description ? `: ${n.description}` : ""}`).join("\n") || "(empty)"}`;
3716
- return `## Their understanding of ${s.title} (/${s.id}.md)\n\nYour reading so far: ${s.reading || "none yet."}`;
3772
+ return `## The folder ${s.id || "at the top of the base"}: its notes\n\n${s.notes.map((n) => `- ${n.title} (/${n.id}.md)${n.description ? `: ${n.description}` : ""}`).join("\n") || "(empty)"}`;
3717
3773
  }
3718
3774
  /** An earlier turn as it is sent: in the shape `conversation()` shortens. */
3719
3775
  function turnExchange(t) {
3720
3776
  const text = t.items.filter((i) => i.kind === "text").map((i) => i.text).join("\n\n").trim();
3721
3777
  const changed = t.items.flatMap((i) => i.kind === "change" ? [`${i.why === "create" ? "made" : i.why === "delete" ? "deleted" : i.why === "move" ? "moved" : "changed"} ${i.title} (/${i.note}.md)`] : []);
3722
3778
  const asked = t.items.flatMap((i) => i.kind === "question" ? [`You asked them: ${i.title}${i.made ? ` (/${i.made}.md)` : ""}.`] : i.kind === "offer" ? [`You offered them a passage for /${i.note}.md, which they ${i.taken ? "inserted" : "have not inserted"}.`] : []);
3723
- const gave = t.given.filter((g) => g.kind !== "understanding").map((g) => g.kind === "passage" ? `a passage of /${g.id}.md` : `/${g.id}${g.kind === "note" ? ".md" : "/"}`);
3779
+ const gave = t.given.map((g) => g.kind === "passage" ? `a passage of /${g.id}.md` : `/${g.id}${g.kind === "note" ? ".md" : "/"}`);
3724
3780
  return {
3725
3781
  id: t.id,
3726
3782
  note: "",
@@ -3729,7 +3785,6 @@ function turnExchange(t) {
3729
3785
  }
3730
3786
  /** The messages for a turn, and what they hold, part by part. */
3731
3787
  function turnMessages(a) {
3732
- const understood = a.sent.filter((s) => s.kind === "understanding");
3733
3788
  const checking = a.sent.some((s) => s.kind === "note" && s.of === "problem");
3734
3789
  const system = [
3735
3790
  WHO,
@@ -3740,20 +3795,18 @@ function turnMessages(a) {
3740
3795
  ...a.mode === "write" ? [ARTIFACTS] : [],
3741
3796
  ASKING(a.mode === "write"),
3742
3797
  ...checking ? [CHECKING] : [],
3743
- ...understood.length ? [UNDERSTANDING] : [],
3744
3798
  FORMAT_RULES,
3745
3799
  LOOKING(a.mode === "write" ? Math.max(ROUNDS[a.tier], 6) : ROUNDS[a.tier], a.meaning, a.lookup)
3746
3800
  ].join("\n\n");
3747
3801
  const briefed = a.briefings?.length ? `# Earlier in this conversation, summed up\n\n${a.briefings.join("\n\n---\n\n")}` : "";
3748
3802
  const talk = a.earlier.map((t) => turnExchange(t).text).join("\n\n---\n\n");
3749
3803
  const earlier = [briefed, talk ? `# Earlier in this conversation\n\n${talk}` : ""].filter(Boolean).join("\n\n");
3750
- const given = a.sent.filter((s) => s.kind !== "understanding" && s.kind !== "read"), reads = a.sent.filter((s) => s.kind === "read");
3804
+ const given = a.sent.filter((s) => s.kind !== "read"), reads = a.sent.filter((s) => s.kind === "read");
3751
3805
  const gave = given.length ? `# What they gave you\n\n${given.map(sentText).join("\n\n")}` : "";
3752
3806
  const held = reads.length ? `# Notes you read earlier in this conversation\n\n${reads.map(sentText).join("\n\n")}` : "";
3753
3807
  const aside = a.aside?.length ? `# Read earlier, set aside\n\n${a.aside.map((n) => `- ${n.title} (/${n.id}.md)`).join("\n")}` : "";
3754
3808
  const agents = a.agents?.trim() ? `# Their instructions for this knowledge base (AGENTS.md)\n\n${a.agents.trim()}` : "";
3755
3809
  const lent = a.lookup && a.lent?.length ? `# Other knowledge bases lent to you\n\nYou may read these with the same look-up tools, giving their base; you change notes only in this one. Look in them where what is asked may be there: how the person works, their tools, what was settled before.\n\n${a.lent.map((l) => `- ${l.name}: ${l.about}`).join("\n")}` : "";
3756
- const known = understood.length ? `# Their understanding, as you have recorded it\n\n${understood.map(sentText).join("\n\n")}` : "";
3757
3810
  const broken = a.broken?.length ? `# Code links that no longer resolve\n\nSince you were last told, these links in their notes point to files no longer in their repository. Mention it only where it bears on what they ask.\n\n${a.broken.map((b) => `- in ${b.title} (/${b.id}.md): ${b.href}`).join("\n")}` : "";
3758
3811
  const request = `# What they ask now\n\n${a.asked}`;
3759
3812
  return {
@@ -3769,7 +3822,6 @@ function turnMessages(a) {
3769
3822
  agents,
3770
3823
  lent,
3771
3824
  earlier,
3772
- known,
3773
3825
  gave,
3774
3826
  held,
3775
3827
  aside,
@@ -3781,7 +3833,7 @@ function turnMessages(a) {
3781
3833
  {
3782
3834
  name: "instructions and tools",
3783
3835
  detail: `${a.mode} mode${checking ? ", checking" : ""}${a.lookup ? "" : ", no look-ups"}`,
3784
- tokens: estimate(system) + estimate(lent) + (a.lookup ? 500 : 0) + turnTools(a.mode, a.sent).length * 120
3836
+ tokens: estimate(system) + estimate(lent) + (a.lookup ? 500 : 0) + turnTools(a.mode).length * 120
3785
3837
  },
3786
3838
  {
3787
3839
  name: "earlier in this conversation",
@@ -3803,11 +3855,6 @@ function turnMessages(a) {
3803
3855
  detail: `${reads.length} in hand${a.aside?.length ? `, ${a.aside.length} set aside` : ""}`,
3804
3856
  tokens: estimate(held) + estimate(aside)
3805
3857
  }] : [],
3806
- ...understood.length ? [{
3807
- name: "your understanding",
3808
- detail: `${understood.length} ${understood.length === 1 ? "note" : "notes"}`,
3809
- tokens: estimate(known)
3810
- }] : [],
3811
3858
  ...broken ? [{
3812
3859
  name: "code links broken",
3813
3860
  detail: `${a.broken.length}`,
@@ -3821,7 +3868,7 @@ function turnMessages(a) {
3821
3868
  ]
3822
3869
  };
3823
3870
  }
3824
- var MODES, ITEMS, CONVERSATIONS_DIR, conversationPath, WHO, ISORITE, PRACTICE, LEARNING, ASK, WRITE, UNDERSTANDING, ARTIFACTS, ASKING, CHECKING, MODE_RULES, LOOKING, COMPACT$1, str, fn, POINT_TO_NOTE, EDIT_NOTE$1, WRITE_NOTE$1, MOVE_NOTE, DELETE_NOTE, WRITE_UNDERSTANDING, ASK_QUESTION, READ_DATA, ARTIFACT_RECIPE, WRITE_ARTIFACT, READ_ARTIFACT, OFFER_TEXT, MODE_TOOLS;
3871
+ var MODES, GIVEN, ITEMS, CONVERSATIONS_DIR, conversationPath, WHO, ISORITE, PRACTICE, LEARNING, ASK, WRITE, ARTIFACTS, ASKING, CHECKING, MODE_RULES, LOOKING, COMPACT$1, str, fn, POINT_TO_NOTE, EDIT_NOTE$1, WRITE_NOTE$1, MOVE_NOTE, DELETE_NOTE, ASK_QUESTION, READ_DATA, ARTIFACT_RECIPE, WRITE_ARTIFACT, READ_ARTIFACT, OFFER_TEXT, MODE_TOOLS;
3825
3872
  var init_turn = __esmMin((() => {
3826
3873
  init_iso();
3827
3874
  init_loop();
@@ -3830,6 +3877,11 @@ var init_turn = __esmMin((() => {
3830
3877
  "write",
3831
3878
  "build"
3832
3879
  ];
3880
+ GIVEN = [
3881
+ "note",
3882
+ "folder",
3883
+ "passage"
3884
+ ];
3833
3885
  ITEMS = [
3834
3886
  "text",
3835
3887
  "look",
@@ -3900,6 +3952,9 @@ It follows from [surprise](/basics/surprise.md "requires"), but how?
3900
3952
  stop. Explain once they have answered. When they ask for a fact, a check or
3901
3953
  a change, just answer.
3902
3954
  - Prefer a question back, or a problem to try, to a summary.
3955
+ - Where they stand shows in their notes: what they wrote, their open questions,
3956
+ the problems they have solved and not. Where you find it stops, offer it to
3957
+ them as a question to keep (ask_question) or a problem to try.
3903
3958
  - Check a solution; never solve it for them.`;
3904
3959
  ASK = `# Ask mode: what you may do
3905
3960
 
@@ -3913,6 +3968,9 @@ note, no folder.
3913
3968
  the notes you used, so they can open them. Where the base does not hold what
3914
3969
  is asked, answer from what you know of the subject, exactly, and say that
3915
3970
  their notes do not cover it.
3971
+ - Cite in the sentence: the phrase that rests on a note is the link to it,
3972
+ with its kind. No numbered references and no list of sources at the end;
3973
+ the notes you read are shown under your reply.
3916
3974
  - When one of their notes would help them and they may not know it, point it
3917
3975
  out with point_to_note. Use it sparingly: one or two notes.
3918
3976
  - Never invent a note, a link or a quotation.`;
@@ -3930,24 +3988,12 @@ change marked as yours, and can undo it.
3930
3988
  - A new note (write_note) is written as one of theirs: frontmatter with type
3931
3989
  and title, their notation, their level, plain words, linked to the notes it
3932
3990
  rests on.
3991
+ - Write each paragraph and each list item on one line. Do not break lines at a
3992
+ width: the editor wraps them to the pane.
3933
3993
  - Moving or deleting a note is asked of them before it happens. Do it only
3934
3994
  when they asked for it.
3935
3995
  - When you are done, say in a sentence or two what you changed and why. Do not
3936
3996
  repeat the text you wrote: they see it in the note.
3937
- - Never invent a note, a link or a quotation.`;
3938
- UNDERSTANDING = `# Their understanding
3939
-
3940
- Below, for each note in hand, is your reading of their understanding of it so
3941
- far, from its Understanding note.
3942
-
3943
- - Start from where they are: do not explain what they have shown they know; go
3944
- to what they have not yet shown, and ask about that first.
3945
- - At the end of a conversation about a note, or when your reading of them has
3946
- changed, rewrite your reading with write_understanding: a few plain
3947
- sentences on what they can do with the note and what they have not yet
3948
- shown. It is theirs to read. Rest each sentence on evidence they can open,
3949
- linked: what they wrote in a note, a question of theirs open or resolved,
3950
- a problem solved or not. Say nothing the evidence does not show.
3951
3997
  - Never invent a note, a link or a quotation.`;
3952
3998
  ARTIFACTS = `# Artifacts
3953
3999
 
@@ -4086,13 +4132,6 @@ was not said.`;
4086
4132
  }
4087
4133
  }, ["id", "to"]);
4088
4134
  DELETE_NOTE = fn("delete_note", "Delete a note. They are asked first. Its history stays.", { id: str }, ["id"]);
4089
- WRITE_UNDERSTANDING = fn("write_understanding", "Rewrite your reading of their understanding of one note: a few plain sentences on what they can do with it and what they have not yet shown, each resting on evidence linked as [their words](/id.md), [a question](/id.md) or [a problem](/id.md). Replaces the reading you had.", {
4090
- id: {
4091
- ...str,
4092
- description: "The note's id."
4093
- },
4094
- reading: str
4095
- }, ["id", "reading"]);
4096
4135
  ASK_QUESTION = fn("ask_question", "Ask them a question back, kept as an open question in their base, about one or more of their notes.", {
4097
4136
  title: {
4098
4137
  ...str,
@@ -4438,7 +4477,6 @@ var init_engine = __esmMin((() => {
4438
4477
  init_iso();
4439
4478
  init_loop();
4440
4479
  init_lookup();
4441
- init_understand();
4442
4480
  init_hand();
4443
4481
  init_turn();
4444
4482
  init_meaning();
@@ -4451,7 +4489,7 @@ var init_engine = __esmMin((() => {
4451
4489
  DRAFT_PREFIX = "@draft/";
4452
4490
  DRAFT_DIR = "drafts";
4453
4491
  isDraftId = (id) => id.startsWith(DRAFT_PREFIX);
4454
- bare = (id) => isDraftId(id) ? id.slice(7) : isUnderstandingId(id) ? id.slice(UNDERSTANDING_PREFIX.length) : id;
4492
+ bare = (id) => isDraftId(id) ? id.slice(7) : id;
4455
4493
  inBase = (id) => !id.startsWith("@");
4456
4494
  BAD_PART = /^\.|[\0\\:*?"<>|]|^\s|\s$/;
4457
4495
  TURN_ID = /^[0-9A-HJKMNP-TV-Z]{26}$/;
@@ -5192,16 +5230,19 @@ var init_engine = __esmMin((() => {
5192
5230
  };
5193
5231
  });
5194
5232
  }
5195
- /** Where a note's file is: drafts and Understanding notes are among isorite's own files. */
5233
+ /** Where a note's file is: drafts are among isorite's own files. */
5196
5234
  where(id) {
5197
5235
  if (inBase(id) && citeKind(id) === "artifact") return {
5198
5236
  files: this.opts.notes,
5199
5237
  path: checkNoteId(id)
5200
5238
  };
5201
- if (!inBase(id)) return {
5202
- files: this.opts.own,
5203
- path: `${isDraftId(id) ? DRAFT_DIR : UNDERSTANDING_DIR}/${checkNoteId(bare(id))}.md`
5204
- };
5239
+ if (!inBase(id)) {
5240
+ if (!isDraftId(id)) throw new EngineError(`there is no note ${id}`);
5241
+ return {
5242
+ files: this.opts.own,
5243
+ path: `${DRAFT_DIR}/${checkNoteId(bare(id))}.md`
5244
+ };
5245
+ }
5205
5246
  return {
5206
5247
  files: this.opts.notes,
5207
5248
  path: `${checkNoteId(id)}.md`
@@ -5950,6 +5991,32 @@ var init_engine = __esmMin((() => {
5950
5991
  } : t)
5951
5992
  };
5952
5993
  }
5994
+ async conversations() {
5995
+ const study = this.opts.own;
5996
+ const names = await study.list(CONVERSATIONS_DIR).catch(() => []);
5997
+ const all = [];
5998
+ for (const f of names) {
5999
+ const id = f.name.slice(0, -6);
6000
+ if (f.dir || !f.name.endsWith(".jsonl") || !TURN_ID.test(id)) continue;
6001
+ const turns = readTurns(await study.read(conversationPath(id)) ?? "");
6002
+ if (!turns.length) continue;
6003
+ const notes = /* @__PURE__ */ new Set();
6004
+ for (const t of turns) {
6005
+ for (const g of t.given) if (g.kind !== "folder") notes.add(g.id);
6006
+ for (const i of t.items) if (i.kind === "look" && i.step.opened && !i.step.away) notes.add(i.step.opened);
6007
+ else if (i.kind === "point" || i.kind === "change" && i.why !== "delete") notes.add(i.kind === "change" && i.to ? i.to : i.note);
6008
+ }
6009
+ all.push({
6010
+ id,
6011
+ at: turns[0].at,
6012
+ last: turns.at(-1).at,
6013
+ asked: turns[0].asked,
6014
+ turns: turns.length,
6015
+ notes: notes.size
6016
+ });
6017
+ }
6018
+ return all.sort((a, b) => a.last < b.last ? 1 : a.last > b.last ? -1 : 0);
6019
+ }
5953
6020
  /** What was given, checked: notes and passages of the base, and its folders. */
5954
6021
  given(raw) {
5955
6022
  if (!Array.isArray(raw) || raw.length > 40) throw new EngineError("give Iso between none and forty notes, folders and passages");
@@ -5967,10 +6034,6 @@ var init_engine = __esmMin((() => {
5967
6034
  kind: "note",
5968
6035
  id
5969
6036
  };
5970
- if (g?.kind === "understanding") return {
5971
- kind: "understanding",
5972
- id
5973
- };
5974
6037
  if (g?.kind === "passage" && typeof g.text === "string" && g.text.trim() && g.text.length <= 2e4) return {
5975
6038
  kind: "passage",
5976
6039
  id,
@@ -6006,16 +6069,6 @@ var init_engine = __esmMin((() => {
6006
6069
  }
6007
6070
  const c = this.bundle.concepts.get(g.id);
6008
6071
  if (!c) continue;
6009
- if (g.kind === "understanding") {
6010
- const u = await this.understanding(g.id);
6011
- out.push({
6012
- kind: "understanding",
6013
- id: g.id,
6014
- title: c.title,
6015
- reading: u.reading
6016
- });
6017
- continue;
6018
- }
6019
6072
  if (g.kind === "passage") out.push({
6020
6073
  kind: "passage",
6021
6074
  id: g.id,
@@ -6146,7 +6199,7 @@ var init_engine = __esmMin((() => {
6146
6199
  group: "notes",
6147
6200
  kind: g.kind,
6148
6201
  title: g.kind === "folder" ? g.id || "the top of the base" : title(g.id),
6149
- detail: g.kind === "passage" ? "a passage" : g.kind === "folder" ? `${s.kind === "folder" ? s.notes.length : 0} notes listed` : g.kind === "understanding" ? "your understanding" : "",
6202
+ detail: g.kind === "passage" ? "a passage" : g.kind === "folder" ? `${s.kind === "folder" ? s.notes.length : 0} notes listed` : "",
6150
6203
  tokens: scaled(estimate(sentText(s))),
6151
6204
  on: !w.off.has(k),
6152
6205
  by: "you",
@@ -6436,7 +6489,7 @@ var init_engine = __esmMin((() => {
6436
6489
  const big = [...w.built.parts].sort((a, b) => b.tokens - a.tokens)[0], k = (n) => `${Math.round(n / 1e3)}k`;
6437
6490
  throw new EngineError(`This turn would be sent about ${k(size())} tokens, more than the ${k(limit)} the model takes. The largest part is ${big.name} (${k(w.scaled(big.tokens))}): switch parts off in the Context view.`, 413);
6438
6491
  }
6439
- const sent = [...w.sent, ...w.reads], own = turnTools(mode, sent);
6492
+ const sent = [...w.sent, ...w.reads], own = turnTools(mode);
6440
6493
  const inHand = new Map(w.reads.flatMap((r) => r.kind === "read" ? [[r.id, r.sections]] : []));
6441
6494
  for (const g of w.givenOn) if (g.kind === "note") inHand.set(g.id, "whole");
6442
6495
  const { messages } = turnMessages({
@@ -6724,7 +6777,7 @@ var init_engine = __esmMin((() => {
6724
6777
  if (name === "edit_note") {
6725
6778
  const id = noteId(this.bundle, args.id);
6726
6779
  if (!id) return `There is no note ${String(args.id ?? "")}. Search for it by its words.`;
6727
- const old = String(args.old ?? ""), next = String(args.new ?? "");
6780
+ const old = String(args.old ?? ""), next = unwrap(String(args.new ?? ""));
6728
6781
  if (!old) return "Give the passage as it is now, in old.";
6729
6782
  const now = await this.readNote(id);
6730
6783
  const at = now.text.indexOf(old), again = at < 0 ? -1 : now.text.indexOf(old, at + 1);
@@ -6745,7 +6798,7 @@ var init_engine = __esmMin((() => {
6745
6798
  if (name === "write_note") {
6746
6799
  const id = this.newNoteId(args.id);
6747
6800
  if (!id) return `${String(args.id ?? "")} is not a note's id in their base: give folder/name, lower case with hyphens.`;
6748
- let text = String(args.text ?? "");
6801
+ let text = unwrap(String(args.text ?? ""));
6749
6802
  if (!text.trim()) return "Give the note's whole text.";
6750
6803
  if (!/^---\n/.test(text)) text = `---\ntype: Concept\ntitle: ${JSON.stringify(/^#\s+(.+)$/m.exec(text)?.[1]?.trim() ?? id.split("/").at(-1).replace(/-/g, " "))}\n---\n\n${text.replace(/^#\s+.+\n+/, "")}`;
6751
6804
  const was = this.bundle.concepts.has(id) ? await this.readNote(id) : null;
@@ -6851,28 +6904,6 @@ var init_engine = __esmMin((() => {
6851
6904
  });
6852
6905
  return `It is offered to them for ${agents ? "their AGENTS.md" : title(id)}.`;
6853
6906
  }
6854
- if (name === "write_understanding") {
6855
- const id = noteId(this.bundle, args.id);
6856
- if (!id) return `There is no note ${String(args.id ?? "")}.`;
6857
- const reading = String(args.reading ?? "").trim();
6858
- if (!reading) return "Give your reading.";
6859
- const uid = understandingId(id), was = await this.readNote(uid).catch(() => null);
6860
- const text = understandingText(id, title(id), {
6861
- ...parseUnderstanding(was?.text ?? null),
6862
- reading
6863
- });
6864
- const saved = await this.one(() => this.put(uid, text, was?.version ?? null, "edit", {}, t.who));
6865
- if (saved.entry) await t.item({
6866
- kind: "change",
6867
- note: uid,
6868
- entry: saved.entry,
6869
- why: was ? "edit" : "create",
6870
- title: `Understanding · ${title(id)}`,
6871
- added: 0,
6872
- removed: 0
6873
- });
6874
- return `Your reading of ${title(id)} is written.`;
6875
- }
6876
6907
  if (name === "read_data") {
6877
6908
  const path = filePathOf(args.path);
6878
6909
  if (!path) return `${String(args.path ?? "")} is not a file's path in their base.`;
@@ -6968,29 +6999,6 @@ var init_engine = __esmMin((() => {
6968
6999
  ];
6969
7000
  return `${was === null ? "Written" : "Written anew"}: ${path}. ${run ? "It was run, with every control touched" : "It couldn't be run here (no browser), so only its rules were checked"}${problems.length ? `, and:\n${problems.map((p) => `- ${p}`).join("\n")}\nFix these and call write_artifact again with the whole file.` : run ? ", with no error." : "."}`;
6970
7001
  }
6971
- /** A note's Understanding note (M10): Iso's reading and your scratchpad. */
6972
- async understanding(note) {
6973
- if (!inBase(checkNoteId(note))) throw new EngineError("only a note of the base has an Understanding note");
6974
- const uid = understandingId(note);
6975
- const source = await this.readNote(uid).catch(() => null);
6976
- return {
6977
- note,
6978
- ...parseUnderstanding(source?.text ?? null),
6979
- version: source?.version ?? null
6980
- };
6981
- }
6982
- /** Your scratchpad in a note's Understanding note: yours alone; Iso does not write it. `base` as for saveNote, or
6983
- * null when there is no Understanding note yet. */
6984
- async saveScratchpad(note, scratchpad, base) {
6985
- if (!inBase(checkNoteId(note))) throw new EngineError("only a note of the base has an Understanding note");
6986
- const uid = understandingId(note), was = await this.readNote(uid).catch(() => null);
6987
- const title = this.bundle.concepts.get(note)?.title ?? note;
6988
- await this.one(() => this.put(uid, understandingText(note, title, {
6989
- ...parseUnderstanding(was?.text ?? null),
6990
- scratchpad
6991
- }), base));
6992
- return this.understanding(note);
6993
- }
6994
7002
  /** A question asked in a turn, made a question note: in the folder of the first note it is about, named by its
6995
7003
  * title. */
6996
7004
  async makeQuestion(q, who, asked) {
@@ -7426,7 +7434,6 @@ var init_src = __esmMin((() => {
7426
7434
  init_iso();
7427
7435
  init_meaning();
7428
7436
  init_agent();
7429
- init_understand();
7430
7437
  init_turn();
7431
7438
  }));
7432
7439
  //#endregion
@@ -8609,7 +8616,7 @@ function toTurns(messages, { live = /* @__PURE__ */ new Map(), ours = /* @__PURE
8609
8616
  /** What is asked from a page, with what was given in the Iso pane's context: notes and folders by name, for the
8610
8617
  * agent to read with isorite's tools, and passages as they are. */
8611
8618
  function given(asked, parts, title) {
8612
- const notes = parts.filter((p) => p.kind === "note" || p.kind === "understanding").map((p) => `- ${title(p.id)} (${p.id})`);
8619
+ const notes = parts.filter((p) => p.kind === "note").map((p) => `- ${title(p.id)} (${p.id})`);
8613
8620
  const folders = parts.filter((p) => p.kind === "folder").map((p) => `- the folder ${p.id}`);
8614
8621
  const passages = parts.flatMap((p) => p.kind === "passage" ? [`From ${title(p.id)} (${p.id}):\n${p.text.split("\n").map((l) => "> " + l).join("\n")}`] : []);
8615
8622
  const out = [asked.trim()];
@@ -8933,10 +8940,6 @@ const Given = z.discriminatedUnion("kind", [
8933
8940
  kind: z.literal("passage"),
8934
8941
  id: z.string().max(400),
8935
8942
  text: z.string().max(2e4)
8936
- }).strict(),
8937
- z.object({
8938
- kind: z.literal("understanding"),
8939
- id: z.string().max(400)
8940
8943
  }).strict()
8941
8944
  ]);
8942
8945
  const Mode = z.enum([
@@ -9154,11 +9157,6 @@ function createApp({ api, token, loopback, web, allowHosts = [], notes, terminal
9154
9157
  });
9155
9158
  post("/api/iso/ask", Ask, (_c, b) => api.ask(b));
9156
9159
  post("/api/iso/converse", TurnAsk, (_c, b) => api.converse(b));
9157
- app.get("/api/understanding", (c) => answer(c, () => api.understanding(id(c))));
9158
- post("/api/understanding/scratchpad", z.object({
9159
- scratchpad: z.string().max(2e5),
9160
- base: z.string().max(64).nullable()
9161
- }).strict(), (c, b) => api.saveScratchpad(id(c), b.scratchpad, b.base));
9162
9160
  post("/api/iso/take", z.object({
9163
9161
  conversation: z.string().max(64),
9164
9162
  turn: z.string().max(64),
@@ -9189,6 +9187,7 @@ function createApp({ api, token, loopback, web, allowHosts = [], notes, terminal
9189
9187
  "reject"
9190
9188
  ])
9191
9189
  }).strict(), (_c, b) => api.answer(b.turn, b.permission, b.decision));
9190
+ app.get("/api/conversations", (c) => answer(c, () => api.conversations()));
9192
9191
  app.get("/api/conversation", (c) => answer(c, async () => await api.conversation(c.req.query("id") || void 0) ?? {
9193
9192
  id: "",
9194
9193
  turns: []
@@ -9779,7 +9778,7 @@ var Setup = class {
9779
9778
  };
9780
9779
  //#endregion
9781
9780
  //#region packages/server/src/version.ts
9782
- const VERSION = "0.11.0";
9781
+ const VERSION = "0.12.1";
9783
9782
  //#endregion
9784
9783
  //#region packages/server/src/git.ts
9785
9784
  /** The top of the repository `folder` is in, or null: the nearest folder holding `.git`, a directory or, in a
@@ -11869,7 +11868,7 @@ const ISO_QUESTIONS = {
11869
11868
  about: [TRIANGLE]
11870
11869
  }
11871
11870
  };
11872
- /** Iso's reading of you on Gauss–Bonnet, each sentence resting on what you can open. */
11871
+ /** Iso's answer to where you stand on Gauss–Bonnet, each sentence resting on what you can open. */
11873
11872
  const READING = `You can state it and use it: the [examples](/${BONNET}.md) are yours, and you [solved the torus](/${TORUS}.md). [The proof sketch](/${BONNET}.md) is still mine, untouched: you have not yet said it in your own words. [Where compactness is used](/${COMPACT}.md) is open, and so is [the triangle](/${TRIANGLE}.md), whose solution leans on a step not yet shown.`;
11874
11873
  const anchor = (text) => ({
11875
11874
  text,
@@ -12118,7 +12117,6 @@ async function buildGeometry(sources, code, now = Date.now()) {
12118
12117
  await write(CYLINDER, final(CYLINDER));
12119
12118
  await engine.setDone(CYLINDER, true, (await engine.readNote(CYLINDER)).version);
12120
12119
  });
12121
- on(1, 55, async () => engine.saveScratchpad(BONNET, "Compact: I think it is only that the triangulation is finite. Check against the Möbius band, which is compact and still fails, so orientation must be a separate matter.", null));
12122
12120
  on(0, -40, async () => {
12123
12121
  replies.push({
12124
12122
  calls: [{
@@ -12134,34 +12132,20 @@ async function buildGeometry(sources, code, now = Date.now()) {
12134
12132
  usd: .0071
12135
12133
  }
12136
12134
  }, {
12137
- calls: [{
12138
- name: "write_understanding",
12139
- arguments: {
12140
- id: BONNET,
12141
- reading: READING
12142
- }
12143
- }],
12135
+ text: READING,
12144
12136
  usage: {
12145
12137
  in: 7200,
12146
12138
  out: 150,
12147
12139
  cached: 4200,
12148
12140
  usd: .0103
12149
12141
  }
12150
- }, {
12151
- text: "I have written my reading of you on Gauss–Bonnet.",
12152
- usage: {
12153
- in: 7500,
12154
- out: 20,
12155
- cached: 7e3,
12156
- usd: .0037
12157
- }
12158
12142
  });
12159
12143
  const { turn: id } = await engine.converse({
12160
12144
  mode: "ask",
12161
12145
  asked: "where do I stand on Gauss–Bonnet?",
12162
12146
  tier: "mid",
12163
12147
  given: [{
12164
- kind: "understanding",
12148
+ kind: "note",
12165
12149
  id: BONNET
12166
12150
  }]
12167
12151
  });