@bongos/core 1.21.112 → 1.21.114
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +616 -186
- package/.claude/skills/ask-for-help/SKILL.md +1 -1
- package/.claude/skills/bongos-feedback/SKILL.md +1 -1
- package/.claude/skills/builder-sequence/SKILL.md +1 -1
- package/.claude/skills/builder-ship/SKILL.md +1 -0
- package/GOVERNMENT.md +128 -11
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +28 -0
- package/clients/bongos-client/index.cjs +28 -0
- package/clients/bongos-client/index.d.ts +45 -2
- package/clients/bongos-client/index.mjs +28 -0
- package/docs/adr/0372-an-ask-is-stored-per-docket-matter-and-the-server-refuses-slop.md +33 -0
- package/docs/adr/0373-an-ai-session-files-a-blocker-with-an-ask.md +31 -0
- package/docs/adr/0374-a-building-lead-is-a-held-position-appointed-by-a-people-matter.md +30 -0
- package/docs/adr/0375-who-may-build-without-a-vote-comes-from-the-form-of-government.md +38 -0
- package/docs/adr/0376-every-ask-is-routed-to-a-real-seat-on-the-server.md +30 -0
- package/docs/adr/0377-governance-is-a-held-position-read-on-me.md +24 -0
- package/docs/adr/0378-a-leads-build-outs-are-rows-built-at-once-or-by-a-passed-vote.md +33 -0
- package/docs/adr/0379-the-treasury-is-pots-per-goal-a-reserve-and-borrowing-over-real-spend.md +32 -0
- package/docs/adr/0380-a-strictness-preset-is-a-bundle-of-laws-switched-by-one-laws-vote.md +26 -0
- package/docs/adr/README.md +10 -1
- package/docs/api/openapi.json +989 -87
- package/docs/api-reference.md +18 -4
- package/docs/copy-inventory.md +343 -262
- package/docs/copy-registry.json +1223 -426
- package/docs/file-map.md +2 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/packs/engineer.md +2 -1
- package/docs/page-inventory.json +91 -6
- package/docs/page-readings.json +5017 -4458
- package/modules/builder-settings/law-locks.js +202 -0
- package/modules/builder-settings/routes/settings.js +66 -6
- package/modules/builder-settings/settings.js +7 -0
- package/modules/discord/ship-broadcast.js +22 -13
- package/modules/government/asks-db.js +54 -0
- package/modules/government/asks.js +269 -0
- package/modules/government/board-idea-reads.js +68 -0
- package/modules/government/board.js +21 -57
- package/modules/government/build-rights.js +148 -0
- package/modules/government/buildouts.js +160 -0
- package/modules/government/counts.js +81 -0
- package/modules/government/db.js +242 -0
- package/modules/government/decision-board.js +234 -8
- package/modules/government/decision-rules.js +2 -0
- package/modules/government/docket.js +114 -6
- package/modules/government/law-board.js +131 -0
- package/modules/government/laws.js +241 -0
- package/modules/government/leads.js +170 -0
- package/modules/government/migrations/government_027_laws.sql +143 -0
- package/modules/government/migrations/government_028_asks.sql +57 -0
- package/modules/government/migrations/government_029_leads.sql +135 -0
- package/modules/government/migrations/government_030_buildouts.sql +111 -0
- package/modules/government/migrations/government_031_treasury.sql +107 -0
- package/modules/government/module.json +2 -1
- package/modules/government/pollers/treasury-borrow.js +62 -0
- package/modules/government/positions.js +107 -0
- package/modules/government/presets.js +149 -0
- package/modules/government/routes/government.js +425 -2
- package/modules/government/routing.js +154 -0
- package/modules/government/treasury-db.js +166 -0
- package/modules/government/treasury.js +241 -0
- package/modules/hall-ui/public/atlas.html +2 -2
- package/modules/hall-ui/public/blockers.html +2 -2
- package/modules/hall-ui/public/board-room.html +2 -2
- package/modules/hall-ui/public/board-room.js +2 -0
- package/modules/hall-ui/public/city-draw.js +604 -11
- package/modules/hall-ui/public/city.css +157 -1
- package/modules/hall-ui/public/city.html +37 -7
- package/modules/hall-ui/public/city.js +441 -21
- package/modules/hall-ui/public/city.states.json +110 -5
- package/modules/hall-ui/public/collab.html +2 -2
- package/modules/hall-ui/public/commands.html +2 -2
- package/modules/hall-ui/public/copy-desk.html +2 -2
- package/modules/hall-ui/public/craft-mode.js +112 -29
- package/modules/hall-ui/public/deploy.html +2 -2
- package/modules/hall-ui/public/diagram-viewer.css +12 -0
- package/modules/hall-ui/public/diagram-viewer.js +4 -0
- package/modules/hall-ui/public/diagrams.html +2 -2
- package/modules/hall-ui/public/drachmae.html +2 -2
- package/modules/hall-ui/public/fleet.html +2 -2
- package/modules/hall-ui/public/gate.html +2 -2
- package/modules/hall-ui/public/goal-map.html +2 -2
- package/modules/hall-ui/public/goals.html +2 -2
- package/modules/hall-ui/public/gov-feeds.js +141 -0
- package/modules/hall-ui/public/gov-living.js +1002 -0
- package/modules/hall-ui/public/gov-positions.js +97 -0
- package/modules/hall-ui/public/government.html +2 -2
- package/modules/hall-ui/public/hall-render.js +3 -0
- package/modules/hall-ui/public/idea.html +2 -2
- package/modules/hall-ui/public/ideas.html +2 -2
- package/modules/hall-ui/public/index.html +3 -3
- package/modules/hall-ui/public/modules.html +2 -2
- package/modules/hall-ui/public/my-agents.css +334 -0
- package/modules/hall-ui/public/my-agents.html +72 -0
- package/modules/hall-ui/public/my-agents.js +603 -0
- package/modules/hall-ui/public/my-agents.states.json +37 -0
- package/modules/hall-ui/public/primer.html +2 -2
- package/modules/hall-ui/public/profile-connections.js +40 -28
- package/modules/hall-ui/public/profile.html +2 -2
- package/modules/hall-ui/public/project-settings.html +2 -2
- package/modules/hall-ui/public/ranks.html +2 -2
- package/modules/hall-ui/public/roadmap.html +2 -2
- package/modules/hall-ui/public/room.css +8 -0
- package/modules/hall-ui/public/roster.html +2 -2
- package/modules/hall-ui/public/sessions.html +2 -2
- package/modules/hall-ui/public/settings.html +2 -2
- package/modules/hall-ui/public/shell.js +116 -16
- package/modules/hall-ui/public/street-card.js +18 -1
- package/modules/hall-ui/public/street-deck.css +299 -0
- package/modules/hall-ui/public/street-deck.js +1145 -0
- package/modules/hall-ui/public/street-depth.js +712 -0
- package/modules/hall-ui/public/street-desk.js +5 -2
- package/modules/hall-ui/public/street-draw.js +109 -16
- package/modules/hall-ui/public/street-inside.js +137 -22
- package/modules/hall-ui/public/street-office.js +705 -0
- package/modules/hall-ui/public/street.css +161 -11
- package/modules/hall-ui/public/street.html +41 -11
- package/modules/hall-ui/public/street.js +78 -66
- package/modules/hall-ui/public/street.states.json +129 -21
- package/modules/hall-ui/public/studio.html +2 -2
- package/modules/hall-ui/public/style.css +5 -1
- package/modules/hall-ui/public/task.html +2 -2
- package/modules/hall-ui/public/thinking.html +2 -2
- package/modules/hall-ui/public/tweak-editor.html +2 -2
- package/modules/hall-ui/public/watch.html +2 -2
- package/modules/hall-ui/public/watch.js +14 -1
- package/modules/hall-ui/public/work.html +2 -2
- package/modules/hall-ui/records/living-city.md +57 -0
- package/modules/hall-ui/records/my-agents.md +21 -0
- package/modules/hall-ui/records/oversight-family.md +1 -0
- package/modules/hall-ui/records/profile-connections.md +10 -0
- package/modules/hall-ui/records/reading-rooms.md +2 -0
- package/modules/ideas/routes/blockers.js +92 -3
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/moving.js +17 -1
- package/modules/lifecycle/routes/moving.js +6 -0
- package/modules/lifecycle/routes/project-settings.js +23 -0
- package/modules/onboarding/approval-broadcast.js +8 -0
- package/modules/onboarding/routes/access-requests.js +16 -6
- package/modules/platform-identity/connections.js +13 -2
- package/modules/platform-identity/routes/connections.js +7 -1
- package/modules/public-landing/public/account-private.states.json +2 -2
- package/modules/public-landing/public/account.html +65 -5
- package/modules/public-landing/public/account.states.json +3 -3
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp.json +13 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp.json +13 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp.json +13 -0
- package/modules/public-landing/public/assets/world.css +4 -4
- package/modules/public-landing/public/connections-empty.states.json +14 -0
- package/modules/public-landing/public/connections-inactive.states.json +14 -0
- package/modules/public-landing/public/connections.html +450 -0
- package/modules/public-landing/public/connections.probes.json +414 -0
- package/modules/public-landing/public/connections.states.json +17 -0
- package/modules/public-landing/public/contact.html +2 -2
- package/modules/public-landing/public/guild.html +2 -2
- package/modules/public-landing/public/index.html +2 -2
- package/modules/public-landing/public/invitations.html +2 -2
- package/modules/public-landing/public/privacy.html +2 -2
- package/modules/public-landing/public/projects.html +2 -2
- package/modules/public-landing/public/projects.probes.json +6 -2
- package/modules/public-landing/public/terms.html +2 -2
- package/modules/ui-design/kit/fixtures/connections-empty.json +12 -0
- package/modules/ui-design/kit/fixtures/connections-inactive.json +7 -0
- package/modules/ui-design/kit/fixtures/connections.json +22 -0
- package/modules/ui-design/kit/serve.js +95 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +128 -0
- package/scripts/discord/README.md +8 -5
- package/scripts/discord/post.js +12 -1
- package/scripts/gds/artifact-audit.js +20 -3
- package/scripts/gds/ask.js +164 -0
- package/scripts/gds/autobongos-run.js +20 -5
- package/scripts/gds/autobongos-tweak-lane.js +262 -0
- package/scripts/gds/autobongos-verify.js +21 -2
- package/scripts/gds/claim.js +2 -0
- package/scripts/gds/government-walk.js +638 -4
- package/scripts/gds/pulse.js +9 -6
- package/scripts/gds/ship-escalation.js +22 -3
- package/scripts/gds/ship-finish.js +5 -4
- package/scripts/gds/ship-merge-gate.js +18 -6
- package/scripts/gds/tweak-halt.js +1 -0
- package/scripts/hall-preview/README.md +41 -1
- package/scripts/hall-preview/pref-writes.js +140 -0
- package/scripts/hall-preview/refresh-fixtures.js +101 -7
- package/scripts/hall-preview/scrub-identity.js +366 -7
- package/scripts/hall-preview/server.js +27 -1
- package/scripts/render-diagrams.sh +4 -2
- package/src/bongos/auth-idp.js +6 -1
- package/src/bongos/project-settings.js +41 -0
- package/src/bongos/route-rank-check.js +27 -0
- package/src/bongos/routes/me.js +68 -7
- package/src/bongos/serve-internal.js +5 -1
- package/src/module-api.js +7 -1
- package/src/platform-server.js +10 -0
- package/tests/application_lifecycle.mjs +47 -5
- package/tests/autobongos_tweak_lane.mjs +208 -0
- package/tests/conductor_main_e2e.mjs +21 -0
- package/tests/connections_api.mjs +18 -0
- package/tests/craft_mode_nav.mjs +5 -3
- package/tests/craft_mode_tallies.mjs +52 -16
- package/tests/discord_approvals.mjs +11 -0
- package/tests/discord_channels.mjs +20 -0
- package/tests/fixtures/gov-seated.mjs +64 -0
- package/tests/fixtures/government-ask-cards.json +1284 -0
- package/tests/government_act_alone_buildings.mjs +1 -0
- package/tests/government_agent_asks.mjs +322 -0
- package/tests/government_asks.mjs +408 -0
- package/tests/government_build_rights.mjs +253 -0
- package/tests/government_buildouts.mjs +382 -0
- package/tests/government_counts.mjs +143 -0
- package/tests/government_decision_rules.mjs +5 -1
- package/tests/government_laws.mjs +488 -0
- package/tests/government_laws_session.mjs +321 -0
- package/tests/government_leads.mjs +375 -0
- package/tests/government_positions.mjs +127 -0
- package/tests/government_presets.mjs +215 -0
- package/tests/government_routes.mjs +8 -1
- package/tests/government_routing.mjs +118 -0
- package/tests/government_seed.mjs +5 -0
- package/tests/government_treasury.mjs +394 -0
- package/tests/governor_docket.mjs +26 -2
- package/tests/hall_buildouts.mjs +208 -0
- package/tests/hall_city.mjs +14 -6
- package/tests/hall_city_living.mjs +617 -0
- package/tests/hall_gov_counts.mjs +336 -0
- package/tests/hall_gov_living.mjs +636 -0
- package/tests/hall_gov_real_site.mjs +655 -0
- package/tests/hall_matter_card.mjs +6 -1
- package/tests/hall_my_agents.mjs +481 -0
- package/tests/hall_nav.mjs +107 -4
- package/tests/hall_page_gate_map.mjs +4 -0
- package/tests/hall_preview_governor_fixtures.mjs +30 -3
- package/tests/hall_preview_scrub.mjs +59 -1
- package/tests/hall_preview_security.mjs +169 -0
- package/tests/hall_reading_world.mjs +44 -0
- package/tests/hall_roster_world.mjs +6 -2
- package/tests/hall_street.mjs +18 -2
- package/tests/hall_street_amend.mjs +1 -1
- package/tests/hall_street_deck.mjs +609 -0
- package/tests/hall_street_depth.mjs +559 -0
- package/tests/hall_street_desk.mjs +1 -1
- package/tests/hall_street_inside.mjs +29 -5
- package/tests/hall_tweak_editor.mjs +4 -1
- package/tests/hub_account_page.mjs +94 -3
- package/tests/hub_connections_page.mjs +507 -0
- package/tests/hub_invite_badge.mjs +1 -1
- package/tests/idp_device_exchange.mjs +18 -0
- package/tests/landing_legal_pages.mjs +8 -5
- package/tests/landing_page.mjs +23 -4
- package/tests/nav_permission_atoms.mjs +4 -1
- package/tests/profile_connections_ui.mjs +88 -25
- package/tests/projects_hub.mjs +15 -0
- package/tests/render_check.mjs +2 -2
- package/tests/ship_broadcast_routing.mjs +57 -16
- package/tests/ship_broadcast_server.mjs +40 -7
- package/tests/ship_merge_gate.mjs +19 -0
- package/tests/ui_design_kit.mjs +2 -2
- package/tests/watch_applications_queue.mjs +23 -0
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
// modules/hall-ui/public/my-agents.js — MY AGENTS: how your own Claude works for
|
|
2
|
+
// you, and which of those settings the project's laws lock (task 1004685, the
|
|
3
|
+
// living-city prototype; round-3 boards 6 "the city and My agents", 7 "how strict"
|
|
4
|
+
// and 8 "governance is a position"; made real by task 1004714).
|
|
5
|
+
//
|
|
6
|
+
// THE SPLIT THIS PAGE EXISTS TO SHOW. The city steers everyone; My agents steers
|
|
7
|
+
// yours. Every row is one of your agent settings, and a project law can LOCK it.
|
|
8
|
+
// Every part of it is a real read (task 1004717: the prototype overlay is gone):
|
|
9
|
+
// real EVERY ROW (task 1004714). GET /me's interaction profile, wandering,
|
|
10
|
+
// model allocation and session cards; GET /me/skill-prefs; GET
|
|
11
|
+
// /specialities; the sounds switch (off by the owner's call,
|
|
12
|
+
// .claude/sounds/play.js). Writes go to the real routes (PATCH
|
|
13
|
+
// /me/interaction|wandering|model-allocation|render|skill-prefs, POST
|
|
14
|
+
// /specialities/:id/adopt), so in the preview harness they reach its
|
|
15
|
+
// recorder (scripts/hall-preview/pref-writes.js).
|
|
16
|
+
// real the project's laws and the locks they put on your settings (task
|
|
17
|
+
// 1004707): GET /me's `laws` block — the very words every session reads —
|
|
18
|
+
// and `laws.locks`, what each lock did to you, enforced by the server. A
|
|
19
|
+
// lock that FIXES a value locks the row; one that only CAPS it offers the
|
|
20
|
+
// values under the cap (`allowed`, law-locks.js). Only a law in force
|
|
21
|
+
// locks a row.
|
|
22
|
+
// real "Make it a law": a habit of yours proposed as a law files a real laws
|
|
23
|
+
// vote (POST /government/board/laws, task 1004706) — offered only to a
|
|
24
|
+
// builder /me says may open a board item (`permissions['board.item.open']`).
|
|
25
|
+
// real the strictness preset strip (task 1004715): GET /government/presets —
|
|
26
|
+
// the three presets (Open workshop, Guild, Factory), which one the laws
|
|
27
|
+
// in force amount to, and the locks a switch to each would leave in force
|
|
28
|
+
// (modules/government/presets.js). A preview counts those locks against
|
|
29
|
+
// your rows; it never locks a row. "Put it to a vote" files the switch as
|
|
30
|
+
// ONE laws vote (POST /government/board/presets), offered only to a
|
|
31
|
+
// builder who may open a board item; only the vote passing changes a law.
|
|
32
|
+
// gone the five agent settings no table held (when stuck, when it asks, how it
|
|
33
|
+
// asks, at once, going live): dropped from this page by task 1004714 rather
|
|
34
|
+
// than shown as settings nothing stores or obeys. How an agent asks is a
|
|
35
|
+
// project rule now (the ask shape, ADR 0372-0373), not a personal choice.
|
|
36
|
+
//
|
|
37
|
+
// THE DESK LINE RULE (the owner's, records/governor-street.md): a row is a name of
|
|
38
|
+
// at most three words, one fact and at most two buttons; choices are one-tap chips;
|
|
39
|
+
// a tile is one number and two words, hidden at zero; it acts in place. Motion is
|
|
40
|
+
// CSS only (my-agents.css): JS sets an attribute, CSS plays the one-shot.
|
|
41
|
+
//
|
|
42
|
+
// TWO HALVES, the roadmap-timeline.js seam: the top is PURE (buildView, no DOM, no
|
|
43
|
+
// fetch, no clock — tests/hall_my_agents.mjs requires it in node and runs it over
|
|
44
|
+
// the real fixtures), the bottom wires the page and only runs where a document is.
|
|
45
|
+
|
|
46
|
+
(function (root) {
|
|
47
|
+
'use strict';
|
|
48
|
+
|
|
49
|
+
// ---------- words (the desk's voice: short, plain) ----------
|
|
50
|
+
// A setting's row name (three words at most) and what each value reads as.
|
|
51
|
+
const SETTING = {
|
|
52
|
+
technical_level: { name: 'Technical level', words: { non_technical: 'plain words', mixed: 'some tech', engineer: 'engineer' } },
|
|
53
|
+
toolchain_familiarity: { name: 'Your tooling', words: { new: 'walk me through', comfortable: 'I know it' } },
|
|
54
|
+
verbosity: { name: 'Reply length', words: { concise: 'short', detailed: 'detailed' } },
|
|
55
|
+
machinery: { name: 'Machinery', words: { show: 'shows the work', quiet: 'hidden' } },
|
|
56
|
+
wandering: { name: 'Off-task', words: { locked: 'never', focused: 'one note', balanced: 'asks first', exploratory: 'roams' } },
|
|
57
|
+
'model.main': { name: 'Main session' },
|
|
58
|
+
'model.subagentDefault': { name: 'Helpers' },
|
|
59
|
+
'model.subagentRoutine': { name: 'Routine helpers' },
|
|
60
|
+
render: { name: 'Session cards', words: { minimal: 'one line', text: 'plain text', standard: 'rich card', rich: 'rich, open' } },
|
|
61
|
+
};
|
|
62
|
+
// The law a habit would become, in the ask-format voice: a short plain sentence.
|
|
63
|
+
const LAW_LINES = {
|
|
64
|
+
technical_level: { non_technical: 'Agents talk in plain words', mixed: 'Agents assume some tech', engineer: 'Agents talk like engineers' },
|
|
65
|
+
toolchain_familiarity: { new: 'Agents walk people through tools', comfortable: 'Agents skip the tool basics' },
|
|
66
|
+
verbosity: { concise: 'Agents keep replies short', detailed: 'Agents reply in detail' },
|
|
67
|
+
machinery: { show: 'Agents show their work', quiet: 'Agents hide the machinery' },
|
|
68
|
+
};
|
|
69
|
+
const GROUPS = [
|
|
70
|
+
{ key: 'talk', title: 'How it talks to you', keys: ['technical_level', 'toolchain_familiarity', 'verbosity', 'machinery', 'render', 'sounds'] },
|
|
71
|
+
{ key: 'work', title: 'How your agents work', keys: ['wandering'] },
|
|
72
|
+
{ key: 'models', title: 'Which models run', keys: ['model.main', 'model.subagentDefault', 'model.subagentRoutine', 'skills'] },
|
|
73
|
+
{ key: 'know', title: 'What it knows', keys: ['specialities'] },
|
|
74
|
+
];
|
|
75
|
+
// the permission atom that may file a laws vote (POST /government/board/laws)
|
|
76
|
+
const PROPOSE_ATOM = 'board.item.open';
|
|
77
|
+
|
|
78
|
+
const str = (v) => (v == null ? '' : String(v));
|
|
79
|
+
const obj = (v) => (v && typeof v === 'object' && !Array.isArray(v) ? v : null);
|
|
80
|
+
const arr = (v) => (Array.isArray(v) ? v : []);
|
|
81
|
+
const wordFor = (key, value) => {
|
|
82
|
+
const s = SETTING[key];
|
|
83
|
+
if (s && s.words && Object.prototype.hasOwnProperty.call(s.words, String(value))) return s.words[String(value)];
|
|
84
|
+
return str(value);
|
|
85
|
+
};
|
|
86
|
+
const nameFor = (key) => (SETTING[key] ? SETTING[key].name : key);
|
|
87
|
+
|
|
88
|
+
// ---------- the pure view ----------
|
|
89
|
+
// buildView({ me, constitution, presets, skills, specialities, preview,
|
|
90
|
+
// proposed, switched, adopted, as, project, now }, { GovLiving, GovPositions })
|
|
91
|
+
// → { project, positions, strict, groups[], habits[], canPropose, reads, counts }
|
|
92
|
+
// `preview` is a strictness preset key to look at instead of the project's own;
|
|
93
|
+
// `presets` is GET /government/presets' body (task 1004715); `switched` maps a
|
|
94
|
+
// preset key to the board item its switch was filed as.
|
|
95
|
+
function buildView(input, libs) {
|
|
96
|
+
const o = obj(input) || {};
|
|
97
|
+
const L = obj(libs) || {};
|
|
98
|
+
const me = obj(o.me) || {};
|
|
99
|
+
const project = str(o.project) || 'The project';
|
|
100
|
+
const presetsRead = obj(o.presets) && Array.isArray(o.presets.presets) ? o.presets : null;
|
|
101
|
+
const presets = presetsRead ? presetsRead.presets.filter((p) => obj(p) && p.key) : [];
|
|
102
|
+
const currentKey = presetsRead && presets.some((p) => p.key === presetsRead.current) ? str(presetsRead.current) : null;
|
|
103
|
+
const previewKey = o.preview && o.preview !== currentKey && presets.some((p) => p.key === o.preview) ? o.preview : null;
|
|
104
|
+
const shownKey = previewKey || currentKey;
|
|
105
|
+
|
|
106
|
+
// THE ROWS: /me's settings and the locks the laws in force put on them (task 1004714)
|
|
107
|
+
const real = L.GovLiving ? L.GovLiving.livingModel({ me, constitution: o.constitution, now: o.now }) : null;
|
|
108
|
+
const byKey = {};
|
|
109
|
+
(real ? real.myAgents.settings : []).forEach((s) => { byKey[s.key] = s; });
|
|
110
|
+
|
|
111
|
+
// ---- rows ----
|
|
112
|
+
const lawText = (by) => project + ': ' + by.text + (by.no ? ' · Law ' + by.no : '');
|
|
113
|
+
const settingRow = (key) => {
|
|
114
|
+
const s = byKey[key];
|
|
115
|
+
if (!s) return null;
|
|
116
|
+
const lock = s.locked && s.lockedBy && s.lockedBy.real
|
|
117
|
+
? { law_id: s.lockedBy.law_id || null, no: s.lockedBy.no || null, text: lawText(s.lockedBy), real: true } : null;
|
|
118
|
+
const cap = !lock && s.cappedBy ? { law_id: s.cappedBy.law_id || null, no: s.cappedBy.no || null, text: lawText(s.cappedBy), max: s.cappedBy.max, real: true } : null;
|
|
119
|
+
const value = s.value;
|
|
120
|
+
const row = {
|
|
121
|
+
key, name: nameFor(key), fact: wordFor(key, s.effective),
|
|
122
|
+
yours: (lock || cap) && String(value) !== String(s.effective) ? wordFor(key, value) : null,
|
|
123
|
+
locked: !!lock, lock, cap, control: 'chips', value, effective: s.effective,
|
|
124
|
+
options: arr(s.options).map((v) => ({ value: v, label: wordFor(key, v), pressed: String(v) === String(s.effective) })),
|
|
125
|
+
};
|
|
126
|
+
if (key.indexOf('model.') === 0) row.options.forEach((x) => { x.label = str(x.value); });
|
|
127
|
+
return row;
|
|
128
|
+
};
|
|
129
|
+
const extraRow = (key) => {
|
|
130
|
+
if (key === 'render') {
|
|
131
|
+
const r = obj(me.render);
|
|
132
|
+
if (!r) return null;
|
|
133
|
+
const levels = arr(r.levels).map((l) => l.value);
|
|
134
|
+
const level = str(r.level || r.derived_level);
|
|
135
|
+
return {
|
|
136
|
+
key, name: nameFor(key), fact: wordFor(key, level), locked: false, lock: null, cap: null, control: 'chips',
|
|
137
|
+
value: level, effective: level, options: levels.map((v) => ({ value: v, label: wordFor(key, v), pressed: v === level })),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (key === 'sounds') {
|
|
141
|
+
// .claude/sounds/play.js SOUNDS_ON = false (task 1004554, the owner's call).
|
|
142
|
+
return { key, name: 'Sounds', fact: 'all off for now', locked: false, lock: null, cap: null, control: 'none', options: [] };
|
|
143
|
+
}
|
|
144
|
+
if (key === 'skills') {
|
|
145
|
+
const sk = obj(o.skills);
|
|
146
|
+
if (!sk) return null;
|
|
147
|
+
const eff = arr(sk.effective);
|
|
148
|
+
const changed = eff.filter((e) => e.override_model != null).length;
|
|
149
|
+
return {
|
|
150
|
+
key, name: 'Per skill', fact: eff.length + ' skills' + (changed ? ' · ' + changed + ' yours' : ' · all default'),
|
|
151
|
+
locked: false, lock: null, cap: null, control: 'expand', options: [],
|
|
152
|
+
skills: eff.map((e) => ({
|
|
153
|
+
key: 'skill.' + e.skill, skill: e.skill, value: e.effective_model,
|
|
154
|
+
options: arr(sk.allowed_models).map((v) => ({ value: v, label: v, pressed: v === e.effective_model })),
|
|
155
|
+
})),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (key === 'specialities') {
|
|
159
|
+
const adopted = arr(me.specialities).map((x) => str(x.name)).filter(Boolean)
|
|
160
|
+
.concat(arr(o.adopted).map(str)).filter((v, i, a) => a.indexOf(v) === i);
|
|
161
|
+
const offer = arr(o.specialities && o.specialities.specialities).filter((x) => x && x.adoptable !== false).slice(0, 3);
|
|
162
|
+
return {
|
|
163
|
+
key, name: 'Specialities', fact: adopted.length ? adopted.join(', ') : 'none yet', locked: false, lock: null, cap: null, control: 'adopt',
|
|
164
|
+
options: offer.map((x) => ({ value: String(x.id), label: str(x.name).toLowerCase(), pressed: adopted.indexOf(str(x.name)) >= 0 })),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
};
|
|
169
|
+
const groups = GROUPS.map((g) => {
|
|
170
|
+
const rows = g.keys.map((k) => settingRow(k) || extraRow(k)).filter(Boolean);
|
|
171
|
+
return { key: g.key, title: g.title, rows, source: 'real', locked: rows.filter((r) => r.locked).length };
|
|
172
|
+
}).filter((g) => g.rows.length);
|
|
173
|
+
const lockableKeys = groups.reduce((a, g) => a.concat(g.rows.filter((r) => r.key !== 'sounds' && r.key !== 'specialities').map((r) => r.key)), []);
|
|
174
|
+
const total = lockableKeys.length;
|
|
175
|
+
const lockedNow = groups.reduce((n, g) => n + g.locked, 0);
|
|
176
|
+
|
|
177
|
+
// ---- strictness (REAL, task 1004715): what each preset WOULD lock of these
|
|
178
|
+
// rows — the server's locks after a switch to it (GET /government/presets),
|
|
179
|
+
// fixed or capped, counted against the rows. It previews; it never locks a
|
|
180
|
+
// row: only a passed vote changes a law. ----
|
|
181
|
+
const wouldLock = (key) => {
|
|
182
|
+
const p = presets.find((x) => x.key === key);
|
|
183
|
+
const held = arr(p && p.locks).filter((l) => l && l.setting).map((l) => String(l.setting));
|
|
184
|
+
return lockableKeys.map((k) => held.indexOf(k) >= 0);
|
|
185
|
+
};
|
|
186
|
+
const shownPips = shownKey ? wouldLock(shownKey) : lockableKeys.map(() => false);
|
|
187
|
+
const canSwitch = !!(obj(me.permissions) && me.permissions[PROPOSE_ATOM] === true);
|
|
188
|
+
const switched = obj(o.switched) || {};
|
|
189
|
+
const strict = presets.length ? {
|
|
190
|
+
current: presets.find((p) => p.key === currentKey) || null,
|
|
191
|
+
shown: presets.find((p) => p.key === shownKey) || null,
|
|
192
|
+
previewing: !!previewKey,
|
|
193
|
+
presets: presets.map((p) => ({ key: p.key, name: str(p.name), line: str(p.line), locks: wouldLock(p.key).filter(Boolean).length, current: p.key === currentKey, shown: p.key === shownKey })),
|
|
194
|
+
locked: shownPips.filter(Boolean).length,
|
|
195
|
+
total,
|
|
196
|
+
pips: shownPips,
|
|
197
|
+
// the switch: one laws vote, for a builder who may open one
|
|
198
|
+
canSwitch: canSwitch && !!previewKey,
|
|
199
|
+
filed: previewKey && switched[previewKey] ? (switched[previewKey] === true ? '' : str(switched[previewKey])) : null,
|
|
200
|
+
real: true,
|
|
201
|
+
} : null;
|
|
202
|
+
|
|
203
|
+
// ---- habits: your own non-default choices that a law could make everyone's ----
|
|
204
|
+
// Real (task 1004714): proposing one files a laws vote whose law locks the
|
|
205
|
+
// setting to your value. Offered only to a builder who may open a board item.
|
|
206
|
+
const canPropose = !!(obj(me.permissions) && me.permissions[PROPOSE_ATOM] === true);
|
|
207
|
+
const habits = [];
|
|
208
|
+
const proposed = obj(o.proposed) || {};
|
|
209
|
+
const habit = (key, value, law) => ({
|
|
210
|
+
key, name: nameFor(key) + ' ' + wordFor(key, value), fact: proposed[key] ? 'proposed' : 'only you, for now',
|
|
211
|
+
law, lock: { setting: key, value }, proposed: !!proposed[key], item: proposed[key] && proposed[key] !== true ? str(proposed[key]) : null,
|
|
212
|
+
});
|
|
213
|
+
const held = (key) => !!(byKey[key] && (byKey[key].locked || byKey[key].cappedBy));
|
|
214
|
+
const inter = obj(me.interaction);
|
|
215
|
+
if (inter) {
|
|
216
|
+
const stored = obj(inter.stored) || {};
|
|
217
|
+
arr(inter.fields).forEach((f) => {
|
|
218
|
+
const v = stored[f.field];
|
|
219
|
+
if (v == null || v === f.default_value || held(f.field)) return;
|
|
220
|
+
habits.push(habit(f.field, v, (LAW_LINES[f.field] && LAW_LINES[f.field][v]) || ('Every agent: ' + nameFor(f.field).toLowerCase() + ' ' + wordFor(f.field, v))));
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
const wand = obj(me.wandering);
|
|
224
|
+
if (wand && wand.override_level != null && !held('wandering')) {
|
|
225
|
+
const v = wand.effective_key;
|
|
226
|
+
habits.push(habit('wandering', v, 'Agents go off-task: ' + wordFor('wandering', v)));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ---- what the agent reads first: the project's laws, then yours — both real ----
|
|
230
|
+
// The laws are GET /me's `laws` block: exactly what the Conductor sends every
|
|
231
|
+
// session (task 1004707).
|
|
232
|
+
const sent = obj(me.laws);
|
|
233
|
+
const laws = arr(sent && sent.laws).map((l, i) => ({ id: str(l.id), no: Number(l.no) || i + 1, text: str(l.text), effect: str(l.effect_on_ai) }));
|
|
234
|
+
const mine = [];
|
|
235
|
+
if (inter) mine.push({ key: 'interaction', title: 'How it talks to you', sent: !!inter.inject, text: inter.inject ? str(inter.contract) : '' });
|
|
236
|
+
if (wand) {
|
|
237
|
+
mine.push({
|
|
238
|
+
key: 'wandering', title: 'Off-task', sent: !!wand.inject,
|
|
239
|
+
text: wand.inject ? str(wand.contract) : '',
|
|
240
|
+
note: wand.inject ? '' : 'Not sent: ' + str(wand.effective_label || wand.effective_key).toLowerCase() + ' is what an agent does anyway.',
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
arr(me.specialities).forEach((x) => {
|
|
244
|
+
if (x && x.inject && x.contract) mine.push({ key: 'speciality:' + str(x.discipline), title: str(x.name) || 'Speciality', sent: true, text: str(x.contract), note: 'Only on a claim in its craft.' });
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// task 1004713: the seats are /me's server-resolved `positions` block.
|
|
248
|
+
const positions = L.GovPositions ? L.GovPositions.positionsFor({ me, as: o.as }) : null;
|
|
249
|
+
return {
|
|
250
|
+
project, positions, strict, groups, habits: canPropose ? habits : [], canPropose,
|
|
251
|
+
// `sent`: the project's laws really reach every session (a law is in force)
|
|
252
|
+
reads: { laws, mine, sent: !!(sent && sent.inject && laws.length) },
|
|
253
|
+
counts: { rows: groups.reduce((n, g) => n + g.rows.length, 0), locked: lockedNow, total, habits: canPropose ? habits.length : 0 },
|
|
254
|
+
_sources: { positions: 'real', strict: 'real', groups: 'real', habits: 'real', 'reads.laws': 'real', 'reads.mine': 'real' },
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// The write a row's choice makes, against the real route (null: no route).
|
|
259
|
+
function writeFor(key, value, view) {
|
|
260
|
+
if (['technical_level', 'toolchain_familiarity', 'verbosity', 'machinery'].indexOf(key) >= 0) return { method: 'PATCH', path: '/me/interaction', body: { [key]: value }, slice: 'interaction' };
|
|
261
|
+
if (key === 'wandering') {
|
|
262
|
+
const levels = arr(view && view.me && view.me.wandering && view.me.wandering.levels);
|
|
263
|
+
const n = levels.findIndex((l) => l.key === value);
|
|
264
|
+
return n < 0 ? null : { method: 'PATCH', path: '/me/wandering', body: { wandering: n }, slice: 'wandering' };
|
|
265
|
+
}
|
|
266
|
+
if (key.indexOf('model.') === 0) return { method: 'PATCH', path: '/me/model-allocation', body: { [key.slice(6)]: value }, slice: 'model_allocation' };
|
|
267
|
+
if (key === 'render') return { method: 'PATCH', path: '/me/render', body: { widgets: value }, slice: 'render' };
|
|
268
|
+
if (key.indexOf('skill.') === 0) return { method: 'PATCH', path: '/me/skill-prefs', body: { [key.slice(6)]: value }, slice: 'skills' };
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// The laws vote a habit files (POST /government/board/laws): one law, locking the
|
|
273
|
+
// setting to the proposer's value.
|
|
274
|
+
function proposalFor(h, login) {
|
|
275
|
+
return {
|
|
276
|
+
enact: [{ text: h.law, locks: [h.lock] }],
|
|
277
|
+
rationale_md: 'Proposed from My agents. Today only ' + (login || 'one builder') + ' has this: ' + h.name.toLowerCase() + '. As a law it would hold for every agent in the project.',
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const pure = { buildView, writeFor, proposalFor, SETTING, LAW_LINES, GROUPS };
|
|
282
|
+
root.OTBMyAgents = pure;
|
|
283
|
+
if (typeof module !== 'undefined' && module.exports) { module.exports = pure; return; }
|
|
284
|
+
if (typeof document === 'undefined' || !root.BongosClient) return;
|
|
285
|
+
|
|
286
|
+
// ====================================================================
|
|
287
|
+
// ---------- the page half ----------
|
|
288
|
+
// ====================================================================
|
|
289
|
+
const API = '/api/bongos';
|
|
290
|
+
const api = root.BongosClient.createClient({ baseUrl: '', credentials: 'same-origin', throwOnError: false });
|
|
291
|
+
const $ = (id) => document.getElementById(id);
|
|
292
|
+
const esc = (s) => str(s).replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
293
|
+
const toast = (msg) => { if (root.OTB && root.OTB.toast) root.OTB.toast(msg); };
|
|
294
|
+
const brand = root.__BRANDING__ || {};
|
|
295
|
+
const PROJECT = (brand.identity && (brand.identity.productName || brand.identity.worldName)) || 'The project';
|
|
296
|
+
const LOCK = '<svg class="ma-lock__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3.5" y="7" width="9" height="6.5" rx="1.5"/><path d="M5.5 7V5.2a2.5 2.5 0 0 1 5 0V7"/></svg>';
|
|
297
|
+
const TICK = '<svg class="ma-tick" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3.5 8.5l3 3 6-7"/></svg>';
|
|
298
|
+
|
|
299
|
+
const S = { me: null, constitution: null, presets: null, skills: null, specialities: null, preview: null, proposed: {}, switched: {}, adopted: [], skillsOpen: false, readAll: false, view: null };
|
|
300
|
+
|
|
301
|
+
function asNow() {
|
|
302
|
+
try { return root.sessionStorage.getItem('hall-as') === 'nobody' || /[?&]as=nobody\b/.test(root.location.search) ? 'nobody' : null; }
|
|
303
|
+
catch (_) { return /[?&]as=nobody\b/.test(root.location.search) ? 'nobody' : null; }
|
|
304
|
+
}
|
|
305
|
+
function view() {
|
|
306
|
+
S.view = buildView({
|
|
307
|
+
me: S.me, constitution: S.constitution, presets: S.presets, skills: S.skills, specialities: S.specialities,
|
|
308
|
+
preview: S.preview, proposed: S.proposed, switched: S.switched, adopted: S.adopted, as: asNow(), project: PROJECT, now: Date.now(),
|
|
309
|
+
}, { GovLiving: root.GovLiving, GovPositions: root.GovPositions });
|
|
310
|
+
return S.view;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ---- render ----
|
|
314
|
+
function chipsHtml(row, label) {
|
|
315
|
+
return '<div class="ma-chips" role="group" aria-label="' + esc(label) + '">' + row.options.map((op) =>
|
|
316
|
+
'<button type="button" class="ma-chip" data-set="' + esc(row.key) + '" data-value="' + esc(op.value) + '" aria-pressed="' + (op.pressed ? 'true' : 'false') + '">' + esc(op.label) + '</button>').join('') + '</div>';
|
|
317
|
+
}
|
|
318
|
+
function controlHtml(row) {
|
|
319
|
+
// every lock is a real law, enforced by the server (task 1004714)
|
|
320
|
+
if (row.locked) return '<span class="ma-lock">' + LOCK + '<span>' + esc(row.lock.text) + '</span></span>';
|
|
321
|
+
if (row.control === 'chips') return chipsHtml(row, row.name);
|
|
322
|
+
if (row.control === 'adopt') {
|
|
323
|
+
return row.options.length ? '<div class="ma-chips" role="group" aria-label="Adopt a speciality">' + row.options.map((op) =>
|
|
324
|
+
'<button type="button" class="ma-chip" data-adopt="' + esc(op.value) + '" aria-pressed="' + (op.pressed ? 'true' : 'false') + '"' + (op.pressed ? ' disabled' : '') + '>' + esc(op.pressed ? op.label : '+ ' + op.label) + '</button>').join('') + '</div>' : '';
|
|
325
|
+
}
|
|
326
|
+
if (row.control === 'expand') {
|
|
327
|
+
return '<button type="button" class="btn-ghost ma-more" data-expand="skills" aria-expanded="' + (S.skillsOpen ? 'true' : 'false') + '" aria-controls="ma-skills">' + (S.skillsOpen ? 'hide' : 'show') + '</button>';
|
|
328
|
+
}
|
|
329
|
+
return '';
|
|
330
|
+
}
|
|
331
|
+
function rowHtml(row) {
|
|
332
|
+
// a cap (task 1004714): the chips offer the values under it, and the line says which law
|
|
333
|
+
const sub = (row.cap ? '<span class="ma-row__yours ma-row__cap">' + LOCK + 'capped by ' + esc(row.cap.text) + '</span>' : '') +
|
|
334
|
+
(row.yours ? '<span class="ma-row__yours">yours: ' + esc(row.yours) + '</span>' : '');
|
|
335
|
+
let html = '<li class="ma-row' + (row.locked ? ' ma-row--locked' : '') + '" data-row="' + esc(row.key) + '">' +
|
|
336
|
+
'<span class="ma-row__name">' + esc(row.name) + TICK + '</span>' +
|
|
337
|
+
'<span class="ma-row__fact"><span>' + esc(row.fact) + '</span>' + sub + '</span>' +
|
|
338
|
+
'<span class="ma-row__act">' + controlHtml(row) + '</span></li>';
|
|
339
|
+
if (row.control === 'expand' && S.skillsOpen && !row.locked) {
|
|
340
|
+
html += '<li class="ma-sub" id="ma-skills"><ul class="ma-rows ma-rows--sub">' + row.skills.map((s) =>
|
|
341
|
+
'<li class="ma-row ma-row--sub" data-row="' + esc(s.key) + '"><span class="ma-row__name ma-mono">' + esc(s.skill) + TICK + '</span><span class="ma-row__fact"><span>' + esc(s.value) + '</span></span>' +
|
|
342
|
+
'<span class="ma-row__act">' + chipsHtml({ key: s.key, options: s.options }, s.skill) + '</span></li>').join('') + '</ul></li>';
|
|
343
|
+
}
|
|
344
|
+
return html;
|
|
345
|
+
}
|
|
346
|
+
function seatsHtml(v) {
|
|
347
|
+
const p = v.positions;
|
|
348
|
+
if (!p || !p.known) return '';
|
|
349
|
+
if (!p.has_position) {
|
|
350
|
+
return '<p class="ma-seat"><span class="ma-seat__l">Your seat</span><span class="ma-seat__none">None. Governing is held, not picked: you steer your own agents.</span></p>';
|
|
351
|
+
}
|
|
352
|
+
return '<p class="ma-seat"><span class="ma-seat__l">Your seats</span>' + p.positions.map((s) =>
|
|
353
|
+
'<span class="fact-pill' + (s.kind === 'lead' ? ' ma-seat__lead' : '') + '">' + esc(s.title) + '</span>').join('') + '</p>';
|
|
354
|
+
}
|
|
355
|
+
function strictHtml(v) {
|
|
356
|
+
const st = v.strict;
|
|
357
|
+
if (!st) return '';
|
|
358
|
+
const shown = st.shown || st.current;
|
|
359
|
+
const lockedTile = st.locked > 0
|
|
360
|
+
? '<span class="ma-tile"><span class="ma-tile__n">' + esc(st.locked) + '</span><span class="ma-tile__l">would lock</span></span>'
|
|
361
|
+
: '<span class="ma-tile ma-tile--quiet"><span class="ma-tile__l">would lock none</span></span>';
|
|
362
|
+
return seatsHtml(v) +
|
|
363
|
+
'<div class="ma-strict__band">' +
|
|
364
|
+
'<div class="ma-strict__now">' +
|
|
365
|
+
(st.previewing ? '<p class="ma-strict__eyebrow">previewing</p>' : '') +
|
|
366
|
+
'<h2 class="ma-strict__h" id="ma-strict-h">' + esc(shown ? shown.name : 'No preset yet') + '</h2>' +
|
|
367
|
+
'<p class="ma-strict__line">' + esc(shown ? shown.line : 'Laws are passed one at a time. Pick a preset to see what it would lock.') + '</p>' +
|
|
368
|
+
'<p class="ma-strict__who">' + (st.previewing ? esc(v.project) + (st.current ? ' runs as ' + esc(st.current.name) : ' has no preset yet') + '. Switching is a vote.' : 'How strict ' + esc(v.project) + ' is. Switching is a vote.') + '</p>' +
|
|
369
|
+
// the switch sits under the words, so the locks column keeps its width
|
|
370
|
+
(st.filed !== null ? '<p class="ma-strict__vote"><a class="ma-row__link ma-filed" href="/board-room' + (st.filed ? '#item-' + esc(st.filed) : '') + '">up for a vote</a></p>'
|
|
371
|
+
: st.canSwitch ? '<p class="ma-strict__vote"><button type="button" class="btn-ghost ma-switch" data-switch="' + esc(shown.key) + '">put ' + esc(shown.name) + ' to a vote</button></p>' : '') +
|
|
372
|
+
'</div>' +
|
|
373
|
+
'<div class="ma-strict__presets" role="group" aria-label="Preview a strictness">' + st.presets.map((p) =>
|
|
374
|
+
'<button type="button" class="ma-preset" data-preset="' + esc(p.key) + '" aria-pressed="' + (p.shown ? 'true' : 'false') + '">' +
|
|
375
|
+
'<span class="ma-preset__name">' + esc(p.name) + (p.current ? '<span class="ma-preset__ours">ours</span>' : '') + '</span>' +
|
|
376
|
+
'<span class="ma-preset__n">' + esc(p.locks) + ' of ' + esc(st.total) + ' lock</span></button>').join('') +
|
|
377
|
+
'</div>' +
|
|
378
|
+
'<div class="ma-strict__locks">' + lockedTile +
|
|
379
|
+
'<span class="ma-pips" role="img" aria-label="' + esc(st.locked) + ' of ' + esc(st.total) + ' of your settings this preset would lock">' +
|
|
380
|
+
st.pips.map((on) => '<span class="ma-pip"' + (on ? ' data-on' : '') + '></span>').join('') + '</span>' +
|
|
381
|
+
(st.previewing ? '<button type="button" class="btn-ghost ma-back" data-preset="' + esc(st.current ? st.current.key : '') + '">back to ' + esc(st.current ? st.current.name : 'the project') + '</button>' : '') +
|
|
382
|
+
'</div>' +
|
|
383
|
+
'</div>';
|
|
384
|
+
}
|
|
385
|
+
function rowsHtml(v) {
|
|
386
|
+
let html = v.groups.map((g) =>
|
|
387
|
+
'<div class="ov-group ma-group" data-group="' + esc(g.key) + '">' +
|
|
388
|
+
'<div class="ov-group__head"><span class="ov-group__name">' + esc(g.title) + '</span>' +
|
|
389
|
+
(g.locked ? '<span class="ov-group__n">' + esc(g.locked) + ' locked</span>' : '') +
|
|
390
|
+
'<span class="ov-group__note">live</span></div>' +
|
|
391
|
+
'<ul class="ma-rows">' + g.rows.map(rowHtml).join('') + '</ul></div>').join('');
|
|
392
|
+
// "Make it a law" files a real laws vote: only for a builder who may open one
|
|
393
|
+
if (!v.canPropose) return html;
|
|
394
|
+
html += '<div class="ov-group ma-group" data-group="habits">' +
|
|
395
|
+
'<div class="ov-group__head"><span class="ov-group__name">Make it a law</span>' +
|
|
396
|
+
(v.habits.length ? '<span class="ov-group__n">' + esc(v.habits.length) + '</span>' : '') +
|
|
397
|
+
'<span class="ov-group__note">a habit can become everyone\'s</span></div>' +
|
|
398
|
+
(v.habits.length ? '<ul class="ma-rows">' + v.habits.map((h) =>
|
|
399
|
+
'<li class="ma-row' + (h.proposed ? ' ma-row--done' : '') + '" data-row="habit:' + esc(h.key) + '">' +
|
|
400
|
+
'<span class="ma-row__name">' + esc(h.name) + TICK + '</span>' +
|
|
401
|
+
'<span class="ma-row__fact"><span>' + esc(h.fact) + '</span>' + (h.proposed ? '<a class="ma-row__link" href="/board-room' + (h.item ? '#item-' + esc(h.item) : '') + '">up for a vote</a>' : '') + '</span>' +
|
|
402
|
+
'<span class="ma-row__act">' + (h.proposed ? '' : '<button type="button" class="btn-ghost" data-propose="' + esc(h.key) + '">propose to ' + esc(v.project) + '</button>') + '</span></li>').join('') + '</ul>'
|
|
403
|
+
: '<p class="ma-quiet">No habits of your own yet. Change a setting above and it shows here.</p>') +
|
|
404
|
+
'</div>';
|
|
405
|
+
return html;
|
|
406
|
+
}
|
|
407
|
+
function readsHtml(v) {
|
|
408
|
+
const laws = v.reads.laws;
|
|
409
|
+
const lawsHtml = laws.length
|
|
410
|
+
? '<ol class="ma-laws">' + laws.map((l) => '<li class="ma-law"><span class="ma-law__no">' + esc(l.no) + '</span><span class="ma-law__t">' + esc(l.text) + '</span>' + (l.effect ? '<span class="ma-law__e">' + esc(l.effect) + '</span>' : '') + '</li>').join('') + '</ol>'
|
|
411
|
+
: '<p class="ma-quiet">No laws yet.</p>';
|
|
412
|
+
// The contract's own words, line by line: the clause before " — " is the setting,
|
|
413
|
+
// the rest is the instruction. Clamped to two lines until "read it all".
|
|
414
|
+
const line = (s) => {
|
|
415
|
+
const t = s.replace(/^- /, '');
|
|
416
|
+
const cut = t.indexOf(' — ');
|
|
417
|
+
return cut > 0 ? '<li><b>' + esc(t.slice(0, cut)) + '</b> ' + esc(t.slice(cut + 3)) + '</li>' : '<li>' + esc(t) + '</li>';
|
|
418
|
+
};
|
|
419
|
+
const mine = v.reads.mine.map((x) =>
|
|
420
|
+
'<div class="ma-said" data-said="' + esc(x.key) + '"><p class="ma-said__t">' + esc(x.title) + '</p>' +
|
|
421
|
+
(x.text ? '<ul class="ma-said__lines' + (S.readAll ? ' ma-said__lines--all' : '') + '">' + x.text.split(/\n- |\n/).filter((s) => s.trim() && !/^Interaction profile/.test(s)).map(line).join('') + '</ul>' : '') +
|
|
422
|
+
(x.note ? '<p class="ma-said__note">' + esc(x.note) + '</p>' : '') + '</div>').join('') +
|
|
423
|
+
(v.reads.mine.some((x) => x.text) ? '<button type="button" class="btn-ghost ma-readall" data-readall aria-pressed="' + (S.readAll ? 'true' : 'false') + '">' + (S.readAll ? 'shorter' : 'read it all') + '</button>' : '');
|
|
424
|
+
// the project's laws (GET /me laws): shown wherever one is in force; with none, nothing is drawn
|
|
425
|
+
const lawsShown = v.reads.sent;
|
|
426
|
+
return (lawsShown ? '<div class="ma-read ma-read--project">' +
|
|
427
|
+
'<p class="ma-read__l"><span class="ma-read__n">1</span>' + esc(v.project) + '\'s laws<span class="ma-read__who">' + (v.reads.sent ? 'sent to every agent, first' : 'for every agent') + '</span></p>' + lawsHtml + '</div>' : '') +
|
|
428
|
+
'<div class="ma-read ma-read--mine">' +
|
|
429
|
+
'<p class="ma-read__l">' + (lawsShown ? '<span class="ma-read__n">2</span>' : '') + 'Yours<span class="ma-read__who">sent today, live</span></p>' + (mine || '<p class="ma-quiet">Nothing personal is sent.</p>') + '</div>';
|
|
430
|
+
}
|
|
431
|
+
function render() {
|
|
432
|
+
const v = view();
|
|
433
|
+
const strict = $('ma-strict');
|
|
434
|
+
if (strict) {
|
|
435
|
+
strict.innerHTML = strictHtml(v);
|
|
436
|
+
if (v.strict) strict.removeAttribute('hidden'); else strict.setAttribute('hidden', '');
|
|
437
|
+
}
|
|
438
|
+
const sub = $('ma-sub');
|
|
439
|
+
if (sub && v.reads.sent) sub.textContent = 'How your Claude works for you. The project\'s laws come first; the rest is yours.';
|
|
440
|
+
const rows = $('ma-rows');
|
|
441
|
+
if (rows) rows.innerHTML = rowsHtml(v);
|
|
442
|
+
const reads = $('ma-reads-body');
|
|
443
|
+
if (reads) reads.innerHTML = readsHtml(v);
|
|
444
|
+
const lede = $('ma-desk-lede');
|
|
445
|
+
if (lede) {
|
|
446
|
+
lede.textContent = !v.counts.locked ? 'All yours. Tap to change; it saves straight away.'
|
|
447
|
+
: v.counts.locked >= v.counts.total ? 'Every setting here follows the project. Nothing is yours to change.'
|
|
448
|
+
: v.counts.locked + ' of your ' + v.counts.total + ' settings follow a law. The rest are yours: tap to change.';
|
|
449
|
+
}
|
|
450
|
+
const as = $('ma-as');
|
|
451
|
+
const nobody = asNow() === 'nobody';
|
|
452
|
+
if (as) {
|
|
453
|
+
if (nobody) { as.innerHTML = 'Viewing as a builder with no seat. <a href="?as=me">Back to you</a>'; as.removeAttribute('hidden'); } else as.setAttribute('hidden', '');
|
|
454
|
+
}
|
|
455
|
+
const main = $('hall-main');
|
|
456
|
+
if (main) main.setAttribute('aria-busy', 'false');
|
|
457
|
+
}
|
|
458
|
+
// the lit tick: the row stays, the attribute plays the CSS one-shot
|
|
459
|
+
function lit(key) {
|
|
460
|
+
const el = document.querySelector('[data-row="' + key.replace(/"/g, '') + '"]');
|
|
461
|
+
if (!el) return;
|
|
462
|
+
el.setAttribute('data-saved', '');
|
|
463
|
+
setTimeout(() => { el.removeAttribute('data-saved'); }, 1800);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// ---- reads ----
|
|
467
|
+
async function getJSON(path) {
|
|
468
|
+
const r = await api.request('GET', API + path).catch(() => null);
|
|
469
|
+
if (!r) return null;
|
|
470
|
+
if (r.status === 200) return r.data;
|
|
471
|
+
return r.status === 403 ? false : null;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// ---- writes ----
|
|
475
|
+
function choose(key, value) {
|
|
476
|
+
const row = findRow(key);
|
|
477
|
+
if (!row || row.locked) return;
|
|
478
|
+
const w = writeFor(key, value, S);
|
|
479
|
+
const said = (key.indexOf('skill.') === 0 ? key.slice(6) : nameFor(key).toLowerCase()) + ' ' + wordFor(key, value);
|
|
480
|
+
if (!w) return;
|
|
481
|
+
api.request(w.method, API + w.path, { body: w.body }).then((r) => {
|
|
482
|
+
if (r && r.ok && r.data) {
|
|
483
|
+
if (w.slice === 'skills') S.skills = r.data; else S.me[w.slice] = r.data;
|
|
484
|
+
render(); lit(key);
|
|
485
|
+
toast('Saved. Next session: ' + said + '.');
|
|
486
|
+
} else if (r && r.status === 501) {
|
|
487
|
+
applyLocally(w, key, value); render(); lit(key);
|
|
488
|
+
toast('Saved here only: the preview keeps nothing. ' + said + '.');
|
|
489
|
+
} else {
|
|
490
|
+
const code = r && r.data && r.data.error && (r.data.error.code || r.data.error);
|
|
491
|
+
// a law holds this setting (409 locked_by_law): say which, in the law's words
|
|
492
|
+
if (code === 'locked_by_law' && r.data.error.message) { toast(String(r.data.error.message) + '. Nothing changed.'); return; }
|
|
493
|
+
toast('Not saved' + (code ? ': ' + String(code).replace(/_/g, ' ') : '') + '. Nothing changed.');
|
|
494
|
+
}
|
|
495
|
+
}, () => toast('Not saved: no answer. Nothing changed.'));
|
|
496
|
+
}
|
|
497
|
+
// the 501 path: show the choice as made, without pretending the contract moved
|
|
498
|
+
function applyLocally(w, key, value) {
|
|
499
|
+
if (w.slice === 'interaction' && S.me.interaction) { S.me.interaction.effective = Object.assign({}, S.me.interaction.effective, { [key]: value }); S.me.interaction.stored = Object.assign({}, S.me.interaction.stored, { [key]: value }); }
|
|
500
|
+
if (w.slice === 'wandering' && S.me.wandering) { S.me.wandering.effective_key = value; S.me.wandering.override_level = w.body.wandering; }
|
|
501
|
+
if (w.slice === 'model_allocation' && S.me.model_allocation) S.me.model_allocation.effective = Object.assign({}, S.me.model_allocation.effective, w.body);
|
|
502
|
+
if (w.slice === 'render' && S.me.render) S.me.render.level = value;
|
|
503
|
+
if (w.slice === 'skills' && S.skills) arr(S.skills.effective).forEach((e) => { if (e.skill === key.slice(6)) { e.effective_model = value; e.override_model = value; } });
|
|
504
|
+
}
|
|
505
|
+
function findRow(key) {
|
|
506
|
+
const v = S.view;
|
|
507
|
+
if (!v) return null;
|
|
508
|
+
for (const g of v.groups) {
|
|
509
|
+
for (const r of g.rows) {
|
|
510
|
+
if (r.key === key) return r;
|
|
511
|
+
if (r.skills) { const s = r.skills.find((x) => x.key === key); if (s) return Object.assign({ locked: r.locked }, s); }
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
function adopt(id) {
|
|
517
|
+
const sp = arr(S.specialities && S.specialities.specialities).find((x) => String(x.id) === String(id));
|
|
518
|
+
if (!sp) return;
|
|
519
|
+
api.request('POST', API + '/specialities/' + encodeURIComponent(id) + '/adopt', { body: {} }).then((r) => {
|
|
520
|
+
if (r && r.ok) {
|
|
521
|
+
S.adopted.push(str(sp.name)); render(); lit('specialities');
|
|
522
|
+
toast(sp.name + ' adopted. Its skills stay off until you turn them on.');
|
|
523
|
+
} else toast('Not adopted' + (r && r.status ? ' (' + r.status + ')' : '') + '. Nothing changed.');
|
|
524
|
+
}, () => toast('Not adopted: no answer.'));
|
|
525
|
+
}
|
|
526
|
+
// A habit proposed as a law: a real laws vote (POST /government/board/laws,
|
|
527
|
+
// task 1004706), sat under the constitution in force like any amendment.
|
|
528
|
+
function propose(key) {
|
|
529
|
+
const v = S.view;
|
|
530
|
+
const h = v && v.habits.find((x) => x.key === key);
|
|
531
|
+
if (!h || h.proposed) return;
|
|
532
|
+
const login = S.me && S.me.builder ? (S.me.builder.github_login || S.me.builder.display_name || null) : null;
|
|
533
|
+
api.request('POST', API + '/government/board/laws', { body: proposalFor(h, login) }).then((r) => {
|
|
534
|
+
if (r && (r.ok || r.status === 501)) {
|
|
535
|
+
const id = r.ok && r.data && r.data.item && r.data.item.id;
|
|
536
|
+
S.proposed[key] = id ? String(id) : true; render(); lit('habit:' + key);
|
|
537
|
+
toast('Proposed to ' + v.project + ' as a law. It goes to a vote.' + (r.status === 501 ? ' (Preview: nothing filed.)' : ''));
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
const code = r && r.data && r.data.error && (r.data.error.code || r.data.error);
|
|
541
|
+
if (code === 'setting_already_locked' || code === 'already_in_force') { toast('Not proposed: a law already holds this. Nothing filed.'); return; }
|
|
542
|
+
toast('Not proposed' + (r && r.status ? ' (' + r.status + ')' : '') + '. Nothing filed.');
|
|
543
|
+
}, () => toast('Not proposed: no answer.'));
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// A preset switch: ONE real laws vote (POST /government/board/presets, task
|
|
547
|
+
// 1004715) enacting the preset's laws and repealing the other presets'. Nothing
|
|
548
|
+
// changes until the board passes it.
|
|
549
|
+
function switchTo(key) {
|
|
550
|
+
const v = S.view;
|
|
551
|
+
const p = v && v.strict && v.strict.presets.find((x) => x.key === key);
|
|
552
|
+
if (!p || !v.strict.canSwitch || S.switched[key]) return;
|
|
553
|
+
api.request('POST', API + '/government/board/presets', { body: { preset: key } }).then((r) => {
|
|
554
|
+
if (r && (r.ok || r.status === 501)) {
|
|
555
|
+
const id = r.ok && r.data && r.data.item && r.data.item.id;
|
|
556
|
+
S.switched[key] = id ? String(id) : true; render();
|
|
557
|
+
toast('Put to a vote: ' + p.name + '. Nothing changes until it passes.' + (r.status === 501 ? ' (Preview: nothing filed.)' : ''));
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const err = r && r.data && r.data.error;
|
|
561
|
+
const code = err && (err.code || err);
|
|
562
|
+
if (code === 'preset_in_force') { toast('Not filed: ' + p.name + ' is already in force.'); return; }
|
|
563
|
+
if (code === 'setting_already_locked') { toast('Not filed: a law of the project\'s own already holds ' + str(err.details && err.details.setting || 'that setting') + '. Repeal it first.'); return; }
|
|
564
|
+
toast('Not filed' + (r && r.status ? ' (' + r.status + ')' : '') + '. Nothing changed.');
|
|
565
|
+
}, () => toast('Not filed: no answer.'));
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function wire() {
|
|
569
|
+
const main = $('hall-main');
|
|
570
|
+
if (!main) return;
|
|
571
|
+
main.addEventListener('click', (e) => {
|
|
572
|
+
const b = e.target.closest && e.target.closest('button');
|
|
573
|
+
if (!b || b.disabled) return;
|
|
574
|
+
if (b.hasAttribute('data-preset')) { const k = b.getAttribute('data-preset'); const cur = S.presets && S.presets.current; S.preview = !k || k === cur ? null : k; render(); return; }
|
|
575
|
+
if (b.hasAttribute('data-switch')) { switchTo(b.getAttribute('data-switch')); return; }
|
|
576
|
+
if (b.hasAttribute('data-set')) { const row = findRow(b.getAttribute('data-set')); const raw = b.getAttribute('data-value'); const opt = row && row.options.find((x) => String(x.value) === raw); if (opt && !opt.pressed) choose(row.key, opt.value); return; }
|
|
577
|
+
if (b.hasAttribute('data-expand')) { S.skillsOpen = !S.skillsOpen; render(); return; }
|
|
578
|
+
if (b.hasAttribute('data-readall')) { S.readAll = !S.readAll; render(); return; }
|
|
579
|
+
if (b.hasAttribute('data-adopt')) { adopt(b.getAttribute('data-adopt')); return; }
|
|
580
|
+
if (b.hasAttribute('data-propose')) { propose(b.getAttribute('data-propose')); }
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
async function boot() {
|
|
585
|
+
wire();
|
|
586
|
+
const me = await getJSON('/me');
|
|
587
|
+
if (me === null || me === false || !me.builder) {
|
|
588
|
+
const rows = $('ma-rows');
|
|
589
|
+
if (rows) rows.innerHTML = '<p class="ma-quiet">Sign in to see your agents. <a href="' + API + '/auth/web/start">Sign in</a></p>';
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
S.me = me;
|
|
593
|
+
const [constitution, presets, skills, specialities] = await Promise.all([
|
|
594
|
+
getJSON('/government/constitution'), getJSON('/government/presets'), getJSON('/me/skill-prefs'), getJSON('/specialities'),
|
|
595
|
+
]);
|
|
596
|
+
S.presets = presets || null;
|
|
597
|
+
S.constitution = constitution;
|
|
598
|
+
S.skills = skills || null;
|
|
599
|
+
S.specialities = specialities || null;
|
|
600
|
+
render();
|
|
601
|
+
}
|
|
602
|
+
boot();
|
|
603
|
+
}(typeof window !== 'undefined' ? window : globalThis));
|