@metaphi-ai/hum 0.1.67 → 0.1.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.mjs +249 -64
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -83589,7 +83589,7 @@ function runningVersion(candidates = engineCandidates(), opts = {}) {
83589
83589
  }
83590
83590
  return null;
83591
83591
  }
83592
- function startFailure(failures, { npm = /^(node|bun)(\.exe)?$/i.test(String(process.execPath).split(/[\\/]/).pop()), platform: platform2 = process.platform, version = true ? "0.1.67" : "" } = {}) {
83592
+ function startFailure(failures, { npm = /^(node|bun)(\.exe)?$/i.test(String(process.execPath).split(/[\\/]/).pop()), platform: platform2 = process.platform, version = true ? "0.1.69" : "" } = {}) {
83593
83593
  const refused = failures.find((f) => f.error && f.error.code !== "ENOENT");
83594
83594
  const fix = npm ? npmFix(version) : updateCommand({ kind: "native" }, platform2);
83595
83595
  if (!refused) {
@@ -83811,6 +83811,7 @@ var agents_exports = {};
83811
83811
  __export(agents_exports, {
83812
83812
  alive: () => alive,
83813
83813
  fmtCost: () => fmtCost,
83814
+ grouped: () => grouped,
83814
83815
  liveRows: () => liveRows,
83815
83816
  needCount: () => needCount,
83816
83817
  peekSession: () => peekSession,
@@ -83844,6 +83845,8 @@ function rowOf(rec) {
83844
83845
  steps: Number(rec.steps || 0),
83845
83846
  spent_usd: Number(rec.spent_usd || 0),
83846
83847
  summary: rec.summary || "",
83848
+ agents: Number(rec.agents || 0),
83849
+ // the sub-agents it runs right now, from its own run file
83847
83850
  cwd: rec.cwd || "",
83848
83851
  port: rec.port || 0,
83849
83852
  updated: Number(rec.updated || 0)
@@ -83871,6 +83874,35 @@ function liveRows() {
83871
83874
  rows.sort((a, b) => rank(a) - rank(b) || b.updated - a.updated);
83872
83875
  return rows;
83873
83876
  }
83877
+ function grouped(rows) {
83878
+ const teams = /* @__PURE__ */ new Map();
83879
+ const loose = [];
83880
+ for (const r of rows) {
83881
+ if (!r.team) {
83882
+ loose.push(r);
83883
+ continue;
83884
+ }
83885
+ const g = teams.get(r.team) || { lead: null, workers: [] };
83886
+ if (r.role === "lead" && !g.lead) g.lead = r;
83887
+ else g.workers.push(r);
83888
+ teams.set(r.team, g);
83889
+ }
83890
+ const groups = [...teams.values()];
83891
+ for (const g of groups) {
83892
+ g.workers.sort((a, b) => rank(a) - rank(b) || b.updated - a.updated);
83893
+ const all = [g.lead, ...g.workers].filter(Boolean);
83894
+ g.best = Math.min(...all.map(rank));
83895
+ g.at = Math.max(...all.map((r) => r.updated));
83896
+ }
83897
+ groups.sort((a, b) => a.best - b.best || b.at - a.at);
83898
+ const out = [];
83899
+ for (const g of groups) {
83900
+ if (g.lead) out.push({ ...g.lead, depth: 0 });
83901
+ for (const w of g.workers) out.push({ ...w, depth: g.lead ? 1 : 0 });
83902
+ }
83903
+ for (const r of loose) out.push({ ...r, depth: 0 });
83904
+ return { rows: out, teams: groups.length };
83905
+ }
83874
83906
  function stopSession(row, done = () => {
83875
83907
  }) {
83876
83908
  const token2 = (readRun(row.run_id) || {}).token || "";
@@ -83929,7 +83961,7 @@ async function peekSession(row, ready, make = (port2, token2) => new Engine(port
83929
83961
  if (ev.type === "replay") {
83930
83962
  replay = ev.items || [];
83931
83963
  timer = setTimeout(() => settle(null), PEEK_SETTLE_MS);
83932
- } else if (ev.type === "question") settle(ev);
83964
+ } else if (ev.type === "question") settle(asked(ev));
83933
83965
  else if (ev.type === "bye") settle(null);
83934
83966
  });
83935
83967
  await engine2.connect();
@@ -83941,7 +83973,7 @@ async function peekSession(row, ready, make = (port2, token2) => new Engine(port
83941
83973
  }
83942
83974
  };
83943
83975
  }
83944
- var firstLine, rank, needCount, stateText, fmtCost, PEEK_SETTLE_MS;
83976
+ var firstLine, rank, needCount, stateText, fmtCost, PEEK_SETTLE_MS, asked;
83945
83977
  var init_agents = __esm({
83946
83978
  "src/agents.js"() {
83947
83979
  init_engine();
@@ -83951,6 +83983,11 @@ var init_agents = __esm({
83951
83983
  stateText = (r) => r.needs ? "needs you" : r.state === "busy" ? "working" : r.state;
83952
83984
  fmtCost = (c) => c >= 0.01 ? `$${c.toFixed(2)}` : c > 0 ? `$${c.toFixed(4)}` : "$0";
83953
83985
  PEEK_SETTLE_MS = 300;
83986
+ asked = (ev) => {
83987
+ const first = ev.questions && ev.questions[0] || ev;
83988
+ const options2 = (first.options || []).map((o) => typeof o === "string" ? { label: o } : o);
83989
+ return { ...ev, question: first.question || ev.question || "", options: options2, default: first.default ?? ev.default ?? null };
83990
+ };
83954
83991
  }
83955
83992
  });
83956
83993
 
@@ -85170,7 +85207,7 @@ __export(agents_exports2, {
85170
85207
  default: () => Agents
85171
85208
  });
85172
85209
  function Agents({ onAttach, onQuit, read = liveRows, peek = peekSession, stop = stopSession, everyMs = EVERY_MS }) {
85173
- const [rows, setRows] = (0, import_react25.useState)(() => read());
85210
+ const [rows, setRows] = (0, import_react25.useState)(() => grouped(read()).rows);
85174
85211
  const [index, setIndex] = (0, import_react25.useState)(0);
85175
85212
  const [open, setOpen] = (0, import_react25.useState)(null);
85176
85213
  const [value, setValue] = (0, import_react25.useState)("");
@@ -85178,7 +85215,7 @@ function Agents({ onAttach, onQuit, read = liveRows, peek = peekSession, stop =
85178
85215
  const { stdout } = use_stdout_default();
85179
85216
  const width = stdout && stdout.columns || 100;
85180
85217
  (0, import_react25.useEffect)(() => {
85181
- const t = setInterval(() => setRows(read()), everyMs);
85218
+ const t = setInterval(() => setRows(grouped(read()).rows), everyMs);
85182
85219
  return () => clearInterval(t);
85183
85220
  }, [read, everyMs]);
85184
85221
  const row = rows[Math.min(index, Math.max(0, rows.length - 1))];
@@ -85240,7 +85277,7 @@ function Agents({ onAttach, onQuit, read = liveRows, peek = peekSession, stop =
85240
85277
  setNote(`stopping ${row.name} \u2026`);
85241
85278
  stop(row, (ok) => {
85242
85279
  setNote(ok ? `stopped ${row.name}` : `could not stop ${row.name}`);
85243
- setRows(read());
85280
+ setRows(grouped(read()).rows);
85244
85281
  });
85245
85282
  } else if (input === "q") onQuit();
85246
85283
  });
@@ -85267,7 +85304,7 @@ function Agents({ onAttach, onQuit, read = liveRows, peek = peekSession, stop =
85267
85304
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { borderStyle: "round", borderColor: "gray", paddingX: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: value || /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: q.default ? `enter takes the default: ${q.default}` : "your answer" }) }) }),
85268
85305
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: "enter answers and leaves \xB7 esc leaves it open" })
85269
85306
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
85270
- open.lines.length ? open.lines.slice(-PEEK_LINES).map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: l.slice(0, width - 4) }, i)) : open.waiting ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: "nothing on its transcript yet" }),
85307
+ open.lines.length ? open.lines.slice(-PEEK_LINES2).map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: l.slice(0, width - 4) }, i)) : open.waiting ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: "nothing on its transcript yet" }),
85271
85308
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { dimColor: true, children: [
85272
85309
  "\n",
85273
85310
  "esc goes back"
@@ -85275,17 +85312,20 @@ function Agents({ onAttach, onQuit, read = liveRows, peek = peekSession, stop =
85275
85312
  ] })
85276
85313
  ] });
