instar 1.3.729 → 1.3.731

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 (107) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +254 -2
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/CoherenceGate.d.ts +119 -0
  5. package/dist/core/CoherenceGate.d.ts.map +1 -1
  6. package/dist/core/CoherenceGate.js +342 -19
  7. package/dist/core/CoherenceGate.js.map +1 -1
  8. package/dist/core/CoherenceReviewer.d.ts +30 -0
  9. package/dist/core/CoherenceReviewer.d.ts.map +1 -1
  10. package/dist/core/CoherenceReviewer.js +14 -0
  11. package/dist/core/CoherenceReviewer.js.map +1 -1
  12. package/dist/core/FileClassifier.d.ts.map +1 -1
  13. package/dist/core/FileClassifier.js +7 -0
  14. package/dist/core/FileClassifier.js.map +1 -1
  15. package/dist/core/ModelSwapService.d.ts +13 -0
  16. package/dist/core/ModelSwapService.d.ts.map +1 -1
  17. package/dist/core/ModelSwapService.js +11 -0
  18. package/dist/core/ModelSwapService.js.map +1 -1
  19. package/dist/core/ModelTierEscalation.d.ts +10 -0
  20. package/dist/core/ModelTierEscalation.d.ts.map +1 -1
  21. package/dist/core/ModelTierEscalation.js +4 -0
  22. package/dist/core/ModelTierEscalation.js.map +1 -1
  23. package/dist/core/PostUpdateMigrator.d.ts +1 -0
  24. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  25. package/dist/core/PostUpdateMigrator.js +43 -0
  26. package/dist/core/PostUpdateMigrator.js.map +1 -1
  27. package/dist/core/ProactiveSwapMonitor.d.ts +74 -20
  28. package/dist/core/ProactiveSwapMonitor.d.ts.map +1 -1
  29. package/dist/core/ProactiveSwapMonitor.js +349 -28
  30. package/dist/core/ProactiveSwapMonitor.js.map +1 -1
  31. package/dist/core/QuotaAwareScheduler.d.ts +70 -3
  32. package/dist/core/QuotaAwareScheduler.d.ts.map +1 -1
  33. package/dist/core/QuotaAwareScheduler.js +100 -16
  34. package/dist/core/QuotaAwareScheduler.js.map +1 -1
  35. package/dist/core/ResponseReviewDecisionLog.d.ts +45 -0
  36. package/dist/core/ResponseReviewDecisionLog.d.ts.map +1 -0
  37. package/dist/core/ResponseReviewDecisionLog.js +85 -0
  38. package/dist/core/ResponseReviewDecisionLog.js.map +1 -0
  39. package/dist/core/SessionManager.d.ts +28 -0
  40. package/dist/core/SessionManager.d.ts.map +1 -1
  41. package/dist/core/SessionManager.js +102 -0
  42. package/dist/core/SessionManager.js.map +1 -1
  43. package/dist/core/SessionRefresh.d.ts +116 -1
  44. package/dist/core/SessionRefresh.d.ts.map +1 -1
  45. package/dist/core/SessionRefresh.js +280 -17
  46. package/dist/core/SessionRefresh.js.map +1 -1
  47. package/dist/core/SwapAntiThrash.d.ts +329 -0
  48. package/dist/core/SwapAntiThrash.d.ts.map +1 -0
  49. package/dist/core/SwapAntiThrash.js +1025 -0
  50. package/dist/core/SwapAntiThrash.js.map +1 -0
  51. package/dist/core/SwapLedger.d.ts +168 -0
  52. package/dist/core/SwapLedger.d.ts.map +1 -0
  53. package/dist/core/SwapLedger.js +275 -0
  54. package/dist/core/SwapLedger.js.map +1 -0
  55. package/dist/core/SwapWorkGate.d.ts +100 -0
  56. package/dist/core/SwapWorkGate.d.ts.map +1 -0
  57. package/dist/core/SwapWorkGate.js +175 -0
  58. package/dist/core/SwapWorkGate.js.map +1 -0
  59. package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
  60. package/dist/core/WriteDomainRegistry.js +21 -0
  61. package/dist/core/WriteDomainRegistry.js.map +1 -1
  62. package/dist/core/conversationContextWiring.d.ts +51 -0
  63. package/dist/core/conversationContextWiring.d.ts.map +1 -0
  64. package/dist/core/conversationContextWiring.js +78 -0
  65. package/dist/core/conversationContextWiring.js.map +1 -0
  66. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  67. package/dist/core/devGatedFeatures.js +6 -0
  68. package/dist/core/devGatedFeatures.js.map +1 -1
  69. package/dist/core/reviewers/conversational-tone.d.ts.map +1 -1
  70. package/dist/core/reviewers/conversational-tone.js +15 -1
  71. package/dist/core/reviewers/conversational-tone.js.map +1 -1
  72. package/dist/core/types.d.ts +102 -1
  73. package/dist/core/types.d.ts.map +1 -1
  74. package/dist/core/types.js.map +1 -1
  75. package/dist/core/untrustedConversationContext.d.ts +77 -0
  76. package/dist/core/untrustedConversationContext.d.ts.map +1 -0
  77. package/dist/core/untrustedConversationContext.js +134 -0
  78. package/dist/core/untrustedConversationContext.js.map +1 -0
  79. package/dist/monitoring/ReviewCanaryBattery.d.ts +160 -0
  80. package/dist/monitoring/ReviewCanaryBattery.d.ts.map +1 -0
  81. package/dist/monitoring/ReviewCanaryBattery.js +416 -0
  82. package/dist/monitoring/ReviewCanaryBattery.js.map +1 -0
  83. package/dist/monitoring/guardManifest.d.ts.map +1 -1
  84. package/dist/monitoring/guardManifest.js +40 -1
  85. package/dist/monitoring/guardManifest.js.map +1 -1
  86. package/dist/scaffold/templates.d.ts.map +1 -1
  87. package/dist/scaffold/templates.js +6 -0
  88. package/dist/scaffold/templates.js.map +1 -1
  89. package/dist/server/AgentServer.d.ts +4 -0
  90. package/dist/server/AgentServer.d.ts.map +1 -1
  91. package/dist/server/AgentServer.js +20 -0
  92. package/dist/server/AgentServer.js.map +1 -1
  93. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  94. package/dist/server/CapabilityIndex.js +1 -0
  95. package/dist/server/CapabilityIndex.js.map +1 -1
  96. package/dist/server/routes.d.ts +5 -0
  97. package/dist/server/routes.d.ts.map +1 -1
  98. package/dist/server/routes.js +72 -4
  99. package/dist/server/routes.js.map +1 -1
  100. package/package.json +1 -1
  101. package/src/data/builtin-manifest.json +65 -65
  102. package/src/scaffold/templates/jobs/instar/review-canary-battery.md +31 -0
  103. package/src/scaffold/templates.ts +6 -0
  104. package/upgrades/1.3.730.md +112 -0
  105. package/upgrades/1.3.731.md +97 -0
  106. package/upgrades/side-effects/context-aware-outbound-review.md +62 -0
  107. package/upgrades/side-effects/swap-continuity-antithrash.md +118 -0
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA+CH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAU3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAyBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA6H7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAgFtD;;;;;;;;;;;;;;;;;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;AAi/CD,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,CAijBN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA+hkBtE;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;AAgDH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAU3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAyBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA6H7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAgFtD;;;;;;;;;;;;;;;;;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;AAu/CD,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,CAijBN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA4ykBtE;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"}
@@ -26,6 +26,7 @@ import { SpawningTopicsRegistry } from '../core/SpawningTopicsRegistry.js';
26
26
  import { TopicReachabilityVerifier } from '../monitoring/TopicReachabilityVerifier.js';
