instar 1.2.82 → 1.2.83

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.
Files changed (41) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +99 -1
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  5. package/dist/config/ConfigDefaults.js +23 -0
  6. package/dist/config/ConfigDefaults.js.map +1 -1
  7. package/dist/core/SessionManager.d.ts +43 -0
  8. package/dist/core/SessionManager.d.ts.map +1 -1
  9. package/dist/core/SessionManager.js +123 -24
  10. package/dist/core/SessionManager.js.map +1 -1
  11. package/dist/core/types.d.ts +26 -0
  12. package/dist/core/types.d.ts.map +1 -1
  13. package/dist/core/types.js.map +1 -1
  14. package/dist/monitoring/SessionReaper.d.ts +153 -0
  15. package/dist/monitoring/SessionReaper.d.ts.map +1 -0
  16. package/dist/monitoring/SessionReaper.js +376 -0
  17. package/dist/monitoring/SessionReaper.js.map +1 -0
  18. package/dist/monitoring/TokenLedger.d.ts +12 -0
  19. package/dist/monitoring/TokenLedger.d.ts.map +1 -1
  20. package/dist/monitoring/TokenLedger.js +22 -0
  21. package/dist/monitoring/TokenLedger.js.map +1 -1
  22. package/dist/monitoring/transcriptProber.d.ts +44 -0
  23. package/dist/monitoring/transcriptProber.d.ts.map +1 -0
  24. package/dist/monitoring/transcriptProber.js +57 -0
  25. package/dist/monitoring/transcriptProber.js.map +1 -0
  26. package/dist/scaffold/templates.d.ts.map +1 -1
  27. package/dist/scaffold/templates.js +6 -0
  28. package/dist/scaffold/templates.js.map +1 -1
  29. package/dist/server/AgentServer.d.ts +3 -0
  30. package/dist/server/AgentServer.d.ts.map +1 -1
  31. package/dist/server/AgentServer.js +1 -0
  32. package/dist/server/AgentServer.js.map +1 -1
  33. package/dist/server/routes.d.ts +3 -0
  34. package/dist/server/routes.d.ts.map +1 -1
  35. package/dist/server/routes.js +10 -0
  36. package/dist/server/routes.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/data/builtin-manifest.json +48 -48
  39. package/src/scaffold/templates.ts +6 -0
  40. package/upgrades/1.2.83.md +26 -0
  41. package/upgrades/side-effects/session-reaper.md +42 -0
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA0QH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAiqDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAikMtE;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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA0QH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAiqDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA6pMtE;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"}
@@ -4874,6 +4874,12 @@ export async function startServer(options) {
4874
4874
  // is OFF for Telegram by default and, when enabled, coalesces into ONE
4875
4875
  // consolidated message to the existing system topic. No new-topic-per-event.
4876
4876
  // Spec: docs/specs/silently-stopped-trio.md.
4877
+ //
4878
+ // Captured out of the trio block so the SessionReaper's recovery veto can
4879
+ // compose socket + silence in too (SESSION-REAPER-SPEC §4 "compose, don't
4880
+ // replace"). undefined when the corresponding sentinel is disabled.
4881
+ let socketRecoveryActive;
4882
+ let silenceRecoveryActive;
4877
4883
  {
4878
4884
  const { SocketDisconnectSentinel } = await import('../monitoring/SocketDisconnectSentinel.js');
4879
4885
  const { ActiveWorkSilenceSentinel } = await import('../monitoring/ActiveWorkSilenceSentinel.js');
@@ -4926,6 +4932,7 @@ export async function startServer(options) {
4926
4932
  socketSentinel.on('recovered', (n) => notifier.record('recovered', 'socket-disconnect', n));
4927
4933
  socketSentinel.on('recovery-error', (e) => notifier.record('recovery-error', 'socket-disconnect', e.sessionName, e.err instanceof Error ? e.err.message : String(e.err)));
4928
4934
  socketSentinel.start();
4935
+ socketRecoveryActive = (s) => socketSentinel.isRecoveryActive(s);
4929
4936
  console.log(pc.green(' SocketDisconnectSentinel enabled (connection-drop recovery)'));
4930
4937
  }
4931
4938
  const silenceCfg = config.monitoring?.activeWorkSilenceSentinel ?? { enabled: true };
@@ -4939,11 +4946,23 @@ export async function startServer(options) {
4939
4946
  silenceSentinel.on('recovered', (n) => notifier.record('recovered', 'active-silence', n));
4940
4947
  silenceSentinel.on('nudge-error', (e) => notifier.record('nudge-error', 'active-silence', e.sessionName, e.err instanceof Error ? e.err.message : String(e.err)));
4941
4948
  silenceSentinel.start();
4949
+ silenceRecoveryActive = (s) => silenceSentinel.isRecoveryActive(s);
4942
4950
  console.log(pc.green(telegramEscalation
4943
4951
  ? ' ActiveWorkSilenceSentinel enabled (silent-freeze watchdog — Telegram escalation ON, consolidated)'
4944
4952
  : ' ActiveWorkSilenceSentinel enabled (silent-freeze watchdog — logs only, Telegram escalation OFF)'));
4945
4953
  }
4946
4954
  }
4955
+ // Recompose the zombie-kill veto to include ALL four recovery sentinels now
4956
+ // that socket + silence exist (the interim set above covered only compaction
4957
+ // + rate-limit, before those two were constructed). This single composed
4958
+ // predicate is the superset — it drops none — and is reused as the
4959
+ // SessionReaper's recovery gate (G) so the reaper never kills a session any
4960
+ // sentinel is reviving. SESSION-REAPER-SPEC §4 "compose, don't replace".
4961
+ const composedRecoveryActive = (session) => compactionSentinel.isRecoveryActive(session.tmuxSession) ||
4962
+ rateLimitSentinel.isRecoveryActive(session.tmuxSession) ||
4963
+ (socketRecoveryActive?.(session.tmuxSession) ?? false) ||
4964
+ (silenceRecoveryActive?.(session.tmuxSession) ?? false);
4965
+ sessionManager.setActiveRecoveryChecker(composedRecoveryActive);
4947
4966
  // Trigger 1: PreCompact hook event — report to sentinel.
4948
4967
  hookEventReceiver.on('PreCompact', () => {
4949
4968
  // Delay to let compaction + recovery hooks finish
@@ -7193,7 +7212,86 @@ export async function startServer(options) {
7193
7212
  },
7194
7213
  });
7195
7214
  }