85277
85314
  }
85278
- const nameW = 14;
85315
+ const nameW = 18;
85279
85316
  const roleW = 9;
85280
85317
  const stateW = 10;
85281
85318
  const needsW = 9;
85282
85319
  const stepsW = 6;
85283
85320
  const spentW = 8;
85321
+ const teams = new Set(rows.filter((r) => r.team).map((r) => r.team)).size;
85322
+ const count = `${rows.length} on this machine${teams ? ` \xB7 ${teams} team${teams === 1 ? "" : "s"}` : ""}`;
85323
+ const nameOf = (r) => `${" ".repeat(r.depth || 0)}${r.name}${r.agents ? ` +${r.agents}` : ""}`;
85284
85324
  const taskW = Math.max(12, width - 2 - (2 + nameW + roleW + stateW + needsW + stepsW + spentW + 6));
85285
85325
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, children: [
85286
85326
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: ACCENT, bold: true, children: [
85287
85327
  "Agents ",
85288
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: `(${rows.length} on this machine)` })
85328
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: `(${count})` })
85289
85329
  ] }),
85290
85330
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { dimColor: true, children: [
85291
85331
  " ",
@@ -85306,7 +85346,7 @@ function Agents({ onAttach, onQuit, read = liveRows, peek = peekSession, stop =
85306
85346
  rows.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "column", children: [
85307
85347
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { children: [
85308
85348
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { width: 2, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: ACCENT, children: i === index ? "\u276F" : "" }) }),
85309
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { bold: i === index, children: pad(r.name, nameW) }),
85349
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { bold: i === index, children: pad(nameOf(r), nameW) }),
85310
85350
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { dimColor: true, children: [
85311
85351
  " ",
85312
85352
  pad(r.role, roleW)
@@ -85342,7 +85382,7 @@ function Agents({ onAttach, onQuit, read = liveRows, peek = peekSession, stop =
85342
85382
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: FOOTER }) })
85343
85383
  ] });