27
27
  import { SingleInstanceLock, installReleaseHandlers } from '../core/SingleInstanceLock.js';
28
28
  import { resolveDevAgentGate, resolveStateSyncStores } from '../core/devAgentGate.js';
29
+ import { resolveAntiThrashKnobs, readingValidity } from '../core/SwapAntiThrash.js';
29
30
  import { shouldReleaseOnComplete, planClaimOnSpawn, ownershipNonce } from '../core/ownershipFollowsLiveWork.js';
30
31
  import { PrHandLease } from '../core/PrHandLease.js';
31
32
  import { claimSuspensionExcludesPin } from '../core/TopicClaimAnnotationStore.js';
@@ -760,6 +761,12 @@ let _quotaAwareScheduler = null;
760
761
  // Subscription & Auth Standard P1.3 — pre-limit proactive swap monitor. Null
761
762
  // until wired (requires the scheduler; gated behind subscriptionPool.proactiveSwap).
762
763
  let _proactiveSwapMonitor = null;
764
+ // swap-continuity-antithrash §3/§4 — the anti-thrash spine. The ledger + engine
765
+ // load and hydrate UNCONDITIONALLY at boot (R4-L3: reactive rows append
766
+ // regardless of antiThrash.enabled, so the index is warm at a mid-run flag
767
+ // flip). Null only when construction itself failed (non-fatal).
768
+ let _swapAntiThrashEngine = null;
769
+ let _swapWorkGate = null;
763
770
  async function spawnSessionForTopic(sessionManager, telegram, sessionName, topicId, latestMessage, topicMemory, userProfile, precomputedContext,
764
771
  /** Subscription & Auth Standard P1.3 (additive): launch under this account's
765
772
  * config home + record its id (the quota-aware account-swap mechanism).
@@ -14360,17 +14367,96 @@ export async function startServer(options) {
14360
14367
  // even without ANTHROPIC_API_KEY. Falls back to direct Anthropic API if a key is set
14361
14368
  // and no intelligence is available.
14362
14369
  let responseReviewGate;
14370
+ let reviewCanaryBattery;
14363
14371
  if (config.responseReview?.enabled) {
14364
14372
  if (sharedIntelligence) {
14365
14373
  const { CoherenceGate } = await import('../core/CoherenceGate.js');
14374
+ const { buildConversationContext } = await import('../core/conversationContextWiring.js');
14375
+ // Context-aware reviewers (context-aware-outbound-review §D10): the
14376
+ // WIRING layer resolves the dev-gate (resolveDevAgentGate needs the
14377
+ // top-level developmentAgent flag, which the gate never receives) and
14378
+ // hands the gate a PRE-RESOLVED conversationalContext block through
14379
+ // the LIVE getter — the kill-switch (`conversationalContext.enabled:
14380
+ // false`) then takes effect at the NEXT evaluate, no restart.
14381
+ const resolveConversationalContext = () => {
14382
+ const cc = liveConfig.get('responseReview.conversationalContext', config.responseReview?.conversationalContext) ?? {};
14383
+ const dev = liveConfig.get('developmentAgent', config.developmentAgent);
14384
+ return {
14385
+ enabled: resolveDevAgentGate(typeof cc.enabled === 'boolean' ? cc.enabled : undefined, { developmentAgent: dev }),
14386
+ maxMessages: typeof cc.maxMessages === 'number' ? cc.maxMessages : undefined,
14387
+ maxCharsPerMessage: typeof cc.maxCharsPerMessage === 'number' ? cc.maxCharsPerMessage : undefined,
14388
+ maxTotalChars: typeof cc.maxTotalChars === 'number' ? cc.maxTotalChars : undefined,
14389
+ injectReviewers: Array.isArray(cc.injectReviewers) ? cc.injectReviewers : undefined,
14390
+ };
14391
+ };
14366
14392
  responseReviewGate = new CoherenceGate({
14367
14393
  config: config.responseReview,
14368
14394
  stateDir: config.stateDir,
14369
14395
  intelligence: sharedIntelligence,
14370
14396
  relationships: relationships ?? undefined,
14371
14397
  adaptiveTrust: adaptiveTrust ?? undefined,
14398
+ // LIVE config getter (round-1 M2 build items 1+2): carries BOTH the
14399
+ // CMT-1794 fail-closed kill-switch AND the resolved
14400
+ // conversationalContext block. Reads through LiveConfig so a config
14401
+ // edit applies at the next evaluate without a restart.
14402
+ liveConfig: () => ({
14403
+ failClosedOnCriticalAbstain: liveConfig.get('responseReview.failClosedOnCriticalAbstain', config.responseReview?.failClosedOnCriticalAbstain),
14404
+ conversationalContext: resolveConversationalContext(),
14405
+ }),
14406
+ // §D1 conversation source: TopicMemory rows + the topic's verified
14407
+ // operator binding, distilled by the wiring-layer principal
14408
+ // computation (buildConversationContext — Know Your Principal). Any
14409
+ // throw here is caught at acquisition inside the gate (§D5).
14410
+ conversationContextProvider: (topicId, limit) => {
14411
+ if (!topicMemory)
14412
+ return { messages: [], askLicenseMode: 'weak-corroboration-only' };
14413
+ const rows = topicMemory.getRecentMessages(topicId, limit);
14414
+ const operator = _agentServerRef?.getTopicOperatorStore()?.getOperator(topicId) ?? null;
14415
+ return buildConversationContext(rows, operator);
14416
+ },
14372
14417
  });
14373
14418
  console.log(pc.green(` Response review pipeline: enabled via shared IntelligenceProvider (${Object.keys(config.responseReview.reviewers ?? {}).length} reviewers configured)`));
14419
+ if (resolveConversationalContext().enabled) {
14420
+ console.log(pc.green(' Response review conversational context: LIVE (context-aware reviewers — dev-gate resolved)'));
14421
+ }
14422
+ else {
14423
+ console.log(pc.dim(' Response review conversational context: dark (dev-gate resolved off)'));
14424
+ }
14425
+ // §D9.4b canary-battery driver — replays through the Bearer-gated
14426
+ // /review/test route (the SAME plumbing real reviews use; no auth-less
14427
+ // bypass seam) and writes its per-run summary through the gate's §D8
14428
+ // writer. The trigger route 503s while the feature is dark.
14429
+ if (topicMemory) {
14430
+ const { ReviewCanaryBattery } = await import('../monitoring/ReviewCanaryBattery.js');
14431
+ const gateForBattery = responseReviewGate;
14432
+ reviewCanaryBattery = new ReviewCanaryBattery({
14433
+ topicMemory,
14434
+ callReviewTest: async (body) => {
14435
+ const resp = await fetch(`http://127.0.0.1:${config.port}/review/test`, {
14436
+ method: 'POST',
14437
+ headers: {
14438
+ 'Content-Type': 'application/json',
14439
+ Authorization: `Bearer ${config.authToken ?? ''}`,
14440
+ },
14441
+ body: JSON.stringify(body),
14442
+ });
14443
+ let parsed = null;
14444
+ try {
14445
+ parsed = (await resp.json());
14446
+ }
14447
+ catch {
14448
+ // @silent-fallback-ok — a non-JSON body is reported as a null
14449
+ // body; the driver records the arm as an error (inconclusive),
14450
+ // never a silent skip.
14451
+ }
14452
+ return { status: resp.status, body: parsed };
14453
+ },
14454
+ writeDecisionRow: (row) => gateForBattery.appendDecisionRow(row),
14455
+ isFeatureLive: () => resolveConversationalContext().enabled,
14456
+ isObserveOnly: () => liveConfig.get('responseReview.observeOnly', config.responseReview?.observeOnly) === true,
14457
+ isTestEndpointEnabled: () => liveConfig.get('responseReview.testEndpointDisabled', config.responseReview?.testEndpointDisabled) !== true,
14458
+ });
14459
+ }
14374
14460
  }
14375
14461
  else {
14376
14462
  console.warn(pc.yellow(` Response review pipeline: configured but no IntelligenceProvider available (path-constraints.md Rule 2 forbids direct API path)`));
@@ -14962,6 +15048,94 @@ export async function startServer(options) {
14962
15048
  divergenceChecker = undefined;
14963
15049
  }
14964
15050
  }
15051
+ // ── Swap-continuity anti-thrash spine (swap-continuity-antithrash §3/§4) ──
15052
+ // SwapLedger + SwapAntiThrashEngine + SwapWorkGate. Constructed and
15053
+ // hydrated UNCONDITIONALLY (R4-L3): the ledger loads at every boot
15054
+ // regardless of antiThrash.enabled so the in-memory index (dwell /
15055
+ // reversal / breaker / frequency) is warm the moment the flag flips on
15056
+ // mid-run — the §7.1 half-enable trap closed for this key. All antiThrash
15057
+ // knobs are read LIVE per tick via the getter (the config object is
15058
+ // shared + mutated in place by config PATCHes).
15059
+ const antiThrashKnobsGetter = () => resolveAntiThrashKnobs(config.subscriptionPool?.proactiveSwap?.antiThrash, {
15060
+ thresholdPct: config.subscriptionPool?.proactiveSwap?.thresholdPct,
15061
+ tickMs: config.subscriptionPool?.proactiveSwap?.tickMs,
15062
+ });
15063
+ // swapContinuity (Piece 2, the work gate): the `enabled` KEY IS OMITTED
15064
+ // from shipped config on purpose — omission routes it through the
15065
+ // dev-agent gate (live on a dev agent, dark on the fleet; the #1001
15066
+ // anti-mechanism). `enabled` is RESTART-REQUIRED (§7.1 — the gate wiring
15067
+ // into SessionRefresh is constructor-injected); dryRun + numeric knobs
15068
+ // are read live per evaluation via the getters below.
15069
+ const swapContinuityEnabled = resolveDevAgentGate(config.subscriptionPool?.swapContinuity?.enabled, config);
15070
+ const swapContinuityKnobs = () => {
15071
+ const c = config.subscriptionPool?.swapContinuity;
15072
+ return {
15073
+ enabled: swapContinuityEnabled,
15074
+ dryRun: c?.dryRun ?? true,
15075
+ deferralCeilingMs: c?.deferralCeilingMs ?? 1_800_000,
15076
+ reactiveGraceMs: c?.reactiveGraceMs ?? 120_000,
15077
+ recheckMs: c?.recheckMs ?? 10_000,
15078
+ };
15079
+ };
15080
+ try {
15081
+ const { SwapLedger } = await import('../core/SwapLedger.js');
15082
+ const { SwapAntiThrashEngine, retentionBoundMs, crossKnobWarnings } = await import('../core/SwapAntiThrash.js');
15083
+ const { SwapWorkGate } = await import('../core/SwapWorkGate.js');
15084
+ const swapLedger = new SwapLedger({
15085
+ filePath: path.join(config.stateDir, 'state', 'swap-ledger.jsonl'),
15086
+ windowMs: () => retentionBoundMs(antiThrashKnobsGetter()),
15087
+ // Counts supplied at outage-resume time (the ledger-lost refusals the
15088
+ // writer itself could not record — §3.5/I5).
15089
+ outageRefusalCount: () => _swapAntiThrashEngine?.ledgerLostRefusalCount() ?? 0,
15090
+ machineId: _meshSelfId ?? 'local',
15091
+ logger: { log: (m) => console.log(m), warn: (m) => console.warn(m) },
15092
+ });
15093
+ _swapAntiThrashEngine = new SwapAntiThrashEngine({
15094
+ ledger: swapLedger,
15095
+ getKnobs: antiThrashKnobsGetter,
15096
+ machineId: _meshSelfId ?? 'local',
15097
+ // P17: episode-deduped attention (the engine dedupes on episodeId;
15098
+ // the id passed here is the stable per-episode dedupe key).
15099
+ raiseAttention: (id, title, body) => {
15100
+ void telegram
15101
+ ?.createAttentionItem({
15102
+ id,
15103
+ title,
15104
+ summary: body,
15105
+ category: 'subscription-pool',
15106
+ priority: 'HIGH',
15107
+ sourceContext: 'subscription-pool:swap-antithrash',
15108
+ })
15109
+ .catch(() => {
15110
+ /* @silent-fallback-ok: attention is best-effort; the ledger row is the durable trace */
15111
+ });
15112
+ },
15113
+ logger: { log: (m) => console.log(m), warn: (m) => console.warn(m) },
15114
+ });
15115
+ _swapAntiThrashEngine.hydrate();
15116
+ // Cross-knob coherence warnings (§7, R4-L2 — warn-only, once at boot).
15117
+ // 15 min = the QuotaPoller's default cadence (pollIntervalMs ?? 15*60_000).
15118
+ for (const w of crossKnobWarnings(antiThrashKnobsGetter(), 15 * 60_000))
15119
+ console.warn(w);
15120
+ _swapWorkGate = new SwapWorkGate({
15121
+ checkSessionWorkState: (tmux) => sessionManager.checkSessionWorkState(tmux),
15122
+ getClaudeSessionId: (tmux) => state.listSessions({ status: 'running' }).find((s) => s.tmuxSession === tmux)?.claudeSessionId ?? null,
15123
+ hasActiveSubagents: (id) => subagentTracker.hasActiveSubagents(id),
15124
+ getActiveSubagents: (id) => subagentTracker.getActiveSubagents(id).map((r) => ({
15125
+ agentType: r.agentType,
15126
+ startedAt: r.startedAt,
15127
+ ...(r.transcriptPath ? { transcriptPath: r.transcriptPath } : {}),
15128
+ })),
15129
+ });
15130
+ }
15131
+ catch (err) {
15132
+ // @silent-fallback-ok: cascade-isolation — a spine construction failure
15133
+ // must never 503 the server; behavior degrades to pre-brake semantics
15134
+ // (the legacy path) and re-attempts next boot.
15135
+ console.warn('[instar] swap-continuity anti-thrash spine init failed (non-fatal):', err);
15136
+ _swapAntiThrashEngine = null;
15137
+ _swapWorkGate = null;
15138
+ }
14965
15139
  // SessionRefresh — agent-initiated session respawn. Requires a Telegram