7196
- 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, coherenceGate: scopeVerifier, contextHierarchy, canonicalState, operationGate, sentinel, adaptiveTrust, memoryMonitor, orphanReaper, coherenceMonitor, commitmentTracker, semanticMemory, activitySentinel, rateLimitSentinel, messageRouter, summarySentinel, spawnManager, systemReviewer, capabilityMapper, selfKnowledgeTree, coverageAuditor, topicResumeMap: _topicResumeMap ?? undefined, sessionRefresh: _sessionRefresh ?? undefined, autonomyManager, trustElevationTracker, autonomousEvolution, coordinator: coordinator.enabled ? coordinator : undefined, localSigningKeyPem, 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, responseReviewGate, messagingToneGate, outboundDedupGate, telemetryHeartbeat, pasteManager, featureRegistry, discoveryEvaluator, completionEvaluator, unifiedTrust, liveConfig, sharedStateLedger, ledgerSessionRegistry, worktreeManager, oidcEnrolledRepos: parallelDevConfig?.oidcEnrolledRepos, initiativeTracker, projectRoundRunner, projectDriftChecker, machineHeartbeat, proxyCoordinator, topicIntentStore, usherSignalStore, intelligence: sharedIntelligence ?? undefined, telegramBridgeConfig, telegramBridge: telegramBridge ?? undefined, threadlineObservability, workingMemory, taskFlowRegistry, threadlineFlowBridge });
7215
+ // ── SessionReaper (SESSION-REAPER-SPEC) ──────────────────────────────
7216
+ // Pressure-aware reaper of idle-but-alive sessions. Ships OFF + dry-run by
7217
+ // default; the classifier's positive-evidence + confidence-contract is what
7218
+ // guarantees it never reaps a working session. Reuses composedRecoveryActive
7219
+ // (gate G) so it defers to every recovery sentinel. Pressure is freemem-tiered
7220
+ // for v1 (advisory; spawn-denial-primary is a tracked follow-up) — and note
7221
+ // an over-eager tier can only reap a GENUINELY-idle session sooner, never a
7222
+ // working one (the classifier protects working sessions regardless of tier).
7223
+ const { SessionReaper, fileAuditSink } = await import('../monitoring/SessionReaper.js');
7224
+ const _os = await import('node:os');
7225
+ const _resolveTopic = (tmuxSession) => {
7226
+ const t = telegram?.getTopicForSession(tmuxSession);
7227
+ if (t == null)
7228
+ return null;
7229
+ const n = typeof t === 'number' ? t : Number(t);
7230
+ return Number.isFinite(n) ? n : null;
7231
+ };
7232
+ const sessionReaper = new SessionReaper({
7233
+ listRunningSessions: () => sessionManager.listRunningSessions(),
7234
+ captureOutput: (s, n) => sessionManager.captureOutput(s, n) ?? '',
7235
+ hasActiveProcesses: (s) => sessionManager.hasActiveProcesses(s),
7236
+ frameworkForSession: (s) => sessionManager.frameworkForSession(s),
7237
+ isRecoveryActive: (session) => composedRecoveryActive(session),
7238
+ isRelayLeaseActive: (id) => sessionManager.isRelayLeaseActive(id),
7239
+ hasPendingInjection: (s) => sessionManager.getPendingInjection(s) != null,
7240
+ topicBinding: _resolveTopic,
7241
+ // Gate I is a v1 stub (returns false): active conversation is already
7242
+ // covered by the relay-lease + pending-injection gates and by render
7243
+ // stasis (a session being talked to is not render-static for the full
7244
+ // hysteresis+threshold window). Promoting to a real message-recency
7245
+ // query is a tracked tuning follow-up.
7246
+ recentUserMessage: () => false,
7247
+ activeCommitmentForTopic: (topicId) => {
7248
+ try {
7249
+ return commitmentTracker.getActive().some(c => c.topicId === topicId);
7250
+ }
7251
+ catch {
7252
+ return true;
7253
+ } // cannot tell → protect
7254
+ },
7255
+ activeSubagentCount: (csid) => {
7256
+ try {
7257
+ return csid ? subagentTracker.getActiveSubagents(csid).length : 0;
7258
+ }
7259
+ catch {
7260
+ return 1;
7261
+ } // cannot tell → protect
7262
+ },
7263
+ buildOrAutonomousActive: (topicId) => {
7264
+ const fresh = (p) => {
7265
+ try {
7266
+ return fs.existsSync(p) && (Date.now() - fs.statSync(p).mtimeMs) < 30 * 60_000;
7267
+ }
7268
+ catch {
7269
+ return false;
7270
+ }
7271
+ };
7272
+ if (topicId != null && fresh(path.join(config.stateDir, 'autonomous', `${topicId}.local.md`)))
7273
+ return true;
7274
+ return fresh(path.join(config.stateDir, 'state', 'build', 'build-state.json'));
7275
+ },
7276
+ protectedSessions: () => sessionManager.getProtectedSessions(),
7277
+ pressure: () => {
7278
+ const total = _os.totalmem();
7279
+ const freePct = total > 0 ? (_os.freemem() / total) * 100 : 100;
7280
+ const tier = freePct < 5 ? 'critical' : freePct < 12 ? 'moderate' : 'normal';
7281
+ return { tier, inputs: { freePct: Math.round(freePct * 10) / 10 } };
7282
+ },
7283
+ terminate: (id, reason) => sessionManager.terminateSession(id, reason),
7284
+ markReaping: (id) => sessionManager.markReaping(id),
7285
+ clearReaping: (id) => sessionManager.clearReaping(id),
7286
+ audit: fileAuditSink(config.stateDir),
7287
+ }, config.monitoring?.sessionReaper);
7288
+ sessionReaper.start();
7289
+ if (config.monitoring?.sessionReaper?.enabled) {
7290
+ console.log(pc.green(config.monitoring.sessionReaper.dryRun === false
7291
+ ? ' SessionReaper enabled (idle-session reaper — LIVE)'
7292
+ : ' SessionReaper enabled (idle-session reaper — dry-run, logs only)'));
7293
+ }
7294
+ 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, coherenceGate: scopeVerifier, contextHierarchy, canonicalState, operationGate, sentinel, adaptiveTrust, memoryMonitor, orphanReaper, coherenceMonitor, commitmentTracker, semanticMemory, activitySentinel, rateLimitSentinel, messageRouter, summarySentinel, spawnManager, systemReviewer, capabilityMapper, selfKnowledgeTree, coverageAuditor, topicResumeMap: _topicResumeMap ?? undefined, sessionRefresh: _sessionRefresh ?? undefined, autonomyManager, trustElevationTracker, autonomousEvolution, coordinator: coordinator.enabled ? coordinator : undefined, localSigningKeyPem, 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, responseReviewGate, messagingToneGate, outboundDedupGate, telemetryHeartbeat, pasteManager, featureRegistry, discoveryEvaluator, completionEvaluator, unifiedTrust, liveConfig, sharedStateLedger, ledgerSessionRegistry, worktreeManager, oidcEnrolledRepos: parallelDevConfig?.oidcEnrolledRepos, initiativeTracker, projectRoundRunner, projectDriftChecker, machineHeartbeat, proxyCoordinator, topicIntentStore, usherSignalStore, intelligence: sharedIntelligence ?? undefined, telegramBridgeConfig, telegramBridge: telegramBridge ?? undefined, threadlineObservability, workingMemory, taskFlowRegistry, threadlineFlowBridge, sessionReaper });
7197
7295
  // Boot-recovery (tunnel-failure-resilience spec Part 6): if the agent
7198
7296
  // died mid-relay-episode, the persisted tunnel.json carries
7199
7297
  // rotationPending=true. Rotate the dashboard PIN + authToken BEFORE