85344
85384
  }
85345
- var import_react25, import_jsx_runtime3, EVERY_MS, PEEK_LINES, pad, padLeft, FOOTER;
85385
+ var import_react25, import_jsx_runtime3, EVERY_MS, PEEK_LINES2, pad, padLeft, FOOTER;
85346
85386
  var init_agents2 = __esm({
85347
85387
  async "src/agents.jsx"() {
85348
85388
  import_react25 = __toESM(require_react(), 1);
@@ -85351,7 +85391,7 @@ var init_agents2 = __esm({
85351
85391
  init_agents();
85352
85392
  import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
85353
85393
  EVERY_MS = 2e3;
85354
- PEEK_LINES = 20;
85394
+ PEEK_LINES2 = 20;
85355
85395
  pad = (s, n) => String(s || "").slice(0, n).padEnd(n);
85356
85396
  padLeft = (s, n) => String(s || "").slice(0, n).padStart(n);
85357
85397
  FOOTER = "\u2191\u2193 move \xB7 enter attach \xB7 space peek \xB7 s stop \xB7 q quit";
@@ -85568,7 +85608,7 @@ var input_default = MultilineInput;
85568
85608
 
85569
85609
  // src/app.jsx
85570
85610
  var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
85571
- var MINE = true ? "0.1.67" : "";
85611
+ var MINE = true ? "0.1.69" : "";
85572
85612
  var fmtDur = (s) => s < 60 ? `${Math.max(1, Math.round(s))}s` : `${Math.floor(s / 60)}m ${Math.round(s % 60)}s`;
85573
85613
  var plural = (n, w) => `${n} ${w}${n === 1 ? "" : "s"}`;
85574
85614
  var firstResp = (e) => e.first_response_s ? ` \xB7 first response ${e.first_response_s.toFixed(1)}s` : "";
@@ -85655,6 +85695,7 @@ var COMMANDS = [
85655
85695
  { name: "/goal", desc: "the goal the session keeps working toward on its own, exchange after exchange, until the model marks it complete or blocked (/goal <objective>; /goal pause \xB7 resume \xB7 clear; /goal alone shows where it stands)" },
85656
85696
  { name: "/image", desc: "attach an image file to your next message (/image <path>); ctrl+v pastes a screenshot" },
85657
85697
  { name: "/mcp", desc: "the MCP servers in this session \u2014 what each mounted, and any that did not" },
85698
+ { name: "/comms", desc: "the channels in the loop on this session (Slack), and whether you are paired" },
85658
85699
  { name: "/tools", desc: "the tools the model has, and the sandbox they run in" },
85659
85700
  { name: "/hooks", desc: "the lifecycle hooks in scope (~/.hum/hooks.json, <repo>/.hum/hooks.json; Claude Code and Codex hook files too)" },
85660
85701
  { name: "/permissions", desc: "the rules in force here \u2014 what runs without asking, what is refused, and where each rule came from" },
@@ -85674,6 +85715,7 @@ var REWIND_CHOICES = [
85674
85715
  { key: "both", label: "both", desc: "" }
85675
85716
  ];
85676
85717
  var ERR_TAIL_LINES = 5;
85718
+ var PEEK_LINES = 12;
85677
85719
  var THOUGHT_TAIL_LINES = 3;
85678
85720
  var diagLine = (d) => `${d.file ? `${d.file}${d.line != null ? `:${d.line}` : ""}: ` : ""}${d.severity || "error"}: ${d.message || ""}`;
85679
85721
  var toolLine = (text) => {
@@ -85690,7 +85732,7 @@ var tallyLine = (tally) => Object.entries(tally).map(([name, t]) => {
85690
85732
  const parts = [t.ok ? `${t.ok} ok` : null, t.failed ? `${t.failed} failed` : null, t.error ? `${t.error} error` : null].filter(Boolean);
85691
85733
  return `${short} ${parts.join(" \xB7 ")}`;
85692
85734
  }).join(" ");
85693
- function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2 = null }) {
85735
+ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2 = null, peek = peekSession }) {
85694
85736
  const { exit } = use_app_default();
85695
85737
  const { stdout } = use_stdout_default();
85696
85738
  const width = (stdout?.columns || 80) - 2;
@@ -85704,6 +85746,9 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
85704
85746
  const [session, setSession] = (0, import_react24.useState)(null);
85705
85747
  const [spend, setSpend] = (0, import_react24.useState)(0);
85706
85748
  const [agents, setAgents] = (0, import_react24.useState)([]);
85749
+ const [panelIndex, setPanelIndex] = (0, import_react24.useState)(-1);
85750
+ const [looking, setLooking] = (0, import_react24.useState)(null);
85751
+ const lookRef = (0, import_react24.useRef)(null);
85707
85752
  const [draft, setDraft] = (0, import_react24.useState)("");
85708
85753
  const [thinking, setThinking] = (0, import_react24.useState)("");
85709
85754
  const [value, setValue] = (0, import_react24.useState)("");
@@ -85712,7 +85757,8 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
85712
85757
  const [ctrlc, setCtrlc] = (0, import_react24.useState)(0);
85713
85758
  const [picker, setPicker] = (0, import_react24.useState)(null);
85714
85759
  const [proposal, setProposal] = (0, import_react24.useState)(null);
85715
- const [question, setQuestion] = (0, import_react24.useState)(null);
85760
+ const [questions, setQuestions] = (0, import_react24.useState)([]);
85761
+ const question = questions.length ? questions[0] : null;
85716
85762
  const [qStep, setQStep] = (0, import_react24.useState)(0);
85717
85763
  const [qAnswers, setQAnswers] = (0, import_react24.useState)({});
85718
85764
  const [qPicks, setQPicks] = (0, import_react24.useState)([]);
@@ -85738,6 +85784,7 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
85738
85784
  const modelRef = (0, import_react24.useRef)("");
85739
85785
  const lastOutRef = (0, import_react24.useRef)(null);
85740
85786
  const helloRef = (0, import_react24.useRef)(false);
85787
+ const asksRef = (0, import_react24.useRef)([]);
85741
85788
  const startedRef = (0, import_react24.useRef)(false);
85742
85789
  const escRef = (0, import_react24.useRef)(0);
85743
85790
  const rewindRef = (0, import_react24.useRef)({ files: true, conversation: true });
@@ -85750,6 +85797,37 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
85750
85797
  setPhase(p);
85751
85798
  };
85752
85799
  const append = (item) => setItems((prev) => [...prev, { id: nextId.current++, ...item }]);
85800
+ const setAsks = (next) => {
85801
+ asksRef.current = next;
85802
+ setQuestions(next);
85803
+ };
85804
+ const restartAsk = () => {
85805
+ setQStep(0);
85806
+ setQAnswers({});
85807
+ setQPicks([]);
85808
+ setQCursor(0);
85809
+ };
85810
+ const askOf = (ev) => ({
85811
+ id: ev.id,
85812
+ question: ev.question || "",
85813
+ options: ev.options || [],
85814
+ default: ev.default || null,
85815
+ questions: ev.questions || null,
85816
+ kind: ev.kind || null,
85817
+ summary: ev.summary || "",
85818
+ evidence: ev.evidence || []
85819
+ });
85820
+ const addAsk = (ev) => {
85821
+ if (asksRef.current.some((q) => q.id === ev.id)) return;
85822
+ const shown = asksRef.current.length > 0;
85823
+ setAsks([...asksRef.current, askOf(ev)]);
85824
+ if (!shown) restartAsk();
85825
+ };
85826
+ const dropAsk = (id) => {
85827
+ const wasShown = asksRef.current.length > 0 && asksRef.current[0].id === id;
85828
+ setAsks(asksRef.current.filter((q) => q.id !== id));
85829
+ if (wasShown) restartAsk();
85830
+ };
85753
85831
  const steps = question ? question.questions && question.questions.length ? question.questions : [{ id: question.id, question: question.question, options: (question.options || []).map((o) => ({ label: o })), default: question.default }] : [];
85754
85832
  const step = steps.length ? steps[Math.min(qStep, steps.length - 1)] : null;
85755
85833
  const stepOptions = step ? (step.options || []).map((o) => typeof o === "string" ? { label: o } : o) : [];
@@ -85774,6 +85852,8 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
85774
85852
  };
85775
85853
  const resetForSession = () => {
85776
85854
  setAgents([]);
85855
+ setPanelIndex(-1);
85856
+ closeLook();
85777
85857
  setItems([]);
85778
85858
  setDraft("");
85779
85859
  draftRef.current = "";
@@ -85781,11 +85861,8 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
85781
85861
  setThinking("");
85782
85862
  thinkChars.current = 0;
85783
85863
  setProposal(null);
85784
- setQuestion(null);
85785
- setQStep(0);
85786
- setQAnswers({});
85787
- setQPicks([]);
85788
- setQCursor(0);
85864
+ setAsks([]);
85865
+ restartAsk();
85789
85866
  setPaused(false);
85790
85867
  setPicker(null);
85791
85868
  setConfirm(null);
@@ -85801,6 +85878,30 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
85801
85878
  wroteRef.current = false;
85802
85879
  setMeter((m) => ({ ...m, tokens: 0, input: 0, output: 0 }));
85803
85880
  };
85881
+ const closeLook = () => {
85882
+ if (lookRef.current && lookRef.current.handle) lookRef.current.handle.close();
85883
+ lookRef.current = null;
85884
+ setLooking(null);
85885
+ };
85886
+ const showLook = (v) => {
85887
+ lookRef.current = v;
85888
+ setLooking(v);
85889
+ };
85890
+ const openLook = async (a) => {
85891
+ const row = { run_id: String(a.n), name: a.name || String(a.n), port: (readRun(String(a.n)) || {}).port || 0, task: a.desc || "" };
85892
+ showLook({ row, question: null, lines: [], waiting: true, handle: null });
85893
+ const here = () => lookRef.current && lookRef.current.row.run_id === row.run_id;
85894
+ try {
85895
+ const handle2 = await peek(row, ({ question: question2, lines }) => {
85896
+ if (here()) showLook({ ...lookRef.current, question: question2, lines, waiting: false });
85897
+ });
85898
+ if (here()) showLook({ ...lookRef.current, handle: handle2 });
85899
+ else handle2.close();
85900
+ } catch (e) {
85901
+ showLook(null);
85902
+ append({ kind: "warn", text: `could not look at ${row.name}: ${e.message}` });
85903
+ }
85904
+ };
85804
85905
  const flushDraft = () => {
85805
85906
  const d = draftRef.current.trim();
85806
85907
  draftRef.current = "";
@@ -85839,9 +85940,12 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
85839
85940
  }
85840
85941
  }
85841
85942
  switch (ev.type) {
85842
- case "agents":
85843
- setAgents(ev.items || []);
85943
+ case "agents": {
85944
+ const items2 = ev.items || [];
85945
+ setAgents(items2);
85946
+ setPanelIndex((i) => i >= items2.length ? items2.length - 1 : i);
85844
85947
  break;
85948
+ }
85845
85949
  case "agent_start":
85846
85950
  foldThinking();
85847
85951
  flushDraft();
@@ -85865,6 +85969,7 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
85865
85969
  setMeter((m) => ({ ...m, tokens: 0, cap: ev.compact_at || 0 }));
85866
85970
  setGate(ev.gate === "approve" ? "approve" : "off");
85867
85971
  if (ev.session) setSession(ev.session);
85972
+ (ev.questions || []).forEach(addAsk);
85868
85973
  if (ev.fresh) append({ kind: "wordmark" });
85869
85974
  setLatest({ version: ev.version || "", latest_version: ev.latest_version || "", release_notes_url: ev.release_notes_url || "" });
85870
85975
  append({ kind: "banner", lines: bannerLines(ev, homeRel, MINE), instruction: ev.session ? ev.instruction : null });
@@ -86075,27 +86180,10 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
86075
86180
  case "question":
86076
86181
  foldThinking();
86077
86182
  flushDraft();
86078
- setQuestion({
86079
- id: ev.id,
86080
- question: ev.question || "",
86081
- options: ev.options || [],
86082
- default: ev.default || null,
86083
- questions: ev.questions || null,
86084
- kind: ev.kind || null,
86085
- summary: ev.summary || "",
86086
- evidence: ev.evidence || []
86087
- });
86088
- setQStep(0);
86089
- setQAnswers({});
86090
- setQPicks([]);
86091
- setQCursor(0);
86183
+ addAsk(ev);
86092
86184
  break;
86093
86185
  case "question_closed":
86094
- setQuestion(null);
86095
- setQStep(0);
86096
- setQAnswers({});
86097
- setQPicks([]);
86098
- setQCursor(0);
86186
+ dropAsk(ev.id);
86099
86187
  append({ kind: "info", text: `answered: ${ev.answer}` });
86100
86188
  break;
86101
86189
  case "mcp":
@@ -86321,6 +86409,38 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
86321
86409
  }
86322
86410
  return;
86323
86411
  }
86412
+ if (looking) {
86413
+ if (key.escape || key.return && !looking.question) {
86414
+ closeLook();
86415
+ return;
86416
+ }
86417
+ return;
86418
+ }
86419
+ const panelOpen = agents.length > 0 && !value && !picker && !confirm && !proposal && !question && !reflection;
86420
+ if (panelOpen && panelIndex < 0 && key.downArrow) {
86421
+ setPanelIndex(0);
86422
+ return;
86423
+ }
86424
+ if (panelOpen && panelIndex >= 0) {
86425
+ if (key.downArrow) {
86426
+ setPanelIndex((i) => Math.min(i + 1, agents.length - 1));
86427
+ return;
86428
+ }
86429
+ if (key.upArrow) {
86430
+ setPanelIndex((i) => i - 1);
86431
+ return;
86432
+ }
86433
+ if (key.escape) {
86434
+ setPanelIndex(-1);
86435
+ return;
86436
+ }
86437
+ if (key.return) {
86438
+ const a = agents[panelIndex];
86439
+ if (a && a.worker) openLook(a);
86440
+ return;
86441
+ }
86442
+ if (input && !key.ctrl && !key.meta && input >= " ") setPanelIndex(-1);
86443
+ }
86324
86444
  if (confirm && !picker) {
86325
86445
  if (key.return) {
86326
86446
  setConfirm(null);
@@ -86560,11 +86680,7 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
86560
86680
  setQCursor(0);
86561
86681
  return;
86562
86682
  }
86563
- setQuestion(null);
86564
- setQStep(0);
86565
- setQAnswers({});
86566
- setQPicks([]);
86567
- setQCursor(0);
86683
+ dropAsk(question.id);
86568
86684
  engine2.send({ cmd: "answer", id: question.id, answers });
86569
86685
  };
86570
86686
  const vote = (polarity, why2 = "") => {
@@ -86635,6 +86751,10 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
86635
86751
  case "/reject":
86636
86752
  vote(-1, arg);
86637
86753
  break;
86754
+ case "/comms":
86755
+ engine2.send({ cmd: "comms" });
86756
+ break;
86757
+ // the engine's words: the channels in the loop here
86638
86758
  case "/hooks":
86639
86759
  engine2.send({ cmd: "hooks" });
86640
86760
  break;
@@ -86675,6 +86795,15 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
86675
86795
  closeReflection(reflection.id, "down", text);
86676
86796
  return;
86677
86797
  }
86798
+ if (looking) {
86799
+ const l = looking;
86800
+ if (l.question && l.handle) {
86801
+ l.handle.answer(l.question.id, text);
86802
+ append({ kind: "info", text: `answered ${l.row.name}${text ? `: ${text}` : ""}` });
86803
+ }
86804
+ closeLook();
86805
+ return;
86806
+ }
86678
86807
  if (!text && attached.length === 0) return;
86679
86808
  if (!text && proposal) return;
86680
86809
  const hist = historyRef.current;
@@ -86711,10 +86840,10 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
86711
86840
  setPhaseBoth("busy");
86712
86841
  }
