@bendyline/gezel-service 0.1.0 → 1.0.0

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 (108) hide show
  1. package/dist/NOTICE.md +1 -1
  2. package/dist/bin/gezeld.js +51635 -50059
  3. package/dist/handboek-content/README.md +24 -2
  4. package/dist/handboek-content/conceptual/connected-apps.md +2 -0
  5. package/dist/handboek-content/technical/building-connected-apps-with-gezel-app-sdk.md +144 -0
  6. package/dist/handboek-content/technical/cli-reference.md +11 -0
  7. package/dist/handboek-content/technical/how-we-test-models.md +57 -9
  8. package/dist/handboek-content/technical/model-scorecard.md +27 -9
  9. package/dist/handboek-content/technical/npm-packages.md +67 -0
  10. package/dist/handboek-content/technical/security-model.md +18 -1
  11. package/dist/handboek-content/technical/where-files-live.md +3 -0
  12. package/dist/handboek-content/technical/writing-scripts-with-gezel-sdk.md +106 -0
  13. package/dist/handboek-content/whats-new/1.26223.md +82 -0
  14. package/dist/handboek-content/whats-new/whats-new-index.md +28 -0
  15. package/dist/handboek.d.ts +53 -26
  16. package/dist/handboek.js +103 -26
  17. package/dist/index-store/static-index-worker.js +476 -82
  18. package/dist/index.d.ts +206 -11
  19. package/dist/index.js +51615 -50051
  20. package/dist/ui/assets/{TerminalCodeEditor-CB-AXtKI.js → TerminalCodeEditor-DQICIlaI.js} +2 -2
  21. package/dist/ui/assets/{_.contribution-C6KlZ1yR.js → _.contribution-BXBGjh98.js} +1 -1
  22. package/dist/ui/assets/{chunk-6N2J7C2B-DIF7DJtk.js → chunk-6N2J7C2B-Du7clJkV.js} +1 -1
  23. package/dist/ui/assets/chunk-ILCJ3WFD-fHFoZ9wm.js +4 -0
  24. package/dist/ui/assets/{chunk-S5PCVMKU-CmFF6ff9.js → chunk-S5PCVMKU-DopU3Q9j.js} +2 -2
  25. package/dist/ui/assets/chunk-USU6HTKB-B-ElCUQS.js +1 -0
  26. package/dist/ui/assets/{chunk-WATAOG4Y-perfmDXF.js → chunk-WATAOG4Y-BnCgSncI.js} +1 -1
  27. package/dist/ui/assets/{cpp.contribution-BBBWN3Dm.js → cpp.contribution-UC-hXlnG.js} +1 -1
  28. package/dist/ui/assets/{csharp.contribution-jBA6TWhG.js → csharp.contribution-VSLdDbxa.js} +1 -1
  29. package/dist/ui/assets/{css.contribution-DQSNU6vC.js → css.contribution-DC9uQgZx.js} +1 -1
  30. package/dist/ui/assets/{cssMode-68UP_lrS.js → cssMode-DZsXawIN.js} +1 -1
  31. package/dist/ui/assets/{csv-Ctux8fO7.js → csv-RAgECoUg.js} +1 -1
  32. package/dist/ui/assets/dist-D6I_6t8i.js +195 -0
  33. package/dist/ui/assets/dist-a57vyaRu.js +2 -0
  34. package/dist/ui/assets/{doc-BdMAKGDm.js → doc-B7NXSLxH.js} +33 -23
  35. package/dist/ui/assets/{dockerfile.contribution-CSpSPKgK.js → dockerfile.contribution-PUE2MRJF.js} +1 -1
  36. package/dist/ui/assets/docx-IZY6hX8A.js +2 -0
  37. package/dist/ui/assets/{epub-oLed1tAU.js → epub-00L2M424.js} +1 -1
  38. package/dist/ui/assets/extract-UTAOG7LV-BZ_L7lLL.js +1 -0
  39. package/dist/ui/assets/{go.contribution-BjibqVfF.js → go.contribution-CsrBAk1W.js} +1 -1
  40. package/dist/ui/assets/{handlebars-Dq5dJ5WJ.js → handlebars-Bqyi6xyi.js} +1 -1
  41. package/dist/ui/assets/handlebars.contribution-C6VLDI51.js +2 -0
  42. package/dist/ui/assets/{html-fQ6X2xaa.js → html-Cgejr9YM.js} +24 -24
  43. package/dist/ui/assets/{html-Bgyk9kiZ.js → html-NLb64cMt.js} +1 -1
  44. package/dist/ui/assets/html.contribution-BP2a6izy.js +2 -0
  45. package/dist/ui/assets/{htmlMode-D5UKInqK.js → htmlMode-Utzysyes.js} +1 -1
  46. package/dist/ui/assets/{index-CPXp6BEZ.js → index-B_NFDPxp.js} +197 -195
  47. package/dist/ui/assets/index-CgP6VwNS.css +1 -0
  48. package/dist/ui/assets/infer-v-bCXeT9.js +2 -0
  49. package/dist/ui/assets/{ini.contribution-BiJ4T5K-.js → ini.contribution-CfehzoVj.js} +1 -1
  50. package/dist/ui/assets/{java.contribution-B8o1lBqP.js → java.contribution-CXk8bwAp.js} +1 -1
  51. package/dist/ui/assets/{javascript-COWkS1tr.js → javascript-BcbxETop.js} +1 -1
  52. package/dist/ui/assets/javascript.contribution-BRDuHf7J.js +2 -0
  53. package/dist/ui/assets/{jsonMode-CWWZvfzr.js → jsonMode-BORbF5qz.js} +1 -1
  54. package/dist/ui/assets/{kotlin.contribution-CxSOPQ6P.js → kotlin.contribution-CbKeqa_3.js} +1 -1
  55. package/dist/ui/assets/layouts-2A7QNF6Y-D94iKLLQ.js +1 -0
  56. package/dist/ui/assets/{less.contribution-Cthyflev.js → less.contribution-BgNGBZAo.js} +1 -1
  57. package/dist/ui/assets/{lua.contribution-894DRgPR.js → lua.contribution-CtZNsqOj.js} +1 -1
  58. package/dist/ui/assets/{markdown.contribution-d_TBTsNA.js → markdown.contribution-CNyxmHy1.js} +1 -1
  59. package/dist/ui/assets/{monaco-PebRhLpo.js → monaco-Ryh-SF_b.js} +3 -3
  60. package/dist/ui/assets/{monaco-base-4S0M5BiY.js → monaco-base-C-4W0je-.js} +1 -1
  61. package/dist/ui/assets/{monaco-setup-7hp0Z9jn.js → monaco-setup-BW0DQ8MV.js} +1 -1
  62. package/dist/ui/assets/{monaco.contribution-DQjPk7Rx.js → monaco.contribution-279M9-Uu.js} +2 -2
  63. package/dist/ui/assets/{monaco.contribution-CKDvyGVE.js → monaco.contribution-Bu1oyef5.js} +2 -2
  64. package/dist/ui/assets/{monaco.contribution-BL_zgiu2.js → monaco.contribution-C57BAnnI.js} +2 -2
  65. package/dist/ui/assets/{monaco.contribution-D61txvUB.js → monaco.contribution-o3cuP4Wp.js} +2 -2
  66. package/dist/ui/assets/{pdf-CwJ6ZJmB.js → pdf-66KslaXf.js} +1 -1
  67. package/dist/ui/assets/{php.contribution-B8kn8QoR.js → php.contribution-BulxzIpe.js} +1 -1
  68. package/dist/ui/assets/pptx-CsdVR0xn.js +6 -0
  69. package/dist/ui/assets/{python-CHygH1eM.js → python-D4zGC71G.js} +1 -1
  70. package/dist/ui/assets/python.contribution-CDI1t73R.js +2 -0
  71. package/dist/ui/assets/{ruby.contribution-BQdPKFM2.js → ruby.contribution-DVOdhLt8.js} +1 -1
  72. package/dist/ui/assets/{rust.contribution-D7wKqShc.js → rust.contribution-DLR9kg2m.js} +1 -1
  73. package/dist/ui/assets/{scss.contribution-kSTLMoty.js → scss.contribution-CYIIxBxH.js} +1 -1
  74. package/dist/ui/assets/{shell.contribution-BQnT3BBJ.js → shell.contribution-D0KjHQ5E.js} +1 -1
  75. package/dist/ui/assets/{sql.contribution-C9z55nWb.js → sql.contribution-CKhLelqr.js} +1 -1
  76. package/dist/ui/assets/{swift.contribution-BZoW5ZZj.js → swift.contribution-DiAAMfJp.js} +1 -1
  77. package/dist/ui/assets/{terminal-monaco-setup-DbSXji0d.js → terminal-monaco-setup-Dhxpm7Z8.js} +1 -1
  78. package/dist/ui/assets/{tsMode-jPpKNC6E.js → tsMode-DFEqg3P9.js} +1 -1
  79. package/dist/ui/assets/{typescript-Djn_8BHF.js → typescript-X_x8S3Dp.js} +1 -1
  80. package/dist/ui/assets/typescript.contribution-jI3BakLa.js +2 -0
  81. package/dist/ui/assets/xlsx-CdLmEC42.js +4 -0
  82. package/dist/ui/assets/{xml-DmE4Aqa-.js → xml-ANmRi8ZZ.js} +1 -1
  83. package/dist/ui/assets/xml.contribution-D5t83zAi.js +2 -0
  84. package/dist/ui/assets/{yaml-BjmK3C-c.js → yaml-R4WpJved.js} +1 -1
  85. package/dist/ui/assets/yaml.contribution-DV8dAjKa.js +2 -0
  86. package/dist/ui/index.html +15 -15
  87. package/package.json +13 -15
  88. package/dist/postinstall.d.ts +0 -2
  89. package/dist/postinstall.js +0 -40
  90. package/dist/ui/assets/chunk-ILCJ3WFD-uwKSpQd8.js +0 -4
  91. package/dist/ui/assets/chunk-USU6HTKB-hbaA8KyS.js +0 -1
  92. package/dist/ui/assets/dist-i9jV8CX8.js +0 -2
  93. package/dist/ui/assets/dist-jh33rACm.js +0 -195
  94. package/dist/ui/assets/docx-CNW-xqHT.js +0 -2
  95. package/dist/ui/assets/extract-UTAOG7LV-WxmEnrrc.js +0 -1
  96. package/dist/ui/assets/handlebars.contribution-DDSQBNPu.js +0 -2
  97. package/dist/ui/assets/html.contribution-DrClfj2L.js +0 -2
  98. package/dist/ui/assets/index-Caz-FRQy.css +0 -1
  99. package/dist/ui/assets/infer-Cc7ks6NX.js +0 -2
  100. package/dist/ui/assets/javascript.contribution-DMEzq0y4.js +0 -2
  101. package/dist/ui/assets/layouts-2A7QNF6Y-DR7-KboS.js +0 -1
  102. package/dist/ui/assets/pptx-Bmj2v9CT.js +0 -6
  103. package/dist/ui/assets/python.contribution-CmwCxp-v.js +0 -2
  104. package/dist/ui/assets/typescript.contribution-D4JmB9tz.js +0 -2
  105. package/dist/ui/assets/xlsx-BCnO44v8.js +0 -4
  106. package/dist/ui/assets/xml.contribution-BepqhMeK.js +0 -2
  107. package/dist/ui/assets/yaml.contribution-BwT8qEVt.js +0 -2
  108. package/postinstall.mjs +0 -8
