reflectt-node 0.1.4 β 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -146
- package/defaults/TEAM-ROLES.yaml +221 -31
- package/dist/activationEvents.d.ts +13 -2
- package/dist/activationEvents.d.ts.map +1 -1
- package/dist/activationEvents.js +172 -38
- package/dist/activationEvents.js.map +1 -1
- package/dist/activity.d.ts +72 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/activity.js +510 -0
- package/dist/activity.js.map +1 -0
- package/dist/alert-preflight.d.ts +33 -0
- package/dist/alert-preflight.d.ts.map +1 -1
- package/dist/alert-preflight.js +218 -2
- package/dist/alert-preflight.js.map +1 -1
- package/dist/assignment.d.ts.map +1 -1
- package/dist/assignment.js +11 -6
- package/dist/assignment.js.map +1 -1
- package/dist/boardHealthWorker.d.ts.map +1 -1
- package/dist/boardHealthWorker.js +25 -12
- package/dist/boardHealthWorker.js.map +1 -1
- package/dist/canvas-slots.d.ts +1 -1
- package/dist/channels.d.ts +1 -1
- package/dist/chat-approval-detector.d.ts.map +1 -1
- package/dist/chat-approval-detector.js +29 -11
- package/dist/chat-approval-detector.js.map +1 -1
- package/dist/chat.d.ts +14 -0
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +68 -4
- package/dist/chat.js.map +1 -1
- package/dist/cli.js +349 -28
- package/dist/cli.js.map +1 -1
- package/dist/cloud.d.ts +28 -1
- package/dist/cloud.d.ts.map +1 -1
- package/dist/cloud.js +62 -25
- package/dist/cloud.js.map +1 -1
- package/dist/compliance-detector.d.ts +42 -0
- package/dist/compliance-detector.d.ts.map +1 -0
- package/dist/compliance-detector.js +286 -0
- package/dist/compliance-detector.js.map +1 -0
- package/dist/continuity-loop.d.ts.map +1 -1
- package/dist/continuity-loop.js +7 -3
- package/dist/continuity-loop.js.map +1 -1
- package/dist/dashboard.d.ts +6 -2
- package/dist/dashboard.d.ts.map +1 -1
- package/dist/dashboard.js +84 -28
- package/dist/dashboard.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +24 -1
- package/dist/db.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +17 -6
- package/dist/doctor.js.map +1 -1
- package/dist/executionSweeper.d.ts +2 -0
- package/dist/executionSweeper.d.ts.map +1 -1
- package/dist/executionSweeper.js +60 -4
- package/dist/executionSweeper.js.map +1 -1
- package/dist/focus.d.ts +20 -0
- package/dist/focus.d.ts.map +1 -0
- package/dist/focus.js +57 -0
- package/dist/focus.js.map +1 -0
- package/dist/health.d.ts +1 -0
- package/dist/health.d.ts.map +1 -1
- package/dist/health.js +47 -15
- package/dist/health.js.map +1 -1
- package/dist/hostConnectGuard.d.ts +25 -0
- package/dist/hostConnectGuard.d.ts.map +1 -0
- package/dist/hostConnectGuard.js +27 -0
- package/dist/hostConnectGuard.js.map +1 -0
- package/dist/index.js +257 -39
- package/dist/index.js.map +1 -1
- package/dist/insight-mutation.d.ts +26 -0
- package/dist/insight-mutation.d.ts.map +1 -1
- package/dist/insight-mutation.js +103 -12
- package/dist/insight-mutation.js.map +1 -1
- package/dist/insight-task-bridge.d.ts +1 -1
- package/dist/insight-task-bridge.d.ts.map +1 -1
- package/dist/insight-task-bridge.js +6 -3
- package/dist/insight-task-bridge.js.map +1 -1
- package/dist/insights.d.ts +20 -0
- package/dist/insights.d.ts.map +1 -1
- package/dist/insights.js +129 -4
- package/dist/insights.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +9 -8
- package/dist/mcp.js.map +1 -1
- package/dist/notificationDedupeGuard.d.ts +33 -0
- package/dist/notificationDedupeGuard.d.ts.map +1 -0
- package/dist/notificationDedupeGuard.js +88 -0
- package/dist/notificationDedupeGuard.js.map +1 -0
- package/dist/openclaw.d.ts.map +1 -1
- package/dist/openclaw.js +3 -2
- package/dist/openclaw.js.map +1 -1
- package/dist/policy.d.ts +1 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +3 -1
- package/dist/policy.js.map +1 -1
- package/dist/prAutoMerge.d.ts.map +1 -1
- package/dist/prAutoMerge.js +23 -0
- package/dist/prAutoMerge.js.map +1 -1
- package/dist/presence.d.ts +16 -1
- package/dist/presence.d.ts.map +1 -1
- package/dist/presence.js +97 -9
- package/dist/presence.js.map +1 -1
- package/dist/pulse.d.ts +60 -0
- package/dist/pulse.d.ts.map +1 -0
- package/dist/pulse.js +139 -0
- package/dist/pulse.js.map +1 -0
- package/dist/reflection-automation.d.ts.map +1 -1
- package/dist/reflection-automation.js +38 -0
- package/dist/reflection-automation.js.map +1 -1
- package/dist/release.d.ts +2 -0
- package/dist/release.d.ts.map +1 -1
- package/dist/release.js +14 -1
- package/dist/release.js.map +1 -1
- package/dist/request-tracker.d.ts +6 -0
- package/dist/request-tracker.d.ts.map +1 -1
- package/dist/request-tracker.js +31 -12
- package/dist/request-tracker.js.map +1 -1
- package/dist/scopeOverlap.d.ts +32 -0
- package/dist/scopeOverlap.d.ts.map +1 -0
- package/dist/scopeOverlap.js +219 -0
- package/dist/scopeOverlap.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +736 -117
- package/dist/server.js.map +1 -1
- package/dist/service-probe.d.ts.map +1 -1
- package/dist/service-probe.js +39 -2
- package/dist/service-probe.js.map +1 -1
- package/dist/shipped-heartbeat.d.ts +1 -1
- package/dist/shipped-heartbeat.js +1 -1
- package/dist/taskPrecheck.js +6 -6
- package/dist/taskPrecheck.js.map +1 -1
- package/dist/tasks-next-diagnostics.d.ts +15 -0
- package/dist/tasks-next-diagnostics.d.ts.map +1 -0
- package/dist/tasks-next-diagnostics.js +33 -0
- package/dist/tasks-next-diagnostics.js.map +1 -0
- package/dist/tasks.d.ts +3 -2
- package/dist/tasks.d.ts.map +1 -1
- package/dist/tasks.js +41 -16
- package/dist/tasks.js.map +1 -1
- package/dist/team-config.d.ts.map +1 -1
- package/dist/team-config.js +20 -0
- package/dist/team-config.js.map +1 -1
- package/dist/todoHoardingGuard.d.ts +35 -0
- package/dist/todoHoardingGuard.d.ts.map +1 -0
- package/dist/todoHoardingGuard.js +150 -0
- package/dist/todoHoardingGuard.js.map +1 -0
- package/dist/types.d.ts +4 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +16 -0
- package/dist/version.js.map +1 -0
- package/dist/working-contract.d.ts.map +1 -1
- package/dist/working-contract.js +59 -3
- package/dist/working-contract.js.map +1 -1
- package/package.json +5 -1
- package/public/dashboard.js +161 -20
- package/public/docs.md +68 -8
- package/public/polls-mock.html +1 -1
package/dist/working-contract.js
CHANGED
|
@@ -14,7 +14,60 @@ import { routeMessage } from './messageRouter.js';
|
|
|
14
14
|
import { listReflections } from './reflections.js';
|
|
15
15
|
import { getEffectiveActivity, formatActivityWarning } from './activity-signal.js';
|
|
16
16
|
// ββ State: track warnings ββ
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* warningTimestamps: key β epoch ms when Phase 1 warning was issued.
|
|
19
|
+
*
|
|
20
|
+
* Backed by SQLite so restarts don't re-fire warnings. In-memory cache
|
|
21
|
+
* is seeded from DB on first use.
|
|
22
|
+
*
|
|
23
|
+
* Root cause of compliance snapshot 3x bug: this was previously a plain
|
|
24
|
+
* in-memory Map that reset on every server restart. Each restart would
|
|
25
|
+
* re-fire the Phase 1 warning for every stale doing task. The stale
|
|
26
|
+
* duration increments between restarts (e.g. "stale for 45m" vs "46m"),
|
|
27
|
+
* which bypassed chat.ts content dedup, resulting in 2β3x identical-
|
|
28
|
+
* looking warning messages per agent per deploy cycle.
|
|
29
|
+
*/
|
|
30
|
+
const warningTimestamps = new Map();
|
|
31
|
+
let _warningDbSeeded = false;
|
|
32
|
+
function ensureWarningTable() {
|
|
33
|
+
const db = getDb();
|
|
34
|
+
db.prepare(`
|
|
35
|
+
CREATE TABLE IF NOT EXISTS wc_warning_timestamps (
|
|
36
|
+
key TEXT PRIMARY KEY,
|
|
37
|
+
warned_at INTEGER NOT NULL
|
|
38
|
+
)
|
|
39
|
+
`).run();
|
|
40
|
+
}
|
|
41
|
+
function seedWarningTimestamps() {
|
|
42
|
+
if (_warningDbSeeded)
|
|
43
|
+
return;
|
|
44
|
+
_warningDbSeeded = true;
|
|
45
|
+
try {
|
|
46
|
+
ensureWarningTable();
|
|
47
|
+
const db = getDb();
|
|
48
|
+
const rows = db.prepare('SELECT key, warned_at FROM wc_warning_timestamps').all();
|
|
49
|
+
for (const row of rows) {
|
|
50
|
+
warningTimestamps.set(row.key, row.warned_at);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch { /* db may not be ready */ }
|
|
54
|
+
}
|
|
55
|
+
function persistWarning(key, timestamp) {
|
|
56
|
+
try {
|
|
57
|
+
ensureWarningTable();
|
|
58
|
+
const db = getDb();
|
|
59
|
+
db.prepare('INSERT OR REPLACE INTO wc_warning_timestamps (key, warned_at) VALUES (?, ?)').run(key, timestamp);
|
|
60
|
+
}
|
|
61
|
+
catch { /* best-effort */ }
|
|
62
|
+
}
|
|
63
|
+
function clearWarning(key) {
|
|
64
|
+
warningTimestamps.delete(key);
|
|
65
|
+
try {
|
|
66
|
+
const db = getDb();
|
|
67
|
+
db.prepare('DELETE FROM wc_warning_timestamps WHERE key = ?').run(key);
|
|
68
|
+
}
|
|
69
|
+
catch { /* best-effort */ }
|
|
70
|
+
}
|
|
18
71
|
// ββ Enforcement tick ββ
|
|
19
72
|
/**
|
|
20
73
|
* Called periodically. Checks all 'doing' tasks for stale status.
|
|
@@ -24,6 +77,8 @@ export async function tickWorkingContract() {
|
|
|
24
77
|
const config = getConfig();
|
|
25
78
|
if (!config.enabled)
|
|
26
79
|
return { warnings: 0, requeued: 0, actions: [] };
|
|
80
|
+
// Seed warningTimestamps from DB on first tick (survives process restarts)
|
|
81
|
+
seedWarningTimestamps();
|
|
27
82
|
const now = Date.now();
|
|
28
83
|
const staleThresholdMs = config.staleAutoRequeueMin * 60_000;
|
|
29
84
|
const graceMs = config.graceAfterWarningMin * 60_000;
|
|
@@ -51,6 +106,7 @@ export async function tickWorkingContract() {
|
|
|
51
106
|
if (!warnedAt) {
|
|
52
107
|
// Phase 1: Issue warning
|
|
53
108
|
warningTimestamps.set(warningKey, now);
|
|
109
|
+
persistWarning(warningKey, now);
|
|
54
110
|
const signalInfo = formatActivityWarning(activitySignal, config.staleAutoRequeueMin, now);
|
|
55
111
|
const action = {
|
|
56
112
|
type: 'warning',
|
|
@@ -80,7 +136,7 @@ export async function tickWorkingContract() {
|
|
|
80
136
|
const activitySinceWarning = getLastActivityForAgent(task.id, agent);
|
|
81
137
|
if (activitySinceWarning && activitySinceWarning > warnedAt) {
|
|
82
138
|
// Agent responded β clear warning
|
|
83
|
-
|
|
139
|
+
clearWarning(warningKey);
|
|
84
140
|
continue;
|
|
85
141
|
}
|
|
86
142
|
// Auto-requeue
|
|
@@ -95,7 +151,7 @@ export async function tickWorkingContract() {
|
|
|
95
151
|
};
|
|
96
152
|
actions.push(action);
|
|
97
153
|
requeued++;
|
|
98
|
-
|
|
154
|
+
clearWarning(warningKey);
|
|
99
155
|
if (!config.dryRun) {
|
|
100
156
|
try {
|
|
101
157
|
// Move task back to todo, clear assignee
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"working-contract.js","sourceRoot":"","sources":["../src/working-contract.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,+BAA+B;AAC/B,EAAE;AACF,sEAAsE;AACtE,kGAAkG;AAClG,uFAAuF;AACvF,4EAA4E;AAC5E,EAAE;AACF,+DAA+D;AAE/D,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAuB,MAAM,sBAAsB,CAAA;AAqCvG,8BAA8B;AAE9B,MAAM,iBAAiB,GAAwB,IAAI,GAAG,EAAE,CAAA,CAAC,
|
|
1
|
+
{"version":3,"file":"working-contract.js","sourceRoot":"","sources":["../src/working-contract.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reflectt-node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Coordinate your AI agent team. Shared tasks, memory, reflections, and presence. Self-host for free.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"test:watch": "vitest",
|
|
30
30
|
"deploy": "bash scripts/post-merge-rebuild.sh",
|
|
31
31
|
"hooks:install": "bash scripts/install-hooks.sh",
|
|
32
|
+
"prepublishOnly": "npm run build",
|
|
32
33
|
"prepack": "chmod +x dist/cli.js"
|
|
33
34
|
},
|
|
34
35
|
"keywords": [
|
|
@@ -87,5 +88,8 @@
|
|
|
87
88
|
],
|
|
88
89
|
"optionalDependencies": {
|
|
89
90
|
"@xenova/transformers": "^2.17.2"
|
|
91
|
+
},
|
|
92
|
+
"overrides": {
|
|
93
|
+
"sharp": "^0.34.0"
|
|
90
94
|
}
|
|
91
95
|
}
|
package/public/dashboard.js
CHANGED
|
@@ -11,7 +11,7 @@ document.addEventListener('keydown', function(e) {
|
|
|
11
11
|
/* ============================================================
|
|
12
12
|
SIDEBAR NAV β hash-based client-side routing
|
|
13
13
|
============================================================ */
|
|
14
|
-
const VALID_PAGES = ['overview', 'tasks', 'chat', 'reviews', 'health', 'outcomes', 'research', 'artifacts'];
|
|
14
|
+
const VALID_PAGES = ['overview', 'tasks', 'chat', 'reviews', 'health', 'outcomes', 'research', 'artifacts', 'doctor'];
|
|
15
15
|
|
|
16
16
|
function navigateTo(page) {
|
|
17
17
|
if (!VALID_PAGES.includes(page)) page = 'overview';
|
|
@@ -71,8 +71,14 @@ function updateFirstBootBanner() {
|
|
|
71
71
|
const el = document.getElementById('first-boot-banner');
|
|
72
72
|
if (!el) return;
|
|
73
73
|
const dismissed = localStorage.getItem('reflectt-first-boot-dismissed');
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
// We seed a welcome task on first boot, so `allTasks.length > 0` is not a good proxy for
|
|
75
|
+
// "user has started using the system". Only hide the banner once a *non-seeded* task exists.
|
|
76
|
+
const seededSources = new Set(['first-boot', 'first-boot-intent']);
|
|
77
|
+
const hasRealTasks = allTasks.some(t => {
|
|
78
|
+
const src = t && t.metadata && t.metadata.source;
|
|
79
|
+
return !src || !seededSources.has(src);
|
|
80
|
+
});
|
|
81
|
+
el.hidden = dismissed === '1' || hasRealTasks;
|
|
76
82
|
}
|
|
77
83
|
function dismissFirstBootBanner() {
|
|
78
84
|
localStorage.setItem('reflectt-first-boot-dismissed', '1');
|
|
@@ -80,6 +86,53 @@ function dismissFirstBootBanner() {
|
|
|
80
86
|
if (el) el.hidden = true;
|
|
81
87
|
}
|
|
82
88
|
|
|
89
|
+
function updateOverviewEmptyState() {
|
|
90
|
+
const el = document.getElementById('overview-empty-state');
|
|
91
|
+
if (!el) return;
|
|
92
|
+
const hasTasks = allTasks.length > 0;
|
|
93
|
+
const hasMessages = allMessages.length > 0;
|
|
94
|
+
el.style.display = (hasTasks || hasMessages) ? 'none' : '';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function updateOverviewSummary() {
|
|
98
|
+
const panel = document.getElementById('overview-summary');
|
|
99
|
+
if (!panel) return;
|
|
100
|
+
const hasTasks = allTasks.length > 0;
|
|
101
|
+
const hasMessages = allMessages.length > 0;
|
|
102
|
+
panel.style.display = (hasTasks || hasMessages) ? '' : 'none';
|
|
103
|
+
|
|
104
|
+
// Stats
|
|
105
|
+
const statsEl = document.getElementById('overview-stats');
|
|
106
|
+
if (statsEl) {
|
|
107
|
+
const counts = { doing: 0, todo: 0, validating: 0, done: 0, blocked: 0 };
|
|
108
|
+
allTasks.forEach(t => { if (counts[t.status] !== undefined) counts[t.status]++; });
|
|
109
|
+
const statStyle = 'text-align:center;padding:10px;background:var(--bg-secondary, rgba(255,255,255,0.03));border-radius:8px;border:1px solid var(--border)';
|
|
110
|
+
const numStyle = 'font-size:20px;font-weight:600;color:var(--text-bright)';
|
|
111
|
+
const labelStyle = 'font-size:11px;color:var(--text-muted);margin-top:2px';
|
|
112
|
+
statsEl.innerHTML = [
|
|
113
|
+
{ label: 'In Progress', count: counts.doing, color: 'var(--accent, #3B57E8)' },
|
|
114
|
+
{ label: 'To Do', count: counts.todo, color: 'var(--text-muted)' },
|
|
115
|
+
{ label: 'Validating', count: counts.validating, color: '#E8A83B' },
|
|
116
|
+
{ label: 'Done', count: counts.done, color: '#4CAF50' },
|
|
117
|
+
{ label: 'Blocked', count: counts.blocked, color: '#E84C3B' },
|
|
118
|
+
].map(s => `<div style="${statStyle}"><div style="${numStyle};color:${s.color}">${s.count}</div><div style="${labelStyle}">${s.label}</div></div>`).join('');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Recent activity β last 8 messages
|
|
122
|
+
const actEl = document.getElementById('overview-activity-list');
|
|
123
|
+
if (actEl && allMessages.length > 0) {
|
|
124
|
+
const recent = allMessages.slice(-8).reverse();
|
|
125
|
+
actEl.innerHTML = recent.map(m => {
|
|
126
|
+
const time = new Date(m.timestamp || m.ts || Date.now()).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
127
|
+
const from = m.from || m.author || 'system';
|
|
128
|
+
const text = (m.content || m.text || '').slice(0, 120);
|
|
129
|
+
return `<div style="padding:4px 0;border-bottom:1px solid var(--border)"><span style="color:var(--accent);font-weight:500">${from}</span> <span style="opacity:0.5">${time}</span><br>${text}${text.length >= 120 ? 'β¦' : ''}</div>`;
|
|
130
|
+
}).join('');
|
|
131
|
+
} else if (actEl) {
|
|
132
|
+
actEl.innerHTML = '<div style="opacity:0.5">No recent messages</div>';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
83
136
|
// Delta cursors for lower payload refreshes
|
|
84
137
|
let lastTaskSync = 0;
|
|
85
138
|
let lastChatSync = 0;
|
|
@@ -258,7 +311,7 @@ function renderLaneTransitionMeta(task) {
|
|
|
258
311
|
return `<div style="margin-top:6px;font-size:11px;color:var(--text-muted)">π§ ${esc(parts.join(' Β· '))}</div>`;
|
|
259
312
|
}
|
|
260
313
|
|
|
261
|
-
function
|
|
314
|
+
function hasMention(message) { return /@([a-zA-Z][a-zA-Z0-9_-]*)/i.test(message || ''); }
|
|
262
315
|
|
|
263
316
|
function resolveSSOTState(lastVerifiedUtc) {
|
|
264
317
|
if (!lastVerifiedUtc) return { state: 'unknown', label: 'unknown', text: 'verification timestamp unavailable' };
|
|
@@ -450,12 +503,12 @@ function formatDurationMin(min) {
|
|
|
450
503
|
|
|
451
504
|
function statusTemplateFor(agent, taskId) {
|
|
452
505
|
const mentions = agent === 'pixel'
|
|
453
|
-
? '@
|
|
506
|
+
? '@owner @link'
|
|
454
507
|
: agent === 'link'
|
|
455
|
-
? '@
|
|
508
|
+
? '@owner @pixel'
|
|
456
509
|
: agent === 'kai'
|
|
457
510
|
? '@link @pixel'
|
|
458
|
-
: '@
|
|
511
|
+
: '@owner @pixel';
|
|
459
512
|
return [
|
|
460
513
|
mentions,
|
|
461
514
|
'Task: ' + (taskId || '<task-id>'),
|
|
@@ -717,6 +770,8 @@ async function loadTasks(forceFull = false) {
|
|
|
717
770
|
const navTaskBadge = document.getElementById('nav-task-count');
|
|
718
771
|
if (navTaskBadge) navTaskBadge.textContent = allTasks.length;
|
|
719
772
|
updateFirstBootBanner();
|
|
773
|
+
updateOverviewEmptyState();
|
|
774
|
+
updateOverviewSummary();
|
|
720
775
|
}
|
|
721
776
|
|
|
722
777
|
function renderProjectTabs() {
|
|
@@ -741,7 +796,7 @@ function toggleTestTasks() {
|
|
|
741
796
|
renderStatusFilterTabs();
|
|
742
797
|
renderKanban();
|
|
743
798
|
// Update task count
|
|
744
|
-
const openCount = getVisibleTasks().filter(t =>
|
|
799
|
+
const openCount = getVisibleTasks().filter(t => !['done', 'resolved_externally', 'cancelled'].includes(t.status)).length;
|
|
745
800
|
document.getElementById('task-count').textContent = currentStatusFilter === 'open'
|
|
746
801
|
? openCount + ' open tasks'
|
|
747
802
|
: getVisibleTasks().length + ' tasks';
|
|
@@ -779,20 +834,45 @@ function renderKanban() {
|
|
|
779
834
|
const filtered = currentProject === 'all' ? visible : visible.filter(t => classifyProject(t) === currentProject);
|
|
780
835
|
const cols = currentStatusFilter === 'open'
|
|
781
836
|
? ['todo', 'doing', 'blocked', 'validating']
|
|
782
|
-
: ['todo', 'doing', 'blocked', 'validating', 'done'];
|
|
837
|
+
: ['todo', 'doing', 'blocked', 'validating', 'done', 'resolved_externally', 'cancelled'];
|
|
783
838
|
const grouped = {}; cols.forEach(c => grouped[c] = []);
|
|
784
|
-
|
|
839
|
+
const tasksForBoard = currentStatusFilter === 'open'
|
|
840
|
+
? filtered.filter(t => cols.includes(t.status || 'todo'))
|
|
841
|
+
: filtered;
|
|
842
|
+
tasksForBoard.forEach(t => { const s = t.status || 'todo'; if (grouped[s]) grouped[s].push(t); else grouped['todo'].push(t); });
|
|
785
843
|
const pOrder = { P0: 0, P1: 1, P2: 2, P3: 3 };
|
|
786
844
|
cols.forEach(c => grouped[c].sort((a, b) => (pOrder[a.priority] ?? 9) - (pOrder[b.priority] ?? 9)));
|
|
787
845
|
|
|
788
846
|
const kanban = document.getElementById('kanban');
|
|
847
|
+
|
|
848
|
+
// Full-board empty state: no tasks at all
|
|
849
|
+
const totalOnBoard = tasksForBoard.length;
|
|
850
|
+
if (totalOnBoard === 0) {
|
|
851
|
+
kanban.innerHTML = '<div class="board-empty-state" style="grid-column:1/-1;text-align:center;padding:40px 20px;color:var(--text-muted)">'
|
|
852
|
+
+ '<div style="font-size:28px;margin-bottom:12px">π</div>'
|
|
853
|
+
+ '<div style="font-size:15px;font-weight:500;color:var(--text-bright);margin-bottom:6px">No tasks yet</div>'
|
|
854
|
+
+ '<div style="font-size:13px;max-width:340px;margin:0 auto;line-height:1.5">Create your first task via the API, or connect an AI agent to start generating work automatically.</div>'
|
|
855
|
+
+ '<div style="margin-top:14px;display:flex;gap:10px;justify-content:center;flex-wrap:wrap">'
|
|
856
|
+
+ '<a href="/docs" target="_blank" style="color:var(--accent);font-size:12px;text-decoration:none">API reference β</a>'
|
|
857
|
+
+ '<a href="/capabilities" target="_blank" style="color:var(--accent);font-size:12px;text-decoration:none">Capabilities β</a>'
|
|
858
|
+
+ '</div></div>';
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
|
|
789
862
|
kanban.innerHTML = cols.map(col => {
|
|
790
863
|
const items = grouped[col];
|
|
791
864
|
const isDone = col === 'done';
|
|
792
865
|
const isTodo = col === 'todo';
|
|
793
866
|
const colLimit = isDone ? 3 : isTodo ? 10 : items.length;
|
|
867
|
+
const emptyMessages = {
|
|
868
|
+
todo: 'No tasks queued',
|
|
869
|
+
doing: 'Nothing in progress',
|
|
870
|
+
blocked: 'Nothing blocked',
|
|
871
|
+
validating: 'Nothing awaiting review',
|
|
872
|
+
done: 'No completed tasks yet',
|
|
873
|
+
};
|
|
794
874
|
const cards = items.length === 0
|
|
795
|
-
? '<div class="empty"
|
|
875
|
+
? '<div class="empty" style="font-size:12px;padding:12px 8px">' + (emptyMessages[col] || 'β') + '</div>'
|
|
796
876
|
: items.map((t, idx) => {
|
|
797
877
|
const isHidden = idx >= colLimit;
|
|
798
878
|
const assigneeAgent = t.assignee ? AGENTS.find(a => a.name === t.assignee) : null;
|
|
@@ -1032,13 +1112,13 @@ async function loadChat(forceFull = false) {
|
|
|
1032
1112
|
if (!channelStats.has(ch)) channelStats.set(ch, { total: 0, mentions: 0 });
|
|
1033
1113
|
const stats = channelStats.get(ch);
|
|
1034
1114
|
stats.total += 1;
|
|
1035
|
-
if (
|
|
1115
|
+
if (hasMention(m.content)) stats.mentions += 1;
|
|
1036
1116
|
});
|
|
1037
1117
|
|
|
1038
1118
|
const tabs = document.getElementById('channel-tabs');
|
|
1039
1119
|
tabs.innerHTML = Array.from(channels).map(ch => {
|
|
1040
1120
|
const stats = ch === 'all'
|
|
1041
|
-
? { total: allMessages.length, mentions: allMessages.filter(m =>
|
|
1121
|
+
? { total: allMessages.length, mentions: allMessages.filter(m => hasMention(m.content)).length }
|
|
1042
1122
|
: (channelStats.get(ch) || { total: 0, mentions: 0 });
|
|
1043
1123
|
const label = ch === 'all' ? 'π all' : '#' + esc(ch);
|
|
1044
1124
|
const countMeta = `<span class="meta">${stats.total}</span>`;
|
|
@@ -1069,7 +1149,7 @@ function renderChat() {
|
|
|
1069
1149
|
body.innerHTML = shown.map(m => {
|
|
1070
1150
|
const agent = AGENT_INDEX.get(m.from);
|
|
1071
1151
|
const roleTag = agent ? `<span class="msg-role">${esc(agent.role)}</span>` : '';
|
|
1072
|
-
const mentioned =
|
|
1152
|
+
const mentioned = hasMention(m.content);
|
|
1073
1153
|
const channelTag = m.channel ? '<span class="msg-channel">#' + esc(m.channel) + '</span>' : '';
|
|
1074
1154
|
const editedTag = m.metadata && m.metadata.editedAt ? '<span class="msg-edited">(edited)</span>' : '';
|
|
1075
1155
|
return `
|
|
@@ -1294,9 +1374,9 @@ async function loadSharedArtifacts() {
|
|
|
1294
1374
|
const files = entries.filter(e => e && e.type === 'file');
|
|
1295
1375
|
count.textContent = files.length + ' files';
|
|
1296
1376
|
|
|
1297
|
-
// Pinned:
|
|
1377
|
+
// Pinned: operator notes (optional)
|
|
1298
1378
|
const pinned = [
|
|
1299
|
-
{ name: "
|
|
1379
|
+
{ name: "OPERATOR-NOTES.md", label: "Operator notes" },
|
|
1300
1380
|
];
|
|
1301
1381
|
|
|
1302
1382
|
const pinnedRows = pinned.map(p => {
|
|
@@ -1540,6 +1620,51 @@ async function loadHealth() {
|
|
|
1540
1620
|
}
|
|
1541
1621
|
}
|
|
1542
1622
|
|
|
1623
|
+
async function loadDoctorPage() {
|
|
1624
|
+
const body = document.getElementById('doctor-body');
|
|
1625
|
+
const raw = document.getElementById('doctor-raw');
|
|
1626
|
+
if (!body) return;
|
|
1627
|
+
body.innerHTML = '<div class="loading">Loading diagnosticsβ¦</div>';
|
|
1628
|
+
|
|
1629
|
+
try {
|
|
1630
|
+
const res = await fetch(BASE + '/health');
|
|
1631
|
+
const data = await res.json();
|
|
1632
|
+
|
|
1633
|
+
// Render pretty summary
|
|
1634
|
+
const rows = [
|
|
1635
|
+
['Status', data.status ?? 'β'],
|
|
1636
|
+
['Version', data.version ?? 'β'],
|
|
1637
|
+
['Uptime', data.uptime != null ? `${Math.floor(data.uptime / 60)}m ${data.uptime % 60}s` : 'β'],
|
|
1638
|
+
['PID', data.pid ?? data.runtime?.pid ?? 'β'],
|
|
1639
|
+
['Node', data.nodeVersion ?? data.runtime?.nodeVersion ?? 'β'],
|
|
1640
|
+
['Port', data.port ?? data.runtime?.port ?? 'β'],
|
|
1641
|
+
['Deploy stale', data.deploy?.stale != null ? (data.deploy.stale ? 'β οΈ Yes' : 'β
No') : 'β'],
|
|
1642
|
+
['Deploy grace', data.deploy?.withinGrace ? `β³ ${Math.round((data.deploy.graceRemainingMs || 0) / 60000)}m left` : 'β'],
|
|
1643
|
+
['Tasks (todo)', data.tasks?.todo ?? 'β'],
|
|
1644
|
+
['Tasks (doing)', data.tasks?.doing ?? 'β'],
|
|
1645
|
+
];
|
|
1646
|
+
|
|
1647
|
+
body.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:13px">
|
|
1648
|
+
${rows.map(([k, v]) => `<tr>
|
|
1649
|
+
<td style="padding:6px 12px 6px 0;color:var(--muted);width:40%;white-space:nowrap">${k}</td>
|
|
1650
|
+
<td style="padding:6px 0;font-family:var(--font-mono,monospace)">${String(v)}</td>
|
|
1651
|
+
</tr>`).join('')}
|
|
1652
|
+
</table>
|
|
1653
|
+
<div style="margin-top:8px;font-size:11px;color:var(--muted)">Last refreshed: ${new Date().toLocaleTimeString()}</div>`;
|
|
1654
|
+
|
|
1655
|
+
if (raw) raw.textContent = JSON.stringify(data, null, 2);
|
|
1656
|
+
} catch (e) {
|
|
1657
|
+
body.innerHTML = `<div class="empty-state">Failed to load diagnostics: ${e.message}</div>`;
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// Hook into page activation: load doctor data when the page is shown
|
|
1662
|
+
const _origActivatePage = activatePage;
|
|
1663
|
+
function activatePage(page) {
|
|
1664
|
+
_origActivatePage(page);
|
|
1665
|
+
if (page === 'doctor') loadDoctorPage();
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1543
1668
|
async function loadReleaseStatus(force = false) {
|
|
1544
1669
|
const badge = document.getElementById('release-badge');
|
|
1545
1670
|
if (!badge) return;
|
|
@@ -1555,6 +1680,7 @@ async function loadReleaseStatus(force = false) {
|
|
|
1555
1680
|
badge.classList.toggle('stale', stale);
|
|
1556
1681
|
badge.classList.toggle('fresh', !stale);
|
|
1557
1682
|
badge.textContent = stale ? 'deploy: stale' : 'deploy: in sync';
|
|
1683
|
+
badge.style.display = '';
|
|
1558
1684
|
|
|
1559
1685
|
const reasons = Array.isArray(status.reasons) ? status.reasons : [];
|
|
1560
1686
|
const startupCommit = status.startup && status.startup.commit ? status.startup.commit.slice(0, 8) : 'unknown';
|
|
@@ -1590,6 +1716,7 @@ async function loadBuildInfo() {
|
|
|
1590
1716
|
badge.classList.toggle('stale', branch !== 'main');
|
|
1591
1717
|
badge.textContent = `${sha} β’ ${uptimeStr}`;
|
|
1592
1718
|
badge.title = `SHA: ${info.gitSha}\nBranch: ${branch}\nCommit: ${info.gitMessage}\nAuthor: ${info.gitAuthor}\nPID: ${info.pid}\nNode: ${info.nodeVersion}\nStarted: ${info.startedAt}`;
|
|
1719
|
+
badge.style.display = '';
|
|
1593
1720
|
} catch (err) {
|
|
1594
1721
|
badge.textContent = 'build: error';
|
|
1595
1722
|
badge.title = 'Failed to load build info';
|
|
@@ -1708,7 +1835,16 @@ function renderReviewQueue() {
|
|
|
1708
1835
|
});
|
|
1709
1836
|
|
|
1710
1837
|
if (validating.length === 0) {
|
|
1711
|
-
panel.style.display = '
|
|
1838
|
+
panel.style.display = '';
|
|
1839
|
+
count.textContent = '';
|
|
1840
|
+
body.innerHTML = '<div style="text-align:center;padding:24px 16px;color:var(--text-muted)">'
|
|
1841
|
+
+ '<div style="font-size:22px;margin-bottom:8px">β</div>'
|
|
1842
|
+
+ '<div style="font-size:13px;font-weight:500;color:var(--text-bright);margin-bottom:4px">Review queue is clear</div>'
|
|
1843
|
+
+ '<div style="font-size:12px;line-height:1.5">Tasks move here when they enter <em>validating</em>. Reviewers will see SLA timers and can approve or request changes.</div>'
|
|
1844
|
+
+ '</div>';
|
|
1845
|
+
// Clear sidebar badge
|
|
1846
|
+
const navReviewBadge = document.getElementById('nav-review-count');
|
|
1847
|
+
if (navReviewBadge) navReviewBadge.textContent = '0';
|
|
1712
1848
|
return;
|
|
1713
1849
|
}
|
|
1714
1850
|
|
|
@@ -1770,7 +1906,7 @@ async function escalateReviewBreaches(breachedTasks) {
|
|
|
1770
1906
|
return '- ' + t.id + ' (' + (t.title || '').slice(0, 50) + ') β reviewer: @' + reviewer + ', waiting ' + formatDuration(t.timeInReview);
|
|
1771
1907
|
});
|
|
1772
1908
|
|
|
1773
|
-
const content = '@
|
|
1909
|
+
const content = '@owner Review SLA breach detected:\n' + lines.join('\n');
|
|
1774
1910
|
|
|
1775
1911
|
try {
|
|
1776
1912
|
await fetch(BASE + '/chat/messages', {
|
|
@@ -2749,6 +2885,11 @@ function toggleFocusMode() {
|
|
|
2749
2885
|
// Persist preference
|
|
2750
2886
|
try { localStorage.setItem('reflectt-focus-mode', focusModeActive ? '1' : '0'); } catch {}
|
|
2751
2887
|
|
|
2888
|
+
// Show/hide dev-only panels
|
|
2889
|
+
document.querySelectorAll('.panel.focus-only').forEach(panel => {
|
|
2890
|
+
panel.style.display = focusModeActive ? '' : 'none';
|
|
2891
|
+
});
|
|
2892
|
+
|
|
2752
2893
|
// Re-render kanban to add/remove QA contract details
|
|
2753
2894
|
renderKanban();
|
|
2754
2895
|
|
|
@@ -2840,9 +2981,9 @@ async function checkGettingStarted() {
|
|
|
2840
2981
|
const hasTasks = (health.tasks?.total || 0) > 0;
|
|
2841
2982
|
const hasMessages = (health.chat?.total || 0) > 0;
|
|
2842
2983
|
|
|
2843
|
-
// Step 1:
|
|
2984
|
+
// Step 1: server running β always done if dashboard loads
|
|
2844
2985
|
const step1 = document.getElementById('gs-preflight');
|
|
2845
|
-
if (step1
|
|
2986
|
+
if (step1) {
|
|
2846
2987
|
step1.classList.add('done');
|
|
2847
2988
|
step1.querySelector('.gs-icon').textContent = 'β';
|
|
2848
2989
|
}
|