86713
86842
  };
86714
- const statusLine = phase === "connecting" ? "starting \u2026" : `${modelRef.current || ""} \xB7 ${fmtCost2(spend)}` + (ctxPct >= 10 ? ` \xB7 ctx ${ctxPct}%` : "") + (gate === "approve" ? " \xB7 approvals on" : "") + (hello && hello.mounting && hello.mounting.length ? ` \xB7 mounting ${hello.mounting.join(", ")}` : "") + (goal && goal.text ? ` \xB7 \u25CE goal ${goal.status || "active"}` : "") + (needYou ? ` \xB7 ${needYou} need you` : "") + (offer ? " \xB7 /accept \xB7 /reject" : "") + (ctrlc ? " \xB7 ctrl+c again to quit" : " \xB7 / for commands");
86843
+ const statusLine = phase === "connecting" ? "starting \u2026" : `${modelRef.current || ""} \xB7 ${fmtCost2(spend)}` + (ctxPct >= 10 ? ` \xB7 ctx ${ctxPct}%` : "") + (gate === "approve" ? " \xB7 approvals on" : "") + (hello && hello.mounting && hello.mounting.length ? ` \xB7 mounting ${hello.mounting.join(", ")}` : "") + (goal && goal.text ? ` \xB7 \u25CE goal ${goal.status || "active"}` : "") + (needYou ? ` \xB7 ${needYou} need you` : "") + (offer ? " \xB7 /accept \xB7 /reject" : "") + (agents.length && panelIndex < 0 && !looking ? " \xB7 \u2193 agents" : "") + (ctrlc ? " \xB7 ctrl+c again to quit" : " \xB7 / for commands");
86715
86844
  const spinnerLabel = phase === "connecting" ? " starting \u2026" : ` ${status || "working"}${elapsed >= 2 ? ` \xB7 ${fmtDur(elapsed)}` : ""}` + (thinkChars.current > 4e3 ? ` \xB7 ${(thinkChars.current / 1e3).toFixed(0)}k thinking` : "");
