@nanocollective/roster 0.1.0-alpha.2 → 0.1.0-alpha.21
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/README.md +70 -84
- package/dist/cli.js +4775 -2495
- package/docs/README.md +19 -11
- package/docs/agents.md +328 -13
- package/docs/architecture.md +13 -5
- package/docs/charters/cmo.md +69 -0
- package/docs/charters/cto.md +71 -0
- package/docs/charters/support.md +60 -0
- package/docs/commands.md +95 -11
- package/docs/concepts.md +64 -12
- package/docs/cost.md +39 -3
- package/docs/developing.md +16 -21
- package/docs/doctor-codes.md +21 -6
- package/docs/export.md +2 -1
- package/docs/extending.md +13 -4
- package/docs/getting-started.md +121 -80
- package/docs/images/brain.jpg +0 -0
- package/docs/images/org.jpg +0 -0
- package/docs/images/prompt.jpg +0 -0
- package/docs/images/setup-org.jpg +0 -0
- package/docs/images/setup-plan.jpg +0 -0
- package/docs/images/staff.jpg +0 -0
- package/docs/manual-steps.md +94 -101
- package/docs/memory.md +29 -8
- package/docs/org-yaml.md +76 -11
- package/docs/portal.md +261 -47
- package/docs/prompts.md +77 -11
- package/docs/security.md +51 -7
- package/docs/session-workflow.md +51 -21
- package/docs/staff-yaml.md +16 -7
- package/docs/troubleshooting.md +23 -20
- package/docs/upgrading.md +9 -3
- package/docs/writing-a-charter.md +33 -17
- package/package.json +1 -1
- package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +7 -0
- package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +16 -4
- package/templates/brain/CHARTER.md +3 -3
- package/templates/brain/README.md +1 -0
- package/templates/brain/log/decisions.md +3 -0
- package/templates/brain/staff.yaml +0 -1
- package/templates/brain/strategy/ideas.md +7 -0
- package/templates/ops/.github/workflows/session.yaml +117 -40
- package/templates/ops/agents.mjs +127 -8
- package/templates/ops/compose.mjs +77 -7
- package/templates/ops/inflight.mjs +157 -0
- package/templates/ops/org/operating.md +21 -7
- package/templates/ops/org/voice.md +9 -0
- package/templates/ops/prompts/_identity.md +8 -1
- package/templates/ops/prompts/_inflight.md +14 -0
- package/templates/ops/prompts/_paths.md +2 -1
- package/templates/ops/prompts/daily.md +16 -7
- package/templates/ops/prompts/mention.md +18 -2
- package/templates/ops/run-record.mjs +144 -0
- package/templates/portal/css/base.css +238 -64
- package/templates/portal/css/brain.css +30 -20
- package/templates/portal/css/diff.css +15 -10
- package/templates/portal/css/graph.css +12 -7
- package/templates/portal/css/health.css +32 -11
- package/templates/portal/css/inbox.css +117 -14
- package/templates/portal/css/layout.css +93 -41
- package/templates/portal/css/markdown.css +57 -15
- package/templates/portal/css/runs.css +13 -0
- package/templates/portal/css/setup.css +83 -39
- package/templates/portal/index.html +24 -3
- package/templates/portal/js/api.js +65 -4
- package/templates/portal/js/app.js +112 -12
- package/templates/portal/js/dialog.js +94 -4
- package/templates/portal/js/dom.js +25 -0
- package/templates/portal/js/icons.js +8 -1
- package/templates/portal/js/lightbox.js +273 -0
- package/templates/portal/js/md.js +23 -6
- package/templates/portal/js/mdedit.js +84 -0
- package/templates/portal/js/mention.js +264 -0
- package/templates/portal/js/refresh.js +136 -6
- package/templates/portal/js/state.js +55 -8
- package/templates/portal/js/views/app.js +23 -5
- package/templates/portal/js/views/checklist.js +29 -10
- package/templates/portal/js/views/credential.js +98 -0
- package/templates/portal/js/views/docs.js +94 -4
- package/templates/portal/js/views/files.js +58 -14
- package/templates/portal/js/views/graph.js +1 -1
- package/templates/portal/js/views/health.js +178 -37
- package/templates/portal/js/views/inbox.js +938 -98
- package/templates/portal/js/views/memory.js +16 -1
- package/templates/portal/js/views/org.js +124 -104
- package/templates/portal/js/views/orgedit.js +213 -0
- package/templates/portal/js/views/paste.js +33 -7
- package/templates/portal/js/views/prompt.js +61 -67
- package/templates/portal/js/views/repos.js +20 -15
- package/templates/portal/js/views/runonce.js +94 -0
- package/templates/portal/js/views/runs.js +165 -0
- package/templates/portal/js/views/setup.js +311 -83
- package/templates/portal/js/views/staff.js +139 -22
- package/templates/portal/js/yaml.js +134 -0
- package/templates/brain/.github/workflows/%%STAFF%%-pr-mention.yaml +0 -50
- package/templates/ops/prompts/pr-mention.md +0 -57
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/* Is the scaffolding still there, has the agent been running, and what does lint say. */
|
|
2
2
|
|
|
3
3
|
import { post } from "../api.js";
|
|
4
|
+
import { askYes } from "../dialog.js";
|
|
4
5
|
import { ago, el, esc, kb } from "../dom.js";
|
|
5
6
|
import { inline } from "../md.js";
|
|
6
7
|
import { render } from "../router.js";
|
|
7
8
|
import { S, staff } from "../state.js";
|
|
8
9
|
import { checklist } from "./checklist.js";
|
|
10
|
+
import { copyAmendBrief } from "./prompt.js";
|
|
11
|
+
import { runOnce } from "./runonce.js";
|
|
9
12
|
|
|
10
13
|
const DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
11
14
|
|
|
@@ -27,20 +30,14 @@ export function viewHealth(m) {
|
|
|
27
30
|
const rig = s.rig ?? {};
|
|
28
31
|
m.append(el("h1", { textContent: s.name + " · health" }));
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const errs = s.problems.filter((p) => p.level === "error");
|
|
39
|
-
const warns = s.problems.filter((p) => p.level === "warning");
|
|
40
|
-
m.append(el("p", { className: "sub", innerHTML:
|
|
41
|
-
"<span class='" + (errs.length ? "err" : "ok") + "'>" + errs.length + " errors</span> · " +
|
|
42
|
-
"<span class='warn'>" + warns.length + " warnings</span> · memory checks are the same ones " +
|
|
43
|
-
"<code>roster lint</code> runs; the rest is read off the checkout." }));
|
|
33
|
+
m.append(
|
|
34
|
+
el("p", {
|
|
35
|
+
className: "sub",
|
|
36
|
+
textContent:
|
|
37
|
+
"The rig, then three sets of checks: the org, the prompts they are sent, and what " +
|
|
38
|
+
"they have written down.",
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
44
41
|
|
|
45
42
|
/* The rig: is the scaffolding still there, and has the agent actually been running. This
|
|
46
43
|
is everything answerable without the GitHub API — installation grants, secrets and
|
|
@@ -82,11 +79,86 @@ export function viewHealth(m) {
|
|
|
82
79
|
}
|
|
83
80
|
m.append(g);
|
|
84
81
|
|
|
85
|
-
|
|
82
|
+
/* Three sets of checks, and they were three different-looking things: hairline rows for
|
|
83
|
+
doctor, bordered boxes for the prompt audit, cards with two buttons each for lint. Same
|
|
84
|
+
question in all three, so one shape: a heading that carries its own count, a line saying
|
|
85
|
+
what the section is, the findings as rows, and the section's actions at the end. */
|
|
86
|
+
/* A workflow that has never run is unproven, and the way to prove it is to run it. Here
|
|
87
|
+
because Health is where "unproven" is read; a success re-runs the checks below. */
|
|
88
|
+
let doctor = null;
|
|
89
|
+
m.append(
|
|
90
|
+
runOnce({
|
|
91
|
+
staff: s.handle,
|
|
92
|
+
name: s.name,
|
|
93
|
+
onDone: (ok) => ok && doctor && checklist(doctor.body, { shell: doctor }),
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
86
96
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
doctor = section(m, "The org, from roster doctor", "doctor");
|
|
98
|
+
checklist(doctor.body, { shell: doctor });
|
|
99
|
+
|
|
100
|
+
/* What is wrong with what this agent is sent. It used to sit in the tree on the Prompt
|
|
101
|
+
screen, beside the prompt itself — which answers "what is sent", not "is it any good".
|
|
102
|
+
Both questions are health questions, so both are here. */
|
|
103
|
+
const prompt = section(m, "Prompt problems", "prompts");
|
|
104
|
+
promptProblems(prompt, s);
|
|
105
|
+
|
|
106
|
+
memoryProblems(section(m, "Memory problems", "memory"), s);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* One section: a heading with a count, a body, and a row for whatever acts on all of it.
|
|
111
|
+
*
|
|
112
|
+
* Returned rather than built in one go because two of the three fill themselves in later, and
|
|
113
|
+
* a heading that appears with its count already right is worth the small indirection.
|
|
114
|
+
*/
|
|
115
|
+
function section(m, title, key) {
|
|
116
|
+
const head = el("div", { className: "hsect" });
|
|
117
|
+
head.append(el("span", { textContent: title }));
|
|
118
|
+
const count = el("i");
|
|
119
|
+
head.append(count);
|
|
120
|
+
const body = el("div", { className: "hbody " + key });
|
|
121
|
+
const actions = el("div", { className: "row hacts" });
|
|
122
|
+
m.append(head, body, actions);
|
|
123
|
+
return {
|
|
124
|
+
body,
|
|
125
|
+
actions,
|
|
126
|
+
/** `n` findings, or a word when a count is the wrong shape of answer. */
|
|
127
|
+
say: (text) => { count.textContent = text; },
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** The same checks `roster lint` runs, over what this staff member has written down. */
|
|
132
|
+
function memoryProblems({ body, actions, say }, s) {
|
|
133
|
+
say(s.problems.length ? String(s.problems.length) : "clean");
|
|
134
|
+
body.append(
|
|
135
|
+
el("p", {
|
|
136
|
+
className: "hnote",
|
|
137
|
+
textContent: s.problems.length
|
|
138
|
+
? "The same checks roster lint runs, over memory/INDEX.md."
|
|
139
|
+
: "Nothing roster lint can fault in memory/INDEX.md.",
|
|
140
|
+
}),
|
|
141
|
+
);
|
|
142
|
+
if (!s.problems.length) return;
|
|
143
|
+
|
|
144
|
+
for (const p of s.problems) {
|
|
145
|
+
const { row, body: text } = finding(
|
|
146
|
+
p.level === "error" ? "error" : "warning",
|
|
147
|
+
{ html: inline(p.message) },
|
|
148
|
+
[p.rule, p.line ? "INDEX.md:" + p.line : (p.file ?? "")],
|
|
149
|
+
);
|
|
150
|
+
const status = el("span", { className: "meta" });
|
|
151
|
+
const btn = el("button", { className: "ghbtn", textContent: "Ask " + s.handle.toUpperCase() + " to fix" });
|
|
152
|
+
btn.onclick = () => askToFix(s, [p], btn, status);
|
|
153
|
+
const acts = el("div", { className: "row facts" }, [btn]);
|
|
154
|
+
if (p.slug) {
|
|
155
|
+
const go = el("button", { className: "ghbtn", textContent: "Show the fact" });
|
|
156
|
+
go.onclick = () => { S.openFile = "fact:" + p.slug; S.fileQuery = ""; S.view = "brain"; render(); };
|
|
157
|
+
acts.append(go);
|
|
158
|
+
}
|
|
159
|
+
acts.append(status);
|
|
160
|
+
text.append(acts);
|
|
161
|
+
body.append(row);
|
|
90
162
|
}
|
|
91
163
|
|
|
92
164
|
/* The portal's one power over an agent is that it acts as the human, so "fix this" is an
|
|
@@ -95,27 +167,96 @@ export function viewHealth(m) {
|
|
|
95
167
|
const all = el("button", { className: "ghbtn primary",
|
|
96
168
|
textContent: "Ask " + s.handle.toUpperCase() + " to fix all " + s.problems.length });
|
|
97
169
|
all.onclick = () => askToFix(s, s.problems, all, allStatus);
|
|
98
|
-
|
|
170
|
+
actions.append(all, allStatus);
|
|
171
|
+
}
|
|
99
172
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
173
|
+
/**
|
|
174
|
+
* One finding, in the shape every section uses: a level marker, what is wrong, and where.
|
|
175
|
+
*
|
|
176
|
+
* The marker carries the severity so the words do not have to. A row that opens with the word
|
|
177
|
+
* "warning" spends its first line saying something a glyph already said.
|
|
178
|
+
*/
|
|
179
|
+
function finding(level, title, tags) {
|
|
180
|
+
const row = el("div", { className: "check " + (level === "error" ? "fail" : "warn") });
|
|
181
|
+
row.append(el("span", { className: "checkmark", textContent: level === "error" ? "✗" : "!" }));
|
|
182
|
+
const body = el("div");
|
|
183
|
+
// Plain text where there is any, markup only where a message carries its own.
|
|
184
|
+
const head = el("b");
|
|
185
|
+
if (title.html) head.innerHTML = title.html;
|
|
186
|
+
else head.textContent = title.text;
|
|
187
|
+
body.append(head);
|
|
188
|
+
for (const t of tags.filter(Boolean)) {
|
|
189
|
+
body.append(el("span", { className: "meta", textContent: t }));
|
|
190
|
+
}
|
|
191
|
+
row.append(body);
|
|
192
|
+
// Everything else goes in the second column, under the text rather than under the marker.
|
|
193
|
+
return { row, body };
|
|
194
|
+
}
|
|
107
195
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Everything the audit says about this staff member's prompts, over every kind of run.
|
|
198
|
+
*
|
|
199
|
+
* Fetched rather than read off the export: composing three prompts and diffing their layers is
|
|
200
|
+
* not something to do on every page paint of every screen, and this is the only one that asks.
|
|
201
|
+
*/
|
|
202
|
+
async function promptProblems({ body, say }, s) {
|
|
203
|
+
say("…");
|
|
204
|
+
body.append(el("p", { className: "hnote", textContent: "Composing the prompts…" }));
|
|
205
|
+
let data;
|
|
206
|
+
try {
|
|
207
|
+
data = await (
|
|
208
|
+
await fetch("/api/promptaudit?staff=" + encodeURIComponent(s.handle), { cache: "no-store" })
|
|
209
|
+
).json();
|
|
210
|
+
} catch (e) {
|
|
211
|
+
say("?");
|
|
212
|
+
body.replaceChildren(el("p", { className: "hnote err", textContent: e.message }));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const problems = data.problems ?? [];
|
|
217
|
+
const errors = data.errors ?? [];
|
|
218
|
+
say(problems.length ? String(problems.length) : errors.length ? "?" : "clean");
|
|
219
|
+
body.replaceChildren(
|
|
220
|
+
el("p", {
|
|
221
|
+
className: "hnote",
|
|
222
|
+
textContent: problems.length
|
|
223
|
+
? "What the audit can be sure about, over every kind of run this staff member has."
|
|
224
|
+
: "Nothing the audit can fault in what this staff member is sent.",
|
|
225
|
+
}),
|
|
226
|
+
);
|
|
227
|
+
for (const bad of errors) {
|
|
228
|
+
body.append(
|
|
229
|
+
el("div", { className: "notice", textContent:
|
|
230
|
+
"The " + bad.kind + " prompt does not compose: " + bad.error }),
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
if (!problems.length) return;
|
|
234
|
+
|
|
235
|
+
/* Worst first, and each one carrying the sentence that fixes it. Knowing there is a problem
|
|
236
|
+
is the hard part; writing the paragraph that asks for the change is not. */
|
|
237
|
+
const rank = { error: 0, warning: 1, note: 2 };
|
|
238
|
+
for (const p of [...problems].sort((a, b) => (rank[a.level] ?? 3) - (rank[b.level] ?? 3))) {
|
|
239
|
+
const { row, body: text } = finding(p.level, { text: p.title }, [(p.kinds ?? []).join(", ")]);
|
|
240
|
+
text.append(el("p", { textContent: p.detail }));
|
|
241
|
+
|
|
242
|
+
const note = el("span", { className: "meta" });
|
|
243
|
+
const fix = el("button", { className: "ghbtn", textContent: "Copy a prompt to fix this" });
|
|
244
|
+
fix.onclick = () => copyAmendBrief(s.handle, p.kind, p.want, fix, note);
|
|
245
|
+
const actions = el("div", { className: "row facts" }, [fix]);
|
|
246
|
+
if (p.path) {
|
|
247
|
+
const go = el("button", { className: "ghbtn", textContent: "Open the file" });
|
|
248
|
+
// The file is a layer of a prompt, so it opens where prompts are read.
|
|
249
|
+
go.onclick = () => {
|
|
250
|
+
S.promptKind = p.kind;
|
|
251
|
+
S.promptOpen = p.path;
|
|
252
|
+
S.view = "prompt";
|
|
253
|
+
render();
|
|
254
|
+
};
|
|
115
255
|
actions.append(go);
|
|
116
256
|
}
|
|
117
|
-
|
|
118
|
-
|
|
257
|
+
actions.append(note);
|
|
258
|
+
text.append(actions);
|
|
259
|
+
body.append(row);
|
|
119
260
|
}
|
|
120
261
|
}
|
|
121
262
|
|
|
@@ -135,13 +276,13 @@ export async function askToFix(s, problems, btn, status) {
|
|
|
135
276
|
"",
|
|
136
277
|
"| rule | where | what |",
|
|
137
278
|
"|---|---|---|",
|
|
138
|
-
...problems.map((p) => "| `" + p.rule + "` | " + (p.line ? "INDEX.md:" + p.line : "—") + " | " +
|
|
279
|
+
...problems.map((p) => "| `" + p.rule + "` | " + (p.line ? "INDEX.md:" + p.line : (p.file ?? "—")) + " | " +
|
|
139
280
|
String(p.message).replace(/\|/g, "\\|") + " |"),
|
|
140
281
|
"",
|
|
141
282
|
"Fix in your next session and close this.",
|
|
142
283
|
].join("\n");
|
|
143
284
|
|
|
144
|
-
if (!
|
|
285
|
+
if (!(await askYes({ title: "Open an issue in " + s.brain + "?", confirm: "Open it" }))) return;
|
|
145
286
|
btn.disabled = true;
|
|
146
287
|
status.textContent = "opening…";
|
|
147
288
|
status.className = "meta";
|