@a-company/gnomon 0.0.0-stage → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +126 -2
  2. package/dist/agent.d.ts +28 -0
  3. package/dist/agent.js +45708 -0
  4. package/dist/build-id.json +1 -0
  5. package/dist/cli/gnomon.js +53946 -0
  6. package/dist/cli/gnomond.js +47744 -0
  7. package/dist/gnomon-local/cli.js +897 -0
  8. package/dist/index.d.ts +3477 -0
  9. package/dist/index.js +13698 -0
  10. package/dist/loops.d.ts +440 -0
  11. package/dist/loops.js +4706 -0
  12. package/dist/signer-CQ3Bpe7G.d.ts +158 -0
  13. package/dist/transport/gnomon-web.js +688 -0
  14. package/gnomon-mods/.purpose +143 -0
  15. package/gnomon-mods/constellation/README.md +27 -0
  16. package/gnomon-mods/constellation/fixtures/map-the-idea.golden.json +19 -0
  17. package/gnomon-mods/constellation/fixtures/map-the-idea.json +3 -0
  18. package/gnomon-mods/constellation/fixtures/summarize-the-session.golden.json +20 -0
  19. package/gnomon-mods/constellation/fixtures/summarize-the-session.json +3 -0
  20. package/gnomon-mods/constellation/fixtures/two-blocks.golden.json +4 -0
  21. package/gnomon-mods/constellation/fixtures/two-blocks.json +3 -0
  22. package/gnomon-mods/constellation/gnomon-manifest.json +68 -0
  23. package/gnomon-mods/constellation/skills/constellation-desk/SKILL.md +35 -0
  24. package/gnomon-mods/ledger/README.md +10 -0
  25. package/gnomon-mods/ledger/fixtures/breaker.golden.json +28 -0
  26. package/gnomon-mods/ledger/fixtures/breaker.json +80 -0
  27. package/gnomon-mods/ledger/fixtures/compacted.golden.json +42 -0
  28. package/gnomon-mods/ledger/fixtures/compacted.json +103 -0
  29. package/gnomon-mods/ledger/fixtures/over-budget.golden.json +24 -0
  30. package/gnomon-mods/ledger/fixtures/over-budget.json +86 -0
  31. package/gnomon-mods/ledger/fixtures/strip-reduced.golden.json +23 -0
  32. package/gnomon-mods/ledger/fixtures/strip-reduced.json +76 -0
  33. package/gnomon-mods/ledger/fixtures/strip.golden.json +18 -0
  34. package/gnomon-mods/ledger/fixtures/strip.json +68 -0
  35. package/gnomon-mods/ledger/gnomon-manifest.json +16 -0
  36. package/gnomon-mods/ledger/index.mjs +77 -0
  37. package/gnomon-mods/ledger/model.mjs +237 -0
  38. package/gnomon-mods/neurons/README.md +31 -0
  39. package/gnomon-mods/neurons/fixtures/capture-a-decision.golden.json +20 -0
  40. package/gnomon-mods/neurons/fixtures/capture-a-decision.json +3 -0
  41. package/gnomon-mods/neurons/fixtures/capture-a-learning.golden.json +19 -0
  42. package/gnomon-mods/neurons/fixtures/capture-a-learning.json +3 -0
  43. package/gnomon-mods/neurons/fixtures/search-the-brain.golden.json +19 -0
  44. package/gnomon-mods/neurons/fixtures/search-the-brain.json +3 -0
  45. package/gnomon-mods/neurons/fixtures/undeclared-kind.golden.json +9 -0
  46. package/gnomon-mods/neurons/fixtures/undeclared-kind.json +3 -0
  47. package/gnomon-mods/neurons/gnomon-manifest.json +87 -0
  48. package/gnomon-mods/neurons/skills/neurons-desk/SKILL.md +44 -0
  49. package/gnomon-mods/paradigm-context/README.md +9 -0
  50. package/gnomon-mods/paradigm-context/fixtures/empty.golden.json +28 -0
  51. package/gnomon-mods/paradigm-context/fixtures/empty.json +48 -0
  52. package/gnomon-mods/paradigm-context/fixtures/full-dark.golden.json +497 -0
  53. package/gnomon-mods/paradigm-context/fixtures/full-dark.json +134 -0
  54. package/gnomon-mods/paradigm-context/fixtures/full.golden.json +497 -0
  55. package/gnomon-mods/paradigm-context/fixtures/full.json +134 -0
  56. package/gnomon-mods/paradigm-context/fixtures/stale.golden.json +205 -0
  57. package/gnomon-mods/paradigm-context/fixtures/stale.json +69 -0
  58. package/gnomon-mods/paradigm-context/fixtures/unfocused.golden.json +28 -0
  59. package/gnomon-mods/paradigm-context/fixtures/unfocused.json +14 -0
  60. package/gnomon-mods/paradigm-context/gnomon-manifest.json +15 -0
  61. package/gnomon-mods/paradigm-context/index.mjs +64 -0
  62. package/gnomon-mods/paradigm-context/model.mjs +137 -0
  63. package/gnomon-mods/workshop/README.md +12 -0
  64. package/gnomon-mods/workshop/fixtures/strip-reduced.golden.json +56 -0
  65. package/gnomon-mods/workshop/fixtures/strip-reduced.json +16 -0
  66. package/gnomon-mods/workshop/fixtures/strip.golden.json +56 -0
  67. package/gnomon-mods/workshop/fixtures/strip.json +16 -0
  68. package/gnomon-mods/workshop/gnomon-manifest.json +14 -0
  69. package/gnomon-mods/workshop/index.mjs +73 -0
  70. package/gnomon-mods/workshop/model.mjs +152 -0
  71. package/package.json +80 -4