86716
86845
  const rows = stdout?.rows || DEFAULT_ROWS;
86717
- const chrome = 1 + 2 + 4 + (value.split("\n").length - 1) + 1 + (menu.length ? menu.length : 0) + (!menu.length && agents.length ? agents.length + 1 : 0) + (question && step ? 6 + stepOptions.length + (question.summary ? question.summary.split("\n").length : 0) + (question.evidence || []).length : 0) + (proposal ? 4 + proposal.calls.length * (1 + Math.min(DIFF_TAIL_LINES, 8)) : 0) + (reflection ? 8 + reflection.details.split("\n").length : 0) + (confirm ? 4 + Math.min(8, String(confirm.stat || "").split("\n").length) : 0) + (picker ? 14 : 0);
86846
+ const chrome = 1 + 2 + 4 + (value.split("\n").length - 1) + 1 + (menu.length ? menu.length : 0) + (!menu.length && agents.length ? agents.length + 1 : 0) + (looking ? 4 + (looking.question ? (looking.question.options || []).length + 2 : Math.min(PEEK_LINES, looking.lines.length) + 1) : 0) + (question && step ? 6 + stepOptions.length + (questions.length > 1 ? 1 : 0) + (question.summary ? question.summary.split("\n").length : 0) + (question.evidence || []).length : 0) + (proposal ? 4 + proposal.calls.length * (1 + Math.min(DIFF_TAIL_LINES, 8)) : 0) + (reflection ? 8 + reflection.details.split("\n").length : 0) + (confirm ? 4 + Math.min(8, String(confirm.stat || "").split("\n").length) : 0) + (picker ? 14 : 0);
86718
86847
  const liveBudget = Math.max(LIVE_MIN_ROWS, Math.min(DRAFT_TAIL_LINES, rows - 1 - chrome));
