@nanocollective/roster 0.1.0-alpha.32 → 0.1.0-alpha.34

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/dist/cli.js CHANGED
@@ -2658,7 +2658,8 @@ async function staffRuns(brain, handle, days = 30) {
2658
2658
  "--created",
2659
2659
  `>=${since}`,
2660
2660
  "--limit",
2661
- "100",
2661
+ // Skipped mentions (the gate saying no) count towards the limit, and there are hundreds.
2662
+ kind === "mention" ? "500" : "100",
2662
2663
  "--json",
2663
2664
  "databaseId,conclusion,status,createdAt,updatedAt,url"
2664
2665
  ]).then((res) => ({ kind, res }))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanocollective/roster",
3
- "version": "0.1.0-alpha.32",
3
+ "version": "0.1.0-alpha.34",
4
4
  "description": "An agent-run org, powered by GitHub. Scaffold AI staff members whose brain is a repo.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -58,8 +58,17 @@
58
58
  extra room is put on the row. General sibling, not adjacent: labels can sit between them. */
59
59
  .tconflict ~ .tacts{margin-top:24px}
60
60
  .tlabels{margin-top:10px}
61
- .cmt{border-top:1px solid var(--line);padding:16px 0}
62
- .cmt.first{border-top:0}
61
+ /* A comment is a card with a header strip, so each message has a clear start and end. */
62
+ .cmt{border:1px solid var(--line);border-radius:var(--r-lg);margin:14px 0;background:var(--panel);overflow:hidden}
63
+ .cmt.human{border-color:color-mix(in srgb,var(--accent) 40%,var(--line))}
64
+ .chead{display:flex;align-items:center;gap:8px;padding:9px 14px;background:var(--panel-2);
65
+ border-bottom:1px solid var(--line);font:13px var(--sans);color:var(--ink-dim)}
66
+ .cmt.human .chead{background:color-mix(in srgb,var(--accent) 8%,var(--panel-2))}
67
+ .chead .ic{--ic:14px;color:var(--ink-faint)}
68
+ .chead b{color:var(--ink);font-weight:600}
69
+ .chead .clogin{font:11.5px var(--mono);color:var(--ink-faint)}
70
+ .chead .cwhen{margin-left:auto;font-size:12px;color:var(--ink-faint);white-space:nowrap}
71
+ .cbodywrap{padding:12px 16px 14px}
63
72
  /* The header already draws a line under itself. Whatever the reversed thread puts first must
64
73
  not draw a second one right under it. */
65
74
  .lead{border-top:0}
@@ -138,7 +147,8 @@ textarea.dropping{border-color:var(--accent);background:color-mix(in srgb,var(--
138
147
  /* ---------- timeline events ----------
139
148
  Not comments, so not comment-shaped: a marker, a line of prose, and whatever it points at.
140
149
  These carry the half of a thread that is agents referring to each other's work. */
141
- .tev{display:flex;gap:10px;align-items:flex-start;padding:9px 0 9px 2px;
150
+ .tev{display:flex;gap:10px;align-items:flex-start;padding:6px 0 6px 12px;margin-left:18px;
151
+ border-left:2px solid var(--line);
142
152
  font:12.5px/1.5 var(--sans);color:var(--ink-dim)}
143
153
  .tev .tico{flex:none;display:inline-flex;align-items:center;justify-content:center;
144
154
  width:16px;height:19px;color:var(--ink-faint);--ic:14px}
@@ -3,7 +3,7 @@
3
3
  import { getLabels, getPr, getThread, post, upload } from "../api.js";
4
4
  import { askText, askYes } from "../dialog.js";
5
5
  import { ago, el, esc, markCurrent, skeleton } from "../dom.js";
6
- import { icon, iconHTML } from "../icons.js";
6
+ import { icon, iconHTML, staffIcon } from "../icons.js";
7
7
  import { mdlite } from "../md.js";
8
8
  import { attachMentions } from "../mention.js";
9
9
  import { ensureInbox, refreshAll, stampCounts } from "../refresh.js";
@@ -1047,15 +1047,36 @@ function xref(source, item, openThread) {
1047
1047
  return b;
1048
1048
  }
1049
1049
 
1050
+ /* Who wrote something, as a person would say it: a staff member by their role, with their
1051
+ icon; one of the humans by name; anyone else by their login. */
1052
+ function whoWrote(login) {
1053
+ const s = (S.data?.staff ?? []).find((x) => (x.soloBots ?? []).includes(login));
1054
+ if (s) return { name: s.name, glyph: staffIcon(s), kind: "staff" };
1055
+ if (isHuman(login)) {
1056
+ const h = humansOf().find((x) => x.github === login);
1057
+ return { name: h?.name ?? login, glyph: "person", kind: "human" };
1058
+ }
1059
+ return { name: login || "?", glyph: "person", kind: "other" };
1060
+ }
1061
+
1062
+ /* A comment is a card: a header strip saying who and when, then what they said. As text
1063
+ between thin rules, it was hard to see where one message ended and the next began. */
1050
1064
  function comment({ author, when, body, first, repo, badge, reactions }) {
1051
- const d = el("div", { className: "cmt" + (first ? " first" : "") });
1052
- const meta = el("div", { className: "meta" });
1053
- meta.append((author || "?") + " · " + new Date(when).toLocaleString());
1054
- if (badge) meta.append(el("span", { className: "pill", style: "margin-left:8px", textContent: badge }));
1055
- d.append(meta);
1056
- d.append(el("div", { className: "md cbody", innerHTML: mdlite(body || "_no description_", { repo }) }));
1065
+ const who = whoWrote(author);
1066
+ const d = el("div", { className: "cmt " + who.kind + (first ? " first" : "") });
1067
+ const head = el("div", { className: "chead" });
1068
+ head.append(icon(who.glyph, "ic"), el("b", { textContent: who.name }));
1069
+ if (who.name !== author && author) head.append(el("span", { className: "clogin", textContent: author }));
1070
+ if (badge) head.append(el("span", { className: "pill", textContent: badge }));
1071
+ head.append(
1072
+ el("span", { className: "cwhen", title: new Date(when).toLocaleString(), textContent: ago(when) }),
1073
+ );
1074
+ d.append(head);
1075
+ const bodyEl = el("div", { className: "cbodywrap" });
1076
+ bodyEl.append(el("div", { className: "md cbody", innerHTML: mdlite(body || "_no description_", { repo }) }));
1057
1077
  const marks = reactionRow(reactions);
1058
- if (marks) d.append(marks);
1078
+ if (marks) bodyEl.append(marks);
1079
+ d.append(bodyEl);
1059
1080
  return d;
1060
1081
  }
1061
1082
 
@@ -29,6 +29,11 @@ export function viewRuns(m) {
29
29
  return;
30
30
  }
31
31
  paint(m, S.runs);
32
+ /* Kept for the session, it could be days old: a tab opened during a rate limit went on
33
+ showing a two-week-old list. Anything older than a few minutes is asked for again. */
34
+ // Only a read with a time on it can be old, and only one re-read is asked for at a time.
35
+ const at = S.runs.fetchedAt ? new Date(S.runs.fetchedAt).getTime() : 0;
36
+ if (at && Date.now() - at > 5 * 60_000 && !asking) load(true);
32
37
  }
33
38
 
34
39
  let asking = false;