14966
15140
  // adapter (v1 scope: Telegram-bound sessions only). The respawner closure
14967
15141
  // captures `topicMemory` by reference, so even if topicMemory is wired up
@@ -14979,6 +15153,43 @@ export async function startServer(options) {
14979
15153
  state,
14980
15154
  telegram: telegramRef,
14981
15155
  topicResumeMap: _topicResumeMap,
15156
+ // swap-continuity-antithrash §4.2 — the in-flight work gate, bound at
15157
+ // THIS primitive (the funnel every session-killing mutation flows
15158
+ // through). Constructor-injected on purpose: `swapContinuity.enabled`
15159
+ // is restart-required (§7.1); dryRun + numeric knobs read live via
15160
+ // getKnobs. Null when the feature is dark on this install ⇒
15161
+ // byte-for-byte today's behavior (unconditional kill).
15162
+ workGateCtx: swapContinuityEnabled && _swapWorkGate
15163
+ ? {
15164
+ probe: (tmux) => _swapWorkGate.probe(tmux),
15165
+ getKnobs: () => {
15166
+ const k = swapContinuityKnobs();
15167
+ return { enabled: k.enabled, dryRun: k.dryRun, reactiveGraceMs: k.reactiveGraceMs, recheckMs: k.recheckMs };
15168
+ },
15169
+ recordProceeded: (args) => _swapAntiThrashEngine?.recordProceeded(args),
15170
+ recordInteractiveRefusal: (args) => _swapAntiThrashEngine?.recordInteractiveRefusal(args),
15171
+ // §4.3(2) — Q4: v1 reads the in-memory exactly-once map (set on
15172
+ // inbound, cleared on reply); 'unknown' = the map/ledger is
15173
+ // unavailable (post-restart, or exactly-once ingress dark).
15174
+ // `messageLedger`/`currentInboundByTopic` are declared later in
15175
+ // this boot function and captured by reference — every call
15176
+ // happens post-boot, so the binding is initialized by then.
15177
+ resolveUnansweredInbound: (topicId) => {
15178
+ if (!messageLedger || !currentInboundByTopic)
15179
+ return 'unknown';
15180
+ const dedupeKey = currentInboundByTopic.get(String(topicId));
15181
+ if (!dedupeKey)
15182
+ return 'none';
15183
+ const entry = messageLedger.get(dedupeKey);
15184
+ if (!entry || entry.inputSnapshot === null)
15185
+ return 'unknown';
15186
+ const from = entry.senderEnvelope?.username ??
15187
+ entry.senderEnvelope?.firstName ??
15188
+ (entry.senderEnvelope?.userId !== undefined ? String(entry.senderEnvelope.userId) : 'unknown sender');
15189
+ return { body: entry.inputSnapshot, from, at: entry.receivedAt };
15190
+ },
15191
+ }
15192
+ : null,
14982
15193
  // §10.5 Slack binding — SlackAdapter satisfies SlackRefreshBinding
14983
15194
  // structurally (getChannelForSession / removeChannelResume /
14984
15195
  // resolveChannelForSessionFromDisk). Null ⇒ Telegram-only, unchanged.
@@ -15042,6 +15253,30 @@ export async function startServer(options) {
15042
15253
  _quotaAwareScheduler = new QuotaAwareScheduler({
15043
15254
  listAccounts: () => subscriptionPool.list(),
15044
15255
  softThresholdPct: config.subscriptionPool?.swapSoftThresholdPct,
15256
+ // swap-continuity-antithrash — the anti-thrash observation + execute-time
15257
+ // revalidation hooks. Reactive decision behavior is untouched (I6):
15258
+ // these only OBSERVE reactive swaps (dwell clock-start, hop alerts,
15259
+ // failed rows) and revalidate PROACTIVE explicit-target calls (§3.3).
15260
+ antiThrash: _swapAntiThrashEngine
15261
+ ? {
15262
+ readingValid: (acct, nowMs) => readingValidity(acct, nowMs, antiThrashKnobsGetter().quotaFreshnessMs).valid,
15263
+ getKnobs: () => {
15264
+ const k = antiThrashKnobsGetter();
15265
+ return { thresholdPct: k.thresholdPct, targetHeadroomPct: k.targetHeadroomPct, minImprovementPct: k.minImprovementPct };
15266
+ },
15267
+ resolveCurrentAccountId: (sessionName) => state.listSessions({ status: 'running' }).find((s) => s.tmuxSession === sessionName)?.subscriptionAccountId ?? null,
15268
+ onReactiveExecuted: (a) => _swapAntiThrashEngine?.recordReactiveExecuted(a),
15269
+ onReactiveFailed: (a) => _swapAntiThrashEngine?.recordExecFailure({
15270
+ session: a.session,
15271
+ from: a.from,
15272
+ ...(a.to ? { to: a.to } : {}),
15273
+ kind: 'reactive',
15274
+ errorClass: a.errorClass,
15275
+ nowMs: a.nowMs,
15276
+ }),
15277
+ onReactiveRateCapRefusal: (s, n) => _swapAntiThrashEngine?.noteReactiveRateCapRefusal(s, n),
15278
+ }
15279
+ : undefined,
15045
15280
  refreshFn: async (o) => {
15046
15281
  if (!_sessionRefresh)
15047
15282
  return false;
@@ -15050,8 +15285,13 @@ export async function startServer(options) {
15050
15285
  reason: o.reason,
15051
15286
  configHome: o.configHome,
15052
15287
  accountId: o.accountId,
15288
+ // I11: callerClass set ONLY server-internally — the scheduler is
15289
+ // the one caller that knows its lane (proactive funnel vs reactive).
15290
+ callerClass: o.callerClass,
15053
15291
  });
15054
- return r.ok;
15292
+ // Object form: surfaces the structured refusal code so the
15293
+ // anti-thrash observation layer can classify failures (§3.1/§3.6).
15294
+ return r.ok ? { ok: true } : { ok: false, code: r.code };
15055
15295
  },
15056
15296
  onNoAlternate: (sessionName, exhaustedAccountId) => {
15057
15297
  void telegramRef.createAttentionItem({
@@ -15133,6 +15373,18 @@ export async function startServer(options) {
15133
15373
  maxSwapsPerCycle: proactiveCfg.maxSwapsPerCycle,
15134
15374
  cooldownMs: proactiveCfg.cooldownMs,
15135
15375
  tickMs: proactiveCfg.tickMs,
15376
+ // swap-continuity-antithrash §3 — the brake engine at the proactive
15377
+ // DECISION chokepoint. Knobs read LIVE per tick (§7.1). dryRun keeps
15378
+ // the legacy decision path byte-identical while the engine logs
15379
+ // would-refuse shadow rows (the rung-2 soak).
15380
+ antiThrash: _swapAntiThrashEngine
15381
+ ? { engine: _swapAntiThrashEngine, getKnobs: antiThrashKnobsGetter }
15382
+ : undefined,
15383
+ // §4 — the in-flight work gate (proactive arm: defer, ceiling-drop).
15384
+ // The monitor owns the deferral lifecycle; the gate is stateless.
15385
+ workGate: _swapWorkGate
15386
+ ? { probe: (s) => _swapWorkGate.probe(s), getContinuity: swapContinuityKnobs }
15387
+ : undefined,
15136
15388
  logger: { log: (m) => console.log(m), warn: (m) => console.warn(m) },
15137
15389
  });
15138
15390
  _proactiveSwapMonitor.start();
@@ -19977,7 +20229,7 @@ export async function startServer(options) {
19977
20229
  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, accountFollowMePeerViews: async () => { const nickById = new Map((_listPoolMachines?.() ?? []).map((m) => [m.machineId, m.nickname ?? m.machineId])); let peers = (_resolvePeerUrls?.() ?? []).map((p) => ({ machineId: p.machineId, nickname: nickById.get(p.machineId) ?? p.machineId, url: p.url })); if (peers.length === 0) {
19978
20230
  peers = (_listPoolMachines?.() ?? []).filter((m) => m.machineId !== _meshSelfId && !!m.lastKnownUrl).map((m) => ({ machineId: m.machineId, nickname: m.nickname ?? m.machineId, url: m.lastKnownUrl }));
19979
20231
  } if (peers.length === 0)
19980
- return []; const { fetchPeerSubscriptionViews } = await import('../core/fetchPeerSubscriptionViews.js'); return fetchPeerSubscriptionViews({ peers: () => peers, fetchImpl: fetch, authToken: config.authToken ?? '' }); }, quotaPoller, quotaAwareScheduler: _quotaAwareScheduler ?? undefined, proactiveSwapMonitor: _proactiveSwapMonitor ?? undefined, inUseAccountResolver, enrollmentWizard, accountFollowMeRevocation, credentialRepointing, semanticMemory, activitySentinel, rateLimitSentinel, releaseReadinessSentinel: releaseReadinessSentinel ?? undefined, greenPrAutoMerger: greenPrAutoMerger ?? undefined, guardLatchStore: guardLatchStore ?? 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, meshBindActive: coordinator.managers.identityManager.hasIdentity() && config.multiMachine?.meshTransport?.enabled !== false, localSigningKeyPem, leaseTransport, peerEndpointRecorder, getSelfMeshEndpoints, 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, threadLog, threadMessageRecorder, 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, ropeHealthMonitor, writeAdmission: writeAdmission ?? undefined, getInboundQueue: () => _inboundQueue, meshRpcDispatcher, workingSetPullCoordinator, commitmentReplicaStore, preferenceReplicaStore, replicatedRecordEmitter, conflictStore, rollbackUnmerge, droppedOriginRegistry, preferencesUnionReader, forwardCommitmentMutate, sessionOwnershipRegistry, topicPinStore: _topicPinStore ?? undefined, topicPinSkewQuarantine: _topicPinSkewQuarantine ?? undefined, topicPinFoldView: _topicPinFoldView ?? undefined, ownershipReconciler: _ownershipReconciler ?? undefined, staleOwnerEngine: _staleOwnerEngine ?? undefined, leaseHandback: _leaseHandbackCtx ?? 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, deliverMandateToMachine: _deliverMandateToMachine ?? undefined, poolLink: _poolLink ?? undefined, poolPollCache: _poolPollCache ?? undefined, sessionPoolE2EResultStore, proxyCoordinator, topicIntentStore, topicIntentArcCheck, usherSignalStore, intelligence: sharedIntelligence ?? undefined, telegramBridgeConfig, telegramBridge: telegramBridge ?? undefined, threadlineObservability, briefDeps, workingMemory, taskFlowRegistry, threadlineFlowBridge, sessionReaper, agentWorktreeReaper, orphanedWorkSentinel, mcpProcessReaper, geminiLoopRunner, sleepController, agentActivityState, reapLog, resumeQueue, resumeDrainer, autonomousLivenessReconciler, enforcedTerminationStatus: () => enforcedTerminationWatchdog?.guardStatus() ?? null, prHandLease: prHandLease ?? undefined, operatorStopRecorder: recordOperatorStop, sleepWakeDetector, unjustifiedStopGate, stopGateDb, stopNotifier, liveTestGate, liveTestGateMode, liveTestRunnerCtx }); // Resolve the late-bound topic-operator getter (increment 2e): routing was
20232
+ return []; const { fetchPeerSubscriptionViews } = await import('../core/fetchPeerSubscriptionViews.js'); return fetchPeerSubscriptionViews({ peers: () => peers, fetchImpl: fetch, authToken: config.authToken ?? '' }); }, quotaPoller, quotaAwareScheduler: _quotaAwareScheduler ?? undefined, proactiveSwapMonitor: _proactiveSwapMonitor ?? undefined, inUseAccountResolver, enrollmentWizard, accountFollowMeRevocation, credentialRepointing, semanticMemory, activitySentinel, rateLimitSentinel, releaseReadinessSentinel: releaseReadinessSentinel ?? undefined, greenPrAutoMerger: greenPrAutoMerger ?? undefined, guardLatchStore: guardLatchStore ?? 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, meshBindActive: coordinator.managers.identityManager.hasIdentity() && config.multiMachine?.meshTransport?.enabled !== false, localSigningKeyPem, leaseTransport, peerEndpointRecorder, getSelfMeshEndpoints, 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, threadLog, threadMessageRecorder, warrantsReplyGate, collaborationSurfacer, threadResumeMap, topicLinkageHandler: topicLinkageHandler ?? undefined, threadlineRelayClient, threadlineReplyWaiters, listenerManager: listenerManager ?? undefined, a2aDeliveryTracker: a2aDeliveryTracker ?? undefined, responseReviewGate, reviewCanaryBattery, messagingToneGate, outboundDedupGate, telemetryHeartbeat, pasteManager, featureRegistry, discoveryEvaluator, completionEvaluator, unifiedTrust, liveConfig, sharedStateLedger, ledgerSessionRegistry, worktreeManager, oidcEnrolledRepos: parallelDevConfig?.oidcEnrolledRepos, initiativeTracker, projectRoundRunner, projectDriftChecker, machineHeartbeat, machinePoolRegistry, ropeHealthMonitor, writeAdmission: writeAdmission ?? undefined, getInboundQueue: () => _inboundQueue, meshRpcDispatcher, workingSetPullCoordinator, commitmentReplicaStore, preferenceReplicaStore, replicatedRecordEmitter, conflictStore, rollbackUnmerge, droppedOriginRegistry, preferencesUnionReader, forwardCommitmentMutate, sessionOwnershipRegistry, topicPinStore: _topicPinStore ?? undefined, topicPinSkewQuarantine: _topicPinSkewQuarantine ?? undefined, topicPinFoldView: _topicPinFoldView ?? undefined, ownershipReconciler: _ownershipReconciler ?? undefined, staleOwnerEngine: _staleOwnerEngine ?? undefined, leaseHandback: _leaseHandbackCtx ?? 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, deliverMandateToMachine: _deliverMandateToMachine ?? undefined, poolLink: _poolLink ?? undefined, poolPollCache: _poolPollCache ?? undefined, sessionPoolE2EResultStore, proxyCoordinator, topicIntentStore, topicIntentArcCheck, usherSignalStore, intelligence: sharedIntelligence ?? undefined, telegramBridgeConfig, telegramBridge: telegramBridge ?? undefined, threadlineObservability, briefDeps, workingMemory, taskFlowRegistry, threadlineFlowBridge, sessionReaper, agentWorktreeReaper, orphanedWorkSentinel, mcpProcessReaper, geminiLoopRunner, sleepController, agentActivityState, reapLog, resumeQueue, resumeDrainer, autonomousLivenessReconciler, enforcedTerminationStatus: () => enforcedTerminationWatchdog?.guardStatus() ?? null, prHandLease: prHandLease ?? undefined, operatorStopRecorder: recordOperatorStop, sleepWakeDetector, unjustifiedStopGate, stopGateDb, stopNotifier, liveTestGate, liveTestGateMode, liveTestRunnerCtx }); // Resolve the late-bound topic-operator getter (increment 2e): routing was
19981
20233
  // wired before the server existed; from here on inbound binds use the
19982
20234
  // server's own store instance.
19983
20235
  _agentServerRef = server;