86719
86848
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", children: [
86720
86849
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Static, { items, children: (item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", marginBottom: ["hum", "you", "banner", "turn"].includes(item.kind) ? 1 : 0, children: [
@@ -86880,6 +87009,7 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
86880
87009
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: spinnerLabel })
86881
87010
  ] }),
86882
87011
  question && step && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: ACCENT, paddingX: 1, marginTop: 1, children: [
87012
+ questions.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: ` 1 of ${questions.length} open asks \xB7 the oldest first` }) : null,
86883
87013
  (question.kind === "feedback" || question.kind === "decision") && qStep === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
86884
87014
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: ACCENT, bold: true, children: question.kind === "decision" ? "\u2726 Decision Review" : "\u2726 hum asks for your judgment" }) }),
86885
87015
  question.summary.split("\n").map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: ` ${l}` }, `s${i}`)),
@@ -86994,6 +87124,38 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
86994
87124
  c.diff && c.diff.split("\n").length > DIFF_TAIL_LINES ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: ` the whole change (${c.diff.split("\n").length} lines) is above` }) : null
86995
87125
  ] }, c.id))
86996
87126
  ] }),
87127
+ looking && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, marginTop: 1, children: [
87128
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
87129
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "magenta", bold: true, children: looking.row.name }),
87130
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { dimColor: true, children: [
87131
+ " ",
87132
+ looking.row.task.slice(0, width - 20)
87133
+ ] })
87134
+ ] }),
87135
+ looking.waiting ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: "looking \u2026" }) : null,
87136
+ looking.question ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", children: [
87137
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
87138
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: ACCENT, bold: true, children: "? " }),
87139
+ looking.question.question
87140
+ ] }),
87141
+ (looking.question.options || []).map((o, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { dimColor: true, children: [
87142
+ " ",
87143
+ i + 1,
87144
+ ". ",
87145
+ typeof o === "string" ? o : o.label,
87146
+ o && o.description ? ` \xB7 ${o.description}` : ""
87147
+ ] }, i)),
87148
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { dimColor: true, children: [
87149
+ " ",
87150
+ "type the answer \xB7 enter answers and leaves",
87151
+ looking.question.default ? ` (empty takes the default: ${looking.question.default})` : "",
87152
+ " \xB7 esc leaves it open"
87153
+ ] })
87154
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", children: [
87155
+ looking.lines.length ? looking.lines.slice(-PEEK_LINES).map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: l.slice(0, width - 4) }, i)) : looking.waiting ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: "nothing on its transcript yet" }),
87156
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: "esc goes back" })
87157
+ ] })
87158
+ ] }),
86997
87159
  picker && picker.kind === "update" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
