@bongos/core 1.24.27 → 1.24.29
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 +53 -58
- package/.claude/skills/builder-claim/SKILL.md +1 -1
- package/.claude/skills/builder-ship/SKILL.md +1 -1
- package/docs/copy-inventory.md +173 -203
- package/docs/copy-registry.json +350 -622
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +8 -8
- package/docs/page-readings.json +346 -331
- package/modules/builder-settings/interaction-prefs.js +2 -2
- package/modules/hall-ui/public/my-agents.css +72 -1
- package/modules/hall-ui/public/my-agents.html +30 -6
- package/modules/hall-ui/public/my-agents.js +98 -56
- package/modules/hall-ui/public/my-agents.states.json +18 -0
- package/modules/hall-ui/public/palette.js +8 -0
- package/modules/hall-ui/public/settings-moved.js +41 -0
- package/modules/hall-ui/public/settings.css +4 -158
- package/modules/hall-ui/public/settings.html +19 -147
- package/modules/hall-ui/public/settings.js +20 -215
- package/modules/hall-ui/public/settings.states.json +2 -4
- package/modules/hall-ui/public/speciality-tools.css +140 -0
- package/modules/hall-ui/public/{settings-specialities.js → speciality-tools.js} +83 -6
- package/modules/hall-ui/records/my-agents.md +5 -2
- package/modules/hall-ui/records/panel-family.md +4 -0
- package/modules/lifecycle/ship-card.js +2 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/hall-preview/server.js +4 -0
- package/src/module-api.js +1 -1
- package/tests/government_tidy_up.mjs +21 -24
- package/tests/hall_loading.mjs +3 -1
- package/tests/hall_my_agents.mjs +190 -2
- package/tests/hall_palette.mjs +12 -0
- package/tests/hall_settings_world.mjs +64 -100
- package/tests/helpers.mjs +162 -0
- package/tests/interaction_posture.mjs +3 -1
- package/tests/speciality_offer_ui.mjs +183 -149
- package/tests/speciality_walkthrough_ui.mjs +182 -161
- package/modules/hall-ui/public/settings-interaction.js +0 -132
- package/modules/hall-ui/public/settings-law-locks.js +0 -53
- package/modules/hall-ui/public/settings-render.js +0 -138
|
@@ -174,7 +174,7 @@ function shouldInject(behaviorPrefs) {
|
|
|
174
174
|
return contractFor(behaviorPrefs) !== '';
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
// The full catalog the /settings
|
|
177
|
+
// The full catalog the My agents page renders (it left /settings, task 1005044).
|
|
178
178
|
function fieldCatalog() {
|
|
179
179
|
return FIELD_KEYS.map((f) => ({
|
|
180
180
|
field: f,
|
|
@@ -185,7 +185,7 @@ function fieldCatalog() {
|
|
|
185
185
|
}));
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
// Everything the /me payload + the
|
|
188
|
+
// Everything the /me payload + the My agents page need in one object.
|
|
189
189
|
function describeForApi(behaviorPrefs) {
|
|
190
190
|
return {
|
|
191
191
|
fields: fieldCatalog(),
|
|
@@ -164,9 +164,12 @@ html[data-theme="dark"] body[data-page="my-agents"] {
|
|
|
164
164
|
.ma-group { margin-top: var(--sp-md); }
|
|
165
165
|
.ma-group:first-child { margin-top: 4px; }
|
|
166
166
|
.ma-rows { list-style: none; margin: 0; padding: 0; }
|
|
167
|
+
/* the fact keeps a floor (task 1005052): a row with more chips than fit beside it
|
|
168
|
+
— session cards, with its way back to the profile — wraps its chips instead of
|
|
169
|
+
squeezing the fact to a word a line */
|
|
167
170
|
.ma-row {
|
|
168
171
|
display: grid;
|
|
169
|
-
grid-template-columns: minmax(118px, 160px) minmax(
|
|
172
|
+
grid-template-columns: minmax(118px, 160px) minmax(132px, 1fr) auto;
|
|
170
173
|
gap: 6px 18px;
|
|
171
174
|
align-items: center;
|
|
172
175
|
min-height: 48px;
|
|
@@ -241,6 +244,74 @@ html[data-theme="dark"] body[data-page="my-agents"] {
|
|
|
241
244
|
.ma-row--sub { min-height: 40px; padding: 4px 0; }
|
|
242
245
|
.ma-row--sub .ma-row__fact { font-size: 13px; }
|
|
243
246
|
|
|
247
|
+
/* ---- what it knows: the speciality tools (task 1005052) ----
|
|
248
|
+
The SAME component Settings mounts (speciality-tools.js; its structure in
|
|
249
|
+
speciality-tools.css, linked before this sheet). Its markup is named in the
|
|
250
|
+
panel family's words (.prow, .btn, .pbtn) and this page has no panel.css, so
|
|
251
|
+
this skin draws those names in this page's own look: a hairline row like the
|
|
252
|
+
desk's, ghost pills, a chosen walkthrough answer as ink on glass. No accent —
|
|
253
|
+
on this page salmon would mean "needs you", and nothing here does. */
|
|
254
|
+
.ma-col { display: flex; flex-direction: column; gap: var(--sp-md); min-width: 0; }
|
|
255
|
+
.ma-know .prows { list-style: none; margin: 4px 0 0; padding: 0; }
|
|
256
|
+
.ma-know .prow { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px 18px; padding: 12px 0; border-top: var(--line) solid var(--rule-soft); }
|
|
257
|
+
.ma-know .prows > .prow:first-child { border-top: 0; }
|
|
258
|
+
.ma-know .prow__main { flex: 1 1 280px; min-width: 0; }
|
|
259
|
+
.ma-know .prow__label { display: flex; flex-wrap: wrap; align-items: baseline; margin: 0; font-size: 14.5px; font-weight: 500; letter-spacing: -0.01em; color: var(--ink); }
|
|
260
|
+
.ma-know .prow__hint { margin: 4px 0 0; max-width: var(--measure); font-size: 13px; line-height: 1.5; color: var(--ink-soft); }
|
|
261
|
+
.ma-know .prow__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px; }
|
|
262
|
+
.ma-know .speciality-create__h { margin: 4px 0 0; font-size: 14px; font-weight: 500; color: var(--ink); }
|
|
263
|
+
.ma-know .btn,
|
|
264
|
+
.ma-know .pbtn {
|
|
265
|
+
display: inline-flex;
|
|
266
|
+
align-items: center;
|
|
267
|
+
justify-content: center;
|
|
268
|
+
min-height: 30px;
|
|
269
|
+
min-width: 30px;
|
|
270
|
+
padding: 0 14px;
|
|
271
|
+
border: var(--line) solid var(--rule);
|
|
272
|
+
border-radius: var(--radius-pill);
|
|
273
|
+
background: none;
|
|
274
|
+
color: var(--ink-soft);
|
|
275
|
+
font: inherit;
|
|
276
|
+
font-size: 13px;
|
|
277
|
+
font-weight: 400;
|
|
278
|
+
text-transform: lowercase;
|
|
279
|
+
white-space: nowrap;
|
|
280
|
+
cursor: pointer;
|
|
281
|
+
}
|
|
282
|
+
.ma-know .btn:hover:not(:disabled),
|
|
283
|
+
.ma-know .pbtn:hover:not(:disabled) { background: none; color: var(--ink); border-color: var(--ink-faint); }
|
|
284
|
+
.ma-know .btn:disabled,
|
|
285
|
+
.ma-know .pbtn:disabled { background: none; color: var(--ink-faint); border-color: var(--rule-soft); cursor: default; }
|
|
286
|
+
/* the walkthrough's answer you gave, and its one commit, read as a chosen chip */
|
|
287
|
+
.ma-know .pbtn[aria-pressed="true"],
|
|
288
|
+
.ma-know .pbtn--save:not(:disabled) { background: var(--glass-strong); color: var(--ink); border-color: var(--ink-soft); }
|
|
289
|
+
/* the 24px floor, drawn in ink: the create form's "for sale" box and the skill picker */
|
|
290
|
+
.ma-know input[type="checkbox"] { width: 24px; height: 24px; margin: 0; flex: none; accent-color: var(--ink); cursor: pointer; }
|
|
291
|
+
.ma-know .speciality-field > span { color: var(--ink-soft); }
|
|
292
|
+
/* "create a speciality": the form folds away until asked for, so the section opens on what you have */
|
|
293
|
+
.ma-know__new { margin-top: 10px; padding-top: 12px; border-top: var(--line) solid var(--rule-soft); }
|
|
294
|
+
.ma-know__new > summary {
|
|
295
|
+
display: inline-flex;
|
|
296
|
+
align-items: center;
|
|
297
|
+
min-height: 30px;
|
|
298
|
+
padding: 0 14px;
|
|
299
|
+
border: var(--line) solid var(--rule);
|
|
300
|
+
border-radius: var(--radius-pill);
|
|
301
|
+
font-size: 13px;
|
|
302
|
+
color: var(--ink-soft);
|
|
303
|
+
list-style: none;
|
|
304
|
+
cursor: pointer;
|
|
305
|
+
}
|
|
306
|
+
.ma-know__new > summary::-webkit-details-marker { display: none; }
|
|
307
|
+
.ma-know__new > summary:hover { color: var(--ink); border-color: var(--ink-faint); }
|
|
308
|
+
.ma-know__new[open] > summary { color: var(--ink); background: var(--glass-strong); border-color: var(--ink-soft); }
|
|
309
|
+
.ma-know__new .speciality-create { margin-top: 4px; padding-top: 0; border-top: 0; }
|
|
310
|
+
/* the fold's own label already names it, so the form's "Create one" would say it twice */
|
|
311
|
+
.ma-know__new .speciality-create > .speciality-create__h:first-child { display: none; }
|
|
312
|
+
.ma-know__status { margin: 8px 0 0; font-size: 13px; color: var(--ink-soft); }
|
|
313
|
+
.ma-know__status:empty { display: none; }
|
|
314
|
+
|
|
244
315
|
/* ---- what the agent reads first ---- */
|
|
245
316
|
.ma-read { margin: 0 0 12px; }
|
|
246
317
|
.ma-read--project { padding: 12px 14px; background: var(--ma-law-wash); border-radius: var(--radius-sm); }
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
group head and the foot line (records/oversight-family.md). This page's
|
|
14
14
|
repeated thing is that row at the desk's one-line density. -->
|
|
15
15
|
<link rel="stylesheet" href="/builders/oversight.css">
|
|
16
|
+
<!-- the speciality tools' structural sheet, shared with Settings (task 1005052);
|
|
17
|
+
before this page's own, whose .ma-know skin draws them in this page's look -->
|
|
18
|
+
<link rel="stylesheet" href="/builders/speciality-tools.css">
|
|
16
19
|
<link rel="stylesheet" href="/builders/my-agents.css">
|
|
17
20
|
</head>
|
|
18
21
|
<body data-page="my-agents" data-page-title="My agents">
|
|
@@ -38,11 +41,30 @@
|
|
|
38
41
|
<section class="ma-strict" id="ma-strict" aria-labelledby="ma-strict-h" hidden></section>
|
|
39
42
|
|
|
40
43
|
<div class="ma-grid">
|
|
41
|
-
<
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
<div class="ma-col">
|
|
45
|
+
<section class="scroll ma-desk" id="ma-desk" aria-labelledby="ma-desk-h">
|
|
46
|
+
<h2 class="scroll__h" id="ma-desk-h">Your agents</h2>
|
|
47
|
+
<p class="scroll__lede" id="ma-desk-lede">One line each. Tap to change; it saves straight away.</p>
|
|
48
|
+
<div id="ma-rows" aria-live="polite"><p class="hall-loading" role="status">Reading your settings…</p></div>
|
|
49
|
+
</section>
|
|
50
|
+
|
|
51
|
+
<!-- task 1005052: WHAT IT KNOWS — every speciality tool Settings has, from
|
|
52
|
+
the SAME component (speciality-tools.js fills these hosts by id). A
|
|
53
|
+
section of its own, not desk rows: a desk row holds at most two
|
|
54
|
+
buttons, and my-agents.js redraws the desk on every change. Ships
|
|
55
|
+
hidden; the component reveals it once its reads answer. -->
|
|
56
|
+
<section class="scroll ma-know" id="specialities-scroll" aria-labelledby="specialities-h" hidden>
|
|
57
|
+
<h2 class="scroll__h" id="specialities-h">What it knows</h2>
|
|
58
|
+
<p class="scroll__lede">A speciality is what you work on, and how you want to be asked about it. The one you use reaches every session where you hold work in its role.</p>
|
|
59
|
+
<div id="speciality-suggestions"></div>
|
|
60
|
+
<ul class="prows" id="specialities-rows"></ul>
|
|
61
|
+
<details class="ma-know__new">
|
|
62
|
+
<summary>create a speciality</summary>
|
|
63
|
+
<form id="speciality-create" class="speciality-create"></form>
|
|
64
|
+
</details>
|
|
65
|
+
<p class="ma-know__status" id="specialities-status" role="status" aria-live="polite"></p>
|
|
66
|
+
</section>
|
|
67
|
+
</div>
|
|
46
68
|
|
|
47
69
|
<aside class="scroll ma-reads" id="ma-reads" aria-labelledby="ma-reads-h">
|
|
48
70
|
<h2 class="scroll__h" id="ma-reads-h">What your agent reads first</h2>
|
|
@@ -67,6 +89,8 @@
|
|
|
67
89
|
<script src="/builders/palette.js" defer></script>
|
|
68
90
|
<script src="/builders/gov-living.js?v=2026-10-10-real1"></script>
|
|
69
91
|
<script src="/builders/gov-positions.js?v=2026-10-09-positions1"></script>
|
|
70
|
-
<script src="/builders/my-agents.js?v=2026-
|
|
92
|
+
<script src="/builders/my-agents.js?v=2026-11-03-defaults"></script>
|
|
93
|
+
<!-- the speciality tools (task 1005052): the one component Settings mounts too -->
|
|
94
|
+
<script src="/builders/speciality-tools.js?v=2026-11-03-shared"></script>
|
|
71
95
|
</body>
|
|
72
96
|
</html>
|
|
@@ -7,12 +7,19 @@
|
|
|
7
7
|
// yours. Every row is one of your agent settings, and a project law can LOCK it.
|
|
8
8
|
// Every part of it is a real read (task 1004717: the prototype overlay is gone):
|
|
9
9
|
// real EVERY ROW (task 1004714). GET /me's interaction profile, wandering,
|
|
10
|
-
// model allocation and session cards; GET /me/skill-prefs;
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// /
|
|
15
|
-
//
|
|
10
|
+
// model allocation and session cards; GET /me/skill-prefs; the sounds
|
|
11
|
+
// switch (off by the owner's call, .claude/sounds/play.js). Writes go to
|
|
12
|
+
// the real routes (PATCH /me/interaction|wandering|model-allocation|
|
|
13
|
+
// render|skill-prefs), so in the preview harness they reach its recorder
|
|
14
|
+
// (scripts/hall-preview/pref-writes.js). Session cards and each skill
|
|
15
|
+
// voice can go BACK to the default (task 1005052): the first chip clears
|
|
16
|
+
// your own pick (PATCH ... null), as Settings' "default" choices do.
|
|
17
|
+
// real WHAT IT KNOWS (task 1005052): every speciality tool Settings has —
|
|
18
|
+
// create, train, choose what it offers, adopt (its instructions read in
|
|
19
|
+
// full first), the skills walkthrough, stop using. Not this file: the
|
|
20
|
+
// SAME component Settings mounts (speciality-tools.js) fills its own
|
|
21
|
+
// section; this page only re-reads /me when it says a speciality changed,
|
|
22
|
+
// so "What your agent reads first" stays true.
|
|
16
23
|
// real the project's laws and the locks they put on your settings (task
|
|
17
24
|
// 1004707): GET /me's `laws` block — the very words every session reads —
|
|
18
25
|
// and `laws.locks`, what each lock did to you, enforced by the server. A
|
|
@@ -57,7 +64,7 @@
|
|
|
57
64
|
'model.main': { name: 'Main session' },
|
|
58
65
|
'model.subagentDefault': { name: 'Helpers' },
|
|
59
66
|
'model.subagentRoutine': { name: 'Routine helpers' },
|
|
60
|
-
render: { name: 'Session cards', words: { minimal: 'one line', text: 'plain text', standard: 'rich card', rich: 'rich, open' } },
|
|
67
|
+
render: { name: 'Session cards', words: { '': 'follow my profile', minimal: 'one line', text: 'plain text', standard: 'rich card', rich: 'rich, open' } },
|
|
61
68
|
};
|
|
62
69
|
// The law a habit would become, in the ask-format voice: a short plain sentence.
|
|
63
70
|
const LAW_LINES = {
|
|
@@ -70,8 +77,26 @@
|
|
|
70
77
|
{ key: 'talk', title: 'How it talks to you', keys: ['technical_level', 'toolchain_familiarity', 'verbosity', 'machinery', 'render', 'sounds'] },
|
|
71
78
|
{ key: 'work', title: 'How your agents work', keys: ['wandering'] },
|
|
72
79
|
{ key: 'models', title: 'Which models run', keys: ['model.main', 'model.subagentDefault', 'model.subagentRoutine', 'skills'] },
|
|
73
|
-
|
|
80
|
+
// 'What it knows' (the specialities) is a section of its own since task
|
|
81
|
+
// 1005052: its tools are more than a desk row's two buttons, and they are the
|
|
82
|
+
// shared speciality-tools.js component, which this desk's redraw would wipe.
|
|
74
83
|
];
|
|
84
|
+
// Deep links (task 1005044): #<anchor> lands on a group this page draws, or on
|
|
85
|
+
// the speciality section. Settings' old links arrive here (settings-moved.js):
|
|
86
|
+
// #talk holds how Claude talks to you and session cards, #work wandering,
|
|
87
|
+
// #skills opens the per-skill voices, #knows is What it knows.
|
|
88
|
+
const ANCHORS = {
|
|
89
|
+
talk: { group: 'talk' },
|
|
90
|
+
work: { group: 'work' },
|
|
91
|
+
models: { group: 'models' },
|
|
92
|
+
skills: { group: 'models', openSkills: true },
|
|
93
|
+
knows: { id: 'specialities-scroll' },
|
|
94
|
+
};
|
|
95
|
+
function anchorFor(hash) {
|
|
96
|
+
const key = String(hash || '').replace(/^#/, '');
|
|
97
|
+
const a = Object.prototype.hasOwnProperty.call(ANCHORS, key) ? ANCHORS[key] : null;
|
|
98
|
+
return a ? { id: a.id || 'ma-group-' + a.group, openSkills: !!a.openSkills } : null;
|
|
99
|
+
}
|
|
75
100
|
// the permission atom that may file a laws vote (POST /government/board/laws)
|
|
76
101
|
const PROPOSE_ATOM = 'board.item.open';
|
|
77
102
|
|
|
@@ -86,8 +111,8 @@
|
|
|
86
111
|
const nameFor = (key) => (SETTING[key] ? SETTING[key].name : key);
|
|
87
112
|
|
|
88
113
|
// ---------- the pure view ----------
|
|
89
|
-
// buildView({ me, constitution, presets, skills,
|
|
90
|
-
// proposed, switched,
|
|
114
|
+
// buildView({ me, constitution, presets, skills, preview,
|
|
115
|
+
// proposed, switched, as, project, now }, { GovLiving, GovPositions })
|
|
91
116
|
// → { project, positions, strict, groups[], habits[], canPropose, reads, counts }
|
|
92
117
|
// `preview` is a strictness preset key to look at instead of the project's own;
|
|
93
118
|
// `presets` is GET /government/presets' body (task 1004715); `switched` maps a
|
|
@@ -132,9 +157,16 @@
|
|
|
132
157
|
if (!r) return null;
|
|
133
158
|
const levels = arr(r.levels).map((l) => l.value);
|
|
134
159
|
const level = str(r.level || r.derived_level);
|
|
160
|
+
// task 1005052: your own pick, or none — then the cards follow the level
|
|
161
|
+
// your interaction profile derives, and "follow my profile" (value '',
|
|
162
|
+
// written as null) is the chip pressed. A pinned level survives a later
|
|
163
|
+
// profile change; following does not pin today's.
|
|
164
|
+
const own = r.override_level == null ? null : str(r.override_level);
|
|
135
165
|
return {
|
|
136
|
-
key, name: nameFor(key), fact: wordFor(key, level),
|
|
137
|
-
|
|
166
|
+
key, name: nameFor(key), fact: wordFor(key, level), note: own === null ? 'follows your profile' : null,
|
|
167
|
+
locked: false, lock: null, cap: null, control: 'chips', value: own === null ? '' : own, effective: level,
|
|
168
|
+
options: [{ value: '', label: wordFor(key, ''), pressed: own === null }]
|
|
169
|
+
.concat(levels.map((v) => ({ value: v, label: wordFor(key, v), pressed: v === own }))),
|
|
138
170
|
};
|
|
139
171
|
}
|
|
140
172
|
if (key === 'sounds') {
|
|
@@ -149,19 +181,17 @@
|
|
|
149
181
|
return {
|
|
150
182
|
key, name: 'Per skill', fact: eff.length + ' skills' + (changed ? ' · ' + changed + ' yours' : ' · all default'),
|
|
151
183
|
locked: false, lock: null, cap: null, control: 'expand', options: [],
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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 })),
|
|
184
|
+
// task 1005052: "default" (value '', written as null) clears your own
|
|
185
|
+
// pick, as Settings' Default does; a model is pressed only if you chose it.
|
|
186
|
+
skills: eff.map((e) => {
|
|
187
|
+
const own = e.override_model == null ? null : str(e.override_model);
|
|
188
|
+
return {
|
|
189
|
+
key: 'skill.' + e.skill, skill: e.skill, value: e.effective_model,
|
|
190
|
+
note: own === null ? null : 'default: ' + str(e.default_model),
|
|
191
|
+
options: [{ value: '', label: 'default', pressed: own === null }]
|
|
192
|
+
.concat(arr(sk.allowed_models).map((v) => ({ value: v, label: v, pressed: v === own }))),
|
|
193
|
+
};
|
|
194
|
+
}),
|
|
165
195
|
};
|
|
166
196
|
}
|
|
167
197
|
return null;
|
|
@@ -170,7 +200,7 @@
|
|
|
170
200
|
const rows = g.keys.map((k) => settingRow(k) || extraRow(k)).filter(Boolean);
|
|
171
201
|
return { key: g.key, title: g.title, rows, source: 'real', locked: rows.filter((r) => r.locked).length };
|
|
172
202
|
}).filter((g) => g.rows.length);
|
|
173
|
-
const lockableKeys = groups.reduce((a, g) => a.concat(g.rows.filter((r) => r.key !== 'sounds'
|
|
203
|
+
const lockableKeys = groups.reduce((a, g) => a.concat(g.rows.filter((r) => r.key !== 'sounds').map((r) => r.key)), []);
|
|
174
204
|
const total = lockableKeys.length;
|
|
175
205
|
const lockedNow = groups.reduce((n, g) => n + g.locked, 0);
|
|
176
206
|
|
|
@@ -273,8 +303,9 @@
|
|
|
273
303
|
return n < 0 ? null : { method: 'PATCH', path: '/me/wandering', body: { wandering: n }, slice: 'wandering' };
|
|
274
304
|
}
|
|
275
305
|
if (key.indexOf('model.') === 0) return { method: 'PATCH', path: '/me/model-allocation', body: { [key.slice(6)]: value }, slice: 'model_allocation' };
|
|
276
|
-
|
|
277
|
-
if (key
|
|
306
|
+
// '' is the way back to the default: null clears your own pick (task 1005052)
|
|
307
|
+
if (key === 'render') return { method: 'PATCH', path: '/me/render', body: { widgets: value === '' ? null : value }, slice: 'render' };
|
|
308
|
+
if (key.indexOf('skill.') === 0) return { method: 'PATCH', path: '/me/skill-prefs', body: { [key.slice(6)]: value === '' ? null : value }, slice: 'skills' };
|
|
278
309
|
return null;
|
|
279
310
|
}
|
|
280
311
|
|
|
@@ -302,7 +333,7 @@
|
|
|
302
333
|
};
|
|
303
334
|
}
|
|
304
335
|
|
|
305
|
-
const pure = { buildView, writeFor, proposalFor, pendingLaws, SETTING, LAW_LINES, GROUPS };
|
|
336
|
+
const pure = { buildView, writeFor, proposalFor, pendingLaws, anchorFor, ANCHORS, SETTING, LAW_LINES, GROUPS };
|
|
306
337
|
root.OTBMyAgents = pure;
|
|
307
338
|
if (typeof module !== 'undefined' && module.exports) { module.exports = pure; return; }
|
|
308
339
|
if (typeof document === 'undefined' || !root.BongosClient) return;
|
|
@@ -322,7 +353,7 @@
|
|
|
322
353
|
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>';
|
|
323
354
|
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>';
|
|
324
355
|
|
|
325
|
-
const S = { me: null, constitution: null, presets: null, skills: null,
|
|
356
|
+
const S = { me: null, constitution: null, presets: null, skills: null, preview: null, proposed: {}, pending: {}, switched: {}, skillsOpen: false, readAll: false, view: null };
|
|
326
357
|
|
|
327
358
|
function asNow() {
|
|
328
359
|
try { return root.sessionStorage.getItem('hall-as') === 'nobody' || /[?&]as=nobody\b/.test(root.location.search) ? 'nobody' : null; }
|
|
@@ -330,8 +361,8 @@
|
|
|
330
361
|
}
|
|
331
362
|
function view() {
|
|
332
363
|
S.view = buildView({
|
|
333
|
-
me: S.me, constitution: S.constitution, presets: S.presets, skills: S.skills,
|
|
334
|
-
preview: S.preview, proposed: S.proposed, pending: S.pending, switched: S.switched,
|
|
364
|
+
me: S.me, constitution: S.constitution, presets: S.presets, skills: S.skills,
|
|
365
|
+
preview: S.preview, proposed: S.proposed, pending: S.pending, switched: S.switched, as: asNow(), project: PROJECT, now: Date.now(),
|
|
335
366
|
}, { GovLiving: root.GovLiving, GovPositions: root.GovPositions });
|
|
336
367
|
return S.view;
|
|
337
368
|
}
|
|
@@ -345,10 +376,6 @@
|
|
|
345
376
|
// every lock is a real law, enforced by the server (task 1004714)
|
|
346
377
|
if (row.locked) return '<span class="ma-lock">' + LOCK + '<span>' + esc(row.lock.text) + '</span></span>';
|
|
347
378
|
if (row.control === 'chips') return chipsHtml(row, row.name);
|
|
348
|
-
if (row.control === 'adopt') {
|
|
349
|
-
return row.options.length ? '<div class="ma-chips" role="group" aria-label="Adopt a speciality">' + row.options.map((op) =>
|
|
350
|
-
'<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>' : '';
|
|
351
|
-
}
|
|
352
379
|
if (row.control === 'expand') {
|
|
353
380
|
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>';
|
|
354
381
|
}
|
|
@@ -357,14 +384,15 @@
|
|
|
357
384
|
function rowHtml(row) {
|
|
358
385
|
// a cap (task 1004714): the chips offer the values under it, and the line says which law
|
|
359
386
|
const sub = (row.cap ? '<span class="ma-row__yours ma-row__cap">' + LOCK + 'capped by ' + esc(row.cap.text) + '</span>' : '') +
|
|
360
|
-
(row.yours ? '<span class="ma-row__yours">yours: ' + esc(row.yours) + '</span>' : '')
|
|
387
|
+
(row.yours ? '<span class="ma-row__yours">yours: ' + esc(row.yours) + '</span>' : '') +
|
|
388
|
+
(row.note ? '<span class="ma-row__yours">' + esc(row.note) + '</span>' : '');
|
|
361
389
|
let html = '<li class="ma-row' + (row.locked ? ' ma-row--locked' : '') + '" data-row="' + esc(row.key) + '">' +
|
|
362
390
|
'<span class="ma-row__name">' + esc(row.name) + TICK + '</span>' +
|
|
363
391
|
'<span class="ma-row__fact"><span>' + esc(row.fact) + '</span>' + sub + '</span>' +
|
|
364
392
|
'<span class="ma-row__act">' + controlHtml(row) + '</span></li>';
|
|
365
393
|
if (row.control === 'expand' && S.skillsOpen && !row.locked) {
|
|
366
394
|
html += '<li class="ma-sub" id="ma-skills"><ul class="ma-rows ma-rows--sub">' + row.skills.map((s) =>
|
|
367
|
-
'<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
|
|
395
|
+
'<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>' + (s.note ? '<span class="ma-row__yours">' + esc(s.note) + '</span>' : '') + '</span>' +
|
|
368
396
|
'<span class="ma-row__act">' + chipsHtml({ key: s.key, options: s.options }, s.skill) + '</span></li>').join('') + '</ul></li>';
|
|
369
397
|
}
|
|
370
398
|
return html;
|
|
@@ -410,7 +438,7 @@
|
|
|
410
438
|
}
|
|
411
439
|
function rowsHtml(v) {
|
|
412
440
|
let html = v.groups.map((g) =>
|
|
413
|
-
'<div class="ov-group ma-group" data-group="' + esc(g.key) + '">' +
|
|
441
|
+
'<div class="ov-group ma-group" id="ma-group-' + esc(g.key) + '" data-group="' + esc(g.key) + '">' +
|
|
414
442
|
'<div class="ov-group__head"><span class="ov-group__name">' + esc(g.title) + '</span>' +
|
|
415
443
|
(g.locked ? '<span class="ov-group__n">' + esc(g.locked) + ' locked</span>' : '') +
|
|
416
444
|
'<span class="ov-group__note">live</span></div>' +
|
|
@@ -502,7 +530,9 @@
|
|
|
502
530
|
const row = findRow(key);
|
|
503
531
|
if (!row || row.locked) return;
|
|
504
532
|
const w = writeFor(key, value, S);
|
|
505
|
-
const
|
|
533
|
+
const skill = key.indexOf('skill.') === 0 ? key.slice(6) : null;
|
|
534
|
+
const said = value === '' ? (skill ? skill + ' on its default model' : nameFor(key).toLowerCase() + ' follow your profile')
|
|
535
|
+
: (skill || nameFor(key).toLowerCase()) + ' ' + wordFor(key, value);
|
|
506
536
|
if (!w) return;
|
|
507
537
|
api.request(w.method, API + w.path, { body: w.body }).then((r) => {
|
|
508
538
|
if (r && r.ok && r.data) {
|
|
@@ -525,8 +555,8 @@
|
|
|
525
555
|
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 }); }
|
|
526
556
|
if (w.slice === 'wandering' && S.me.wandering) { S.me.wandering.effective_key = value; S.me.wandering.override_level = w.body.wandering; }
|
|
527
557
|
if (w.slice === 'model_allocation' && S.me.model_allocation) S.me.model_allocation.effective = Object.assign({}, S.me.model_allocation.effective, w.body);
|
|
528
|
-
if (w.slice === 'render' && S.me.render) S.me.render.level
|
|
529
|
-
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; } });
|
|
558
|
+
if (w.slice === 'render' && S.me.render) Object.assign(S.me.render, value === '' ? { level: S.me.render.derived_level, override_level: null } : { level: value, override_level: value });
|
|
559
|
+
if (w.slice === 'skills' && S.skills) arr(S.skills.effective).forEach((e) => { if (e.skill === key.slice(6)) { e.effective_model = value === '' ? e.default_model : value; e.override_model = value === '' ? null : value; } });
|
|
530
560
|
}
|
|
531
561
|
function findRow(key) {
|
|
532
562
|
const v = S.view;
|
|
@@ -539,16 +569,6 @@
|
|
|
539
569
|
}
|
|
540
570
|
return null;
|
|
541
571
|
}
|
|
542
|
-
function adopt(id) {
|
|
543
|
-
const sp = arr(S.specialities && S.specialities.specialities).find((x) => String(x.id) === String(id));
|
|
544
|
-
if (!sp) return;
|
|
545
|
-
api.request('POST', API + '/specialities/' + encodeURIComponent(id) + '/adopt', { body: {} }).then((r) => {
|
|
546
|
-
if (r && r.ok) {
|
|
547
|
-
S.adopted.push(str(sp.name)); render(); lit('specialities');
|
|
548
|
-
toast(sp.name + ' adopted. Its skills stay off until you turn them on.');
|
|
549
|
-
} else toast('Not adopted' + (r && r.status ? ' (' + r.status + ')' : '') + '. Nothing changed.');
|
|
550
|
-
}, () => toast('Not adopted: no answer.'));
|
|
551
|
-
}
|
|
552
572
|
// A habit proposed as a law: a real laws vote (POST /government/board/laws,
|
|
553
573
|
// task 1004706), sat under the constitution in force like any amendment.
|
|
554
574
|
function propose(key) {
|
|
@@ -604,13 +624,35 @@
|
|
|
604
624
|
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; }
|
|
605
625
|
if (b.hasAttribute('data-expand')) { S.skillsOpen = !S.skillsOpen; render(); return; }
|
|
606
626
|
if (b.hasAttribute('data-readall')) { S.readAll = !S.readAll; render(); return; }
|
|
607
|
-
if (b.hasAttribute('data-adopt')) { adopt(b.getAttribute('data-adopt')); return; }
|
|
608
627
|
if (b.hasAttribute('data-propose')) { propose(b.getAttribute('data-propose')); }
|
|
609
628
|
});
|
|
629
|
+
// The speciality tools (speciality-tools.js, their own section) say when what
|
|
630
|
+
// a session is sent moved: an adopt, a stop, a skills save. /me carries it,
|
|
631
|
+
// so read it again and "What your agent reads first" stays true (task 1005052).
|
|
632
|
+
document.addEventListener('otb:specialities-changed', async () => {
|
|
633
|
+
const me = await getJSON('/me');
|
|
634
|
+
if (me && me.builder) { S.me = me; render(); }
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Land a deep link (task 1005044) once its target is drawn: the desk groups
|
|
639
|
+
// after the first render; What it knows is revealed by speciality-tools.js when
|
|
640
|
+
// its own read lands, so a hidden target is watched (no timer) until it shows.
|
|
641
|
+
function land() {
|
|
642
|
+
const a = anchorFor(root.location.hash);
|
|
643
|
+
if (!a) return;
|
|
644
|
+
if (a.openSkills && !S.skillsOpen && S.view) { S.skillsOpen = true; render(); }
|
|
645
|
+
const el = $(a.id);
|
|
646
|
+
if (!el) return;
|
|
647
|
+
if (!el.hidden) { el.scrollIntoView({ block: 'start' }); return; }
|
|
648
|
+
if (typeof MutationObserver !== 'function') return;
|
|
649
|
+
const watch = new MutationObserver(() => { if (!el.hidden) { watch.disconnect(); el.scrollIntoView({ block: 'start' }); } });
|
|
650
|
+
watch.observe(el, { attributes: true, attributeFilter: ['hidden'] });
|
|
610
651
|
}
|
|
611
652
|
|
|
612
653
|
async function boot() {
|
|
613
654
|
wire();
|
|
655
|
+
root.addEventListener('hashchange', () => land());
|
|
614
656
|
const me = await getJSON('/me');
|
|
615
657
|
if (me === null || me === false || !me.builder) {
|
|
616
658
|
const rows = $('ma-rows');
|
|
@@ -618,16 +660,16 @@
|
|
|
618
660
|
return;
|
|
619
661
|
}
|
|
620
662
|
S.me = me;
|
|
621
|
-
const [constitution, presets, skills,
|
|
622
|
-
getJSON('/government/constitution'), getJSON('/government/presets'), getJSON('/me/skill-prefs'),
|
|
663
|
+
const [constitution, presets, skills, room] = await Promise.all([
|
|
664
|
+
getJSON('/government/constitution'), getJSON('/government/presets'), getJSON('/me/skill-prefs'),
|
|
623
665
|
getJSON('/government/board/items'),
|
|
624
666
|
]);
|
|
625
667
|
S.pending = pendingLaws(room);
|
|
626
668
|
S.presets = presets || null;
|
|
627
669
|
S.constitution = constitution;
|
|
628
670
|
S.skills = skills || null;
|
|
629
|
-
S.specialities = specialities || null;
|
|
630
671
|
render();
|
|
672
|
+
land();
|
|
631
673
|
}
|
|
632
674
|
boot();
|
|
633
675
|
}(typeof window !== 'undefined' ? window : globalThis));
|
|
@@ -25,6 +25,24 @@
|
|
|
25
25
|
"actions": [["wait", 900], ["clickIfVisible", "[data-craft-close]"], ["wait", 300], ["click", "button[data-set=\"verbosity\"][aria-pressed=\"false\"]"], ["wait", 400]],
|
|
26
26
|
"expect": { "visible": [".ma-row[data-saved] .ma-tick"] }
|
|
27
27
|
},
|
|
28
|
+
"what-it-knows": {
|
|
29
|
+
"auth": true,
|
|
30
|
+
"_": "Task 1005052: the speciality tools, the same component Settings mounts. Someone else's speciality (Economics) shows its full instructions open beside its one adopt control; your own (Pricing, in use) carries choose its skills, train, offered skills and stop using. Session cards above follow the profile.",
|
|
31
|
+
"actions": [["wait", 900], ["clickIfVisible", "[data-craft-close]"], ["wait", 300], ["focus", "#specialities-rows button[data-act=\"adopt\"]"], ["wait", 300]],
|
|
32
|
+
"expect": { "visible": ["#specialities-scroll", ".speciality-contract[open]", "#specialities-rows button[data-act=\"abandon\"]", "#specialities-rows button[data-act=\"walk\"]", "button[data-set=\"render\"][data-value=\"\"][aria-pressed=\"true\"]"] }
|
|
33
|
+
},
|
|
34
|
+
"speciality-walk": {
|
|
35
|
+
"auth": true,
|
|
36
|
+
"_": "Adopting Economics opens the skills walkthrough in its card: one skill per step, leave it off or turn it on at equal weight.",
|
|
37
|
+
"actions": [["wait", 900], ["clickIfVisible", "[data-craft-close]"], ["wait", 300], ["click", "#specialities-rows button[data-act=\"adopt\"]"], ["wait", 700]],
|
|
38
|
+
"expect": { "visible": [".skill-walk", ".skill-walk__facts", "button[data-walk=\"on\"]", "button[data-walk=\"off\"]"] }
|
|
39
|
+
},
|
|
40
|
+
"speciality-forms": {
|
|
41
|
+
"auth": true,
|
|
42
|
+
"_": "The owner's tools open in place: Train on Pricing, and the folded create form with its skills picker.",
|
|
43
|
+
"actions": [["wait", 900], ["clickIfVisible", "[data-craft-close]"], ["wait", 300], ["click", "#specialities-rows button[data-act=\"train\"]"], ["wait", 300], ["click", ".ma-know__new > summary"], ["wait", 300], ["click", "#speciality-create-offer > summary"], ["wait", 900]],
|
|
44
|
+
"expect": { "visible": ["form.speciality-train", "#speciality-create input[name=\"name\"]", "#speciality-create-skills .speciality-offer__list"] }
|
|
45
|
+
},
|
|
28
46
|
"nobody": {
|
|
29
47
|
"auth": true,
|
|
30
48
|
"url": "/my-agents?as=nobody",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
// work → work.js makeTabs ids
|
|
54
54
|
// government → government.js makeTabs ids (two rooms since ADR 0266)
|
|
55
55
|
// settings → settings.js RAIL_GROUPS
|
|
56
|
+
// my-agents → my-agents.js ANCHORS (the agent settings left Settings, task 1005044)
|
|
56
57
|
var SECTIONS = [
|
|
57
58
|
{ parent: 'Accomplishments', gate: 'nav-standing', items: [
|
|
58
59
|
{ label: 'Work', href: '/#accomplishments-work' },
|
|
@@ -74,6 +75,13 @@
|
|
|
74
75
|
{ label: 'Constitution', href: '/government#constitution' },
|
|
75
76
|
{ label: 'Ranks & Permissions', href: '/government#ranks' },
|
|
76
77
|
] },
|
|
78
|
+
{ parent: 'My agents', gate: 'nav-my-agents', items: [
|
|
79
|
+
{ label: 'How it talks to you', href: '/my-agents#talk' },
|
|
80
|
+
{ label: 'How your agents work', href: '/my-agents#work' },
|
|
81
|
+
{ label: 'Which models run', href: '/my-agents#models' },
|
|
82
|
+
{ label: 'Skill voices', href: '/my-agents#skills' },
|
|
83
|
+
{ label: 'What it knows', href: '/my-agents#knows' },
|
|
84
|
+
] },
|
|
77
85
|
{ parent: 'Settings', gate: 'shell-user', items: [
|
|
78
86
|
{ label: 'Account', href: '/settings#account' },
|
|
79
87
|
{ label: 'Access', href: '/settings#access' },
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// modules/hall-ui/public/settings-moved.js — where the agent settings went
|
|
2
|
+
// (task 1005044).
|
|
3
|
+
//
|
|
4
|
+
// How Claude talks to you, Wandering, Rendering (session cards), Skill voices and
|
|
5
|
+
// Your specialities were edited on Settings AND on My agents, on the same routes,
|
|
6
|
+
// with nothing saying the two were one setting. The owner kept them on My agents
|
|
7
|
+
// only. Their old deep links (/settings#interaction, #voices, … and the section
|
|
8
|
+
// ids, which settings.js also resolved) still circulate in docs, skills and old
|
|
9
|
+
// messages, so settings.js asks this file first and sends a moved link on to the
|
|
10
|
+
// My agents anchor that holds the same setting (my-agents.js ANCHORS).
|
|
11
|
+
//
|
|
12
|
+
// PURE: no DOM, no fetch. tests/hall_settings_world.mjs requires it in node.
|
|
13
|
+
|
|
14
|
+
(function (root) {
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
// old Settings anchor (and the old section id) → the My agents anchor
|
|
18
|
+
const MOVED = {
|
|
19
|
+
'interaction': 'talk',
|
|
20
|
+
'interaction-scroll': 'talk',
|
|
21
|
+
'rendering': 'talk',
|
|
22
|
+
'render': 'talk',
|
|
23
|
+
'render-scroll': 'talk',
|
|
24
|
+
'wandering': 'work',
|
|
25
|
+
'wandering-scroll': 'work',
|
|
26
|
+
'voices': 'skills',
|
|
27
|
+
'voices-scroll': 'skills',
|
|
28
|
+
'specialities': 'knows',
|
|
29
|
+
'specialities-scroll': 'knows',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// '/my-agents#<anchor>' for a hash that left Settings, or null to stay.
|
|
33
|
+
function movedTarget(hash) {
|
|
34
|
+
const id = String(hash || '').replace(/^#/, '');
|
|
35
|
+
return Object.prototype.hasOwnProperty.call(MOVED, id) ? '/my-agents#' + MOVED[id] : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const api = { MOVED, movedTarget };
|
|
39
|
+
root.OTBSettingsMoved = api;
|
|
40
|
+
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
41
|
+
}(typeof window !== 'undefined' ? window : globalThis));
|