docs/agent.md: fix stale 3-flyout description; drop dead buildLooseEndsList
docs/web-ui/agent.md still described a "loose-ends" and "tasks" flyout that were superseded by the todos flyout when loose-ends-v2 landed — GET /api/loose-ends and GET /api/bash-tasks are both gone server-side. Replaced with an accurate description of the todos flyout (including the mark-done bulk action from #2919), and noted that the ask->operator inline-answer binding this doc also describes is currently non-functional (its data source was the same removed endpoint) — tracked separately as #2922, not fixed here. buildLooseEndsList in app.js rendered the old loose-ends flyout and had zero call sites left; removed it. buildAnswerForm stays — reconcileAskBinds still calls it, even though that path is currently dead per #2922. Fixes #2920
This commit is contained in:
parent
77e30dd1bc
commit
94fb42f2b6
2 changed files with 42 additions and 96 deletions
|
|
@ -792,75 +792,12 @@ window.marked = marked;
|
|||
|
||||
/** ask → operator inline-answer slot registry. See
|
||||
* docs/web-ui.md::Per-agent page (Ask → operator inline-answer
|
||||
* binding) for the matching algorithm + resolution semantics. */
|
||||
* binding) for the matching algorithm + resolution semantics —
|
||||
* currently non-functional (its data source was the old loose-ends
|
||||
* endpoint, since removed — tracked on the forge), kept for when
|
||||
* that's resolved rather than ripped out speculatively. */
|
||||
const pendingAskBinds = [];
|
||||
|
||||
function buildLooseEndsList(threads) {
|
||||
// Returns the <div> the side panel renders. The structural shape
|
||||
// mirrors the legacy <details>-collapsible block — same CSS rules
|
||||
// apply via `hive-side-panel .agent-inbox`.
|
||||
const wrap = el('div', { class: 'agent-inbox' });
|
||||
if (!threads.length) {
|
||||
wrap.append(el('p', { class: 'side-panel-empty' },
|
||||
'no loose ends — every question, approval and reminder is resolved.'));
|
||||
return wrap;
|
||||
}
|
||||
const list = el('ul');
|
||||
const fmtAge = (s) => {
|
||||
if (s < 60) return s + 's';
|
||||
if (s < 3600) return Math.floor(s / 60) + 'm';
|
||||
if (s < 86400) return Math.floor(s / 3600) + 'h';
|
||||
return Math.floor(s / 86400) + 'd';
|
||||
};
|
||||
for (const t of threads) {
|
||||
const li = el('li');
|
||||
if (t.kind === 'approval') {
|
||||
li.append(
|
||||
el('span', { class: 'inbox-from' }, '◇ approval #' + t.id), ' ',
|
||||
el('span', { class: 'inbox-sep' }, t.agent + ' @ ' + (t.commit_ref || '').slice(0, 12)), ' ',
|
||||
el('span', { class: 'inbox-ts' }, fmtAge(t.age_seconds || 0) + ' ago'),
|
||||
);
|
||||
if (t.description) {
|
||||
li.append(el('div', { class: 'inbox-body' }, t.description));
|
||||
}
|
||||
} else if (t.kind === 'question') {
|
||||
const target = t.target || 'operator';
|
||||
li.append(
|
||||
el('span', { class: 'inbox-from' }, '? #' + t.id), ' ',
|
||||
el('span', { class: 'inbox-sep' }, t.asker + ' → ' + target), ' ',
|
||||
el('span', { class: 'inbox-ts' }, fmtAge(t.age_seconds || 0) + ' ago'),
|
||||
el('div', { class: 'inbox-body' }, t.question || ''),
|
||||
buildAnswerForm(t.id),
|
||||
);
|
||||
} else if (t.kind === 'reminder') {
|
||||
// due_at arrives as an ISO 8601 string (DateTime<Utc> on the wire).
|
||||
// Parse it to unix seconds before arithmetic — doing `t.due_at - now`
|
||||
// directly yields NaN because a string minus a number is NaN in JS.
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const dueAtSec = t.due_at ? Math.floor(new Date(t.due_at).getTime() / 1000) : 0;
|
||||
const dueIn = dueAtSec - now;
|
||||
const dueLabel = dueIn >= 0 ? 'in ' + fmtAge(dueIn) : fmtAge(-dueIn) + ' overdue';
|
||||
li.append(
|
||||
el('span', { class: 'inbox-from' }, '⏰ reminder #' + t.id), ' ',
|
||||
el('span', { class: 'inbox-sep' }, t.owner + ' · due ' + dueLabel), ' ',
|
||||
el('span', { class: 'inbox-ts' }, 'scheduled ' + fmtAge(t.age_seconds || 0) + ' ago'),
|
||||
el('div', { class: 'inbox-body' }, t.message || ''),
|
||||
);
|
||||
} else if (t.kind === 'pending_messages') {
|
||||
li.append(
|
||||
el('span', { class: 'inbox-from' }, '✉ inbox'), ' ',
|
||||
el('span', { class: 'inbox-sep' }, (t.count || 0) + ' pending message(s)'), ' ',
|
||||
el('span', { class: 'inbox-ts' }, 'drain with recv'),
|
||||
);
|
||||
} else {
|
||||
li.append(el('span', { class: 'inbox-body' }, JSON.stringify(t)));
|
||||
}
|
||||
list.append(li);
|
||||
}
|
||||
wrap.append(list);
|
||||
return wrap;
|
||||
}
|
||||
|
||||
/** Bulk "mark done" row for the todos flyout: select all / select none
|
||||
* + a mark-done button, disabled until at least one row is checked.
|
||||
* POSTs the checked ids (comma-joined into one field, same shape as
|
||||
|
|
@ -1008,8 +945,9 @@ window.marked = marked;
|
|||
// Inline "answer as operator" form for a question loose-end. POSTs to
|
||||
// the host dashboard (core backend), never this agent's socket — the
|
||||
// core is the only place that can stamp `operator` as the answerer.
|
||||
// Used by both the loose-ends side panel (`buildLooseEndsList`) and
|
||||
// the in-stream ask-row binder (`reconcileAskBinds`).
|
||||
// Only live call site today is the in-stream ask-row binder
|
||||
// (`reconcileAskBinds`) — see its doc comment above for why that path
|
||||
// is currently non-functional.
|
||||
function buildAnswerForm(id) {
|
||||
const wrap = el('div', { class: 'answer-form' });
|
||||
const ta = el('textarea', { rows: '2', placeholder: 'answer as operator…' });
|
||||
|
|
|
|||
Loading…
Reference in a new issue