@bongos/core 1.24.28 → 1.24.30

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 (63) hide show
  1. package/.bongos-core.json +100 -70
  2. package/.claude/skills/builder-claim/SKILL.md +1 -1
  3. package/.claude/skills/builder-ship/SKILL.md +1 -1
  4. package/clients/bongos-client/README.md +1 -1
  5. package/clients/bongos-client/bongos-client.global.js +4 -0
  6. package/clients/bongos-client/index.cjs +4 -0
  7. package/clients/bongos-client/index.d.ts +4 -0
  8. package/clients/bongos-client/index.mjs +4 -0
  9. package/docs/api/openapi.json +36 -1
  10. package/docs/api-reference.md +7 -1
  11. package/docs/copy-inventory.md +247 -280
  12. package/docs/copy-registry.json +381 -691
  13. package/docs/module-api-changelog.md +4 -0
  14. package/docs/page-inventory.json +5 -10
  15. package/docs/page-readings.json +327 -393
  16. package/modules/builder-settings/interaction-prefs.js +2 -2
  17. package/modules/government/db.js +29 -0
  18. package/modules/government/docket.js +61 -4
  19. package/modules/government/routes/government.js +2 -0
  20. package/modules/hall-ui/public/builders.js +3 -79
  21. package/modules/hall-ui/public/hall-render.js +6 -81
  22. package/modules/hall-ui/public/hall-widgets.js +4 -45
  23. package/modules/hall-ui/public/home-city-draw.js +275 -0
  24. package/modules/hall-ui/public/home-city.css +66 -0
  25. package/modules/hall-ui/public/home-city.js +223 -0
  26. package/modules/hall-ui/public/index.html +5 -6
  27. package/modules/hall-ui/public/index.states.json +73 -8
  28. package/modules/hall-ui/public/my-agents.js +35 -2
  29. package/modules/hall-ui/public/palette.js +8 -0
  30. package/modules/hall-ui/public/settings-moved.js +41 -0
  31. package/modules/hall-ui/public/settings.css +4 -28
  32. package/modules/hall-ui/public/settings.html +19 -107
  33. package/modules/hall-ui/public/settings.js +20 -215
  34. package/modules/hall-ui/public/settings.states.json +2 -4
  35. package/modules/hall-ui/records/home-city.md +66 -0
  36. package/modules/hall-ui/records/my-agents.md +3 -2
  37. package/modules/hall-ui/records/panel-family.md +2 -0
  38. package/modules/lifecycle/home-city.js +340 -0
  39. package/modules/lifecycle/module.json +1 -0
  40. package/modules/lifecycle/routes/home-city.js +29 -0
  41. package/modules/lifecycle/ship-card.js +2 -2
  42. package/package-lock.json +2 -2
  43. package/package.json +1 -1
  44. package/release-notes.json +16 -0
  45. package/src/module-api.js +1 -1
  46. package/tests/claim_from_task_and_home.mjs +8 -43
  47. package/tests/government_tidy_up.mjs +21 -24
  48. package/tests/hall_board_world.mjs +10 -12
  49. package/tests/hall_home_ships_bounded.mjs +4 -3
  50. package/tests/hall_my_agents.mjs +88 -2
  51. package/tests/hall_palette.mjs +12 -0
  52. package/tests/hall_settings_world.mjs +64 -100
  53. package/tests/helpers.mjs +6 -5
  54. package/tests/home_bounded_reads.mjs +16 -31
  55. package/tests/home_city.mjs +385 -0
  56. package/tests/home_city_widget.mjs +120 -0
  57. package/tests/interaction_posture.mjs +3 -1
  58. package/tests/leaderboard_roles.mjs +3 -2
  59. package/tests/speciality_offer_ui.mjs +6 -5
  60. package/tests/speciality_walkthrough_ui.mjs +7 -5
  61. package/modules/hall-ui/public/settings-interaction.js +0 -132
  62. package/modules/hall-ui/public/settings-law-locks.js +0 -53
  63. package/modules/hall-ui/public/settings-render.js +0 -138
