@comput/pi-intercom 0.4.2 → 0.5.2

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/src/transport.ts CHANGED
@@ -35,9 +35,20 @@ export function envelope(value: unknown): Envelope {
35
35
  return m;
36
36
  }
37
37
  export interface Endpoint { port: number; close(): Promise<void> }
38
- export async function listen(preferred: number | undefined, accept: (message: Envelope) => Promise<void>): Promise<Endpoint> {
38
+ export async function listen(preferred: number | undefined, accept: (message: Envelope) => Promise<void>, health?: () => { version: 1; sessionId: string }): Promise<Endpoint> {
39
39
  const server = http.createServer(async (req, res) => {
40
40
  const reply = (status: number, data: unknown) => { if (!res.destroyed) { res.writeHead(status, { 'content-type': 'application/json', connection: 'close' }); res.end(JSON.stringify(data)); } };
41
+ if (req.method === 'GET' && req.url === '/intercom/health') {
42
+ try {
43
+ if (!health) reply(404, { error: 'health unavailable' });
44
+ else {
45
+ const identity = health();
46
+ if (identity.version !== 1 || typeof identity.sessionId !== 'string' || !identity.sessionId.trim() || identity.sessionId.length > 256) throw new Error('invalid health identity');
47
+ reply(200, { version: 1, sessionId: identity.sessionId });
48
+ }
49
+ } catch { reply(503, { error: 'health unavailable' }); }
50
+ req.resume(); return;
51
+ }
41
52
  if (req.method !== 'POST' || req.url !== '/intercom') { reply(404, { error: 'POST /intercom required' }); req.resume(); return; }
42
53
  let bytes = 0;
43
54
  const chunks: Buffer[] = [];
package/src/version.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { readFileSync } from 'node:fs';
2
+
3
+ // Both src/ and dist/ sit directly below this package's own manifest. Capture
4
+ // once at process startup, not from cwd or on each render after an update.
5
+ function packageVersion(): string {
6
+ try {
7
+ const manifest = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
8
+ const version: unknown = manifest.version;
9
+ if (typeof version === 'string' && version.length <= 64 && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(version)) return version;
10
+ } catch { /* A display label must never prevent the monitor starting. */ }
11
+ return 'unknown';
12
+ }
13
+ export const INTERCOM_VERSION = packageVersion();
@@ -1,4 +1,5 @@
1
1
  import type { ObservationSnapshot } from './snapshot.js';
2
+ import { currentConnection, sortWorkersByConnection } from './connections.js';
2
3
 
3
4
  const labels: Record<string, string> = {
4
5
  processing: 'Processing', thinking: 'Thinking', responding: 'Writing response',
@@ -46,15 +47,16 @@ export function workerStatusPage(snapshot: ObservationSnapshot, options: { name?
46
47
  roster = roster.filter(a => a.name.toLowerCase() === options.name!.toLowerCase());
47
48
  if (snapshot.config && !roster.length) throw new Error('Worker name not found in the retained snapshot');
48
49
  }
49
- const workers: Array<Record<string, unknown>> = [];
50
+ const workers: Array<{ sessionId: string; [key: string]: unknown }> = [];
50
51
  const result = { version: 1, source: 'local-observations', generatedAt: new Date(now).toISOString(),
51
- statusMeaning: 'Last observed, not live liveness or task completion. Reports are self-reported, not approval.',
52
+ statusMeaning: 'Activity is last observed, not task completion. Connectivity is a separate endpoint check, never proof of termination. Reports are self-reported, not approval.',
52
53
  truncated: snapshot.truncated, errors: snapshot.errors, totalInSnapshot: roster.length, offset,
53
54
  nextOffset: null as number | null, workers };
54
55
  for (const agent of roster.slice(offset, offset + limit)) {
55
56
  const report = snapshot.reports?.find(r => r.sessionId === agent.sessionId && r.status !== 'clear');
56
57
  const reportAge = report ? now - Date.parse(report.updatedAt) : NaN;
57
58
  const entry = { sessionId: agent.sessionId, name: agent.name, ...workerObservation(snapshot, agent.sessionId, now),
59
+ connection: currentConnection(agent.sessionId, snapshot.connections, now),
58
60
  report: report ? { status: report.status, summary: report.summary, updatedAt: report.updatedAt,
59
61
  ageSeconds: Number.isFinite(reportAge) && reportAge >= 0 ? Math.floor(reportAge / 1000) : null,
60
62
  selfReported: true } : null };
@@ -62,5 +64,7 @@ export function workerStatusPage(snapshot: ObservationSnapshot, options: { name?
62
64
  if (Buffer.byteLength(JSON.stringify(result, null, 2)) > 40000) { workers.pop(); break; }
63
65
  }
64
66
  if (offset + workers.length < roster.length) result.nextOffset = offset + workers.length;
67
+ // Page boundaries remain in saved roster order; disconnected entries sort last within the page.
68
+ workers.splice(0, workers.length, ...sortWorkersByConnection(workers, snapshot.connections, now));
65
69
  return result;
66
70
  }