@@ -0,0 +1,143 @@
1
+ version: "2.0"
2
+ description: >-
3
+ packages/gnomon-mods - the first-party Gnomon mods (spec §12.1). They run on
4
+ the same mod-abi:v1 and the same `mod` sandbox profile as any other mod;
5
+ first-party is a trust tier derived from the packageHash pinned in the
6
+ kernel's FIRST_PARTY_PACKAGE_HASHES (packages/gnomon/src/kernel/manifest/install.ts),
7
+ never a wider ABI. Files are LF-pinned (.gitattributes) so the hash is the
8
+ same on every OS.
9
+
10
+ components:
11
+ workshop-mod:
12
+ description: >-
13
+ #workshop-mod (spec §11.6, G-WSP-01; §19.1 M1.11 slice 3) -
14
+ packages/gnomon-mods/workshop: one view `workshop.strip` (slot sidebar,
15
+ append) with two zones, needs you (one cap per pending card, labelled
16
+ by the card kind) and working (one cap per session: hat = work type,
17
+ filled = Claude, outlined = local, tone hue-<0..5> = project
18
+ (HUES = 6, the feed's projectHue), violet only for a KNOT; bob while
19
+ turn.partial streams, still otherwise, `working` text pose under
20
+ reduced motion). S5: every cap is a Text whose `text` is the accessible
21
+ form and whose `cap` is the closed contracts shape {shape tall|wide|
22
+ floppy, hue, filled, state needs|working|idle|knot, label} (needs caps
23
+ carry their session's hue and a `◆ / ✕ / ! / ?` + kind label); the GUI
24
+ draws the hat and the Layout places the strip. model.mjs is a pure reducer
25
+ + render (sessions and cards bounded at 256 each); index.mjs speaks
26
+ mod-abi:v1 directly (no SDK import, so the pinned packageHash never
27
+ depends on an SDK build) and echoes MOD_ABI_ID. State comes ONLY from
28
+ the host's projected kernel-event feed (#mod-host feed.ts, granted by
29
+ the log.read capability), primed at every start with the state in force
30
+ (#mod-host feedBacklog: live sessions, pending cards, paused mods), so a
31
+ Workshop started after a session opened still counts it; a host-initiated fixture render may replay
32
+ input.fixtureEvents, a relayed render never can. Declared effects:
33
+ none beyond idle read; tier first-party; installs with
34
+ `gnomon mod install packages/gnomon-mods/workshop` + the root-key card.
35
+ Fixtures strip.json / strip-reduced.json with golden renders
36
+ (test/workshop-mod.test.ts, every OS).
37
+ tags: [mods, first-party, M1.11]
38
+ oracles: [G-WSP-01]
39
+
40
+ ledger-mod:
41
+ description: >-
42
+ #ledger-mod - packages/gnomon-mods/ledger, the context and budget
43
+ meter (mod #1 of the mods arc). Two views: `ledger.strip` (slot
44
+ composer.footer, append) - one line for the focused session (relay
45
+ input {hsid}, display only) or all sessions, `182k in · 9k out · 71%
46
+ cache · $0.42 · ⇣ 2 compactions (190k → 41k)` (in = fresh + cache
47
+ reads + cache writes; cache = the share read from cache; a local
48
+ session shows its token total), the budget meter (the over-permit row
49
+ `n of m`, else the highest budget.ladder rung `n of 100%`) and the
50
+ alarms (breaker open, permit denied); `ledger.sheet` (slot sidebar,
51
+ append) - a Table row per session plus the total, meter and alarms.
52
+ Tones: quiet; warn (amber) at a rung >= 75% or an over-permit row;
53
+ deny (vermilion) on a breaker or a denied permit; hue-<0..7> ONLY on
54
+ the cap glyph, never as text. Reduced motion renders the meter as Text
55
+ (no Progress fill at all). kit:v1 blocks only (Box, Text, Table,
56
+ Progress). model.mjs is a pure reducer + render (MAX_SESSIONS 256,
57
+ MAX_DIMENSIONS 32, every number re-clamped by count(), unknown kinds
58
+ ignored); index.mjs is the #workshop-mod stdio loop verbatim but for
59
+ the two views (no SDK import, echoes MOD_ABI_ID). State comes ONLY from
60
+ the #mod-host feed projection v2 (numbers only, granted by log.read);
61
+ a relayed render never replays fixtureEvents. requires log.read and
62
+ log.read:usage (Aegis M1: the usage rows reach only that grant),
63
+ egress none, no tools or commands; tier first-party (packageHash pinned in FIRST_PARTY_PACKAGE_HASHES). Fixtures strip, strip-reduced,
64
+ over-budget, breaker, compacted with golden renders
65
+ (test/ledger-mod.test.ts, light and dark); goldens regenerate with
66
+ `npm run repin:first-party` in packages/gnomon.
67
+ tags: [mods, first-party]
68
+ paradigm-context-mod:
69
+ description: >-
70
+ #paradigm-context-mod - packages/gnomon-mods/paradigm-context, Paradigm
71
+ Context (mod #2 of the mods arc). One view `paradigm-context.sheet`
72
+ (slot sidebar, append), read-only (no Button, no command, no tool): a
73
+ head with the project swatch (tone hue-<0..7> on the ● mark only, which
74
+ the GUI draws as the project's ■; the name in ink), a stale line (warn)
75
+ when the newest decision or lore entry is > STALE_DAYS (14) older than
76
+ the slice's `at`; a project with no open tasks, decisions or lore
77
+ shows ONE quiet line (EMPTY_LINE 'nothing yet: no tasks, decisions or
78
+ lore') instead of the zones; otherwise three zones, each headed by a row: the zone name
79
+ (Text tone head, the label style) and its {n, of} count as quiet meta
80
+ (`tasks` `n of m open · k high`, `decisions` `n of m`, `lore` `n of m`;
81
+ 1.1.1, 2026-10-23) - each item a row with a FIXED type prefix
82
+ (`task` / `task ▲` for high, `decision`, `lore <type>`), the id (a
83
+ decision's date its own word after it, so the GUI truncates the id and
84
+ never the date), then the title “quoted” (inner curly quotes flattened), every
85
+ part tone quiet, so a repo title never supplies a status glyph (Aegis
86
+ L2) - then the symbols touched as chips (tone chip,
87
+ mono ink, never a hue), and a skipped line (malformed / refused /
88
+ secret counts). Titles cut to 80 code points. Its ONLY input is the
89
+ host-composed `input.paradigm` (#mod-host K1 paradigm-slice:v1 of the
90
+ focused LIVE session's repo, joined server-side from {focus:{hsid}},
91
+ K3); model.mjs re-validates and bounds it (ids, dates, tokens, symbol
92
+ shape, counts). requires paradigm.read:summary only (no log.read, so
93
+ no feed), egress none (refused with that capability). index.mjs is the
94
+ #ledger-mod stdio loop minus the feed (no SDK import, echoes
95
+ MOD_ABI_ID). Fixtures empty, full, full-dark, stale, unfocused with
96
+ goldens (test/paradigm-context-mod.test.ts, light and dark);
97
+ regenerate with `npm run repin:first-party`; tier first-party once
98
+ pinned in FIRST_PARTY_PACKAGE_HASHES (1.1.1 sha256:22bab5bf…, repinned 2026-10-23 for the fitted zone heads and the one-line empty state).
99
+ tags: [mods, first-party]
100
+ neurons-mod:
101
+ description: >-
102
+ #neurons-mod - packages/gnomon-mods/neurons-brain (the Desk, TD-2026-10-20-812
103
+ slice S2), mod name `neurons-brain`, because `neurons` is a kernel
104
+ namespace token (the §9.1 `neurons.event` kind) and the validator refuses
105
+ it as a mod name. View-less, with no process of its own (no index.mjs; the
106
+ validator needs none). It declares one provides.mcp server `gnomon-neurons-brain`
107
+ (generic default `neurons mcp`, a placeholder that is not absolute, so it
108
+ fails closed until the user sets an absolute command through the
109
+ per-user override; config {command, args, env [NEURONS_VAULT], the
110
+ upstream server's own variable}; no personal path is committed), three
111
+ offers (search-the-brain diverge → neuron_search, capture-a-learning
112
+ synthesize → source_note, capture-a-decision commit → neuron_write; each
113
+ offer's contextIn comes from CONTEXT_SOURCES, and the two capture
114
+ restraints, like the playbook, require showing the exact text and waiting
115
+ for the human to confirm or edit it, flagging fetched, tool-output or
116
+ unverified content), and one skill, skills/neurons-desk/SKILL.md, a
117
+ playbook that falls back to plain suggestions outside Gnomon. Every
118
+ ```offer example in it resolves to a declared offer (tested). requires egress none,
119
+ no capability. Fixtures are model replies with one ```offer block, and
120
+ their goldens come from scripts/offer-golden.mjs (offerBlockOf plus the
121
+ manifest's offer-spec), with one undeclared-kind negative. Tier
122
+ first-party (packageHash pinned in FIRST_PARTY_PACKAGE_HASHES); tested
123
+ in test/neurons-mod.test.ts.
124
+ tags: [mods, first-party, desk]
125
+ constellation-mod:
126
+ description: >-
127
+ #constellation-mod - packages/gnomon-mods/constellation (the Desk,
128
+ TD-2026-10-20-812 slice S2). View-less, with no process of its own. It
129
+ declares one provides.mcp server `gnomon-constellation` (placeholder
130
+ default `constellation-mcp`, never launched: it is not absolute, so it
131
+ fails closed until the user sets an absolute node and
132
+ `<checkout>/mcp/server.ts` through the per-user override; config
133
+ {command, args, env [CONSTELLATION_NO_AUTOSTART]}), two offers
134
+ (map-the-idea diverge → apply, contextIn [recent-messages];
135
+ summarize-the-session synthesize → summarize; the automation offer is
136
+ deferred to O2 behind a confirmation and a disabled save, Aegis H1), and
137
+ one skill, skills/constellation-desk/SKILL.md, a playbook that falls back
138
+ to plain suggestions outside Gnomon. requires egress none, no capability.
139
+ The L3 "sky" world is not declared yet, because manifest:v1 has no world
140
+ field. Offer fixtures plus goldens include a two-blocks negative. Tier
141
+ first-party (packageHash pinned); tested in
142
+ test/constellation-mod.test.ts.
143
+ tags: [mods, first-party, desk]
@@ -0,0 +1,27 @@
1
+ # gnomon.constellation (first-party Gnomon mod)
2
+
3
+ Constellation puts the user's local idea map on the Desk. It has no view and no process of its own. It declares the Constellation MCP server (the kernel names it `gm_gnomon_constellation`), two offers and a playbook skill. Its mod id is `gnomon.constellation` (manifest: 2, publisher `gnomon`, slice S0b); that is what the model writes in an offer block.
4
+
5
+ - **map-the-idea** (diverge, `apply`): lay the ideas in play out as stars and beams on the active session's map.
6
+ - **summarize-the-session** (synthesize, `summarize`): read the session outline, then save a short written summary.
7
+
8
+ Each offer's restraint text says when not to offer it. The playbook is `skills/constellation-desk/SKILL.md`. Outside Gnomon it falls back to plain one-sentence suggestions. An automation offer is deferred to O2, where it will sit behind a confirmation and a disabled save.
9
+
10
+ It requires no capability and lists no egress. The MCP server talks only to the Constellation app on loopback.
11
+
12
+ A Constellation "sky" world is planned (L3: worlds are first-party only, for now). It is not declared here yet, because manifest:v1 has no world field and its validator refuses unknown keys.
13
+
14
+ ## Install
15
+
16
+ ```
17
+ gnomon mod install packages/gnomon-mods/constellation
18
+ ```
19
+
20
+ Approve the card in the GUI. The manifest's default command, `constellation-mcp`, is a placeholder that is never launched. It is not an absolute path, so the server fails closed until you set a per-user override. The manifest declares `config: {command, args, env: [CONSTELLATION_NO_AUTOSTART]}`. Set the override with `gnomon mod config` (slice S1):
21
+
22
+ - the command: the absolute path of your node (`which node`), Node 24 or later
23
+ - the args: `<your a-constellation checkout>/mcp/server.ts`
24
+
25
+ An override command must be an absolute path; a bare `node` is refused. The override belongs to you and is never committed.
26
+
27
+ The packageHash is pinned in the kernel's first-party list, so editing any file here needs a new pin. Run `npm run repin:first-party` in packages/gnomon; it also regenerates the offer goldens in `fixtures/`.
@@ -0,0 +1,19 @@
1
+ {
2
+ "offer": {
3
+ "mod": "gnomon.constellation",
4
+ "kind": "map-the-idea",
5
+ "question": "Want me to map these four options on the Constellation board?"
6
+ },
7
+ "body": "There are four ways to split the router: by phase, by mod, by context source, or a hybrid. Each trades latency for clarity differently.",
8
+ "shown": {
9
+ "mod": "gnomon.constellation",
10
+ "kind": "map-the-idea",
11
+ "phase": "diverge",
12
+ "title": "Map this on the board",
13
+ "contextIn": [
14
+ "recent-messages"
15
+ ],
16
+ "question": "Want me to map these four options on the Constellation board?",
17
+ "handoffTool": "apply"
18
+ }
19
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "reply": "There are four ways to split the router: by phase, by mod, by context source, or a hybrid. Each trades latency for clarity differently.\n\n```offer\n{\"mod\":\"gnomon.constellation\",\"kind\":\"map-the-idea\",\"question\":\"Want me to map these four options on the Constellation board?\"}\n```\n"
3
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "offer": {
3
+ "mod": "gnomon.constellation",
4
+ "kind": "summarize-the-session",
5
+ "question": "Shall I write the session summary to Constellation?"
6
+ },
7
+ "body": "That closes the migration: the schema moved, the backfill ran clean and the old table is gone.",
8
+ "shown": {
9
+ "mod": "gnomon.constellation",
10
+ "kind": "summarize-the-session",
11
+ "phase": "synthesize",
12
+ "title": "Write the session summary",
13
+ "contextIn": [
14
+ "session-summary",
15
+ "recent-messages"
16
+ ],
17
+ "question": "Shall I write the session summary to Constellation?",
18
+ "handoffTool": "summarize"
19
+ }
20
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "reply": "That closes the migration: the schema moved, the backfill ran clean and the old table is gone.\n\n```offer\n{\"mod\":\"gnomon.constellation\",\"kind\":\"summarize-the-session\",\"question\":\"Shall I write the session summary to Constellation?\"}\n```\n"
3
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "offer": null,
3
+ "shown": null
4
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "reply": "Two ideas.\n\n```offer\n{\"mod\":\"gnomon.constellation\",\"kind\":\"map-the-idea\",\"question\":\"Map it?\"}\n```\n\n```offer\n{\"mod\":\"gnomon.constellation\",\"kind\":\"automate-this\",\"question\":\"Automate it?\"}\n```\n"
3
+ }
@@ -0,0 +1,68 @@
1
+ {
2
+ "manifest": 2,
3
+ "kind": "mod",
4
+ "publisher": "gnomon",
5
+ "name": "constellation",
6
+ "version": "1.1.0",
7
+ "abi": "mod-abi:v1",
8
+ "description": "Constellation: the idea map as a Desk mod. Offers to map an idea on the board and to summarize the session, through the local Constellation MCP server.",
9
+ "provides": {
10
+ "mcp": [
11
+ {
12
+ "command": "constellation-mcp",
13
+ "args": [],
14
+ "config": {
15
+ "command": true,
16
+ "args": true,
17
+ "env": [
18
+ "CONSTELLATION_NO_AUTOSTART"
19
+ ]
20
+ }
21
+ }
22
+ ],
23
+ "offers": [
24
+ {
25
+ "kind": "map-the-idea",
26
+ "phase": "diverge",
27
+ "title": "Map this on the board",
28
+ "summary": "Lay the ideas in play out as stars and beams on the Constellation map of the active session, so the options and their links are visible side by side.",
29
+ "contextIn": [
30
+ "recent-messages"
31
+ ],
32
+ "resultOut": "map-ops",
33
+ "reach": "mcp",
34
+ "handoffTool": "apply",
35
+ "related": [
36
+ "summarize-the-session"
37
+ ],
38
+ "restraint": "Not for a single linear task, a quick fix or a yes/no question; only when several ideas or options are on the table."
39
+ },
40
+ {
41
+ "kind": "summarize-the-session",
42
+ "phase": "synthesize",
43
+ "title": "Write the session summary",
44
+ "summary": "Read the session outline, then save a short written summary of what changed and what was decided to the Constellation session.",
45
+ "contextIn": [
46
+ "session-summary",
47
+ "recent-messages"
48
+ ],
49
+ "resultOut": "session-summary",
50
+ "reach": "mcp",
51
+ "handoffTool": "summarize",
52
+ "related": [
53
+ "map-the-idea"
54
+ ],
55
+ "restraint": "Only at a natural stopping point with real progress; not mid-task and not twice in one stretch of work."
56
+ }
57
+ ],
58
+ "skills": [
59
+ {
60
+ "name": "constellation-desk",
61
+ "path": "skills/constellation-desk/SKILL.md"
62
+ }
63
+ ]
64
+ },
65
+ "requires": {
66
+ "egress": "none"
67
+ }
68
+ }
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: constellation-desk
3
+ description: When to offer the Constellation idea map during a conversation - map ideas on the board and write the session summary. Works inside Gnomon as Desk offers and anywhere else as plain suggestions.
4
+ ---
5
+
6
+ # Constellation on the Desk
7
+
8
+ Constellation is the user's local idea map: Spaces hold sessions, and each session holds a map of stars and beams. This playbook says when to offer it. It never acts on its own: every step is an offer the human accepts or declines.
9
+
10
+ ## The offers
11
+
12
+ | Phase | Offer | Tool | Offer it when |
13
+ |---|---|---|---|
14
+ | diverge | `map-the-idea` | `apply` | Several ideas or options are on the table and seeing them side by side would help. Read the map with `get` first. |
15
+ | synthesize | `summarize-the-session` | `summarize` | The work reached a natural stopping point with real progress. Read `summary` (the outline) before writing one. |
16
+
17
+ ## When NOT to offer
18
+
19
+ - A single linear task, a quick fix, or a yes/no question.
20
+ - Mid-task, or when a summary was already written in this stretch of work.
21
+ - After the human said "not now" to Constellation in this conversation: stay quiet for the rest of it.
22
+
23
+ ## Inside Gnomon
24
+
25
+ When an offer fits, end that reply with at most one offer block (never more than one per reply across all mods). Name this mod and one of the kinds above, and ask one short question:
26
+
27
+ ```offer
28
+ {"mod": "gnomon.constellation", "kind": "map-the-idea", "question": "Want me to map these options on the Constellation board?"}
29
+ ```
30
+
31
+ The kernel shows it on the Desk. Call the tool only after the human opens it.
32
+
33
+ ## Outside Gnomon
34
+
35
+ There is no Desk. Make the same offer as one plain sentence at the end of the reply ("I can lay these options out on your Constellation map, want me to?") and call the tool only if the user says yes. If the Constellation MCP server is not connected, mention it once and move on.
@@ -0,0 +1,10 @@
1
+ # ledger (first-party Gnomon mod)
2
+
3
+ The Ledger is a context and budget meter with two views.
4
+
5
+ - **ledger.strip** (`composer.footer`): one line for the focused session, for example `182k in · 9k out · 71% cache · $0.42 · ⇣ 2 compactions (190k → 41k)`. It then shows the budget meter as `n of m` and any breaker or denied permit. The cap at the start carries the session's project hue (never as text). The line turns amber at a warning rung or an over-permit row, and vermilion when a breaker opens or a permit is denied. With reduced motion the meter is plain text (no fill element at all).
6
+ - **ledger.sheet** (`sidebar`): one row per session (in, out, cache share, cost, compactions), the total, then the same meter and alarms.
7
+
8
+ `in` counts every input token (fresh plus cache reads and writes), and `cache` is the share served from the cache. The split and the cost come from Claude's `result.usage`. A local session shows its token total only.
9
+
10
+ Every number comes from the kernel's numbers-only feed (`#mod-host` projection v2, granted by `log.read`). No text, path or tool input reaches the mod. Install it with `gnomon mod install packages/gnomon-mods/ledger`, then approve the card in the GUI. Its packageHash is pinned in the kernel's first-party list, so editing any file here needs a new pin (`npm run repin:first-party` in packages/gnomon).
@@ -0,0 +1,28 @@
1
+ {
2
+ "schemaVersion": "display-block:v1",
3
+ "type": "Box",
4
+ "title": "Ledger · this session",
5
+ "direction": "row",
6
+ "children": [
7
+ {
8
+ "type": "Text",
9
+ "text": "●",
10
+ "tone": "hue-2"
11
+ },
12
+ {
13
+ "type": "Text",
14
+ "text": "182k in · 9k out · 71% cache · $0.42 · ⇣ 2 compactions (190k → 41k)",
15
+ "tone": "deny"
16
+ },
17
+ {
18
+ "type": "Text",
19
+ "text": "✕ breaker open · tokens",
20
+ "tone": "deny"
21
+ },
22
+ {
23
+ "type": "Text",
24
+ "text": "✕ permit denied · api-billing-path",
25
+ "tone": "deny"
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,80 @@
1
+ {
2
+ "view": "ledger.strip",
3
+ "input": {
4
+ "fixtureEvents": [
5
+ {
6
+ "kind": "session.open",
7
+ "payload": {
8
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA",
9
+ "backend": "claude",
10
+ "project": 2
11
+ }
12
+ },
13
+ {
14
+ "kind": "turn.usage",
15
+ "payload": {
16
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA",
17
+ "tokens": 40000
18
+ }
19
+ },
20
+ {
21
+ "kind": "turn.result",
22
+ "payload": {
23
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA",
24
+ "tokens": 0,
25
+ "tokensIn": 12000,
26
+ "tokensOut": 4000,
27
+ "cacheRead": 90000,
28
+ "cacheWrite": 8000,
29
+ "costMicros": 210000
30
+ }
31
+ },
32
+ {
33
+ "kind": "session.compacted",
34
+ "payload": {
35
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA",
36
+ "tokensBefore": 180000,
37
+ "tokensAfter": 52000
38
+ }
39
+ },
40
+ {
41
+ "kind": "turn.result",
42
+ "payload": {
43
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA",
44
+ "tokens": 0,
45
+ "tokensIn": 15000,
46
+ "tokensOut": 5000,
47
+ "cacheRead": 39000,
48
+ "cacheWrite": 18000,
49
+ "costMicros": 210000
50
+ }
51
+ },
52
+ {
53
+ "kind": "session.compacted",
54
+ "payload": {
55
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA",
56
+ "tokensBefore": 190000,
57
+ "tokensAfter": 41000
58
+ }
59
+ },
60
+ {
61
+ "kind": "budget.breaker.open",
62
+ "payload": {
63
+ "dimension": "tokens"
64
+ }
65
+ },
66
+ {
67
+ "kind": "permit.deny",
68
+ "payload": {
69
+ "dimension": "api-billing-path"
70
+ }
71
+ }
72
+ ],
73
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA"
74
+ },
75
+ "width": 80,
76
+ "prefs": {
77
+ "motion": "full",
78
+ "theme": "day"
79
+ }
80
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "schemaVersion": "display-block:v1",
3
+ "type": "Box",
4
+ "title": "Ledger",
5
+ "direction": "column",
6
+ "children": [
7
+ {
8
+ "type": "Table",
9
+ "columns": [
10
+ "session",
11
+ "in",
12
+ "out",
13
+ "cache",
14
+ "cost",
15
+ "⇣ compactions"
16
+ ],
17
+ "rows": [
18
+ [
19
+ "…ZZZZZA",
20
+ "182k",
21
+ "9k",
22
+ "71%",
23
+ "$0.42",
24
+ "2 (190k → 41k)"
25
+ ],
26
+ [
27
+ "…ZZZZZB (ended)",
28
+ "33k",
29
+ "-",
30
+ "-",
31
+ "-",
32
+ "1 (31k → 6k)"
33
+ ]
34
+ ]
35
+ },
36
+ {
37
+ "type": "Text",
38
+ "text": "total · 182k in · 9k out · 71% cache · 33k local · $0.42 · ⇣ 3 compactions (31k → 6k)",
39
+ "tone": "quiet"
40
+ }
41
+ ]
42
+ }
@@ -0,0 +1,103 @@
1
+ {
2
+ "view": "ledger.sheet",
3
+ "input": {
4
+ "fixtureEvents": [
5
+ {
6
+ "kind": "session.open",
7
+ "payload": {
8
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA",
9
+ "backend": "claude",
10
+ "project": 2
11
+ }
12
+ },
13
+ {
14
+ "kind": "turn.usage",
15
+ "payload": {
16
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA",
17
+ "tokens": 40000
18
+ }
19
+ },
20
+ {
21
+ "kind": "turn.result",
22
+ "payload": {
23
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA",
24
+ "tokens": 0,
25
+ "tokensIn": 12000,
26
+ "tokensOut": 4000,
27
+ "cacheRead": 90000,
28
+ "cacheWrite": 8000,
29
+ "costMicros": 210000
30
+ }
31
+ },
32
+ {
33
+ "kind": "session.compacted",
34
+ "payload": {
35
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA",
36
+ "tokensBefore": 180000,
37
+ "tokensAfter": 52000
38
+ }
39
+ },
40
+ {
41
+ "kind": "turn.result",
42
+ "payload": {
43
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA",
44
+ "tokens": 0,
45
+ "tokensIn": 15000,
46
+ "tokensOut": 5000,
47
+ "cacheRead": 39000,
48
+ "cacheWrite": 18000,
49
+ "costMicros": 210000
50
+ }
51
+ },
52
+ {
53
+ "kind": "session.compacted",
54
+ "payload": {
55
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZA",
56
+ "tokensBefore": 190000,
57
+ "tokensAfter": 41000
58
+ }
59
+ },
60
+ {
61
+ "kind": "session.open",
62
+ "payload": {
63
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZB",
64
+ "backend": "local",
65
+ "project": 5
66
+ }
67
+ },
68
+ {
69
+ "kind": "turn.usage",
70
+ "payload": {
71
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZB",
72
+ "tokens": 30000
73
+ }
74
+ },
75
+ {
76
+ "kind": "turn.result",
77
+ "payload": {
78
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZB",
79
+ "tokens": 2500
80
+ }
81
+ },
82
+ {
83
+ "kind": "session.compacted",
84
+ "payload": {
85
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZB",
86
+ "tokensBefore": 31000,
87
+ "tokensAfter": 6000
88
+ }
89
+ },
90
+ {
91
+ "kind": "session.exited",
92
+ "payload": {
93
+ "hsid": "hsid:v1:01HZZZZZZZZZZZZZZZZZZZZZZB"
94
+ }
95
+ }
96
+ ]
97
+ },
98
+ "width": 80,
99
+ "prefs": {
100
+ "motion": "full",
101
+ "theme": "day"
102
+ }
103
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "schemaVersion": "display-block:v1",
3
+ "type": "Box",
4
+ "title": "Ledger · this session",
5
+ "direction": "row",
6
+ "children": [
7
+ {
8
+ "type": "Text",
9
+ "text": "●",
10
+ "tone": "hue-2"
11
+ },
12
+ {
13
+ "type": "Text",
14
+ "text": "182k in · 9k out · 71% cache · $0.42 · ⇣ 2 compactions (190k → 41k)",
15
+ "tone": "warn"
16
+ },
17
+ {
18
+ "type": "Progress",
19
+ "value": 150000,
20
+ "max": 150000,
21
+ "label": "tokens 182k of 150k (permit)"
22
+ }
23
+ ]
24
+ }