@@ -1,132 +0,0 @@
1
- // modules/hall-ui/public/settings-interaction.js — the "How Claude talks to you"
2
- // panel on /builders/settings (task 2010's profile, delivered by task 1002514).
3
- //
4
- // WHY THE PANEL EXISTS. The interaction profile shipped complete except for a
5
- // way to set it: interaction-prefs.js stored it, GET/PATCH /me/interaction served
6
- // it, and the conductor hook already injected the resolved contract into every
7
- // session — but settings.js had 18 `wandering` references and 0 `interaction`
8
- // ones, so the profile was reachable only by a raw API call. In practice that
9
- // meant nobody had one.
10
- //
11
- // Its own file rather than more of settings.js, on the settings-sessions.js
12
- // precedent and for the same stated reason: settings.js sits against the
13
- // 1500-line size ratchet (adding this inline pushed it to 1601 and regressed the
14
- // budget). The panel is self-contained — it shares nothing with the rest of the
15
- // page but the `window.OTB` toolkit and the generated API client, both globals
16
- // loaded before it.
17
- //
18
- // THE ONE SHAPE DIFFERENCE FROM WANDERING, which is the sibling to read first:
19
- // wandering is a single level, so it is one radiogroup. This payload is a FIELD
20
- // CATALOG — several independent fields, each with its own value set — so each
21
- // field is its own single-select and a PATCH carries only the field that changed
22
- // ({ <field>: <value> }). The route merges, so the untouched fields keep their
23
- // values and a field never explicitly set keeps falling back to its default.
24
-
25
- (() => {
26
- 'use strict';
27
-
28
- const API = '/api/bongos';
29
- const { escapeHtml, $ } = window.OTB;
30
- const toast = (msg) => window.OTB.toast(msg);
31
- const api = window.BongosClient.createClient({ baseUrl: '', credentials: 'same-origin', throwOnError: false });
32
-
33
- const toAuth = () => window.location.assign(`${API}/auth/web/start`);
34
- // the project's laws on these fields (task 1004724): settings-law-locks.js
35
- const Laws = window.OTBSettingsLaws || { lockFor: () => null, allows: () => true, lawLine: () => '', tagFor: () => '' };
36
-
37
- function render(data) {
38
- const wrap = $('#interaction-rows');
39
- if (!wrap) return;
40
- const fields = Array.isArray(data && data.fields) ? data.fields : [];
41
- const eff = (data && data.effective) || {};
42
- if (fields.length === 0) {
43
- wrap.innerHTML = window.OTBKit.emptyStateHtml('No interaction settings registered.');
44
- return;
45
- }
46
- // A fieldset per field so the field's label is its radiogroup's accessible
47
- // name — four independent single-selects, not one flat list of radios.
48
- wrap.innerHTML = fields.map((f) => {
49
- const chosenValue = eff[f.field];
50
- const lock = Laws.lockFor(data, f.field);
51
- const rows = (f.values || []).map((v) => {
52
- const id = `interaction-${f.field}-${v.value}`;
53
- const chosen = v.value === chosenValue;
54
- const barred = !Laws.allows(lock, v.value);
55
- // No "default" tag here, deliberately: interaction-prefs.js already ends
56
- // every default value's LABEL with "(default)", so adding the row tag the
57
- // wandering picker uses rendered "Non-technical (default) · DEFAULT".
58
- // One source for the word, and it is the label the API already ships.
59
- return (
60
- `<label class="prow prow--pick" for="${id}"${barred ? ' aria-disabled="true"' : ''}>` +
61
- `<input class="pradio" type="radio" name="interaction-${escapeHtml(f.field)}" id="${id}"` +
62
- ` value="${escapeHtml(v.value)}" data-field="${escapeHtml(f.field)}"${chosen ? ' checked' : ''}${barred ? ' disabled' : ''}>` +
63
- '<span class="prow__main">' +
64
- `<span class="prow__label">${escapeHtml(v.label)}</span>` +
65
- '</span></label>'
66
- );
67
- }).join('');
68
- return (
69
- `<fieldset class="pgroup" id="interaction-field-${escapeHtml(f.field)}">` +
70
- `<legend class="prow__label">${escapeHtml(f.label)}</legend>` +
71
- `<p class="prow__desc">${escapeHtml(f.blurb || '')}</p>` +
72
- (lock ? `<p class="prow__desc prow__law" data-law="${escapeHtml(String(lock.law_no || ''))}">${escapeHtml(Laws.lawLine(lock))}</p>` : '') +
73
- rows +
74
- '</fieldset>'
75
- );
76
- }).join('');
77
- }
78
-
79
- // The human label for a saved value, for the status line — falls back to the
80
- // raw value rather than going blank if the catalog and the payload disagree.
81
- function labelFor(data, field, value) {
82
- const f = (Array.isArray(data && data.fields) ? data.fields : []).find((x) => x.field === field);
83
- const v = f && (f.values || []).find((x) => x.value === value);
84
- return (v && v.label) || value;
85
- }
86
-
87
- async function save(field, value, input) {
88
- const status = $('#interaction-status');
89
- if (status) status.textContent = 'Saving…';
90
- if (input) input.disabled = true;
91
- try {
92
- const res = await api.request('PATCH', `${API}/me/interaction`, { body: { [field]: value } });
93
- if (res.status === 401) { toAuth(); return; }
94
- if (!res.ok) throw new Error(`interaction PATCH → ${res.status}`);
95
- render(res.data);
96
- const msg = `Saved — ${labelFor(res.data, field, value)}.`;
97
- if (status) status.textContent = msg;
98
- toast(msg);
99
- } catch (err) {
100
- console.error('[settings] save interaction failed', err);
101
- if (status) status.textContent = 'Could not save. Try again.';
102
- toast('Could not save your interaction profile. Try again.');
103
- if (input) input.disabled = false;
104
- }
105
- }
106
-
107
- async function setup() {
108
- try {
109
- const res = await api.request('GET', `${API}/me/interaction`);
110
- if (res.status === 401) { toAuth(); return; }
111
- if (!res.ok) throw new Error(`interaction GET → ${res.status}`);
112
- render(res.data);
113
- $('#interaction-scroll').hidden = false;
114
- // Delegate the change so post-render radios stay wired.
115
- $('#interaction-rows').addEventListener('change', (e) => {
116
- const r = e.target.closest('.pradio');
117
- if (!r || r.disabled || !r.checked) return;
118
- const field = r.getAttribute('data-field');
119
- if (field) save(field, r.value, r);
120
- });
121
- } catch (err) {
122
- // Its own try/catch, like every other section: a failing endpoint shows this
123
- // panel's own error rather than taking the settings page down with it.
124
- console.error('[settings] failed to load interaction', err);
125
- $('#interaction-scroll').hidden = false;
126
- $('#interaction-rows').innerHTML =
127
- '<p class="voices-empty">Your interaction profile could not be loaded. Reload the page to try again.</p>';
128
- }
129
- }
130
-
131
- setup();
132
- })();
@@ -1,53 +0,0 @@
1
- // modules/hall-ui/public/settings-law-locks.js — the project's LAWS on the
2
- // /builders/settings rows (task 1004724).
3
- //
4
- // A law passed by the project's government may lock a personal setting: FIX it
5
- // to one value, or CAP it. The server enforces the lock (law-locks.js, task
6
- // 1004707) and every settings read already reports it as `law_locks` — this file
7
- // is only how the page SAYS so: a value the lock does not allow is a disabled
8
- // row tagged with the law, and the setting carries one line naming the law.
9
- // Shared by the "How Claude talks to you" panel (settings-interaction.js) and the
10
- // wandering picker (settings.js). The page decides nothing; a write the law
11
- // forbids is refused by the route either way (409 locked_by_law).
12
- //
13
- // PURE: no DOM, no fetch. tests/settings_law_locks.mjs requires it in node.
14
-
15
- (function (root) {
16
- 'use strict';
17
-
18
- const str = (v) => (v == null ? '' : String(v));
19
-
20
- // The lock a law puts on `setting`, from a settings read's `law_locks`, or null.
21
- // Only an ENFORCED lock counts: one naming no real value locks nothing.
22
- function lockFor(data, setting) {
23
- const locks = data && Array.isArray(data.law_locks) ? data.law_locks : [];
24
- return locks.find((l) => l && l.enforced === true && str(l.setting) === str(setting)) || null;
25
- }
26
-
27
- // May `value` still be picked under `lock`? No lock: yes.
28
- function allows(lock, value) {
29
- if (!lock) return true;
30
- if (!Array.isArray(lock.allowed)) return true;
31
- return lock.allowed.map(str).includes(str(value));
32
- }
33
-
34
- // The one line the setting carries: which law, and what it does to the row.
35
- function lawLine(lock) {
36
- if (!lock) return '';
37
- const n = lock.law_no != null ? 'Law ' + str(lock.law_no) : 'A law';
38
- const text = str(lock.law_text).trim();
39
- const what = Object.prototype.hasOwnProperty.call(lock, 'value')
40
- ? ' sets this for every agent in the project'
41
- : ' caps this for every agent in the project';
42
- return n + what + (text ? ': "' + text + '".' : '.') + ' Only a vote can change it.';
43
- }
44
-
45
- // The tag a row the lock rules out wears.
46
- function tagFor(lock) {
47
- return lock ? 'set by ' + (lock.law_no != null ? 'law ' + str(lock.law_no) : 'a law') : '';
48
- }
49
-
50
- const api = { lockFor, allows, lawLine, tagFor };
51
- root.OTBSettingsLaws = api;
52
- if (typeof module !== 'undefined' && module.exports) module.exports = api;
53
- }(typeof window !== 'undefined' ? window : globalThis));
@@ -1,138 +0,0 @@
1
- // modules/hall-ui/public/settings-render.js — the "Rendering" panel on
2
- // /builders/settings: how rich the deterministic session cards should be
3
- // (task 1279's on/off toggle, a four-rung ladder since task 1002518).
4
- //
5
- // WHY ITS OWN FILE. It began as one row with one switch inside settings.js. As a
6
- // ladder it needs a labelled option per rung plus a line explaining what the
7
- // default resolved to and why — and settings.js sits against the 1500-line size
8
- // ratchet, so the panel moved out on the settings-sessions.js / settings-
9
- // interaction.js precedent. Extracting it took settings.js from 1493 to 1425.
10
- //
11
- // THE SHAPE WORTH KNOWING: unlike every other picker on this page, "default"
12
- // here is not a constant — it is DERIVED from the builder's interaction profile
13
- // (render-prefs.js maps technical level, machinery and reply length onto a
14
- // rung). So the default option cannot just say "default"; it has to say which
15
- // rung it landed on and which profile fields put it there, or the reader sees a
16
- // choice the system made for reasons it never stated. The server ships both
17
- // facts (`derived_level`, `derived_reason`) precisely so this panel can.
18
- //
19
- // PATCH body is { widgets: <level> | null } — null clears the override and
20
- // returns the builder to the derived rung. The route still accepts the legacy
21
- // boolean, which is what lets an older hall build keep working against a newer
22
- // core; this panel only ever sends levels or null.
23
-
24
- (() => {
25
- 'use strict';
26
-
27
- const API = '/api/bongos';
28
- const { escapeHtml, $ } = window.OTB;
29
- const toast = (msg) => window.OTB.toast(msg);
30
- const api = window.BongosClient.createClient({ baseUrl: '', credentials: 'same-origin', throwOnError: false });
31
-
32
- const toAuth = () => window.location.assign(`${API}/auth/web/start`);
33
-
34
- // The "let the profile decide" option carries the sentinel value '' rather
35
- // than a level, so the change handler can tell "clear my override" from
36
- // "pin me to the rung that happens to be the derived one today". They differ:
37
- // a pinned rung survives a later change to the interaction profile.
38
- const USE_DEFAULT = '';
39
-
40
- function labelForLevel(data, level) {
41
- const found = (Array.isArray(data && data.levels) ? data.levels : []).find((l) => l.value === level);
42
- return (found && found.label) || level;
43
- }
44
-
45
- function render(data) {
46
- const wrap = $('#render-rows');
47
- if (!wrap) return;
48
- const levels = Array.isArray(data && data.levels) ? data.levels : [];
49
- if (levels.length === 0) {
50
- wrap.innerHTML = window.OTBKit.emptyStateHtml('No rendering options registered.');
51
- return;
52
- }
53
- const override = data.override_level || null;
54
- const derived = data.derived_level;
55
-
56
- // The default row first, so the reader meets "you already have a sensible
57
- // answer, here is what it is" before the list of ways to overrule it.
58
- const defaultRow =
59
- `<label class="prow prow--pick" for="render-default">` +
60
- `<input class="pradio" type="radio" name="render-level" id="render-default"` +
61
- ` value="${USE_DEFAULT}"${override === null ? ' checked' : ''}>` +
62
- '<span class="prow__main">' +
63
- `<span class="prow__label">Match my interaction profile` +
64
- `<span class="prow__tag">default (${escapeHtml(labelForLevel(data, derived))})</span></span>` +
65
- `<span class="prow__desc">${escapeHtml(data.derived_reason || '')} Change that profile and this follows it.</span>` +
66
- '</span></label>';
67
-
68
- const rows = levels.map((l) => {
69
- const id = `render-level-${l.value}`;
70
- return (
71
- `<label class="prow prow--pick" for="${id}">` +
72
- `<input class="pradio" type="radio" name="render-level" id="${id}"` +
73
- ` value="${escapeHtml(l.value)}"${override === l.value ? ' checked' : ''}>` +
74
- '<span class="prow__main">' +
75
- `<span class="prow__label">${escapeHtml(l.label)}</span>` +
76
- `<span class="prow__desc">${escapeHtml(l.blurb)}</span>` +
77
- '</span></label>'
78
- );
79
- }).join('');
80
-
81
- wrap.innerHTML =
82
- '<fieldset class="pgroup" id="render-level-group">' +
83
- '<legend class="prow__label">Session card richness</legend>' +
84
- '<p class="prow__desc">How much the claim and ship cards show. Richer cards explain more and cost more tokens; the confirmation line is always there whichever you pick.</p>' +
85
- defaultRow + rows +
86
- '</fieldset>';
87
- }
88
-
89
- async function save(value, input) {
90
- const status = $('#render-status');
91
- if (status) status.textContent = 'Saving…';
92
- if (input) input.disabled = true;
93
- try {
94
- // The sentinel becomes an explicit null — the route's clear-the-override
95
- // signal. Anything else is a level and is stored as the override.
96
- const body = { widgets: value === USE_DEFAULT ? null : value };
97
- const res = await api.request('PATCH', `${API}/me/render`, { body });
98
- if (res.status === 401) { toAuth(); return; }
99
- if (!res.ok) throw new Error(`render PATCH → ${res.status}`);
100
- render(res.data);
101
- const msg = value === USE_DEFAULT
102
- ? `Saved — following your interaction profile (${labelForLevel(res.data, res.data.derived_level)}).`
103
- : `Saved — ${labelForLevel(res.data, value)}.`;
104
- if (status) status.textContent = msg;
105
- toast(msg);
106
- } catch (err) {
107
- console.error('[settings] save render failed', err);
108
- if (status) status.textContent = 'Could not save. Try again.';
109
- toast('Could not save your rendering preference. Try again.');
110
- if (input) input.disabled = false;
111
- }
112
- }
113
-
114
- async function setup() {
115
- try {
116
- const res = await api.request('GET', `${API}/me/render`);
117
- if (res.status === 401) { toAuth(); return; }
118
- if (!res.ok) throw new Error(`render GET → ${res.status}`);
119
- render(res.data);
120
- $('#render-scroll').hidden = false;
121
- // Delegate the change so post-render radios stay wired.
122
- $('#render-rows').addEventListener('change', (e) => {
123
- const r = e.target.closest('.pradio');
124
- if (!r || r.disabled || !r.checked) return;
125
- save(r.value, r);
126
- });
127
- } catch (err) {
128
- // Its own try/catch, like every other section: a failing endpoint shows
129
- // this panel's own error rather than taking the settings page down.
130
- console.error('[settings] failed to load render', err);
131
- $('#render-scroll').hidden = false;
132
- $('#render-rows').innerHTML =
133
- '<p class="voices-empty">Your rendering preference could not be loaded. Reload the page to try again.</p>';
134
- }
135
- }
136
-
137
- setup();
138
- })();