@@ -0,0 +1,82 @@
1
+ ---
2
+ id: whats-new/1.26223
3
+ title: 1.26223 — 11 August 2026
4
+ order: -26223
5
+ summary: Measured model scores, per-model memory controls, editable Office documents, and much stricter rules about what a gezel may install.
6
+ ---
7
+
8
+ # 1.26223 — 11 August 2026
9
+
10
+ This is the first release with written release notes, so it covers everything since **1.26219** on 7 August. It is a big one: four days of work, and most of it is visible the moment you open the app.
11
+
12
+ If you read only one thing: gezel now publishes **measured scores** for the models it ships, your gezellen are much more tightly fenced in about what they may install on your machine, and Word, PowerPoint, Excel and PDF files in a project can now be edited as ordinary text.
13
+
14
+ ## Model scores you can actually check
15
+
16
+ Choosing a local model used to mean guessing. Now there is a scoreboard.
17
+
18
+ The new **Model scorecard** in the Technical section of this Handboek lists every model we have tested and how many jobs it actually finished correctly — writing a small working program, fixing a bug from its symptoms, following a procedure and stopping when something is wrong, turning a pile of documents into one summary, and a set of everyday office and knowledge tasks. Each job is run three times on a real machine and checked by a program, not by an opinion. A companion article, **How we test models**, explains what was counted and what the numbers do not tell you.
19
+
20
+ The first published set covers four models measured on an M4 Max: gemma4-e4b, gemma4-12b, gemma4-31b and ornith-9b, on both task sets. Alongside the pass counts you now also get speed, how much memory a model needed in practice, and a separate quality read from a judging pass.
21
+
22
+ Two rules keep the board honest. A model that was only partly measured is **withheld** rather than shown with a flattering gap, and earlier rounds of testing are kept visible separately instead of being quietly overwritten.
23
+
24
+ ## Your gezellen are on a shorter leash
25
+
26
+ A gezel that can install software is useful. A gezel that can install *anything, anywhere* is not something you should have to think about. This release closes that gap.
27
+
28
+ - **Installing packages is now registry-only and tied to one project.** A gezel can ask for a published package by name and version, into a project that already exists, and nothing else. Names, versions and destinations are all checked before anything runs.
29
+ - **Approving a command now approves that exact command.** Previously, approving a script meant approving the words in it. Now gezel takes a fingerprint of the actual files the command would read — the script, its wrapper, the files it imports — so an approval you gave yesterday cannot be reused tomorrow for a script whose contents have changed underneath it.
30
+ - **Names are checked everywhere.** Gezel, project, session and document identifiers now go through one shared rule, applied on disk, in the schemas, and on every web request, so a hand-crafted name cannot walk out of the folder it belongs in.
31
+ - **On Linux and macOS, gezel's home folder is now locked to your account** (`0700`). Other accounts on the same machine can no longer read your conversations or files.
32
+
33
+ None of this changes anything you do day to day. It changes what happens when something goes wrong.
34
+
35
+ ## Word, PowerPoint, Excel and PDF files are editable now
36
+
37
+ Open a project and select a `.docx`, `.pptx`, `.xlsx`, `.pdf` or `.html` file, and gezel shows you its contents as clean Markdown you can read and search. The original file stays exactly where it was.
38
+
39
+ Editing is deliberately a second step. The imported text is read-only until you choose **Allow editing via markdown** from the file's menu, and the first time you do, gezel takes a copy of the untouched original and keeps it. It is opt-in because rewriting someone's PowerPoint should never be something that happens by accident.
40
+
41
+ ## Real control over local models and memory
42
+
43
+ If you run models on your own machine, this is the release where the machine stops being a mystery.
44
+
45
+ - **Set the context size per model.** A slider in each model manager, instead of one setting for everything.
46
+ - **Set a memory budget**, decide whether a model may **spill into ordinary RAM** when it does not fit on the graphics card, and have models **unload themselves when idle** so a forgotten model stops holding your machine hostage.
47
+ - **An emergency stop.** The engine pill now has a "stop everything" action that cancels every chat in flight, clears anything queued, and drops gezel back into reactive mode — one place to press when something has run away from you.
48
+ - Behind those controls, gezel is better at fitting a model into the memory you actually have: it will trade parallel capacity down to two lanes when doing so buys a meaningfully larger context, rather than refusing or thrashing.
49
+
50
+ ## Threads that are easier to follow
51
+
52
+ - **Thread titles are derived the same way everywhere.** The name you see in the sidebar, in search and in the thread list is now the same name, worked out once instead of three times slightly differently.
53
+ - **Recent threads appear as pills** above the conversation, so hopping between the two or three things you have going takes one click and puts you back where you were looking. Threads that are working, or that hit an error, are shown first — you should not have to go hunting for the one that needs you.
54
+ - **Terminal threads show their last command** in the list, so you can tell them apart at a glance.
55
+ - Chat references are more compact, the file tree and sidebar behave more predictably, and the Handboek's own table of contents now collapses by section, with craftbooks grouped into shelves rather than one long list.
56
+
57
+ ## Connecting gezel to other things
58
+
59
+ - **Codex CLI can now use your local models.** A setup card in Settings writes a dedicated profile for the OpenAI Codex command-line tool that points it at gezel, so a coding tool you already use can run against a model on your own machine.
60
+ - **New GitHub connectors** bring pull requests, releases and wiki pages in as project material, alongside the existing Google Calendar connector. Connector output now lands in the project's artifacts, where the rest of your work lives.
61
+ - **The OpenAI-compatible endpoint speaks the Responses API**, including streaming — so apps built against the newer OpenAI shape work against gezel without a translation layer of their own.
62
+ - Four new Handboek articles document all of this for developers: the npm package map, writing scripts with the gezel SDK, building connected apps with the app SDK, and how we test models.
63
+
64
+ ## Catalog updates without an app update
65
+
66
+ Gezel's descriptions of models, craftbooks and roles — the *gilde* content — can now be refreshed on its own, without waiting for a new version of the app. It checks about once a day, verifies what it downloads, and only switches over after confirming that everything that worked before still works.
67
+
68
+ It is **off unless you turn it on**, under Settings → About → Catalog content, and it will never touch a machine that has been pointed at a local content folder.
69
+
70
+ ## Smaller things
71
+
72
+ - Windows machines without a working CUDA setup no longer get probed for one.
73
+ - The Nemotron model family is recognized under its aliases and given the right tool format.
74
+ - Installing gezel from npm no longer runs any install-time scripts; a permission repair that used to happen at install time now happens once, lazily, the first time it is needed.
75
+ - Refreshed underlying editor and content packages (squisq 2.7.1, gilde 0.1.21).
76
+ - A large set of new automated tests covering everyday office work — meeting follow-ups, research briefs, document round-trips, and whether a gezel reaches for the right tool — so regressions in ordinary tasks get caught before you meet them.
77
+
78
+ ## Anything to do?
79
+
80
+ No. Update when you are offered it. Your gezellen, projects, threads and files are ordinary files on your disk and are read as-is by the new version.
81
+
82
+ If you run local models and have been fighting for memory, it is worth a visit to the model manager for the new per-model context and memory settings. And if you want to see which model to trust with what, start at the [Model scorecard](../technical/model-scorecard.md).
@@ -0,0 +1,28 @@
1
+ ---
2
+ id: whats-new-index
3
+ title: What's new
4
+ order: -999999
5
+ summary: Every gezel release, in plain language — what changed and what it means for you.
6
+ ---
7
+
8
+ # What's new
9
+
10
+ Gezel changes often. This section is the running record of those changes, one article per release, newest first.
11
+
12
+ ## Recent releases
13
+
14
+ ::handboek-whats-new-list
15
+
16
+ Each article is written for the person using gezel, not for the person who wrote the code. It says what is different now, what you can do that you could not do before, and what — if anything — you need to do about it. Where a change is only interesting to developers, it is marked as such so you can skip it.
17
+
18
+ ## How to read a version number
19
+
20
+ A gezel version looks like **1.26223.47**. The middle part is the date: `26` is the year 2026, and `223` is the day of that year — so `26223` is 11 August 2026. The last part is the build number for that day. You can always tell how old your copy of gezel is by reading the middle number.
21
+
22
+ Your installed version is shown in **Settings → About**.
23
+
24
+ ## Updating
25
+
26
+ Packaged installs check for new releases on their own and offer the update when one is ready. Nothing in your work is touched by an update: your gezellen, projects, threads, memories and files all live in ordinary files on your disk, and a new version simply reads the same files.
27
+
28
+ Separately from app releases, gezel can pick up new catalog content — model descriptions, craftbooks, role templates — without a full update. That is the **Catalog content** setting under Settings → About, and it is off unless you turn it on.
@@ -1,4 +1,4 @@
1
- import { Poppetje, ModelTier, CatalogKind, CatalogItemSummary, CatalogItemDetail, HandboekRenderMode, HandboekFigure, HandboekArea, HandboekToc, HandboekArticle } from '@bendyline/gezel';
1
+ import { Poppetje, ModelTier, HandboekArea, CatalogKind, CatalogItemSummary, CatalogItemDetail, HandboekRenderMode, HandboekFigure, HandboekToc, HandboekArticle } from '@bendyline/gezel';
2
2
 
