instar 1.3.490 → 1.3.491
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/index.html +30 -0
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +146 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/GuardPostureStore.d.ts +38 -0
- package/dist/core/GuardPostureStore.d.ts.map +1 -0
- package/dist/core/GuardPostureStore.js +87 -0
- package/dist/core/GuardPostureStore.js.map +1 -0
- package/dist/core/MachinePoolRegistry.d.ts +16 -0
- package/dist/core/MachinePoolRegistry.d.ts.map +1 -1
- package/dist/core/MachinePoolRegistry.js +24 -1
- package/dist/core/MachinePoolRegistry.js.map +1 -1
- package/dist/core/PeerPresencePuller.d.ts +9 -0
- package/dist/core/PeerPresencePuller.d.ts.map +1 -1
- package/dist/core/PeerPresencePuller.js +1 -1
- package/dist/core/PeerPresencePuller.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +30 -2
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/types.d.ts +29 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/ActiveWorkSilenceSentinel.d.ts +8 -0
- package/dist/monitoring/ActiveWorkSilenceSentinel.d.ts.map +1 -1
- package/dist/monitoring/ActiveWorkSilenceSentinel.js +8 -0
- package/dist/monitoring/ActiveWorkSilenceSentinel.js.map +1 -1
- package/dist/monitoring/ContextWedgeSentinel.d.ts +8 -0
- package/dist/monitoring/ContextWedgeSentinel.d.ts.map +1 -1
- package/dist/monitoring/ContextWedgeSentinel.js +8 -0
- package/dist/monitoring/ContextWedgeSentinel.js.map +1 -1
- package/dist/monitoring/GuardPostureTripwire.d.ts +3 -27
- package/dist/monitoring/GuardPostureTripwire.d.ts.map +1 -1
- package/dist/monitoring/GuardPostureTripwire.js +8 -76
- package/dist/monitoring/GuardPostureTripwire.js.map +1 -1
- package/dist/monitoring/GuardRegistry.d.ts +48 -0
- package/dist/monitoring/GuardRegistry.d.ts.map +1 -0
- package/dist/monitoring/GuardRegistry.js +49 -0
- package/dist/monitoring/GuardRegistry.js.map +1 -0
- package/dist/monitoring/SessionReaper.d.ts +7 -0
- package/dist/monitoring/SessionReaper.d.ts.map +1 -1
- package/dist/monitoring/SessionReaper.js +9 -0
- package/dist/monitoring/SessionReaper.js.map +1 -1
- package/dist/monitoring/SessionWatchdog.d.ts +8 -0
- package/dist/monitoring/SessionWatchdog.d.ts.map +1 -1
- package/dist/monitoring/SessionWatchdog.js +8 -0
- package/dist/monitoring/SessionWatchdog.js.map +1 -1
- package/dist/monitoring/SocketDisconnectSentinel.d.ts +8 -0
- package/dist/monitoring/SocketDisconnectSentinel.d.ts.map +1 -1
- package/dist/monitoring/SocketDisconnectSentinel.js +8 -0
- package/dist/monitoring/SocketDisconnectSentinel.js.map +1 -1
- package/dist/monitoring/guardManifest.d.ts +67 -0
- package/dist/monitoring/guardManifest.d.ts.map +1 -0
- package/dist/monitoring/guardManifest.js +536 -0
- package/dist/monitoring/guardManifest.js.map +1 -0
- package/dist/monitoring/guardPosture.d.ts +77 -0
- package/dist/monitoring/guardPosture.d.ts.map +1 -0
- package/dist/monitoring/guardPosture.js +198 -0
- package/dist/monitoring/guardPosture.js.map +1 -0
- package/dist/monitoring/guardPostureView.d.ts +100 -0
- package/dist/monitoring/guardPostureView.d.ts.map +1 -0
- package/dist/monitoring/guardPostureView.js +294 -0
- package/dist/monitoring/guardPostureView.js.map +1 -0
- package/dist/monitoring/probes/GuardPostureProbe.d.ts +82 -0
- package/dist/monitoring/probes/GuardPostureProbe.d.ts.map +1 -0
- package/dist/monitoring/probes/GuardPostureProbe.js +384 -0
- package/dist/monitoring/probes/GuardPostureProbe.js.map +1 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +9 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/scheduler/JobScheduler.d.ts +10 -0
- package/dist/scheduler/JobScheduler.d.ts.map +1 -1
- package/dist/scheduler/JobScheduler.js +12 -0
- package/dist/scheduler/JobScheduler.js.map +1 -1
- package/dist/server/AgentServer.d.ts +8 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +2 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +11 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/peerUrlGuard.d.ts +35 -0
- package/dist/server/peerUrlGuard.d.ts.map +1 -0
- package/dist/server/peerUrlGuard.js +93 -0
- package/dist/server/peerUrlGuard.js.map +1 -0
- package/dist/server/routes.d.ts +14 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +136 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +3 -2
- package/scripts/lint-guard-manifest.js +264 -0
- package/src/data/builtin-manifest.json +65 -65
- package/src/data/state-coherence-registry.json +54 -18
- package/src/scaffold/templates.ts +9 -0
- package/upgrades/1.3.491.md +37 -0
- package/upgrades/side-effects/guard-posture-endpoint.md +101 -0
package/dashboard/index.html
CHANGED
|
@@ -4976,6 +4976,35 @@
|
|
|
4976
4976
|
if (m.loadAvg < 4) return 'working';
|
|
4977
4977
|
return 'busy';
|
|
4978
4978
|
}
|
|
4979
|
+
function machineGuardsLine(m) {
|
|
4980
|
+
// Guard posture (GUARD-POSTURE-ENDPOINT-SPEC §2.3): last-known summary
|
|
4981
|
+
// with RECEIVER-side age. No posture ever received → honest "unknown",
|
|
4982
|
+
// never "0 on / 0 off". Only problem classes get named; dark-default
|
|
4983
|
+
// offs stay quiet (alarm-fatigue is how the next Mini stays off while
|
|
4984
|
+
// displayed).
|
|
4985
|
+
const p = m.guardPosture;
|
|
4986
|
+
if (!p) return 'guards: unknown';
|
|
4987
|
+
const problems = [];
|
|
4988
|
+
if (p.offDeviant) problems.push(p.offDeviant + ' off that should be on');
|
|
4989
|
+
if (p.offRuntimeDivergent) problems.push(p.offRuntimeDivergent + ' off despite settings');
|
|
4990
|
+
if (p.onStale) problems.push(p.onStale + ' frozen');
|
|
4991
|
+
if (p.missing) problems.push(p.missing + ' missing');
|
|
4992
|
+
if (p.errored) problems.push(p.errored + ' erroring');
|
|
4993
|
+
if (p.divergedPendingRestart) problems.push(p.divergedPendingRestart + ' awaiting restart');
|
|
4994
|
+
const okPart = 'guards: ' + ((p.onConfirmed ?? 0) + (p.onUnverified ?? 0) + (p.onDryRun ?? 0)) + ' on' +
|
|
4995
|
+
(p.onConfirmed ? ' (' + p.onConfirmed + ' confirmed)' : '');
|
|
4996
|
+
let age = '';
|
|
4997
|
+
if (m.guardPostureReceivedAt) {
|
|
4998
|
+
const ms = Date.now() - Date.parse(m.guardPostureReceivedAt);
|
|
4999
|
+
if (isFinite(ms) && ms >= 0) {
|
|
5000
|
+
const mins = Math.round(ms / 60000);
|
|
5001
|
+
age = mins < 2 ? 'just now' : mins < 90 ? 'as of ' + mins + 'm ago'
|
|
5002
|
+
: mins < 2880 ? 'as of ' + Math.round(mins / 60) + 'h ago'
|
|
5003
|
+
: 'as of ' + Math.round(mins / 1440) + 'd ago';
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
return [okPart, problems.length ? '⚠ ' + problems.join(', ') : '', age].filter(Boolean).join(' · ');
|
|
5007
|
+
}
|
|
4979
5008
|
function renderMachineCard(m, routerHolder) {
|
|
4980
5009
|
const online = !!m.online;
|
|
4981
5010
|
const dot = !online ? 'off' : (m.clockSkewStatus === 'suspect-clock-removed' ? 'warn' : 'ok');
|
|
@@ -4994,6 +5023,7 @@
|
|
|
4994
5023
|
'</div>' +
|
|
4995
5024
|
'<div class="machine-meta">' + escapeHtml(machineSpecLine(m.hardware)) + '</div>' +
|
|
4996
5025
|
'<div class="machine-meta">' + tail + '</div>' +
|
|
5026
|
+
'<div class="machine-meta">' + escapeHtml(machineGuardsLine(m)) + '</div>' +
|
|
4997
5027
|
'</div>';
|
|
4998
5028
|
}
|
|
4999
5029
|
async function saveMachineNickname(input) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgCH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAS3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAuBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgCH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAS3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAuBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAyG7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAsBtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC1C,OAAO,CAUT;AAyID,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAmyCD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,cAAc,EAC9B,YAAY,CAAC,EAAE,YAAY,EAC3B,WAAW,CAAC,EAAE,WAAW,EACzB,WAAW,CAAC,EAAE,WAAW,EACzB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EAGvE,UAAU,CAAC,EAAE,MAAM,OAAO,8BAA8B,EAAE,WAAW,GAAG,IAAI,EAK5E,qBAAqB,CAAC,EAAE,MAAM,OAAO,gCAAgC,EAAE,kBAAkB,GAAG,IAAI,EAKhG,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GACpD,IAAI,CAwVN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAmrWtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
|
package/dist/commands/server.js
CHANGED
|
@@ -65,6 +65,12 @@ import { QuotaNotifier } from '../monitoring/QuotaNotifier.js';
|
|
|
65
65
|
import { QuotaManager } from '../monitoring/QuotaManager.js';
|
|
66
66
|
import { classifySessionDeath, detectContextExhaustion } from '../monitoring/QuotaExhaustionDetector.js';
|
|
67
67
|
import { SessionWatchdog } from '../monitoring/SessionWatchdog.js';
|
|
68
|
+
import { GuardRegistry } from '../monitoring/GuardRegistry.js';
|
|
69
|
+
import { resolveGuardConfigSnapshot, readGuardPostureBootSnapshot } from '../monitoring/guardPosture.js';
|
|
70
|
+
import { buildGuardInventory, buildHeartbeatPostureBlock } from '../monitoring/guardPostureView.js';
|
|
71
|
+
import { createGuardPostureProbes } from '../monitoring/probes/GuardPostureProbe.js';
|
|
72
|
+
import { GuardPostureStore } from '../core/GuardPostureStore.js';
|
|
73
|
+
import { isPeerUrlAllowedForCredentials } from '../server/peerUrlGuard.js';
|
|
68
74
|
import { formatWatchdogUserMessage } from '../monitoring/watchdog-notifications.js';
|
|
69
75
|
import { StallTriageNurse } from '../monitoring/StallTriageNurse.js';
|
|
70
76
|
import { TriageOrchestrator } from '../monitoring/TriageOrchestrator.js';
|
|
@@ -372,6 +378,7 @@ let _resolveRouterUrl = null;
|
|
|
372
378
|
/** Every OTHER active machine with a known URL — backs GET /sessions?scope=pool
|
|
373
379
|
* (pool-wide session aggregation for the dashboard). Set in the same mesh block. */
|
|
374
380
|
let _resolvePeerUrls = null;
|
|
381
|
+
let _listPoolMachines = null;
|
|
375
382
|
/** Multi-Machine Session Pool §L4: per-topic placement pin store ("move this to <nickname>"). */
|
|
376
383
|
let _topicPinStore = null;
|
|
377
384
|
/** Cross-machine secret-sync (spec Phase 4): route-facing handle (push lever + read-only status). */
|
|
@@ -4029,9 +4036,15 @@ export async function startServer(options) {
|
|
|
4029
4036
|
: 'detect-only';
|
|
4030
4037
|
console.log(pc.green(` Prompt Gate: enabled (mode: ${mode})`));
|
|
4031
4038
|
}
|
|
4039
|
+
// ── GuardRegistry (GUARD-POSTURE-ENDPOINT-SPEC §2.1) ──────────────
|
|
4040
|
+
// Constructed guard components self-register SYNC runtime getters at
|
|
4041
|
+
// their construction sites below; GET /guards reconciles registrations
|
|
4042
|
+
// against the declared manifest (missing = a state, never an omission).
|
|
4043
|
+
const guardRegistry = new GuardRegistry();
|
|
4032
4044
|
let scheduler;
|
|
4033
4045
|
if (config.scheduler.enabled && coordinator.isAwake) {
|
|
4034
4046
|
scheduler = new JobScheduler(config.scheduler, sessionManager, state, config.stateDir);
|
|
4047
|
+
guardRegistry.register('scheduler.enabled', () => scheduler.guardStatus());
|
|
4035
4048
|
// Wire machine identity for machine-scoped job filtering
|
|
4036
4049
|
if (coordinator.identity) {
|
|
4037
4050
|
scheduler.setMachineIdentity(coordinator.identity.machineId, coordinator.identity.name);
|
|
@@ -5805,6 +5818,7 @@ export async function startServer(options) {
|
|
|
5805
5818
|
if (config.monitoring.watchdog?.enabled) {
|
|
5806
5819
|
watchdog = new SessionWatchdog(config, sessionManager, state);
|
|
5807
5820
|
watchdog.intelligence = sharedIntelligence ?? null;
|
|
5821
|
+
guardRegistry.register('monitoring.watchdog.enabled', () => watchdog.guardStatus());
|
|
5808
5822
|
watchdog.on('intervention', (event) => {
|
|
5809
5823
|
// Routine recovery (Ctrl+C, SIGTERM) stays as console diagnostics only.
|
|
5810
5824
|
// The user only hears when we had to force-kill (SIGKILL / session-kill) —
|
|
@@ -6990,6 +7004,7 @@ export async function startServer(options) {
|
|
|
6990
7004
|
socketSentinel.on('recovered', (n) => notifier.record('recovered', 'socket-disconnect', n));
|
|
6991
7005
|
socketSentinel.on('recovery-error', (e) => notifier.record('recovery-error', 'socket-disconnect', e.sessionName, e.err instanceof Error ? e.err.message : String(e.err)));
|
|
6992
7006
|
socketSentinel.start();
|
|
7007
|
+
guardRegistry.register('monitoring.socketDisconnectSentinel.enabled', () => socketSentinel.guardStatus());
|
|
6993
7008
|
socketRecoveryActive = (s) => socketSentinel.isRecoveryActive(s);
|
|
6994
7009
|
console.log(pc.green(' SocketDisconnectSentinel enabled (connection-drop recovery)'));
|
|
6995
7010
|
}
|
|
@@ -7044,6 +7059,7 @@ export async function startServer(options) {
|
|
|
7044
7059
|
silenceSentinel.on('recover-error', (e) => notifier.record('recovery-error', 'active-silence', e.sessionName, e.err instanceof Error ? e.err.message : String(e.err)));
|
|
7045
7060
|
silenceSentinel.on('nudge-error', (e) => notifier.record('nudge-error', 'active-silence', e.sessionName, e.err instanceof Error ? e.err.message : String(e.err)));
|
|
7046
7061
|
silenceSentinel.start();
|
|
7062
|
+
guardRegistry.register('monitoring.activeWorkSilenceSentinel.enabled', () => silenceSentinel.guardStatus());
|
|
7047
7063
|
silenceRecoveryActive = (s) => silenceSentinel.isRecoveryActive(s);
|
|
7048
7064
|
const silenceMode = silenceAutoRecover ? ' — auto-heal LIVE' : '';
|
|
7049
7065
|
console.log(pc.green(telegramEscalation
|
|
@@ -7086,6 +7102,16 @@ export async function startServer(options) {
|
|
|
7086
7102
|
wedgeSentinel.on('false-alarm', (e) => notifier.record('false-alarm', 'context-wedge', e.sessionName, 'signature scrolled out of tail'));
|
|
7087
7103
|
wedgeSentinel.on('recovery-error', (e) => notifier.record('recovery-error', 'context-wedge', e.sessionName, e.err instanceof Error ? e.err.message : String(e.err)));
|
|
7088
7104
|
wedgeSentinel.start();
|
|
7105
|
+
guardRegistry.register('monitoring.contextWedgeSentinel.enabled', () => wedgeSentinel.guardStatus());
|
|
7106
|
+
// Sub-guard row (spec §2.1): the destructive auto-recovery arm reports
|
|
7107
|
+
// its OWN posture so 'autoRecovery silently off inside an on-confirmed
|
|
7108
|
+
// sentinel' cannot hide.
|
|
7109
|
+
guardRegistry.register('monitoring.contextWedgeSentinel.autoRecovery.enabled', () => ({
|
|
7110
|
+
enabled: autoRecovery.enabled === true,
|
|
7111
|
+
dryRun: autoRecovery.dryRun !== false,
|
|
7112
|
+
// Deliberately no lastTickAt: the autoRecovery arm is config-derived
|
|
7113
|
+
// (no tick loop of its own); the parent sentinel row carries liveness.
|
|
7114
|
+
}));
|
|
7089
7115
|
wedgeRecoveryActive = (s) => wedgeSentinel.isRecoveryActive(s);
|
|
7090
7116
|
const mode = autoRecovery.enabled ? (autoRecovery.dryRun ? 'auto-recover dry-run' : 'auto-recover LIVE') : 'detect-only';
|
|
7091
7117
|
console.log(pc.green(` ContextWedgeSentinel enabled (thinking-block-400 + aup-rejection wedges — ${mode})`));
|
|
@@ -9230,6 +9256,68 @@ export async function startServer(options) {
|
|
|
9230
9256
|
lockFilePath: path.join(config.stateDir, 'lifeline.lock'),
|
|
9231
9257
|
isEnabled: () => fs.existsSync(path.join(config.stateDir, 'lifeline.lock')),
|
|
9232
9258
|
}),
|
|
9259
|
+
...createGuardPostureProbes({
|
|
9260
|
+
// Same one-read inventory GET /guards serves; null on read failure.
|
|
9261
|
+
getLocalPosture: () => {
|
|
9262
|
+
try {
|
|
9263
|
+
const snap = resolveGuardConfigSnapshot(config.projectDir);
|
|
9264
|
+
if (snap.readError)
|
|
9265
|
+
return null;
|
|
9266
|
+
return buildGuardInventory({
|
|
9267
|
+
snapshot: snap,
|
|
9268
|
+
bootSnapshot: readGuardPostureBootSnapshot(config.stateDir),
|
|
9269
|
+
registry: guardRegistry,
|
|
9270
|
+
});
|
|
9271
|
+
}
|
|
9272
|
+
catch {
|
|
9273
|
+
return null; /* @silent-fallback-ok — probe degrades to no-local-posture for this tick; the route surfaces config errors loudly */
|
|
9274
|
+
}
|
|
9275
|
+
},
|
|
9276
|
+
// Heartbeat-sourced (durable last-known for offline peers) — never a
|
|
9277
|
+
// doomed fan-out for a dark peer (spec §2.4 data-source rule).
|
|
9278
|
+
getPeerPostures: () => {
|
|
9279
|
+
try {
|
|
9280
|
+
return (machinePoolRegistry?.getCapacities() ?? [])
|
|
9281
|
+
.filter((m) => m.machineId !== (_meshSelfId ?? ''))
|
|
9282
|
+
.map((m) => ({
|
|
9283
|
+
machineId: m.machineId,
|
|
9284
|
+
nickname: m.nickname,
|
|
9285
|
+
online: m.online,
|
|
9286
|
+
posture: m.guardPosture ?? null,
|
|
9287
|
+
postureAgeMs: m.guardPostureReceivedAt ? Date.now() - Date.parse(m.guardPostureReceivedAt) : null,
|
|
9288
|
+
}));
|
|
9289
|
+
}
|
|
9290
|
+
catch {
|
|
9291
|
+
return []; /* @silent-fallback-ok — pool not wired yet (boot order): peers none this tick, next tick reads the live registry */
|
|
9292
|
+
}
|
|
9293
|
+
},
|
|
9294
|
+
// Spec §2.4 deep-read fallback: ONLY for an ONLINE peer whose
|
|
9295
|
+
// heartbeat posture block is missing/stale — a plain GET /guards
|
|
9296
|
+
// (never ?scope=pool), token attached only past the URL guard.
|
|
9297
|
+
deepReadPeer: async (machineId) => {
|
|
9298
|
+
if (!_listPoolMachines)
|
|
9299
|
+
return null; // pool not wired on this host — explicit, not incidental
|
|
9300
|
+
const m = _listPoolMachines().find((x) => x.machineId === machineId);
|
|
9301
|
+
if (!m?.lastKnownUrl)
|
|
9302
|
+
return null;
|
|
9303
|
+
const extra = config.multiMachine?.peerUrlAllowlist;
|
|
9304
|
+
if (!isPeerUrlAllowedForCredentials(m.lastKnownUrl, extra).ok)
|
|
9305
|
+
return null;
|
|
9306
|
+
const r = await fetch(`${m.lastKnownUrl}/guards`, {
|
|
9307
|
+
headers: { Authorization: `Bearer ${config.authToken}` },
|
|
9308
|
+
signal: AbortSignal.timeout(5000),
|
|
9309
|
+
});
|
|
9310
|
+
if (!r.ok)
|
|
9311
|
+
return null;
|
|
9312
|
+
return r.json();
|
|
9313
|
+
},
|
|
9314
|
+
emitAttention: async (item) => {
|
|
9315
|
+
if (!telegram)
|
|
9316
|
+
return;
|
|
9317
|
+
await telegram.createAttentionItem(item);
|
|
9318
|
+
},
|
|
9319
|
+
stateDir: config.stateDir,
|
|
9320
|
+
}),
|
|
9233
9321
|
...createPlatformProbes({
|
|
9234
9322
|
tmuxPath,
|
|
9235
9323
|
}),
|
|
@@ -10755,6 +10843,7 @@ export async function startServer(options) {
|
|
|
10755
10843
|
console.log(pc.green(' Unkillability backstop enabled (§P5 — signal-only, never auto-kills)'));
|
|
10756
10844
|
}
|
|
10757
10845
|
}
|
|
10846
|
+
guardRegistry.register('monitoring.sessionReaper.enabled', () => sessionReaper.guardStatus());
|
|
10758
10847
|
if (config.monitoring?.sessionReaper?.enabled) {
|
|
10759
10848
|
console.log(pc.green(config.monitoring.sessionReaper.dryRun === false
|
|
10760
10849
|
? ' SessionReaper enabled (idle-session reaper — LIVE)'
|
|
@@ -10936,6 +11025,9 @@ export async function startServer(options) {
|
|
|
10936
11025
|
const failoverThresholdMs = (config.multiMachine?.failoverTimeoutMinutes ?? 15) * 60_000;
|
|
10937
11026
|
const clockSkewToleranceMs = config.multiMachine?.sessionPool?.clockSkewToleranceMs ?? 300_000;
|
|
10938
11027
|
machinePoolRegistry = new poolMod.MachinePoolRegistry({
|
|
11028
|
+
// Durable last-known posture (GUARD-POSTURE-ENDPOINT-SPEC §2.3(c)) —
|
|
11029
|
+
// survives local restarts so a dark peer renders with its real age.
|
|
11030
|
+
postureStore: new GuardPostureStore(config.stateDir),
|
|
10939
11031
|
listMachines: () => poolIdMgr.getActiveMachines().map(({ machineId, entry }) => ({
|
|
10940
11032
|
machineId,
|
|
10941
11033
|
nickname: entry.nickname,
|
|
@@ -10978,6 +11070,47 @@ export async function startServer(options) {
|
|
|
10978
11070
|
return undefined; /* unknown ≠ blocked */
|
|
10979
11071
|
}
|
|
10980
11072
|
};
|
|
11073
|
+
// Self guard-posture block riding the capacity heartbeat (spec §2.3).
|
|
11074
|
+
// Computed per beat from the same one-read snapshot GET /guards uses;
|
|
11075
|
+
// a failed compute omits the block (older-peer semantics), never throws.
|
|
11076
|
+
// The resolved-config snapshot is the expensive half (defaults clone
|
|
11077
|
+
// + deep merge); cache it keyed on config.json mtime so the 30s
|
|
11078
|
+
// beat pays one cheap fs.stat instead (perf review 2026-06-12 #1).
|
|
11079
|
+
// The INVENTORY still rebuilds every beat — runtime states
|
|
11080
|
+
// (lastTickAt staleness, self-reported enabled) must stay live.
|
|
11081
|
+
let _postureComputeWarned = false;
|
|
11082
|
+
let _postureSnapCache = null;
|
|
11083
|
+
const selfGuardPosture = () => {
|
|
11084
|
+
try {
|
|
11085
|
+
let mtimeMs = -1;
|
|
11086
|
+
try {
|
|
11087
|
+
mtimeMs = fs.statSync(path.join(config.stateDir, 'config.json')).mtimeMs;
|
|
11088
|
+
}
|
|
11089
|
+
catch { /* @silent-fallback-ok — absent config file: mtime -1 still caches the defaults-only snapshot */ }
|
|
11090
|
+
if (!_postureSnapCache || _postureSnapCache.mtimeMs !== mtimeMs) {
|
|
11091
|
+
_postureSnapCache = { mtimeMs, snap: resolveGuardConfigSnapshot(config.projectDir) };
|
|
11092
|
+
}
|
|
11093
|
+
const snap = _postureSnapCache.snap;
|
|
11094
|
+
if (snap.readError)
|
|
11095
|
+
return undefined;
|
|
11096
|
+
const inv = buildGuardInventory({
|
|
11097
|
+
snapshot: snap,
|
|
11098
|
+
bootSnapshot: readGuardPostureBootSnapshot(config.stateDir),
|
|
11099
|
+
registry: guardRegistry,
|
|
11100
|
+
});
|
|
11101
|
+
return buildHeartbeatPostureBlock(inv, new Date().toISOString());
|
|
11102
|
+
}
|
|
11103
|
+
catch (err) {
|
|
11104
|
+
// @silent-fallback-ok — posture is optional on a beat (the pool
|
|
11105
|
+
// renders "unknown" honestly), and a PERSISTENT compute failure
|
|
11106
|
+
// is not invisible: the first occurrence logs below.
|
|
11107
|
+
if (!_postureComputeWarned) {
|
|
11108
|
+
_postureComputeWarned = true;
|
|
11109
|
+
console.log(pc.yellow(` [guards] heartbeat posture compute failed (beats will omit posture until it recovers): ${err instanceof Error ? err.message : String(err)}`));
|
|
11110
|
+
}
|
|
11111
|
+
return undefined;
|
|
11112
|
+
}
|
|
11113
|
+
};
|
|
10981
11114
|
const refreshPool = () => {
|
|
10982
11115
|
try {
|
|
10983
11116
|
machinePoolRegistry.recordHeartbeat({
|
|
@@ -10985,6 +11118,7 @@ export async function startServer(options) {
|
|
|
10985
11118
|
selfReportedLastSeen: new Date().toISOString(),
|
|
10986
11119
|
loadAvg: osMod.loadavg()[0],
|
|
10987
11120
|
quotaState: selfQuotaState(),
|
|
11121
|
+
guardPosture: selfGuardPosture(),
|
|
10988
11122
|
});
|
|
10989
11123
|
const hbApi = machineHeartbeat?.api;
|
|
10990
11124
|
if (hbApi) {
|
|
@@ -11427,6 +11561,14 @@ export async function startServer(options) {
|
|
|
11427
11561
|
.getActiveMachines()
|
|
11428
11562
|
.filter((m) => m.machineId !== meshSelfId && !!m.entry.lastKnownUrl)
|
|
11429
11563
|
.map((m) => ({ machineId: m.machineId, url: m.entry.lastKnownUrl }));
|
|
11564
|
+
// EVERY registered non-revoked machine — URL or not — so the /guards
|
|
11565
|
+
// pool view can account for each by name (no-known-url is a NAMED row,
|
|
11566
|
+
// never a silent omission — GUARD-POSTURE-ENDPOINT-SPEC §2.3).
|
|
11567
|
+
_listPoolMachines = () => meshIdMgr.getActiveMachines().map((m) => ({
|
|
11568
|
+
machineId: m.machineId,
|
|
11569
|
+
nickname: m.entry.nickname,
|
|
11570
|
+
lastKnownUrl: m.entry.lastKnownUrl ?? null,
|
|
11571
|
+
}));
|
|
11430
11572
|
// Pool Dashboard Streaming requesting side (§2.2): build the connector
|
|
11431
11573
|
// the WebSocketManager uses to open an upstream /pool-stream to a peer.
|
|
11432
11574
|
// connect() is synchronous (PeerStreamProxy contract) but the mint +
|
|
@@ -12203,6 +12345,9 @@ export async function startServer(options) {
|
|
|
12203
12345
|
journalAdvert,
|
|
12204
12346
|
...(cap.commitmentsAdvert ? { commitmentsAdvert: cap.commitmentsAdvert } : {}),
|
|
12205
12347
|
...(cap.quotaState ? { quotaState: cap.quotaState } : {}),
|
|
12348
|
+
// Guard posture rides the same pass-through (the A2 narrowing
|
|
12349
|
+
// lesson): dropping it here would blind the pool to peers' posture.
|
|
12350
|
+
...(cap.guardPosture ? { guardPosture: cap.guardPosture } : {}),
|
|
12206
12351
|
};
|
|
12207
12352
|
}
|
|
12208
12353
|
return null;
|
|
@@ -12496,7 +12641,7 @@ export async function startServer(options) {
|
|
|
12496
12641
|
carrier: _topicProfileCarrier,
|
|
12497
12642
|
}
|
|
12498
12643
|
: null;
|
|
12499
|
-
const server = new AgentServer({ config, sessionManager, state, scheduler, telegram, relationships, feedback, feedbackAnomalyDetector, dispatches, updateChecker, autoUpdater, autoDispatcher, quotaTracker, quotaManager, publisher, viewer, tunnel, evolution, watchdog, topicMemory, triageNurse, projectMapper, cartographer: cartographer ?? undefined, coherenceGate: scopeVerifier, contextHierarchy, canonicalState, operationGate, sentinel, adaptiveTrust, memoryMonitor, orphanReaper, coherenceMonitor, commitmentTracker, subscriptionPool, quotaPoller, quotaAwareScheduler: _quotaAwareScheduler ?? undefined, proactiveSwapMonitor: _proactiveSwapMonitor ?? undefined, inUseAccountResolver, enrollmentWizard, semanticMemory, activitySentinel, rateLimitSentinel, releaseReadinessSentinel: releaseReadinessSentinel ?? undefined, messageRouter, summarySentinel, spawnManager, systemReviewer, capabilityMapper, selfKnowledgeTree, coverageAuditor, topicResumeMap: _topicResumeMap ?? undefined, topicProfile: _topicProfileCtx ?? undefined, sessionRefresh: _sessionRefresh ?? undefined, autonomyManager, trustElevationTracker, autonomousEvolution, coordinator: coordinator.enabled ? coordinator : undefined, localSigningKeyPem, leaseTransport, onLeasePullRequest: () => leaseCoordinatorRef?.currentLease() ?? null, liveTailReceiver, handoffWireTransport, onHandoffBegin, onHandoffInitiate: handoffInitiate, handoffInProgress: handoffSentinelInProgress, messageLedger, currentInboundByTopic, replyMarkerTransport, onReplyMarker: messageLedger ? (marker) => { const m = marker; messageLedger.applyRemoteReplyMarker(m.dedupeKey, { platform: m.platform, replyIdempotencyKey: m.replyIdempotencyKey, epoch: m.epoch, topic: m.topic ?? null }); } : undefined, whatsapp: whatsappAdapter, slack: slackAdapter, imessage: imessageAdapter, whatsappBusinessBackend, messageBridge, hookEventReceiver, worktreeMonitor, subagentTracker, instructionsVerifier, handshakeManager: threadlineHandshake, threadlineRouter, conversationStore, warrantsReplyGate, collaborationSurfacer, threadResumeMap, topicLinkageHandler: topicLinkageHandler ?? undefined, threadlineRelayClient, threadlineReplyWaiters, listenerManager: listenerManager ?? undefined, a2aDeliveryTracker: a2aDeliveryTracker ?? undefined, responseReviewGate, messagingToneGate, outboundDedupGate, telemetryHeartbeat, pasteManager, featureRegistry, discoveryEvaluator, completionEvaluator, unifiedTrust, liveConfig, sharedStateLedger, ledgerSessionRegistry, worktreeManager, oidcEnrolledRepos: parallelDevConfig?.oidcEnrolledRepos, initiativeTracker, projectRoundRunner, projectDriftChecker, machineHeartbeat, machinePoolRegistry, meshRpcDispatcher, workingSetPullCoordinator, commitmentReplicaStore, forwardCommitmentMutate, sessionOwnershipRegistry, topicPinStore: _topicPinStore ?? undefined, streamTicketStore: _streamTicketStore ?? undefined, poolStreamAllowRemoteInput: config.dashboard?.poolStream?.allowRemoteInput ?? false, poolStreamConnector: _poolStreamConnector ?? undefined, secretSync: _secretSyncHandle ?? undefined, meshSelfId: _meshSelfId ?? undefined, resolveRouterUrl: _resolveRouterUrl ?? undefined, resolvePeerUrls: _resolvePeerUrls ?? undefined, sessionPoolE2EResultStore, proxyCoordinator, topicIntentStore, topicIntentArcCheck, usherSignalStore, intelligence: sharedIntelligence ?? undefined, telegramBridgeConfig, telegramBridge: telegramBridge ?? undefined, threadlineObservability, briefDeps, workingMemory, taskFlowRegistry, threadlineFlowBridge, sessionReaper, agentWorktreeReaper, mcpProcessReaper, geminiLoopRunner, sleepController, agentActivityState, reapLog, sleepWakeDetector, unjustifiedStopGate, stopGateDb, stopNotifier });
|
|
12644
|
+
const server = new AgentServer({ config, sessionManager, state, scheduler, telegram, relationships, feedback, feedbackAnomalyDetector, dispatches, updateChecker, autoUpdater, autoDispatcher, quotaTracker, quotaManager, publisher, viewer, tunnel, evolution, watchdog, topicMemory, triageNurse, projectMapper, cartographer: cartographer ?? undefined, coherenceGate: scopeVerifier, contextHierarchy, canonicalState, operationGate, sentinel, adaptiveTrust, memoryMonitor, orphanReaper, coherenceMonitor, commitmentTracker, subscriptionPool, quotaPoller, quotaAwareScheduler: _quotaAwareScheduler ?? undefined, proactiveSwapMonitor: _proactiveSwapMonitor ?? undefined, inUseAccountResolver, enrollmentWizard, semanticMemory, activitySentinel, rateLimitSentinel, releaseReadinessSentinel: releaseReadinessSentinel ?? undefined, messageRouter, summarySentinel, spawnManager, systemReviewer, capabilityMapper, selfKnowledgeTree, coverageAuditor, topicResumeMap: _topicResumeMap ?? undefined, topicProfile: _topicProfileCtx ?? undefined, sessionRefresh: _sessionRefresh ?? undefined, autonomyManager, trustElevationTracker, autonomousEvolution, coordinator: coordinator.enabled ? coordinator : undefined, localSigningKeyPem, leaseTransport, onLeasePullRequest: () => leaseCoordinatorRef?.currentLease() ?? null, liveTailReceiver, handoffWireTransport, onHandoffBegin, onHandoffInitiate: handoffInitiate, handoffInProgress: handoffSentinelInProgress, messageLedger, currentInboundByTopic, replyMarkerTransport, onReplyMarker: messageLedger ? (marker) => { const m = marker; messageLedger.applyRemoteReplyMarker(m.dedupeKey, { platform: m.platform, replyIdempotencyKey: m.replyIdempotencyKey, epoch: m.epoch, topic: m.topic ?? null }); } : undefined, whatsapp: whatsappAdapter, slack: slackAdapter, imessage: imessageAdapter, whatsappBusinessBackend, messageBridge, hookEventReceiver, worktreeMonitor, subagentTracker, instructionsVerifier, handshakeManager: threadlineHandshake, threadlineRouter, conversationStore, warrantsReplyGate, collaborationSurfacer, threadResumeMap, topicLinkageHandler: topicLinkageHandler ?? undefined, threadlineRelayClient, threadlineReplyWaiters, listenerManager: listenerManager ?? undefined, a2aDeliveryTracker: a2aDeliveryTracker ?? undefined, responseReviewGate, messagingToneGate, outboundDedupGate, telemetryHeartbeat, pasteManager, featureRegistry, discoveryEvaluator, completionEvaluator, unifiedTrust, liveConfig, sharedStateLedger, ledgerSessionRegistry, worktreeManager, oidcEnrolledRepos: parallelDevConfig?.oidcEnrolledRepos, initiativeTracker, projectRoundRunner, projectDriftChecker, machineHeartbeat, machinePoolRegistry, meshRpcDispatcher, workingSetPullCoordinator, commitmentReplicaStore, forwardCommitmentMutate, sessionOwnershipRegistry, topicPinStore: _topicPinStore ?? undefined, streamTicketStore: _streamTicketStore ?? undefined, poolStreamAllowRemoteInput: config.dashboard?.poolStream?.allowRemoteInput ?? false, poolStreamConnector: _poolStreamConnector ?? undefined, secretSync: _secretSyncHandle ?? undefined, meshSelfId: _meshSelfId ?? undefined, resolveRouterUrl: _resolveRouterUrl ?? undefined, resolvePeerUrls: _resolvePeerUrls ?? undefined, guardRegistry, listPoolMachines: _listPoolMachines ?? undefined, sessionPoolE2EResultStore, proxyCoordinator, topicIntentStore, topicIntentArcCheck, usherSignalStore, intelligence: sharedIntelligence ?? undefined, telegramBridgeConfig, telegramBridge: telegramBridge ?? undefined, threadlineObservability, briefDeps, workingMemory, taskFlowRegistry, threadlineFlowBridge, sessionReaper, agentWorktreeReaper, mcpProcessReaper, geminiLoopRunner, sleepController, agentActivityState, reapLog, sleepWakeDetector, unjustifiedStopGate, stopGateDb, stopNotifier });
|
|
12500
12645
|
// Resolve the late-bound topic-operator getter (increment 2e): routing was
|
|
12501
12646
|
// wired before the server existed; from here on inbound binds use the
|
|
12502
12647
|
// server's own store instance.
|