86998
87160
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
86999
87161
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "yellow", children: "\u2728 " }),
@@ -87185,7 +87347,7 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
87185
87347
  onChange: setValue,
87186
87348
  onSubmit,
87187
87349
  focus: phase !== "dead" && !picker && !confirm,
87188
- navActive: menu.length === 0,
87350
+ navActive: menu.length === 0 && panelIndex < 0 && !looking,
87189
87351
  history: historyRef.current,
87190
87352
  mask: !!(step && step.secret),
87191
87353
  swallow: (input, key) => {
@@ -87210,24 +87372,47 @@ function App2({ engine: engine2, task, resume, images = [], onUpdate: onUpdate2
87210
87372
  ] }, c.name)),
87211
87373
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: "tab to complete \xB7 enter to run" })
87212
87374
  ] }),
87213
- !menu.length && agents.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, children: [
87375
+ !menu.length && agents.length > 0 && // one flat list: ○ a sub-agent of this process, ◇ a worker session of the team (its state, what it needs,
87376
+ // +N sub-agents of its own); the engine puts what wants a person first. ↓ moves a highlight in from the box
87377
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, children: [
87214
87378
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
87379
+ " ",
87215
87380
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "magenta", children: "\u25CF" }),
87216
87381
  " main"
87217
87382
  ] }),