3
3
  /**
4
4
  * What the handboek engine may know about *this install* when rendering
@@ -37,6 +37,51 @@ interface HandboekDeviceInfo {
37
37
  /** Device info for `site`/`agent-less` contexts: nothing personal to tell. */
38
38
  declare const siteDeviceInfo: HandboekDeviceInfo;
39
39
 
40
+ /** Area folder order — also the TOC order. */
41
+ declare const HANDBOEK_AREAS: readonly HandboekArea[];
42
+ declare const HANDBOEK_AREA_TITLES: Record<HandboekArea, string>;
43
+ /**
44
+ * The `whats-new` section index. Excluded from its own release list, and
45
+ * the one article in that area whose `order` is not a negated calendar
46
+ * line — see `docs/handboek/README.md`.
47
+ */
48
+ declare const WHATS_NEW_INDEX_ID = "whats-new-index";
49
+ /** One release, as the `whats-new-list` macro renders it. */
50
+ interface ReleaseNoteEntry {
51
+ /** Article id — link targets resolve ids directly in app and site. */
52
+ id: string;
53
+ title: string;
54
+ /** The tweet-sized line. Absent only if an author forgot one. */
55
+ summary?: string;
56
+ }
57
+ interface CuratedArticle {
58
+ id: string;
59
+ area: HandboekArea;
60
+ title: string;
61
+ order: number;
62
+ summary?: string;
63
+ /** Article body — squisq-flavored markdown, macros not yet expanded. */
64
+ body: string;
65
+ /** Per-block seconds override for video playback. */
66
+ defaultDuration?: number;
67
+ /** When false, the CLI static-site export skips the article. */
68
+ siteVisible: boolean;
69
+ }
70
+ /**
71
+ * Locate the shipped handboek content tree. Same probe-chain shape as
72
+ * `findBundledUi()` in bin/gezeld.ts: an env override for tests and
73
+ * operators, the tsup-copied `dist/handboek-content/` in built trees,
74
+ * and the repo `docs/handboek/` when running from source (vitest, tsx).
75
+ */
76
+ declare function findHandboekContent(): string | null;
77
+ /**
78
+ * Release-note articles, newest first. Release `order` is the negated
79
+ * calendar line (`1.26223` → `-26223`), so the area's own ascending sort
80
+ * already puts the newest release on top; this only drops the section
81
+ * index out of the list it introduces.
82
+ */
83
+ declare function listReleaseNotes(articles: CuratedArticle[]): ReleaseNoteEntry[];
84
+
40
85
  /** The slice of CatalogService the handboek needs — stubbable in tests. */
