instar 1.3.1610 → 1.3.1612
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/dashboard/console/console.css +5 -1
- package/dashboard/console/console.js +31 -8
- package/dashboard/console/home-model.js +24 -1
- package/dashboard/console/messages-model.js +2 -1
- package/dashboard/console/messages.js +1 -1
- package/dashboard/console/pace-model.js +5 -0
- package/dashboard/console/pace.js +3 -1
- package/dashboard/console/plans-model.js +17 -1
- package/dashboard/console/promises.js +2 -1
- package/dashboard/console/schedule.js +4 -4
- package/dashboard/console/secrets-model.js +11 -1
- package/dashboard/console/sessions-model.js +18 -0
- package/dashboard/console/sessions.js +55 -19
- package/dashboard/console/ui.js +11 -0
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +6 -4
- package/dist/commands/server.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +26 -2
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/data/standards-guard-index.json +1 -1
- package/dist/data/standards-guard-index.meta.json +2 -2
- package/dist/data/standards-registry.meta.json +1 -1
- package/dist/lifeline/TelegramLifeline.d.ts +2 -0
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +28 -0
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/localVerify/localVerifyWiring.d.ts +11 -0
- package/dist/localVerify/localVerifyWiring.d.ts.map +1 -1
- package/dist/localVerify/localVerifyWiring.js +9 -0
- package/dist/localVerify/localVerifyWiring.js.map +1 -1
- package/dist/messaging/TelegramAdapter.d.ts +19 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +137 -27
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/messaging/slack/SlackAdapter.d.ts +10 -0
- package/dist/messaging/slack/SlackAdapter.d.ts.map +1 -1
- package/dist/messaging/slack/SlackAdapter.js +29 -4
- package/dist/messaging/slack/SlackAdapter.js.map +1 -1
- package/dist/messaging/slack/types.d.ts +5 -0
- package/dist/messaging/slack/types.d.ts.map +1 -1
- package/dist/messaging/slack/types.js.map +1 -1
- package/dist/monitoring/GreenPrAutoMerger.d.ts +4 -0
- package/dist/monitoring/GreenPrAutoMerger.d.ts.map +1 -1
- package/dist/monitoring/GreenPrAutoMerger.js +40 -9
- package/dist/monitoring/GreenPrAutoMerger.js.map +1 -1
- package/dist/monitoring/SessionMonitor.d.ts +21 -0
- package/dist/monitoring/SessionMonitor.d.ts.map +1 -1
- package/dist/monitoring/SessionMonitor.js +84 -1
- package/dist/monitoring/SessionMonitor.js.map +1 -1
- package/dist/monitoring/greenPrAutomergeWiring.d.ts.map +1 -1
- package/dist/monitoring/greenPrAutomergeWiring.js +3 -0
- package/dist/monitoring/greenPrAutomergeWiring.js.map +1 -1
- package/dist/native/instar-desktop-host +0 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +40 -5
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +64 -64
- package/src/data/standards-guard-index.json +1 -1
- package/src/data/standards-guard-index.meta.json +2 -2
- package/src/data/standards-registry.meta.json +1 -1
- package/src/data/state-coherence-registry.json +18 -0
- package/upgrades/1.3.1611.md +64 -0
- package/upgrades/1.3.1612.md +72 -0
- package/upgrades/side-effects/blocker-text-is-the-message.eli16.md +5 -0
- package/upgrades/side-effects/blocker-text-is-the-message.md +38 -0
- package/upgrades/side-effects/console-failures-link-to-problem.eli16.md +7 -0
- package/upgrades/side-effects/console-failures-link-to-problem.md +31 -0
- package/upgrades/side-effects/console-needs-row-tidy.eli16.md +13 -0
- package/upgrades/side-effects/console-needs-row-tidy.md +24 -0
- package/upgrades/side-effects/console-partial-read-grace.eli16.md +9 -0
- package/upgrades/side-effects/console-partial-read-grace.md +33 -0
- package/upgrades/side-effects/console-secret-drop-names.eli16.md +5 -0
- package/upgrades/side-effects/console-secret-drop-names.md +34 -0
- package/upgrades/side-effects/green-pr-eligible-needs-passing-record.eli16.md +7 -0
- package/upgrades/side-effects/green-pr-eligible-needs-passing-record.md +39 -0
- package/upgrades/side-effects/green-pr-poke-unknown-mergeable.eli16.md +5 -0
- package/upgrades/side-effects/green-pr-poke-unknown-mergeable.md +41 -0
- package/upgrades/side-effects/green-pr-skip-unactionable-head.eli16.md +7 -0
- package/upgrades/side-effects/green-pr-skip-unactionable-head.md +41 -0
- package/upgrades/side-effects/pool-rows-use-local-machine-name.eli16.md +5 -0
- package/upgrades/side-effects/pool-rows-use-local-machine-name.md +34 -0
- package/upgrades/side-effects/session-history-topic-backfill.eli16.md +9 -0
- package/upgrades/side-effects/session-history-topic-backfill.md +39 -0
- package/upgrades/side-effects/slack-session-stopped-once.eli16.md +28 -0
- package/upgrades/side-effects/slack-session-stopped-once.md +89 -0
- package/upgrades/side-effects/topic-names-through-lifeline.eli16.md +9 -0
- package/upgrades/side-effects/topic-names-through-lifeline.md +39 -0
- package/upgrades/side-effects/topic-probe-no-repeat-refusals.eli16.md +7 -0
- package/upgrades/side-effects/topic-probe-no-repeat-refusals.md +34 -0
|
@@ -980,6 +980,8 @@ button.run-mark:focus-visible { outline: 2px solid var(--accent); outline-offset
|
|
|
980
980
|
.stale-banner { margin: var(--s-2) 0; padding: var(--s-2) var(--s-3); border: 1px dashed var(--unknown-line); border-radius: var(--r-md); color: var(--text-muted); font-size: 14px; }
|
|
981
981
|
.machine-line { margin: var(--s-1) 0; padding: var(--s-2) var(--s-3); border: 1px solid var(--hairline); border-radius: var(--r-md); font-size: 14.5px; color: var(--text); overflow-wrap: anywhere; }
|
|
982
982
|
.machine-line.dashed { border-style: dashed; border-color: var(--unknown-line); color: var(--text-muted); }
|
|
983
|
+
a.machine-line-link { display: block; text-decoration: none; }
|
|
984
|
+
a.machine-line-link:hover { border-color: var(--accent); color: var(--text); }
|
|
983
985
|
.sess-pick { margin-top: var(--s-10); text-align: center; }
|
|
984
986
|
.sess-open { display: flex; flex-direction: column; gap: var(--s-2); min-height: calc(100dvh - 140px); }
|
|
985
987
|
.sess-bar { display: flex; align-items: center; gap: var(--s-2); }
|
|
@@ -1159,8 +1161,10 @@ button.run-mark:focus-visible { outline: 2px solid var(--accent); outline-offset
|
|
|
1159
1161
|
/* Queue pace card (docs/specs/queue-pace.md) */
|
|
1160
1162
|
.pace-card { grid-column: 1 / -1; margin-top: var(--s-4, 16px); }
|
|
1161
1163
|
.pace-lines { list-style: none; margin: var(--s-2, 8px) 0 0; padding: 0; display: grid; gap: 8px; }
|
|
1162
|
-
.pace-line { display:
|
|
1164
|
+
.pace-line { display: grid; grid-template-columns: auto 1fr; align-items: baseline; column-gap: 8px; font-size: 14px; color: var(--text); }
|
|
1163
1165
|
.pace-line .dot { position: relative; top: -1px; }
|
|
1166
|
+
.pace-line .pace-text { min-width: 0; }
|
|
1167
|
+
.pace-line .pace-prs { grid-column: 2; }
|
|
1164
1168
|
.pace-line[data-tone="muted"] .pace-text { color: var(--text-muted); }
|
|
1165
1169
|
.pace-prs { display: inline-flex; gap: 6px; flex-wrap: wrap; font-size: 13px; }
|
|
1166
1170
|
.mb-pace { margin-top: 4px; }
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
failure, buildNeeds, buildDoing, buildPromised, buildRunning, buildAllowed, headline, ago, combineNeeds, machineNames,
|
|
10
10
|
} from './home-model.js';
|
|
11
11
|
import { GROUPS, legacyHref, icon, tabHref } from './nav-model.js';
|
|
12
|
+
import { fillPartial } from './home-model.js';
|
|
12
13
|
import { renderPromises, topicLink } from './promises.js';
|
|
13
14
|
import { renderMachines } from './machines.js';
|
|
14
15
|
import { renderAccounts } from './accounts.js';
|
|
@@ -29,7 +30,7 @@ import { renderSessions } from './sessions.js';
|
|
|
29
30
|
import { renderMaturation, maturationTile } from './maturation-board.js';
|
|
30
31
|
import { renderIdeas, ideasTile } from './ideas.js';
|
|
31
32
|
import { paceCard } from './pace.js';
|
|
32
|
-
import { sheetIsOpen, closeSheet, unlockCheck } from './ui.js';
|
|
33
|
+
import { sheetIsOpen, closeSheet, unlockCheck, machineIssue, MACHINES_PAGE } from './ui.js';
|
|
33
34
|
|
|
34
35
|
const TOKEN_KEY = 'instar_token'; // shared with the legacy dashboard
|
|
35
36
|
const THEME_KEY = 'instar_console_theme';
|
|
@@ -392,6 +393,8 @@ function paintCard(def, model, repaint = false) {
|
|
|
392
393
|
foot.append(b);
|
|
393
394
|
} else foot.append(el('span', { class: 'card-age', text: extra > 0 ? `and ${extra} more` : '' }));
|
|
394
395
|
if (def.href) foot.append(el('a', { href: def.href, text: 'Open', 'aria-label': `Open ${def.title} in the dashboard` }));
|
|
396
|
+
// An empty footer would leave a blank band (and its divider) under the rows.
|
|
397
|
+
foot.hidden = !foot.textContent.trim() && !foot.querySelector('a, button');
|
|
395
398
|
return model;
|
|
396
399
|
}
|
|
397
400
|
|
|
@@ -419,8 +422,11 @@ function paintHeadline(models) {
|
|
|
419
422
|
}
|
|
420
423
|
const statusNote = $('#homeStatusNote');
|
|
421
424
|
if (statusNote) {
|
|
422
|
-
|
|
423
|
-
statusNote.
|
|
425
|
+
const noteText = models.needs?.statusNote || '';
|
|
426
|
+
statusNote.replaceChildren(document.createTextNode(noteText));
|
|
427
|
+
// A machine that did not answer: link to where its state is shown.
|
|
428
|
+
if (noteText && machineIssue(noteText)) statusNote.append(' ', el('a', { href: MACHINES_PAGE, text: 'See machines' }));
|
|
429
|
+
statusNote.hidden = !noteText;
|
|
424
430
|
}
|
|
425
431
|
const issueLink = $('#heroIssueLink');
|
|
426
432
|
if (issueLink) {
|
|
@@ -430,6 +436,20 @@ function paintHeadline(models) {
|
|
|
430
436
|
$('#heroTime').textContent = `${h.tone === 'attn' ? 'What needs you' : 'Updated'} · ${new Date().toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}`;
|
|
431
437
|
}
|
|
432
438
|
|
|
439
|
+
// A machine that misses one pooled read (slow for a moment, or restarting)
|
|
440
|
+
// should not turn Home into "part could not be checked". While it is retried,
|
|
441
|
+
// its rows from the last complete answer stand in, for up to two minutes; the
|
|
442
|
+
// machines that did answer always show their fresh rows.
|
|
443
|
+
const lastComplete = {};
|
|
444
|
+
const ROW_LISTS = { attention: 'items', sessions: 'sessions' };
|
|
445
|
+
function rememberComplete(key, result) {
|
|
446
|
+
if (ROW_LISTS[key]) lastComplete[key] = { result, at: Date.now() };
|
|
447
|
+
}
|
|
448
|
+
function fillFromLastComplete(key, fresh) {
|
|
449
|
+
const prev = lastComplete[key];
|
|
450
|
+
return ROW_LISTS[key] && prev ? fillPartial(fresh, prev.result, ROW_LISTS[key], Date.now(), prev.at) : null;
|
|
451
|
+
}
|
|
452
|
+
|
|
433
453
|
function loadHome() {
|
|
434
454
|
const run = homeRun;
|
|
435
455
|
if (!run || !run.active || run.document !== document) return;
|
|
@@ -475,7 +495,9 @@ function loadHome() {
|
|
|
475
495
|
const partial = result.ok && (['attention', 'sessions'].includes(key) && Array.isArray(result.data?.pool?.failed) && result.data.pool.failed.length > 0
|
|
476
496
|
|| key === 'topics' && ((result.data?.pool?.failed?.length || 0) > 0 || (result.data?.pool?.unresolved?.length || 0) > 0));
|
|
477
497
|
const retryable = partial || (!result.ok && !['not signed in', 'switched off on this agent'].includes(result.reason));
|
|
478
|
-
|
|
498
|
+
const filled = partial ? fillFromLastComplete(key, result) : null;
|
|
499
|
+
if (result.ok && !partial) rememberComplete(key, result);
|
|
500
|
+
results[key] = filled || (partial ? { ...result, retrying: true } : result);
|
|
479
501
|
if (key === 'systemTopics' && result.ok) {
|
|
480
502
|
const t = Number(result.data?.topics?.attention);
|
|
481
503
|
attentionTopicId = Number.isFinite(t) && t > 1 ? t : null;
|
|
@@ -496,7 +518,9 @@ function loadHome() {
|
|
|
496
518
|
// Every source has one in-flight request and its own bounded retry timer.
|
|
497
519
|
// A slow source cannot block another card or start overlapping refreshes.
|
|
498
520
|
read('pool', '/pool', POOL_TIMEOUT_MS);
|
|
499
|
-
|
|
521
|
+
// Open items only: a machine's full history (closed items included) can be
|
|
522
|
+
// over a megabyte, enough to miss the per-machine time limit.
|
|
523
|
+
read('attention', '/attention?scope=pool&status=OPEN', POOL_TIMEOUT_MS);
|
|
500
524
|
read('systemTopics', '/telegram/system-topics');
|
|
501
525
|
read('sessions', '/sessions?scope=pool', POOL_TIMEOUT_MS);
|
|
502
526
|
read('topics', '/telegram/topics?scope=pool', POOL_TIMEOUT_MS);
|
|
@@ -543,7 +567,6 @@ function renderGroup(main, g) {
|
|
|
543
567
|
el('section', { class: 'group-head' },
|
|
544
568
|
el('div', { class: 'eyebrow', text: g.label }),
|
|
545
569
|
el('h1', { text: g.question }),
|
|
546
|
-
el('p', { text: 'Each of these opens in the dashboard, where everything works exactly as before.' }),
|
|
547
570
|
),
|
|
548
571
|
el('div', { class: 'tiles' }, g.tabs.map((t) => tile(t))),
|
|
549
572
|
...groupExtras(g, { get }), // the Development tile (Work, maintainer mode only) and the developer-tools switch (Settings)
|
|
@@ -563,8 +586,8 @@ function renderGroup(main, g) {
|
|
|
563
586
|
|
|
564
587
|
function footNote() {
|
|
565
588
|
return el('p', { class: 'foot-note' },
|
|
566
|
-
document.createTextNode('
|
|
567
|
-
el('a', { href: '/dashboard', text: 'legacy dashboard' }),
|
|
589
|
+
document.createTextNode('The older dashboard is still available: '),
|
|
590
|
+
el('a', { href: '/dashboard', text: 'open the legacy dashboard' }),
|
|
568
591
|
document.createTextNode('.'),
|
|
569
592
|
);
|
|
570
593
|
}
|
|
@@ -166,7 +166,10 @@ export function buildNeeds(res, now = Date.now(), names = {}) {
|
|
|
166
166
|
return {
|
|
167
167
|
key: groupKey(top), stale, last: g.last, prio: PRIORITY[top.priority] ?? 9,
|
|
168
168
|
main: top.title || top.summary || 'Untitled item',
|
|
169
|
-
|
|
169
|
+
// The machine only when the title does not already start with it; the
|
|
170
|
+
// priority word is the queue's bookkeeping, not something to act on.
|
|
171
|
+
sub: [g.items.length > 1 ? `${g.items.length} copies` : null,
|
|
172
|
+
[...g.machines].filter((m) => !String(top.title || top.summary || '').startsWith(`${m}:`)).join(', ') || null].filter(Boolean).join(' · '),
|
|
170
173
|
end: (ago(g.last, now) || '').replace(' ago', '') ? `${ago(g.last, now)}` : '',
|
|
171
174
|
tone: stale ? 'unknown' : (top.priority === 'URGENT' ? 'bad' : 'attn'),
|
|
172
175
|
detail: top.description || top.body || '',
|
|
@@ -573,3 +576,23 @@ export function headline(allCards) {
|
|
|
573
576
|
if (bad || otherAttn || blind) return { text: 'Nothing needs you.', tone: 'ok', sub: 'I’m handling the rest. It’s under “See agent status”.' };
|
|
574
577
|
return { text: 'All quiet.', tone: 'ok', sub: 'Nothing needs you right now.' };
|
|
575
578
|
}
|
|
579
|
+
|
|
580
|
+
// ── Partial pooled reads ──────────────────────────────────────────────────
|
|
581
|
+
/** How long a machine's last complete rows may stand in while it is retried. */
|
|
582
|
+
export const PARTIAL_GRACE_MS = 2 * 60 * 1000;
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Merge a partial pooled answer (some machines did not answer) with the last
|
|
586
|
+
* complete one: fresh rows from the machines that answered, plus the missing
|
|
587
|
+
* machines' rows from the last complete answer, if it is recent. Returns null
|
|
588
|
+
* when that is not possible, so the partial answer is shown as it is.
|
|
589
|
+
*/
|
|
590
|
+
export function fillPartial(fresh, previous, listKey, now, at) {
|
|
591
|
+
const failed = fresh?.data?.pool?.failed;
|
|
592
|
+
if (!previous || !Array.isArray(failed) || !failed.length || now - at > PARTIAL_GRACE_MS) return null;
|
|
593
|
+
const missing = new Set(failed.map((f) => f && f.machineId).filter(Boolean));
|
|
594
|
+
if (missing.size !== failed.length) return null; // a failure we cannot attribute to a machine stays visible
|
|
595
|
+
const rows = (previous.data?.[listKey] || []).filter((r) => r && missing.has(r.machineId));
|
|
596
|
+
return { ...fresh, retrying: true, filledFrom: at,
|
|
597
|
+
data: { ...fresh.data, [listKey]: [...(fresh.data?.[listKey] || []), ...rows], pool: { ...fresh.data.pool, failed: [] } } };
|
|
598
|
+
}
|
|
@@ -82,7 +82,8 @@ export function buildMessagesPage(statusRes, healthRes, now = Date.now(), topicN
|
|
|
82
82
|
const automatic = held.filter(h => h.producerKind === 'server-automation').length;
|
|
83
83
|
headline = heldHeadline(held.length, replies, automatic, held.length - replies - automatic);
|
|
84
84
|
} else if (expired) headline = `${expired} ${expired === 1 ? 'message' : 'messages'} expired before delivery.`;
|
|
85
|
-
|
|
85
|
+
// Held replies are a fault, never a feature to report on: with none, the page is just Messages.
|
|
86
|
+
else headline = 'Messages to you';
|
|
86
87
|
const sub = [
|
|
87
88
|
held.length ? 'I need to restore delivery. This is not a task for you.' : null,
|
|
88
89
|
individual.length && individual[individual.length - 1].sinceTs ? `The oldest has waited ${ago(individual[individual.length - 1].sinceTs, now).replace(' ago', '')}.` : null,
|
|
@@ -43,7 +43,7 @@ export async function renderMessages(main, ctx) {
|
|
|
43
43
|
el('span', { class: 'need-sub', text: 'Who wrote each message is private to you. One unlock lasts about 15 minutes in this page.' }),
|
|
44
44
|
el('button', { class: 'btn-primary btn-inline', type: 'button', text: 'Unlock', onclick: async () => { const a = await askPin({ title: 'Message records', explain: 'Unlock message records. Your PIN is not kept; the unlock lives in this page until it expires.', adapter: 'session' }); if (a) refresh(); } })) : null;
|
|
45
45
|
setKids(main, el('div', { class: 'msg-page' },
|
|
46
|
-
pageHead({ eyebrow: 'People · Messages to you', headline: hf ? heldHeadline(hf.count, hf.sessionReplies, hf.automaticMessages, hf.otherMessages) : 'Messages to you', sub: hf ? 'I’m handling delivery. Unlock to see the individual records.' : '', tone: hf && hf.count ? 'attn' : 'muted' }),
|
|
46
|
+
pageHead({ eyebrow: 'People · Messages to you', headline: hf && hf.count ? heldHeadline(hf.count, hf.sessionReplies, hf.automaticMessages, hf.otherMessages) : 'Messages to you', sub: hf && hf.count ? 'I’m handling delivery. Unlock to see the individual records.' : '', tone: hf && hf.count ? 'attn' : 'muted' }),
|
|
47
47
|
unlock || cantTell(page.reason), legacyLink()));
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
@@ -60,6 +60,11 @@ export function paceLine(q, showMachine = false) {
|
|
|
60
60
|
if (person && q.verdict === 'falling-behind') verdict = 'waiting on you';
|
|
61
61
|
if (q.verdict === 'catching-up' && typeof q.daysToClear === 'number' && !person) verdict += `, about ${fmtRate(q.daysToClear)} days to clear`;
|
|
62
62
|
const inR = fmtRate(q.inflowPerDay), outR = fmtRate(q.outflowPerDay);
|
|
63
|
+
// Nothing in, nothing done, but items still open: that is a stalled pile, not "steady".
|
|
64
|
+
const open = Number(q.open);
|
|
65
|
+
if (!Number(q.inflowPerDay) && !Number(q.outflowPerDay) && open > 0) {
|
|
66
|
+
return `${label}${where}: nothing came in or got done this week; ${open} still open`;
|
|
67
|
+
}
|
|
63
68
|
const rates = inR !== null && outR !== null ? `${inR} a day in, ${outR} done — ` : '';
|
|
64
69
|
let line = `${label}${where}: ${rates}${verdict}`;
|
|
65
70
|
if (q.verdict === 'falling-behind') {
|
|
@@ -17,7 +17,9 @@ export function paceCard(get) {
|
|
|
17
17
|
const machines = new Set(queues.map((q) => q.machine));
|
|
18
18
|
const anyBehind = queues.some((q) => toneOf(q) === 'attn');
|
|
19
19
|
host.dataset.state = anyBehind ? 'attn' : 'ok';
|
|
20
|
-
|
|
20
|
+
// A queue with nothing coming in, nothing done and nothing open says nothing.
|
|
21
|
+
const idle = (q) => !Number(q.inflowPerDay) && !Number(q.outflowPerDay) && !Number(q.open);
|
|
22
|
+
const lines = queues.filter((q) => !idle(q)).map((q) => {
|
|
21
23
|
const tone = toneOf(q);
|
|
22
24
|
const li = el('li', { class: 'pace-line', 'data-tone': tone },
|
|
23
25
|
el('span', { class: `dot ${DOT[tone] || ''}`.trim() }),
|
|
@@ -162,9 +162,25 @@ function recheckText(e, now) {
|
|
|
162
162
|
return { text: `rechecking ${new Date(t).toLocaleDateString([], { weekday: 'short', month: 'short', day: 'numeric' })}`, overdue: false };
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
// The deferral detector recorded whole reply commands (`cat <<'EOF' | <reply script> N`
|
|
166
|
+
// + the message + `EOF`), signed links included (2026-11-02). Show the message's first line,
|
|
167
|
+
// with credential-looking query values masked; a command with no message text is named, not
|
|
168
|
+
// printed.
|
|
169
|
+
const SECRET_QUERY = /([?&](?:sig|signature|token|key|auth|access_token)[^=&\s]*=)[^&\s]+/gi;
|
|
170
|
+
const SHELL_START = /^\s*(?:cd|cat|curl|echo|node|bash|sh|gh|git|python3?|npx)\b/;
|
|
171
|
+
export function blockerText(raw) {
|
|
172
|
+
const s = String(raw || '');
|
|
173
|
+
const doc = /<<-?\s*['"]?(\w+)['"]?[^\n]*\n([\s\S]*?)(?:\n[ \t]*\1[ \t]*(?:\n|$)|$)/.exec(s);
|
|
174
|
+
let body = doc ? doc[2] : s;
|
|
175
|
+
if (!doc && SHELL_START.test(body)) return 'Flagged in a command I ran (no message text)';
|
|
176
|
+
body = body.replace(SECRET_QUERY, '$1…');
|
|
177
|
+
const line = body.split('\n').map((l) => l.trim()).find(Boolean) || '';
|
|
178
|
+
return line.length > 220 ? `${line.slice(0, 219)}…` : line;
|
|
179
|
+
}
|
|
180
|
+
|
|
165
181
|
/** A blocker as a row: { key, main, sub, end, tone, kind, record }. */
|
|
166
182
|
export function blockerRow(e, now = Date.now()) {
|
|
167
|
-
const main = e.detectedText || e.origin || 'A blocker';
|
|
183
|
+
const main = blockerText(e.detectedText) || e.origin || 'A blocker';
|
|
168
184
|
let sub; let tone = null; let kind;
|
|
169
185
|
if (e.state === 'true-blocker') { const r = recheckText(e, now); sub = `Truly stuck for now · ${r.text}`; tone = 'bad'; kind = 'stuck'; }
|
|
170
186
|
else if (e.state === 'access-requested') { sub = 'Waiting on you for access'; tone = 'attn'; kind = 'needs-you'; }
|
|
@@ -56,7 +56,8 @@ export async function renderPromises(main, ctx) {
|
|
|
56
56
|
}),
|
|
57
57
|
page.coverage ? note(page.coverage) : null,
|
|
58
58
|
el('div', { class: 'page-sections' },
|
|
59
|
-
|
|
59
|
+
// When nothing waits on the operator the headline already says so; an empty card would repeat it.
|
|
60
|
+
s.waiting.length ? section({ id: 'waiting', title: 'Waiting on you', rows: s.waiting.map((r) => ({ ...r, tone: 'attn' })), empty: 'Nothing is waiting on you.', onRow: open }) : null,
|
|
60
61
|
section({ id: 'carrying', title: 'I’m handling', rows: s.carrying, collapsed: true, onRow: open }),
|
|
61
62
|
section({ id: 'handled', title: 'Older, still open', rows: s.handled.map((r) => ({ ...r, tone: 'unknown' })), collapsed: true, onRow: open }),
|
|
62
63
|
section({ id: 'closed', title: 'Closed this week', rows: s.closed, collapsed: true, onRow: open }),
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import {
|
|
11
11
|
buildSchedulePage, buildHistory, markText, storySentence, storyLines, jobActions, toggleConfirm, runStartWords, runFollowWords, saveRefusalWords, originWords, scheduleWords, ago, HISTORY_LIMIT, STRIP_LIMIT,
|
|
12
12
|
} from './schedule-model.js';
|
|
13
|
-
import { el, setKids, pageHead, section, sheet, closeSheet, sheetIsOpen, story, details, note, cantTell } from './ui.js';
|
|
13
|
+
import { el, setKids, pageHead, section, sheet, closeSheet, sheetIsOpen, story, details, note, noteLinked, cantTell } from './ui.js';
|
|
14
14
|
import { legacyHref } from './nav-model.js';
|
|
15
15
|
import { verifiedDisplayName } from './display-identity.js';
|
|
16
16
|
|
|
@@ -73,7 +73,7 @@ function paint() {
|
|
|
73
73
|
setKids(main, el('div', { class: 'schedule-page' },
|
|
74
74
|
pageHead({ eyebrow: EYEBROW, headline: page.headline, sub: page.sub, tone: page.tone, facts: page.facts }),
|
|
75
75
|
liveNote(),
|
|
76
|
-
page.notes.map((t) =>
|
|
76
|
+
page.notes.map((t) => noteLinked(t)),
|
|
77
77
|
el('div', { class: 'page-sections' }, needs, coming, every, setupSection(page.setup, refresh)),
|
|
78
78
|
legacyLink(),
|
|
79
79
|
));
|
|
@@ -150,7 +150,7 @@ function setupSection(setup, refresh) {
|
|
|
150
150
|
}
|
|
151
151
|
body.append(row);
|
|
152
152
|
}
|
|
153
|
-
for (const t of setup.notes) body.append(
|
|
153
|
+
for (const t of setup.notes) body.append(noteLinked(t));
|
|
154
154
|
wrap.append(head, body);
|
|
155
155
|
return wrap;
|
|
156
156
|
}
|
|
@@ -215,7 +215,7 @@ function historyView(h) {
|
|
|
215
215
|
strip.append(b);
|
|
216
216
|
}
|
|
217
217
|
const wrap = el('div', { class: 'history' }, strip, detail);
|
|
218
|
-
for (const t of h.notes) wrap.append(
|
|
218
|
+
for (const t of h.notes) wrap.append(noteLinked(t));
|
|
219
219
|
{
|
|
220
220
|
const list = el('ol', { class: 'run-list', hidden: true });
|
|
221
221
|
let day = null;
|
|
@@ -124,8 +124,18 @@ export function buildRequests(res, opts = {}) {
|
|
|
124
124
|
* (names only); falls back to the secret-sync key list when that is switched off. Lengths
|
|
125
125
|
* and values are never read into the model.
|
|
126
126
|
*/
|
|
127
|
+
// A saved Secret Drop is stored under `secretDrops.<64-hex token>` (often with
|
|
128
|
+
// "(+N nested)"): a token, not a name. Show them as one counted line instead.
|
|
129
|
+
const DROP_KEY = /^secretDrops\.[0-9a-f]{16,}\b/;
|
|
130
|
+
function groupDrops(names) {
|
|
131
|
+
const drops = names.filter((n) => DROP_KEY.test(n)).length;
|
|
132
|
+
if (!drops) return names;
|
|
133
|
+
const rest = names.filter((n) => !DROP_KEY.test(n));
|
|
134
|
+
return [...rest, drops === 1 ? 'Saved Secret Drop submission (1)' : `Saved Secret Drop submissions (${drops})`].sort((a, b) => a.localeCompare(b));
|
|
135
|
+
}
|
|
136
|
+
|
|
127
137
|
export function buildKeys(ctxRes, syncRes) {
|
|
128
|
-
const clean = (arr) => [...new Set(arr.filter((n) => typeof n === 'string' && n.trim()).map((n) => n.trim()))].sort((a, b) => a.localeCompare(b));
|
|
138
|
+
const clean = (arr) => groupDrops([...new Set(arr.filter((n) => typeof n === 'string' && n.trim()).map((n) => n.trim()))]).sort((a, b) => a.localeCompare(b));
|
|
129
139
|
if (ctxRes && ctxRes.ok && ctxRes.data && Array.isArray(ctxRes.data.names)) {
|
|
130
140
|
const vaultState = ctxRes.data.vaultState;
|
|
131
141
|
if (vaultState === 'decrypt-failed') return { ok: false, decryptFailed: true, names: [], reason: 'Can’t open the saved keys on this machine.' };
|
|
@@ -131,6 +131,24 @@ export function historyUrl(s, before = null) {
|
|
|
131
131
|
return `/sessions/${encodeURIComponent(s.tmuxSession)}/transcript?${q}`;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
/** After a session's own record runs out, History continues into its topic's earlier
|
|
135
|
+
* messages (earlier sessions of the same conversation), a page at a time. */
|
|
136
|
+
export const TOPIC_HISTORY_PAGE = 50;
|
|
137
|
+
export function topicOf(s) {
|
|
138
|
+
const n = Number(s && s.platform === 'telegram' ? s.platformId : NaN);
|
|
139
|
+
return Number.isSafeInteger(n) && n > 1 ? n : null;
|
|
140
|
+
}
|
|
141
|
+
export function topicHistoryUrl(topicId, beforeMs) {
|
|
142
|
+
const q = new URLSearchParams({ limit: String(TOPIC_HISTORY_PAGE), scope: 'pool', before: String(Math.floor(beforeMs)) });
|
|
143
|
+
return `/telegram/topics/${encodeURIComponent(topicId)}/messages?${q}`;
|
|
144
|
+
}
|
|
145
|
+
/** A topic message as a History entry: who, when, what. */
|
|
146
|
+
export function topicHistoryEntry(m) {
|
|
147
|
+
if (!m || typeof m.text !== 'string' || !m.text.trim()) return null;
|
|
148
|
+
const at = Date.parse(m.timestamp);
|
|
149
|
+
return { role: m.fromUser ? 'user' : 'assistant', text: m.text, at: Number.isFinite(at) ? m.timestamp : null, atMs: Number.isFinite(at) ? at : null };
|
|
150
|
+
}
|
|
151
|
+
|
|
134
152
|
/** Words for a failed History read: never "no history" when the read failed. */
|
|
135
153
|
export function historyErrorWords(res) {
|
|
136
154
|
const d = (res && res.data) || {};
|
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
buildSessions, sessionsHeadline, sessionsFacts, partialWords, titleLine, closeRequest, closeConfirmWords,
|
|
12
12
|
closeResultWords, doorChoices, modelChoices, doorModelWords, regimeWords, machineChoices, PLATFORMS,
|
|
13
13
|
platformAllowsDoor, createBody, createResultWords, defaultWords, profileSourceWords, switchLanded,
|
|
14
|
-
canSwitchDoor, attachmentMessage, historyUrl, historyErrorWords, historyText, KEYS, STOP_KEY, frame, nextHistoryBatch, streamErrorWords, composerRows, modelWords, HISTORY_FIRST,
|
|
14
|
+
canSwitchDoor, attachmentMessage, historyUrl, historyErrorWords, historyText, topicOf, topicHistoryUrl, topicHistoryEntry, TOPIC_HISTORY_PAGE, KEYS, STOP_KEY, frame, nextHistoryBatch, streamErrorWords, composerRows, modelWords, HISTORY_FIRST,
|
|
15
15
|
} from './sessions-model.js';
|
|
16
|
-
import { el, setKids, pageHead, sheet, closeSheet, story, details, note, cantTell } from './ui.js';
|
|
16
|
+
import { el, setKids, pageHead, sheet, closeSheet, story, details, note, noteLinked, cantTell, MACHINES_PAGE } from './ui.js';
|
|
17
17
|
import { legacyHref } from './nav-model.js';
|
|
18
18
|
import { topicNameMap } from './topic-names.js';
|
|
19
19
|
|
|
@@ -122,13 +122,14 @@ function paintList(box, m, openKey) {
|
|
|
122
122
|
parts.push(cantTell(m.reason));
|
|
123
123
|
} else {
|
|
124
124
|
const pw = partialWords(m);
|
|
125
|
-
|
|
125
|
+
// A machine that did not answer links to the Machines page, where its state and reachability are shown.
|
|
126
|
+
if (pw) parts.push(noteLinked(pw));
|
|
126
127
|
if (!m.rows.length) parts.push(el('p', { class: 'empty-line', text: m.multi ? 'Nothing is running on any machine.' : 'Nothing is running on this machine.' }));
|
|
127
128
|
if (m.needsLook.length) parts.push(group('Needs a look', 'look', m.needsLook, openKey));
|
|
128
129
|
if (m.working.length) parts.push(group('Working', 'working', m.working, openKey));
|
|
129
130
|
if (m.unanswered.length || m.idleMachines.length) {
|
|
130
131
|
parts.push(el('section', { class: 'list-section', id: 'sec-idle' }, el('div', { class: 'list-head' }, el('h2', { class: 'eyebrow', text: 'Other machines' })),
|
|
131
|
-
m.unanswered.map((u) => el('
|
|
132
|
+
m.unanswered.map((u) => el('a', { class: 'machine-line dashed machine-line-link', href: MACHINES_PAGE, text: `${u.name}: ${u.words} ›` })),
|
|
132
133
|
m.idleMachines.map((i) => el('p', { class: `machine-line${i.ok ? '' : ' dashed'}`, text: `${i.name}: ${i.words}` }))));
|
|
133
134
|
}
|
|
134
135
|
}
|
|
@@ -434,11 +435,20 @@ function sendKey(key) {
|
|
|
434
435
|
|
|
435
436
|
// ── History: the conversation record, newest at the bottom, older on scroll-up ──
|
|
436
437
|
async function openHistory(view) {
|
|
437
|
-
view.history = { before: null, done: false, loading: false, count: 0 };
|
|
438
|
+
view.history = { before: null, done: false, loading: false, count: 0, phase: 'session', oldestMs: null, topicBefore: null };
|
|
438
439
|
view.historyBox.addEventListener('scroll', () => { if (view.historyBox.scrollTop < 200) loadHistory(view); });
|
|
439
440
|
await loadHistory(view);
|
|
440
441
|
}
|
|
441
442
|
|
|
443
|
+
function historyRow(e) {
|
|
444
|
+
const text = historyText(e);
|
|
445
|
+
if (!text) return null;
|
|
446
|
+
const when = e.at ? new Date(e.at).toLocaleString([], { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : null;
|
|
447
|
+
return e.role === 'tool'
|
|
448
|
+
? el('p', { class: 'sess-h sess-h-tool', text: `→ ${text}` })
|
|
449
|
+
: el('div', { class: `sess-h sess-h-${e.role}` }, el('div', { class: 'sess-h-who', text: [e.role === 'user' ? 'Message in' : 'Session', when].filter(Boolean).join(' · ') }), el('div', { class: 'sess-h-text', text }));
|
|
450
|
+
}
|
|
451
|
+
|
|
442
452
|
async function loadHistory(view) {
|
|
443
453
|
const h = view.history;
|
|
444
454
|
if (!h || h.loading || h.done || st.view !== view) return;
|
|
@@ -446,33 +456,59 @@ async function loadHistory(view) {
|
|
|
446
456
|
const box = view.historyBox;
|
|
447
457
|
const marker = el('p', { class: 'sess-history-note', text: h.count ? 'Loading earlier messages…' : 'Loading the conversation…' });
|
|
448
458
|
box.prepend(marker);
|
|
449
|
-
const
|
|
459
|
+
const topicPhase = h.phase === 'topic';
|
|
460
|
+
const res = await send('GET', topicPhase ? topicHistoryUrl(topicOf(view.s), h.topicBefore) : historyUrl(view.s, h.before));
|
|
450
461
|
marker.remove();
|
|
451
462
|
h.loading = false;
|
|
452
463
|
if (st.view !== view) return;
|
|
453
|
-
|
|
464
|
+
const list = topicPhase ? res.data && res.data.messages : res.data && res.data.entries;
|
|
465
|
+
if (!res.ok || !Array.isArray(list)) {
|
|
454
466
|
h.done = true;
|
|
455
|
-
box.prepend(el('p', { class: 'sess-history-note', text: historyErrorWords(res) }));
|
|
467
|
+
box.prepend(el('p', { class: 'sess-history-note', text: topicPhase ? `Can’t load earlier messages: ${res.reason || 'the server did not answer'}.` : historyErrorWords(res) }));
|
|
456
468
|
return;
|
|
457
469
|
}
|
|
458
470
|
const first = !h.count;
|
|
459
471
|
const prevHeight = box.scrollHeight, prevTop = box.scrollTop;
|
|
460
472
|
const frag = document.createDocumentFragment();
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
473
|
+
// A server without scroll-back support ignores `before` and returns the newest
|
|
474
|
+
// messages: keep only what is strictly older than the cursor, so nothing repeats.
|
|
475
|
+
const entries = topicPhase
|
|
476
|
+
? list.map(topicHistoryEntry).filter((e) => e && e.atMs != null && e.atMs < h.topicBefore)
|
|
477
|
+
: list;
|
|
478
|
+
for (const e of entries) {
|
|
479
|
+
const row = historyRow(e);
|
|
480
|
+
if (row) frag.append(row);
|
|
481
|
+
const ms = e.atMs ?? (e.at ? Date.parse(e.at) : NaN);
|
|
482
|
+
if (Number.isFinite(ms)) h.oldestMs = h.oldestMs == null ? ms : Math.min(h.oldestMs, ms);
|
|
483
|
+
}
|
|
484
|
+
h.count += entries.length;
|
|
485
|
+
if (topicPhase) {
|
|
486
|
+
h.topicCount += entries.length;
|
|
487
|
+
// Nothing earlier after all: the divider would introduce an empty section.
|
|
488
|
+
if (!h.topicCount && h.divider) { h.divider.remove(); h.divider = null; }
|
|
489
|
+
h.topicBefore = h.oldestMs;
|
|
490
|
+
if (list.length < TOPIC_HISTORY_PAGE || entries.length === 0 || h.topicBefore == null) { h.done = true; frag.prepend(el('p', { class: 'sess-history-note', text: 'The start of this conversation.' })); }
|
|
491
|
+
} else {
|
|
492
|
+
h.before = res.data.nextBefore;
|
|
493
|
+
if (h.before == null) {
|
|
494
|
+
// This session's own record ends here. A conversation keeps going back
|
|
495
|
+
// through its earlier sessions: continue with the topic's messages.
|
|
496
|
+
if (topicOf(view.s)) {
|
|
497
|
+
h.phase = 'topic';
|
|
498
|
+
h.topicBefore = h.oldestMs ?? Date.now();
|
|
499
|
+
h.divider = el('p', { class: 'sess-history-note', text: 'Earlier, from this conversation’s previous sessions:' });
|
|
500
|
+
h.topicCount = 0;
|
|
501
|
+
frag.prepend(h.divider);
|
|
502
|
+
} else {
|
|
503
|
+
h.done = true;
|
|
504
|
+
frag.prepend(el('p', { class: 'sess-history-note', text: h.count ? 'The start of this conversation.' : 'Nothing has been said in this session yet.' }));
|
|
505
|
+
}
|
|
506
|
+
}
|
|
468
507
|
}
|
|
469
|
-
h.count += res.data.entries.length;
|
|
470
|
-
h.before = res.data.nextBefore;
|
|
471
|
-
if (h.before == null) { h.done = true; frag.prepend(el('p', { class: 'sess-history-note', text: h.count ? 'The start of this conversation.' : 'Nothing has been said in this session yet.' })); }
|
|
472
508
|
box.prepend(frag);
|
|
473
509
|
if (first) box.scrollTop = box.scrollHeight;
|
|
474
510
|
else box.scrollTop = prevTop + (box.scrollHeight - prevHeight);
|
|
475
|
-
// A short
|
|
511
|
+
// A short page may not fill the box: keep loading until it scrolls.
|
|
476
512
|
if (!h.done && box.scrollHeight <= box.clientHeight + 200) loadHistory(view);
|
|
477
513
|
}
|
|
478
514
|
|
package/dashboard/console/ui.js
CHANGED
|
@@ -32,6 +32,17 @@ export function pageHead({ eyebrow, headline, sub, tone, facts = [] }) {
|
|
|
32
32
|
/** A dashed honesty note (can't tell / partial / stale). */
|
|
33
33
|
export function note(text) { return el('p', { class: 'card-note', text }); }
|
|
34
34
|
|
|
35
|
+
/** Where a machine's reachability is shown. */
|
|
36
|
+
export const MACHINES_PAGE = '#/capacity/machines';
|
|
37
|
+
/** A note about a machine that did not answer: anything that fails links to the problem. */
|
|
38
|
+
export function machineIssue(text) {
|
|
39
|
+
return /did not answer|not answering|didn[’']t answer|Couldn['’]t read other machines/i.test(String(text || ''));
|
|
40
|
+
}
|
|
41
|
+
export function noteLinked(text) {
|
|
42
|
+
if (!machineIssue(text)) return note(text);
|
|
43
|
+
return el('p', { class: 'card-note' }, document.createTextNode(`${text} `), el('a', { href: MACHINES_PAGE, text: 'See machines' }));
|
|
44
|
+
}
|
|
45
|
+
|
|
35
46
|
/** 5. Can't tell, as a whole page. */
|
|
36
47
|
export function cantTell(reason) {
|
|
37
48
|
return el('section', { class: 'card', 'data-state': 'unknown' },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/server.js
CHANGED
|
@@ -11834,7 +11834,7 @@ export async function startServer(options) {
|
|
|
11834
11834
|
const slackChId = slackProxyChannelMap.get(topicId);
|
|
11835
11835
|
if (slackChId && _slackAdapter) {
|
|
11836
11836
|
const msgs = _slackAdapter.getChannelMessages(slackChId, limit);
|
|
11837
|
-
return msgs.map(m => ({ text: m.text, fromUser:
|
|
11837
|
+
return msgs.map(m => ({ text: m.text, fromUser: _slackAdapter.isMessageFromUser(m), timestamp: new Date(parseFloat(m.ts) * 1000).toISOString() }));
|
|
11838
11838
|
}
|
|
11839
11839
|
if (telegram) {
|
|
11840
11840
|
const entries = telegram.getTopicHistory(topicId, limit);
|
|
@@ -11914,7 +11914,7 @@ export async function startServer(options) {
|
|
|
11914
11914
|
const slackChId = slackProxyChannelMap.get(topicId);
|
|
11915
11915
|
if (slackChId && _slackAdapter) {
|
|
11916
11916
|
const msgs = _slackAdapter.getChannelMessages(slackChId, limit);
|
|
11917
|
-
return msgs.map(m => ({ text: m.text, fromUser:
|
|
11917
|
+
return msgs.map(m => ({ text: m.text, fromUser: _slackAdapter.isMessageFromUser(m), timestamp: new Date(parseFloat(m.ts) * 1000).toISOString() }));
|
|
11918
11918
|
}
|
|
11919
11919
|
if (telegram) {
|
|
11920
11920
|
const entries = telegram.getTopicHistory(topicId, limit);
|
|
@@ -12394,7 +12394,7 @@ export async function startServer(options) {
|
|
|
12394
12394
|
const slackChId = slackProxyChannelMap.get(topicId);
|
|
12395
12395
|
if (slackChId && _slackAdapter) {
|
|
12396
12396
|
const msgs = _slackAdapter.getChannelMessages(slackChId, limit);
|
|
12397
|
-
return msgs.map(m => ({ text: m.text, fromUser:
|
|
12397
|
+
return msgs.map(m => ({ text: m.text, fromUser: _slackAdapter.isMessageFromUser(m), timestamp: new Date(parseFloat(m.ts) * 1000).toISOString() }));
|
|
12398
12398
|
}
|
|
12399
12399
|
if (telegram) {
|
|
12400
12400
|
const history = telegram.getMessageLog?.();
|
|
@@ -12434,6 +12434,8 @@ export async function startServer(options) {
|
|
|
12434
12434
|
// (update-train churn) forgot which dead sessions were already
|
|
12435
12435
|
// announced and re-posted the same death once per boot.
|
|
12436
12436
|
statePath: path.join(config.stateDir, 'state', 'session-monitor-ctx-notified.json'),
|
|
12437
|
+
// Same for "session has stopped": one notice per death, across restarts.
|
|
12438
|
+
deadNoticeStatePath: path.join(config.stateDir, 'state', 'session-monitor-dead-notified.json'),
|
|
12437
12439
|
// A topic an ONLINE peer actively owns is not this machine's to monitor:
|
|
12438
12440
|
// route() forwards its inbound to that owner, so a stale local mapping
|
|
12439
12441
|
// must never produce a "session has stopped" notice or a local triage
|
|
@@ -22300,7 +22302,7 @@ export async function startServer(options) {
|
|
|
22300
22302
|
greenPrAutoMerger?.attachLocalVerify({
|
|
22301
22303
|
effectiveFloor: () => (rec ? rec.effectiveFloor() : null),
|
|
22302
22304
|
mainHealth: () => svc.mainHealth().state,
|
|
22303
|
-
hasRecord: (pr, head, base) =>
|
|
22305
|
+
hasRecord: (pr, head, base) => wiring.hasPassingRecord(svc, pr, head, base),
|
|
22304
22306
|
requestVerify: (pr) => svc.request(pr, { explicit: false }),
|
|
22305
22307
|
// Dry-run posting also suppresses the watcher's actions→local disarm write.
|
|
22306
22308
|
postStatuses: () => liveConfig.get('monitoring.localVerify.postStatuses', false) === true,
|