87218
- agents.map((a) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { children: [
87219
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "magenta", children: "\u25CB " }),
87220
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: String(a.kind).padEnd(10) }),
87221
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: a.desc }),
87222
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { dimColor: true, children: [
87223
- " ",
87224
- fmtDur(a.elapsed_s || 0),
87225
- " \xB7 ",
87226
- plural(a.steps || 0, "step"),
87227
- " \xB7 ",
87228
- fmtTokens(a.tokens),
87229
- " \xB7 ",
87230
- fmtCost2(a.cost_usd || 0)
87383
+ agents.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { children: [
87384
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: ACCENT, children: i === panelIndex ? "\u276F " : " " }),
87385
+ a.worker ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
87386
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "magenta", children: "\u25C7 " }),
87387
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { bold: i === panelIndex, children: `${a.kind} worker`.padEnd(16) }),
87388
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: a.desc }),
87389
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { color: a.needs ? "yellow" : void 0, dimColor: !a.needs, children: [
87390
+ " \xB7 ",
87391
+ a.needs ? `needs ${a.needs}` : a.state
87392
+ ] }),
87393
+ a.agents ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "magenta", children: ` +${a.agents}` }) : null,
87394
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { dimColor: true, children: [
87395
+ " ",
87396
+ fmtDur(a.elapsed_s || 0),
87397
+ " \xB7 ",
87398
+ plural(a.steps || 0, "step"),
87399
+ " \xB7 ",
87400
+ fmtCost2(a.cost_usd || 0)
87401
+ ] })
87402
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
87403
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "magenta", children: "\u25CB " }),
87404
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { bold: i === panelIndex, children: String(a.kind).padEnd(10) }),
87405
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: a.desc }),
87406
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { dimColor: true, children: [
87407
+ " ",
87408
+ fmtDur(a.elapsed_s || 0),
87409
+ " \xB7 ",
87410
+ plural(a.steps || 0, "step"),
87411
+ " \xB7 ",
87412
+ fmtTokens(a.tokens),
87413
+ " \xB7 ",
87414
+ fmtCost2(a.cost_usd || 0)
87415
+ ] })
87231
87416
  ] })
87232
87417
  ] }, a.n))
87233
87418
  ] }),
@@ -87241,7 +87426,7 @@ init_update();
87241
87426
  init_install();
87242
87427
  var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
87243
87428
  import { spawnSync as spawnSync5 } from "node:child_process";
87244
- var MINE2 = true ? "0.1.67" : "0.0.0";
87429
+ var MINE2 = true ? "0.1.69" : "0.0.0";
87245
87430
  var layout = installLayout();
87246
87431
  function completeInstall() {
87247
87432
  if (layout.kind !== "npm" || !needsInstall(MINE2)) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaphi-ai/hum",
3
- "version": "0.1.67",
3
+ "version": "0.1.69",
4
4
  "description": "Hum: a self-improving coding agent for your terminal.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",