41
86
  interface HandboekCatalog {
42
87
  list(kind: CatalogKind): Promise<CatalogItemSummary[]>;
@@ -46,6 +91,12 @@ interface MacroContext {
46
91
  mode: HandboekRenderMode;
47
92
  catalog: HandboekCatalog;
48
93
  device: HandboekDeviceInfo;
94
+ /**
95
+ * Release-note articles, newest first. Supplied by the engine from the
96
+ * curated tree rather than read here, so the `whats-new-list` macro
97
+ * stays a pure renderer and the tests can stub a release history.
98
+ */
99
+ releases: ReleaseNoteEntry[];
49
100
  /** Figures referenced by expanded markdown accumulate here. */
50
101
  figures: HandboekFigure[];
51
102
  }
@@ -73,30 +124,6 @@ declare function expandMacros(source: string, ctx: Omit<MacroContext, 'figures'>
73
124
  /** Parse `key=value key2="quoted value"` attribute syntax. */
74
125
  declare function parseAttrs(raw: string): Record<string, string>;
75
126
 
76
- /** Area folder order — also the TOC order. */
77
- declare const HANDBOEK_AREAS: readonly HandboekArea[];
78
- declare const HANDBOEK_AREA_TITLES: Record<HandboekArea, string>;
79
- interface CuratedArticle {
80
- id: string;
81
- area: HandboekArea;
82
- title: string;
83
- order: number;
84
- summary?: string;
85
- /** Article body — squisq-flavored markdown, macros not yet expanded. */
86
- body: string;
87
- /** Per-block seconds override for video playback. */
88
- defaultDuration?: number;
89
- /** When false, the CLI static-site export skips the article. */
90
- siteVisible: boolean;
91
- }
92
- /**
93
- * Locate the shipped handboek content tree. Same probe-chain shape as
94
- * `findBundledUi()` in bin/gezeld.ts: an env override for tests and
95
- * operators, the tsup-copied `dist/handboek-content/` in built trees,
96
- * and the repo `docs/handboek/` when running from source (vitest, tsx).
97
- */
98
- declare function findHandboekContent(): string | null;
99
-
100
127
  interface HandboekEngineOptions {
101
128
  catalog: HandboekCatalog;
102
129
  device: HandboekDeviceInfo;
@@ -131,4 +158,4 @@ interface HandboekEngine {
131
158
  */
132
159
  declare function createHandboekEngine(opts: HandboekEngineOptions): HandboekEngine;
133
160
 
134
- export { type CuratedArticle, HANDBOEK_AREAS, HANDBOEK_AREA_TITLES, type HandboekArticleOptions, type HandboekCatalog, type HandboekDeviceInfo, type HandboekEngine, type HandboekEngineOptions, type HandboekGezelInfo, type HandboekHardwareInfo, type HandboekHardwareTier, type HandboekModelInfo, MACROS, createHandboekEngine, expandMacros, findHandboekContent, parseAttrs, siteDeviceInfo };
161
+ export { type CuratedArticle, HANDBOEK_AREAS, HANDBOEK_AREA_TITLES, type HandboekArticleOptions, type HandboekCatalog, type HandboekDeviceInfo, type HandboekEngine, type HandboekEngineOptions, type HandboekGezelInfo, type HandboekHardwareInfo, type HandboekHardwareTier, type HandboekModelInfo, MACROS, type ReleaseNoteEntry, WHATS_NEW_INDEX_ID, createHandboekEngine, expandMacros, findHandboekContent, listReleaseNotes, parseAttrs, siteDeviceInfo };
package/dist/handboek.js CHANGED
@@ -15,15 +15,18 @@ var HANDBOEK_AREAS = [
15
15
  "gezel-roles",
16
16
  "craftbooks",
17
17
  "project-types",
18
- "technical"
18
+ "technical",
19
+ "whats-new"
19
20
  ];
20
21
  var HANDBOEK_AREA_TITLES = {
21
22
  conceptual: "Concepts",
22
23
  "gezel-roles": "Gezel Roles",
23
24
  craftbooks: "Craftbooks",
24
25
  "project-types": "Project Types",
25
- technical: "Technical"
26
+ technical: "Technical",
27
+ "whats-new": "What's New"
26
28
  };
29
+ var WHATS_NEW_INDEX_ID = "whats-new-index";
27
30
  function findHandboekContent() {
28
31
  const override = process.env.GEZEL_HANDBOEK_DIR?.trim();
29
32
  if (override) return existsSync(override) ? resolve(override) : null;
@@ -66,6 +69,9 @@ function loadCuratedArticles(contentDir) {
66
69
  out.sort((a, b) => a.area === b.area ? a.order - b.order : 0);
67
70
  return out;
68
71
  }
72
+ function listReleaseNotes(articles) {
73
+ return articles.filter((a) => a.area === "whats-new" && a.id !== WHATS_NEW_INDEX_ID).sort((a, b) => a.order - b.order).map((a) => ({ id: a.id, title: a.title, summary: a.summary }));
74
+ }
69
75
  function parseCuratedArticle(source, area, filename) {
70
76
  HandboekAreaSchema.parse(area);
71
77
  const { frontmatter, body } = splitFrontmatterBlock(source);
@@ -90,7 +96,7 @@ function titleFromBody(body, fallback) {
90
96
  }
91
97
 
92
98
  // src/handboek/generated.ts
93
- import { ROLES } from "@bendyline/gezel";
99
+ import { CRAFTBOOK_ROLE_META, ROLES } from "@bendyline/gezel";
94
100
  function firstSentence(text) {
95
101
  const s = text.trim().split(/\.\s/)[0]?.replace(/\.$/, "").trim();
96
102
  return s ? `${s}.` : void 0;
@@ -115,12 +121,20 @@ async function listGeneratedSummaries(catalog) {
115
121
  });
116
122
  const craftbooks = (await catalog.list("craftbook-template")).map((s) => s.manifest).sort((a, b) => a.name.localeCompare(b.name));
117
123
  for (const book of craftbooks) {
124
+ const role = book.role ?? "general";
125
+ const roleIndex = CRAFTBOOK_ROLE_META.findIndex((item) => item.id === role);
126
+ const roleMeta = CRAFTBOOK_ROLE_META[roleIndex] ?? CRAFTBOOK_ROLE_META.at(-1);
118
127
  out.push({
119
128
  id: `craftbook/${book.id}`,
120
129
  area: "craftbooks",
121
130
  title: book.name,
122
131
  order: 10,
123
- summary: firstSentence(book.description)
132
+ summary: firstSentence(book.description),
133
+ subcategory: {
134
+ id: roleMeta.id,
135
+ title: roleMeta.label,
136
+ order: roleIndex < 0 ? CRAFTBOOK_ROLE_META.length : roleIndex
137
+ }
124
138
  });
125
139
  }
126
140
  out.push({
@@ -245,6 +259,7 @@ import {
245
259
  describeProvenance,
246
260
  provenanceDifferences,
247
261
  resolveRoleId as resolveRoleId2,
262
+ scoreModel,
248
263
  toolsetGroupsForRole as toolsetGroupsForRole2
249
264
  } from "@bendyline/gezel";
250
265
  import { BUILTIN_TOOLSETS as BUILTIN_TOOLSETS2 } from "@bendyline/gezel-catalog";
@@ -599,8 +614,10 @@ var MACROS = {
599
614
  "installed-models": installedModels,
600
615
  "model-scorecard": modelScorecard,
601
616
  "project-type-composition": projectTypeComposition,
602
- "suggested-work": suggestedWork
617
+ "suggested-work": suggestedWork,
618
+ "whats-new-list": whatsNewList
603
619
  };
620
+ var WHATS_NEW_LIST_DEFAULT_LIMIT = 12;
604
621
  var DIRECTIVE_RE = /^::handboek-([a-z0-9-]+)(?:\{([^}]*)\})?\s*$/;
605
622
  async function expandMacros(source, ctx) {
606
623
  const figures = [];
@@ -904,6 +921,16 @@ async function craftbookList(attrs, ctx) {
904
921
  });
905
922
  return ["| Craftbook | What it does |", "| --- | --- |", ...rows].join("\n");
906
923
  }
924
+ async function whatsNewList(attrs, ctx) {
925
+ const parsed = Number.parseInt(attrs.limit ?? "", 10);
926
+ const limit = Number.isFinite(parsed) && parsed > 0 ? parsed : WHATS_NEW_LIST_DEFAULT_LIMIT;
927
+ const releases = ctx.releases.slice(0, limit);
928
+ if (releases.length === 0) return "No releases have been written up yet.";
929
+ return releases.map((r) => {
930
+ const line = `- **[${r.title}](${r.id})**`;
931
+ return r.summary ? `${line} \u2014 ${r.summary}` : line;
932
+ }).join("\n");
933
+ }
907
934
  async function deviceHardware(_attrs, ctx) {
908
935
  if (ctx.mode === "site") {
909
936
  return "Gezel classifies each device as tiny, small, medium, or large from the memory available to local models. Open this page in the app to see the current device.";
@@ -944,6 +971,8 @@ function renderScorecardMarkdown(dataset, suiteId, opts) {
944
971
  ].join("\n");
945
972
  }
946
973
  const lines = [];
974
+ const publishable = board.scores.filter((score) => score.unmeasuredScenarios.length === 0);
975
+ const withheld = board.scores.filter((score) => score.unmeasuredScenarios.length > 0);
947
976
  lines.push(
948
977
  `**${describeProvenance(
949
978
  board.run,
@@ -951,33 +980,75 @@ function renderScorecardMarkdown(dataset, suiteId, opts) {
951
980
  )}**`
952
981
  );
953
982
  lines.push("");
954
- lines.push("| Model | Size | Tasks passed | Not measured |");
955
- lines.push("| --- | --- | --- | --- |");
956
- for (const score of board.scores) {
957
- const discarded = score.discardedTrials > 0 ? `${score.discardedTrials} run${score.discardedTrials === 1 ? "" : "s"} lost to the machine` : "\u2014";
958
- lines.push(
959
- `| ${score.result.label} | ${score.result.parameterSize ?? score.result.tier} | ${score.claim} | ${discarded} |`
960
- );
961
- }
962
- if (board.otherRunScores.length > 0) {
983
+ lines.push(...scoreTable(publishable));
984
+ if (withheld.length > 0) {
963
985
  lines.push("");
964
- lines.push("Measured in an earlier round, so not directly comparable with the table above:");
986
+ lines.push("Not published \u2014 some tasks could not be measured on this round:");
965
987
  lines.push("");
966
- lines.push("| Model | Tasks passed | Why it is listed apart |");
967
- lines.push("| --- | --- | --- |");
968
- for (const entry of board.otherRunScores) {
969
- const why = provenanceDifferences(board.run, entry.run);
970
- lines.push(
971
- `| ${entry.result.label} | ${entry.claim} | ${why.length > 0 ? why.join(", ") : "earlier round"} |`
972
- );
988
+ for (const score of withheld) {
989
+ lines.push(`- ${score.result.label}: ${score.unmeasuredScenarios.length} task(s) unmeasured`);
973
990
  }
974
991
  }
992
+ const priorRuns = dataset.runs.filter((run) => run.id !== board.run.id && run.suites.includes(suiteId)).slice(0, PRIOR_ROUNDS_SHOWN);
993
+ for (const run of priorRuns) {
994
+ const scores = dataset.results.filter((result) => result.runId === run.id && result.suiteId === suiteId).map((result) => scoreModel(result, run.provenance.count)).filter((score) => score.unmeasuredScenarios.length === 0).sort((a, b) => b.successes / b.attributableTrials - a.successes / a.attributableTrials);
995
+ if (scores.length === 0) continue;
996
+ const why = provenanceDifferences(board.run, run);
997
+ lines.push("");
998
+ lines.push(`### Earlier round \u2014 ${run.provenance.startedAt.slice(0, 10)}`);
999
+ lines.push("");
1000
+ lines.push(
1001
+ `**${describeProvenance(
1002
+ run,
1003
+ scores.map((s) => s.result.engine)
1004
+ )}**${why.length > 0 ? ` \u2014 ${why.join(", ")}` : ""}`
1005
+ );
1006
+ lines.push("");
1007
+ lines.push(...scoreTable(scores));
1008
+ }
975
1009
  if (opts.includeTaskCount) {
976
1010
  lines.push("");
977
1011
  lines.push(`Tasks in this set: ${board.scenarioIds.length}.`);
978
1012
  }
979
1013
  return lines.join("\n");
980
1014
  }
1015
+ var PRIOR_ROUNDS_SHOWN = 2;
1016
+ function scoreTable(scores) {
1017
+ const anySpeed = scores.some((score) => !!score.result.performance);
1018
+ const anyRuntime = scores.some((score) => !!score.result.runtime);
1019
+ const anyJudge = scores.some((score) => !!score.result.judge);
1020
+ const cols = ["Model", "Size", "Tasks passed"];
1021
+ if (anyJudge) cols.push("Quality");
1022
+ if (anySpeed) cols.push("Reads at", "Writes at");
1023
+ if (anyRuntime) cols.push("Context", "Memory used");
1024
+ const rows = scores.map((score) => {
1025
+ const cells = [
1026
+ score.result.label,
1027
+ score.result.parameterSize ?? score.result.tier,
1028
+ score.claim
1029
+ ];
1030
+ if (anyJudge) {
1031
+ const judge = score.result.judge;
1032
+ cells.push(judge ? `${judge.meanScore}/10 (${judge.artifacts} pieces)` : "\u2014");
1033
+ }
1034
+ if (anySpeed) {
1035
+ const perf = score.result.performance;
1036
+ cells.push(
1037
+ perf ? `${perf.prefillTokensPerSec.toLocaleString()} tok/s` : "\u2014",
1038
+ perf ? `${perf.decodeTokensPerSec} tok/s` : "\u2014"
1039
+ );
1040
+ }
1041
+ if (anyRuntime) {
1042
+ const runtime = score.result.runtime;
1043
+ cells.push(
1044
+ runtime ? `${Math.round(runtime.contextTokens / 1024)}K` : "\u2014",
1045
+ runtime ? `${(runtime.peakMemoryMb / 1024).toFixed(1)} GB` : "\u2014"
1046
+ );
1047
+ }
1048
+ return `| ${cells.join(" | ")} |`;
1049
+ });
1050
+ return [`| ${cols.join(" | ")} |`, `| ${cols.map(() => "---").join(" | ")} |`, ...rows];
1051
+ }
981
1052
  async function projectTypeComposition(attrs, ctx) {
982
1053
  if (!attrs.id) return "";
983
1054
  const detail = await ctx.catalog.get("project-type", attrs.id);
@@ -1076,7 +1147,8 @@ function createHandboekEngine(opts) {
1076
1147
  order: g.order,
1077
1148
  summary: g.summary,
1078
1149
  generated: true,
1079
- siteVisible: true
1150
+ siteVisible: true,
1151
+ subcategory: g.subcategory
1080
1152
  }))
1081
1153
  ].sort((a, b) => a.order - b.order || a.title.localeCompare(b.title));
1082
1154
  return { area, title: HANDBOEK_AREA_TITLES[area], entries };
@@ -1085,12 +1157,14 @@ function createHandboekEngine(opts) {
1085
1157
  },
1086
1158
  async article(id, articleOpts) {
1087
1159
  const mode = articleOpts?.mode ?? "app";
1088
- const curatedMatch = curated().find((a) => a.id === id);
1160
+ const curatedArticles = curated();
1161
+ const curatedMatch = curatedArticles.find((a) => a.id === id);
1089
1162
  if (curatedMatch) {
1090
1163
  const { markdown: markdown2, figures: figures2 } = await expandMacros(curatedMatch.body, {
1091
1164
  mode,
1092
1165
  catalog: opts.catalog,
1093
- device: opts.device
1166
+ device: opts.device,
1167
+ releases: listReleaseNotes(curatedArticles)
1094
1168
  });
1095
1169
  return {
1096
1170
  id,
@@ -1117,7 +1191,8 @@ function createHandboekEngine(opts) {
1117
1191
  const { markdown, figures } = await expandMacros(body, {
1118
1192
  mode,
1119
1193
  catalog: opts.catalog,
1120
- device: opts.device
1194
+ device: opts.device,
1195
+ releases: listReleaseNotes(curatedArticles)
1121
1196
  });
1122
1197
  return {
1123
1198
  id,
@@ -1210,9 +1285,11 @@ export {
1210
1285
  HANDBOEK_AREAS,
1211
1286
  HANDBOEK_AREA_TITLES,
1212
1287
  MACROS,
1288
+ WHATS_NEW_INDEX_ID,
1213
1289
  createHandboekEngine,
1214
1290
  expandMacros,
1215
1291
  findHandboekContent,
1292
+ listReleaseNotes,
1216
1293
  parseAttrs,
1217
1294
  siteDeviceInfo
1218
1295
  };