instar 1.3.912 → 1.3.914

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.
@@ -1067,6 +1067,16 @@
1067
1067
  line-height: 1.55;
1068
1068
  color: var(--text-bright);
1069
1069
  }
1070
+ /* Subscriptions is an operational matrix, not a prose reading column.
1071
+ Keep Process Health's calm 760px measure scoped to .ph-root while this
1072
+ tab uses the full app canvas with responsive gutters. */
1073
+ .subscriptions-root {
1074
+ grid-column: 1 / -1;
1075
+ max-width: none;
1076
+ width: 100%;
1077
+ min-width: 0;
1078
+ box-sizing: border-box;
1079
+ }
1070
1080
  .ph-title { font-size: 22px; font-weight: 600; color: var(--text-bright); line-height: 1.5; }
1071
1081
  .ph-stamp { font-size: 14px; color: var(--text-dim); }
1072
1082
  .ph-headline-wrap { margin: 4px 0 8px; }
@@ -3615,6 +3625,7 @@
3615
3625
  .sub-empty, .sub-disabled { font-size:13px; opacity:.7; padding:8px 0; }
3616
3626
  /* account-machine-matrix grid */
3617
3627
  .sub-matrix { border-collapse:collapse; width:100%; font-size:13px; }
3628
+ #subMatrix { width:100%; min-width:0; overflow-x:auto; }
3618
3629
  .sub-matrix th, .sub-matrix td { border:1px solid var(--border,#2a2a2a); padding:8px 10px; text-align:center; vertical-align:top; }
3619
3630
  .sub-matrix-acct { text-align:left; font-weight:600; word-break:break-all; }
3620
3631
  .sub-matrix-mach { font-weight:600; }
@@ -3634,6 +3645,9 @@
3634
3645
  .sub-matrix-cancel, .sub-matrix-collapse { margin-top:4px; margin-left:4px; padding:2px 8px; cursor:pointer; font-size:11px; opacity:.85; }
3635
3646
  .sub-matrix-held-detail { font-size:11px; color:#f87171; margin-top:4px; }
3636
3647
  .sub-matrix-done-detail { font-size:11px; margin-top:4px; }
3648
+ .sub-matrix-outcome { margin-top:6px; padding:7px 8px; border-radius:7px; text-align:left; font-size:12px; line-height:1.4; }
3649
+ .sub-matrix-outcome-done { color:#22c55e; background:rgba(22,163,74,.10); border:1px solid rgba(22,163,74,.35); }
3650
+ .sub-matrix-outcome-failed { color:#fca5a5; background:rgba(248,113,113,.08); border:1px solid rgba(248,113,113,.30); }
3637
3651
  .sub-matrix-cell.sub-matrix-held .sub-matrix-glyph, .sub-matrix-cell.sub-matrix-expired .sub-matrix-glyph, .sub-matrix-cell.sub-matrix-broken .sub-matrix-glyph { color:#f87171; }
3638
3652
  .sub-matrix-cell.sub-matrix-done .sub-matrix-glyph { color:#22c55e; font-weight:600; }
3639
3653
  .sub-matrix-cell.sub-matrix-just-verified { background:rgba(22,163,74,.14); outline:2px solid #16a34a; animation:subVerifiedPulse 1.2s ease-in-out 3; }
@@ -3644,10 +3658,16 @@
3644
3658
  .sub-pending-failed-head { color:#f87171; font-weight:600; }
3645
3659
  .sub-pending-outcome-body { font-size:12px; opacity:.85; margin-top:4px; }
3646
3660
  .sub-pending-status { font-size:12px; opacity:.85; margin-top:6px; }
3661
+ @media (max-width: 600px) {
3662
+ .subscriptions-root { padding:18px 14px !important; gap:18px !important; }
3663
+ .subscriptions-root .ph-h-sub { font-size:14px; }
3664
+ .sub-matrix { min-width:620px; }
3665
+ .sub-account { padding:12px; }
3666
+ }
3647
3667
  </style>
3648
- <div id="subscriptionsPanel" class="ph-root" style="display:none;flex-direction:column;padding:28px;gap:24px;overflow-y:auto">
3668
+ <div id="subscriptionsPanel" class="tab-panel subscriptions-root" style="display:none;flex-direction:column;padding:28px;gap:24px;overflow-y:auto">
3649
3669
  <h2 class="ph-title">Subscriptions</h2>
3650
- <p class="ph-intro">Your subscription accounts and how much of each is left before it resets plus any logins waiting for you to approve on your phone.</p>
3670
+ <p class="ph-intro">Your subscription accounts, where each one is set up, and how much usage remains before it resets.</p>
3651
3671
  <!-- One-tap follow-me Approve card(s) — rendered only when another machine could use one of
3652
3672
  your subscriptions (ws52-operator-tap-not-text Part A). Leads the panel as the primary
3653
3673
  action when present; silent otherwise. -->
@@ -3665,11 +3685,6 @@
3665
3685
  <p class="ph-h-sub">Each account’s live usage (5-hour and weekly) and when it resets.</p>
3666
3686
  <div id="subAccounts"></div>
3667
3687
  </section>
3668
- <section class="ph-section">
3669
- <h3 class="ph-h">Pending logins</h3>
3670
- <p class="ph-h-sub">A mirror of every sign-in still in flight (each grid cell above carries its own complete flow) — plus explicit “done” and “didn’t finish” cards. An expired code is re-issued automatically.</p>
3671
- <div id="subPending"></div>
3672
- </section>
3673
3688
  </div>
3674
3689
 
3675
3690
  <!-- mandates tab (coordination-mandate spec, decision 2A — the operator's PIN-gated surface) -->
@@ -8831,7 +8846,6 @@
8831
8846
  if (!__subController) {
8832
8847
  const els = {
8833
8848
  accounts: document.getElementById('subAccounts'),
8834
- pending: document.getElementById('subPending'),
8835
8849
  followMe: document.getElementById('subFollowMe'),
8836
8850
  matrix: document.getElementById('subMatrix'),
8837
8851
  };
@@ -726,6 +726,10 @@ export function renderAccountMatrix(doc, target, poolScope, pendingScope, transi
726
726
  td.appendChild(el(doc, 'div', 'sub-matrix-held-detail',
727
727
  'Its sign-in window closed before finishing — tap Retry to start a fresh sign-in.'));
728
728
  }
729
+ if (c.state === 'expired') {
730
+ td.appendChild(el(doc, 'div', 'sub-matrix-outcome sub-matrix-outcome-failed',
731
+ 'Didn’t finish — the sign-in link expired before this account was set up.'));
732
+ }
729
733
  td.appendChild(btn);
730
734
  } else if (c.state === 'in-progress' && c.login && c.login.verificationUrl) {
731
735
  // The cell carries the COMPLETE flow, rebuilt from SERVER pending-login state
@@ -743,6 +747,10 @@ export function renderAccountMatrix(doc, target, poolScope, pendingScope, transi
743
747
  : (justVerified && c.state === 'active' ? 'Active — just set up' : MATRIX_CELL_WORD[c.state]);
744
748
  const glyph = MATRIX_CELL_GLYPH[c.state] || '';
745
749
  td.appendChild(el(doc, 'span', 'sub-matrix-glyph', `${glyph} ${word}`.trim()));
750
+ if (justVerified) {
751
+ td.appendChild(el(doc, 'div', 'sub-matrix-outcome sub-matrix-outcome-done',
752
+ 'Done — this account is set up on this machine.'));
753
+ }
746
754
  // An in-progress (◷) cell gets a tappable Cancel so a mis-tapped setup can be
747
755
  // reversed (abandon the login + tear down its pane). Emitted on the DURABLE
748
756
  // re-rendered cell (not just the live sign-in DOM) so it survives the poll loop.
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA2EH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAU3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AA0BjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA8H7D,OAAO,KAAK,EAAW,oBAAoB,EAAe,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACjG,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;AAgrDD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,oBAAoB,GAAG,IAAI,GACxC;IACD,YAAY,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAqBA;AAED,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,EAQrD,oBAAoB,CAAC,EAAE,MAAM;IAC3B,YAAY,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,GACP,IAAI,CAwtBN;AAgmBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAqwpBtE;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;AA4EH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAU3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AA0BjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA8H7D,OAAO,KAAK,EAAW,oBAAoB,EAAe,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACjG,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;AA+rDD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,oBAAoB,GAAG,IAAI,GACxC;IACD,YAAY,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAqBA;AAED,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,EAQrD,oBAAoB,CAAC,EAAE,MAAM;IAC3B,YAAY,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,GACP,IAAI,CAwtBN;AAgmBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAuypBtE;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"}
@@ -45,6 +45,7 @@ import { configureHostSpawnSemaphore } from '../core/hostSpawnSemaphore.js';
45
45
  import { SpawningTopicsRegistry } from '../core/SpawningTopicsRegistry.js';
46
46
  import { planCredentialIdentityRepair } from '../core/CredentialIdentityRepairPlan.js';
47
47
  import { TopicReachabilityVerifier } from '../monitoring/TopicReachabilityVerifier.js';
48
+ import { probeTranscript } from '../monitoring/transcriptProber.js';
48
49
  import { SingleInstanceLock, installReleaseHandlers } from '../core/SingleInstanceLock.js';
49
50
  import { resolveDevAgentGate, resolveStateSyncStores } from '../core/devAgentGate.js';
50
51
  import { resolveAntiThrashKnobs, readingValidity } from '../core/SwapAntiThrash.js';
@@ -485,6 +486,10 @@ let _spawningTopicsRegistryRef = null;
485
486
  // Module-level reference for session resume mapping.
486
487
  // Set once in startServer() and used by spawnSessionForTopic/respawnSessionForTopic.
487
488
  let _topicResumeMap = null;
489
+ // Context-wall recovery latch. These late-bound seams let every Telegram/Slack
490
+ // spawn chokepoint fail fresh while SessionRecovery owns a durable latch.
491
+ let _contextExhaustionFreshRequired = null;
492
+ let _clearContextExhaustionFreshRequired = null;
488
493
  // ── Multi-Machine Session Pool (§L4) activation refs ──────────────────────
489
494
  // The SessionRouter is constructed in startServer()'s mesh block, but the inbound
490
495
  // dispatch handler that consults it is defined ABOVE startServer — so the router is
@@ -1098,6 +1103,11 @@ spawnOpts) {
1098
1103
  // TopicResumeMap is authoritative — it saved the UUID for this specific topic at kill time
1099
1104
  // or via the refresh heartbeat. Skip LLM validation (which was failing due to JSONL sampling
1100
1105
  // issues and is redundant for an authoritative source).
1106
+ const contextWallForcesFresh = _contextExhaustionFreshRequired?.(topicId) === true;
1107
+ if (contextWallForcesFresh) {
1108
+ _topicResumeMap?.remove(topicId);
1109
+ console.log(`[spawnSessionForTopic] Context-exhaustion latch active for topic ${topicId} — forcing fresh spawn`);
1110
+ }
1101
1111
  let resumeSessionId = _topicResumeMap?.get(topicId) ?? undefined;
1102
1112
  if (resumeSessionId) {
1103
1113
  console.log(`[spawnSessionForTopic] Found resume UUID for topic ${topicId}: ${resumeSessionId} (source: TopicResumeMap — trusted)`);
@@ -1219,6 +1229,9 @@ spawnOpts) {
1219
1229
  }
1220
1230
  }, 8000);
1221
1231
  }
1232
+ if (contextWallForcesFresh) {
1233
+ _clearContextExhaustionFreshRequired?.(topicId);
1234
+ }
1222
1235
  return newSessionName;
1223
1236
  }
1224
1237
  /**
@@ -7826,7 +7839,15 @@ export async function startServer(options) {
7826
7839
  }
7827
7840
  // Check resume map for session continuity (keyed on routing key, so a
7828
7841
  // thread resumes its OWN session and not the channel-root one).
7829
- const resumeInfo = slackAdapter.getChannelResume(routingKey);
7842
+ const contextWallForcesFresh = conversationId !== null
7843
+ && _contextExhaustionFreshRequired?.(conversationId) === true;
7844
+ if (contextWallForcesFresh) {
7845
+ slackAdapter.removeChannelResume(routingKey);
7846
+ console.log(`[slack→session] Context-exhaustion latch active for conversation ${conversationId} — forcing fresh spawn`);
7847
+ }
7848
+ const resumeInfo = contextWallForcesFresh
7849
+ ? null
7850
+ : slackAdapter.getChannelResume(routingKey);
7830
7851
  const resumeSessionId = resumeInfo?.uuid ?? undefined;
7831
7852
  if (resumeInfo) {
7832
7853
  slackAdapter.removeChannelResume(routingKey);
@@ -7913,6 +7934,9 @@ export async function startServer(options) {
7913
7934
  slackAdapter.registerChannelSession(routingKey, newSessionName, isThreadSession ? `${channelId} (thread ${replyThreadTs})` : undefined);
7914
7935
  slackAdapter.trackMessageInjection(channelId, newSessionName, message.content);
7915
7936
  console.log(`[slack→session] ${resumeSessionId ? 'Resumed' : 'Spawned'} "${newSessionName}" for ${isThreadSession ? `thread ${routingKey}` : `channel ${channelId}`}`);
7937
+ if (contextWallForcesFresh && conversationId !== null) {
7938
+ _clearContextExhaustionFreshRequired?.(conversationId);
7939
+ }
7916
7940
  }
7917
7941
  }
7918
7942
  catch (err) {
@@ -10304,6 +10328,18 @@ export async function startServer(options) {
10304
10328
  },
10305
10329
  // P1/P2 cross-check dep — see SessionRecovery.killForRecovery().
10306
10330
  hasActiveProcesses: (name) => sessionManager.hasActiveProcesses(name),
10331
+ probeTranscript: (name) => {
10332
+ const session = sessionManager.listRunningSessions()
10333
+ .find((candidate) => candidate.tmuxSession === name);
10334
+ if (!session?.claudeSessionId) {
10335
+ return { resolved: false, path: '', size: 0, mtime: 0 };
10336
+ }
10337
+ return probeTranscript({
10338
+ framework: session.framework ?? 'claude-code',
10339
+ sessionId: session.claudeSessionId,
10340
+ projectDir: session.cwd ?? config.sessions.projectDir,
10341
+ });
10342
+ },
10307
10343
  respawnSession: async (topicId, _sessionName, recoveryPrompt) => {
10308
10344
  // Check Slack first (synthetic IDs are negative)
10309
10345
  const slackChId = slackProxyChannelMap.get(topicId);
@@ -10571,6 +10607,10 @@ export async function startServer(options) {
10571
10607
  // Clean up after 60 seconds — by then the kill and respawn are done
10572
10608
  setTimeout(() => contextExhaustionKills.delete(sessionName), 60_000);
10573
10609
  });
10610
+ _contextExhaustionFreshRequired = (topicId) => sessionRecovery?.hasContextWedgedSeen(topicId) === true;
10611
+ _clearContextExhaustionFreshRequired = (topicId) => {
10612
+ sessionRecovery?.clearContextWedgedSeen(topicId);
10613
+ };
10574
10614
  console.log(pc.green(' Session Recovery enabled (mechanical fast-path)'));
10575
10615
  }
10576
10616
  // SessionMonitor — proactive session health monitoring
@@ -12979,6 +13019,9 @@ export async function startServer(options) {
12979
13019
  return { recovered: result.recovered };
12980
13020
  }
12981
13021
  : undefined,
13022
+ hasContextExhaustionLatch: sessionRecovery
13023
+ ? (topicId) => sessionRecovery.hasContextWedgedSeen(topicId)
13024
+ : undefined,
12982
13025
  // Shared per-topic mutex — coordinates with PromiseBeacon.
12983
13026
  acquireProxyMutex: (topicId, holder) => proxyCoordinator.tryAcquire(topicId, holder),
12984
13027
  releaseProxyMutex: (topicId, holder) => proxyCoordinator.release(topicId, holder),