instar 1.3.1208 → 1.3.1210
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 +24 -1
- package/dashboard/subscriptions.js +164 -4
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +129 -8
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +12 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/AnthropicReloginBrowserDriver.d.ts +64 -0
- package/dist/core/AnthropicReloginBrowserDriver.d.ts.map +1 -0
- package/dist/core/AnthropicReloginBrowserDriver.js +178 -0
- package/dist/core/AnthropicReloginBrowserDriver.js.map +1 -0
- package/dist/core/BackupManager.d.ts.map +1 -1
- package/dist/core/BackupManager.js +5 -0
- package/dist/core/BackupManager.js.map +1 -1
- package/dist/core/ChromeCdpReloginBrowser.d.ts +41 -0
- package/dist/core/ChromeCdpReloginBrowser.d.ts.map +1 -0
- package/dist/core/ChromeCdpReloginBrowser.js +260 -0
- package/dist/core/ChromeCdpReloginBrowser.js.map +1 -0
- package/dist/core/ClaudePasteBackController.d.ts +24 -0
- package/dist/core/ClaudePasteBackController.d.ts.map +1 -0
- package/dist/core/ClaudePasteBackController.js +68 -0
- package/dist/core/ClaudePasteBackController.js.map +1 -0
- package/dist/core/EnrollmentWizard.d.ts +2 -0
- package/dist/core/EnrollmentWizard.d.ts.map +1 -1
- package/dist/core/EnrollmentWizard.js +1 -1
- package/dist/core/EnrollmentWizard.js.map +1 -1
- package/dist/core/PlaywrightProfileRegistry.d.ts +24 -0
- package/dist/core/PlaywrightProfileRegistry.d.ts.map +1 -1
- package/dist/core/PlaywrightProfileRegistry.js +44 -0
- package/dist/core/PlaywrightProfileRegistry.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +25 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SubscriptionReloginOrchestrator.d.ts +71 -0
- package/dist/core/SubscriptionReloginOrchestrator.d.ts.map +1 -0
- package/dist/core/SubscriptionReloginOrchestrator.js +257 -0
- package/dist/core/SubscriptionReloginOrchestrator.js.map +1 -0
- package/dist/core/SubscriptionReloginPolicy.d.ts +48 -0
- package/dist/core/SubscriptionReloginPolicy.d.ts.map +1 -0
- package/dist/core/SubscriptionReloginPolicy.js +87 -0
- package/dist/core/SubscriptionReloginPolicy.js.map +1 -0
- package/dist/core/SubscriptionReloginRuntime.d.ts +47 -0
- package/dist/core/SubscriptionReloginRuntime.d.ts.map +1 -0
- package/dist/core/SubscriptionReloginRuntime.js +187 -0
- package/dist/core/SubscriptionReloginRuntime.js.map +1 -0
- package/dist/core/SubscriptionReloginService.d.ts +50 -0
- package/dist/core/SubscriptionReloginService.d.ts.map +1 -0
- package/dist/core/SubscriptionReloginService.js +129 -0
- package/dist/core/SubscriptionReloginService.js.map +1 -0
- package/dist/core/SubscriptionReloginStore.d.ts +126 -0
- package/dist/core/SubscriptionReloginStore.d.ts.map +1 -0
- package/dist/core/SubscriptionReloginStore.js +336 -0
- package/dist/core/SubscriptionReloginStore.js.map +1 -0
- package/dist/core/TopicProfileOrchestrator.d.ts +1 -1
- package/dist/core/TopicProfileOrchestrator.d.ts.map +1 -1
- package/dist/core/TopicProfileOrchestrator.js +46 -3
- package/dist/core/TopicProfileOrchestrator.js.map +1 -1
- package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
- package/dist/core/WriteDomainRegistry.js +19 -0
- package/dist/core/WriteDomainRegistry.js.map +1 -1
- package/dist/core/componentCategories.d.ts.map +1 -1
- package/dist/core/componentCategories.js +1 -0
- package/dist/core/componentCategories.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +5 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +13 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/data/llmBenchCoverage.d.ts.map +1 -1
- package/dist/data/llmBenchCoverage.js +10 -0
- package/dist/data/llmBenchCoverage.js.map +1 -1
- package/dist/data/provenanceCoverage.d.ts +2 -0
- package/dist/data/provenanceCoverage.d.ts.map +1 -1
- package/dist/data/provenanceCoverage.js +12 -0
- package/dist/data/provenanceCoverage.js.map +1 -1
- package/dist/data/standards-guard-index.json +1 -1
- package/dist/data/standards-guard-index.meta.json +2 -2
- package/dist/data/standards-registry.meta.json +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +3 -1
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +4 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +27 -2
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +20 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/DashboardOperatorSessionStore.d.ts +22 -0
- package/dist/server/DashboardOperatorSessionStore.d.ts.map +1 -0
- package/dist/server/DashboardOperatorSessionStore.js +57 -0
- package/dist/server/DashboardOperatorSessionStore.js.map +1 -0
- package/dist/server/fileRoutes.d.ts.map +1 -1
- package/dist/server/fileRoutes.js +2 -0
- package/dist/server/fileRoutes.js.map +1 -1
- package/dist/server/routes.d.ts +10 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +235 -101
- package/dist/server/routes.js.map +1 -1
- package/dist/testing/selfActionRegistry.d.ts.map +1 -1
- package/dist/testing/selfActionRegistry.js +39 -0
- package/dist/testing/selfActionRegistry.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +65 -65
- package/src/data/llmBenchCoverage.ts +10 -0
- package/src/data/provenanceCoverage.ts +15 -0
- package/src/data/standards-guard-index.json +1 -1
- package/src/data/standards-guard-index.meta.json +2 -2
- package/src/data/standards-registry.meta.json +1 -1
- package/src/scaffold/templates.ts +3 -1
- package/upgrades/1.3.1209.md +26 -0
- package/upgrades/1.3.1210.md +31 -0
- package/upgrades/side-effects/assisted-subscription-relogin.md +112 -0
- package/upgrades/side-effects/topic-profile-kill-by-tmux-name.md +429 -0
package/dashboard/index.html
CHANGED
|
@@ -3706,6 +3706,15 @@
|
|
|
3706
3706
|
.sub-account-meta { font-size:12px; opacity:.6; margin:2px 0 2px; }
|
|
3707
3707
|
.sub-account-email { font-size:12px; opacity:.5; margin:0 0 10px; }
|
|
3708
3708
|
.sub-account-noquota { font-size:12px; opacity:.6; }
|
|
3709
|
+
.sub-profile-provision { border:1px solid var(--border,#2a2a2a); border-left:4px solid #16a34a;
|
|
3710
|
+
border-radius:10px; padding:14px 16px; display:flex; flex-direction:column; gap:10px; }
|
|
3711
|
+
.sub-profile-title { font-size:16px; font-weight:650; }
|
|
3712
|
+
.sub-profile-copy { margin:0; font-size:13px; line-height:1.5; opacity:.78; }
|
|
3713
|
+
.sub-profile-input { width:100%; box-sizing:border-box; min-height:44px; padding:10px 12px;
|
|
3714
|
+
font-size:16px; border:1px solid var(--border,#2a2a2a); border-radius:8px; background:transparent; color:inherit; }
|
|
3715
|
+
.sub-profile-create { min-height:44px; padding:10px 14px; font-size:14px; font-weight:600;
|
|
3716
|
+
border-radius:8px; border:1px solid #16a34a; background:rgba(22,163,74,.12); color:inherit; cursor:pointer; }
|
|
3717
|
+
.sub-profile-status { min-height:18px; font-size:12px; line-height:1.45; opacity:.8; }
|
|
3709
3718
|
.sub-quota { margin:8px 0; }
|
|
3710
3719
|
.sub-quota-head { display:flex; justify-content:space-between; font-size:12px; margin-bottom:4px; }
|
|
3711
3720
|
.sub-quota-label { opacity:.8; }
|
|
@@ -3773,6 +3782,11 @@
|
|
|
3773
3782
|
your subscriptions (ws52-operator-tap-not-text Part A). Leads the panel as the primary
|
|
3774
3783
|
action when present; silent otherwise. -->
|
|
3775
3784
|
<div id="subFollowMe"></div>
|
|
3785
|
+
<section class="ph-section">
|
|
3786
|
+
<h3 class="ph-h">Dedicated browser profiles</h3>
|
|
3787
|
+
<p class="ph-h-sub">Prepare a private Chrome profile for one Google account. The agent handles the machine; you only respond to a secure password, verification, or approval link when the provider truly requires it.</p>
|
|
3788
|
+
<div id="subProfileProvision"></div>
|
|
3789
|
+
</section>
|
|
3776
3790
|
<!-- Account × Machine matrix (account-machine-matrix) — at-a-glance "which account is set up
|
|
3777
3791
|
on which machine," with a one-tap "Set up" per empty cell that runs the whole sign-in in
|
|
3778
3792
|
the dashboard (PIN → sign-in link → paste the code). Hidden when there's nothing to show. -->
|
|
@@ -4146,6 +4160,9 @@
|
|
|
4146
4160
|
// ── State ────────────────────────────────────────────────
|
|
4147
4161
|
let ws = null;
|
|
4148
4162
|
let token = '';
|
|
4163
|
+
// Short-lived proof of a recent PIN unlock. Kept in memory only: the durable
|
|
4164
|
+
// API token is intentionally insufficient for operator-only actions.
|
|
4165
|
+
let operatorSessionToken = '';
|
|
4149
4166
|
let sessions = [];
|
|
4150
4167
|
// Sessions running on OTHER machines in the pool (GET /sessions?scope=pool).
|
|
4151
4168
|
// Kept separate from `sessions` because the WebSocket replaces that array
|
|
@@ -4193,6 +4210,8 @@
|
|
|
4193
4210
|
.then(data => {
|
|
4194
4211
|
if (data && data.token) {
|
|
4195
4212
|
token = data.token;
|
|
4213
|
+
operatorSessionToken = typeof data.operatorSessionToken === 'string'
|
|
4214
|
+
? data.operatorSessionToken : '';
|
|
4196
4215
|
} else {
|
|
4197
4216
|
// Fall back: treat input as raw Bearer token
|
|
4198
4217
|
token = pin;
|
|
@@ -9670,6 +9689,7 @@
|
|
|
9670
9689
|
accounts: document.getElementById('subAccounts'),
|
|
9671
9690
|
followMe: document.getElementById('subFollowMe'),
|
|
9672
9691
|
matrix: document.getElementById('subMatrix'),
|
|
9692
|
+
profileProvision: document.getElementById('subProfileProvision'),
|
|
9673
9693
|
};
|
|
9674
9694
|
// Pass opts through (method/body/signal) so the controller can POST the follow-me
|
|
9675
9695
|
// scan + the PIN-gated issue-for-machine, not just GET.
|
|
@@ -9677,7 +9697,10 @@
|
|
|
9677
9697
|
...opts,
|
|
9678
9698
|
headers: { 'Authorization': `Bearer ${token}`, ...(opts.headers || {}) },
|
|
9679
9699
|
});
|
|
9680
|
-
__subController = __subModule.createController({
|
|
9700
|
+
__subController = __subModule.createController({
|
|
9701
|
+
doc: document, els, fetchImpl,
|
|
9702
|
+
getOperatorSessionToken: () => operatorSessionToken,
|
|
9703
|
+
});
|
|
9681
9704
|
}
|
|
9682
9705
|
__subController.start();
|
|
9683
9706
|
}
|
|
@@ -240,7 +240,7 @@ export function groupAccountsByProvider(accounts) {
|
|
|
240
240
|
* `inUseAccountId` (optional) is the account the agent is CURRENTLY running on —
|
|
241
241
|
* that card gets an "In use" marker so "active" (healthy) reads distinct from
|
|
242
242
|
* "actually running right now". */
|
|
243
|
-
export function renderAccounts(doc, target, accounts, now = Date.now(), inUseAccountId = null) {
|
|
243
|
+
export function renderAccounts(doc, target, accounts, now = Date.now(), inUseAccountId = null, reloginEpisodes = []) {
|
|
244
244
|
if (!target) return;
|
|
245
245
|
target.replaceChildren();
|
|
246
246
|
if (!Array.isArray(accounts) || accounts.length === 0) {
|
|
@@ -294,6 +294,31 @@ export function renderAccounts(doc, target, accounts, now = Date.now(), inUseAcc
|
|
|
294
294
|
if (refAge) {
|
|
295
295
|
card.appendChild(el(doc, 'div', 'sub-account-refresh', `Token auto-refreshed ${refAge}`));
|
|
296
296
|
}
|
|
297
|
+
const repair = Array.isArray(reloginEpisodes)
|
|
298
|
+
? reloginEpisodes.find((episode) => episode && a && episode.accountId === a.id)
|
|
299
|
+
: null;
|
|
300
|
+
if (repair) {
|
|
301
|
+
const repairBox = el(doc, 'div', 'sub-account-repair');
|
|
302
|
+
const terminal = ['succeeded', 'refused', 'cancelled', 'failed'].includes(repair.state);
|
|
303
|
+
const label = repair.state === 'suggested' ? 'Sign-in repair is ready.'
|
|
304
|
+
: repair.state === 'succeeded' ? 'Sign-in repaired.'
|
|
305
|
+
: repair.state === 'waiting-operator-only' ? 'Sign-in needs your help.'
|
|
306
|
+
: repair.state === 'failed' ? 'Sign-in repair stopped safely.'
|
|
307
|
+
: repair.state === 'refused' ? 'Sign-in repair was refused for safety.'
|
|
308
|
+
: repair.state === 'cancelled' ? 'Sign-in repair cancelled.'
|
|
309
|
+
: 'Repairing sign-in…';
|
|
310
|
+
repairBox.appendChild(el(doc, 'div', 'sub-account-repair-status', label));
|
|
311
|
+
const action = repair.state === 'suggested' ? ['approve', 'Repair sign-in']
|
|
312
|
+
: repair.state === 'failed' ? ['retry', 'Try repair again']
|
|
313
|
+
: (!terminal ? ['cancel', 'Cancel repair'] : null);
|
|
314
|
+
if (action) {
|
|
315
|
+
const button = el(doc, 'button', `sub-account-repair-${action[0]}`, action[1]);
|
|
316
|
+
button.setAttribute('data-relogin-action', action[0]);
|
|
317
|
+
button.setAttribute('data-relogin-id', sanitizeForDisplay(repair.id, 'label'));
|
|
318
|
+
repairBox.appendChild(button);
|
|
319
|
+
}
|
|
320
|
+
card.appendChild(repairBox);
|
|
321
|
+
}
|
|
297
322
|
target.appendChild(card);
|
|
298
323
|
}
|
|
299
324
|
}
|
|
@@ -934,8 +959,54 @@ const URLS = {
|
|
|
934
959
|
accountsPool: '/subscription-pool?scope=pool',
|
|
935
960
|
startCell: '/subscription-pool/matrix/start-cell', // POST (PIN-gated) — start a cell's sign-in
|
|
936
961
|
cancel: '/subscription-pool/follow-me/cancel', // POST (Bearer, no PIN) — cancel an in-flight cell (relay → self/peer)
|
|
962
|
+
relogin: '/subscription-relogin',
|
|
963
|
+
provisionProfile: '/playwright-profiles/provision',
|
|
937
964
|
};
|
|
938
965
|
|
|
966
|
+
export function renderProfileProvisioner(doc, target) {
|
|
967
|
+
if (!target) return;
|
|
968
|
+
const card = el(doc, 'div', 'sub-profile-provision');
|
|
969
|
+
card.setAttribute('data-interaction-open', '1');
|
|
970
|
+
card.appendChild(el(doc, 'div', 'sub-profile-title', 'Create a dedicated sign-in profile'));
|
|
971
|
+
card.appendChild(el(doc, 'p', 'sub-profile-copy',
|
|
972
|
+
'Instar prepares the private Chrome profile on this machine. If a password or verification step is needed, your agent will send a secure link — you never need to access this machine.'));
|
|
973
|
+
|
|
974
|
+
const identity = el(doc, 'input', 'sub-profile-input');
|
|
975
|
+
identity.setAttribute('type', 'email');
|
|
976
|
+
identity.setAttribute('inputmode', 'email');
|
|
977
|
+
identity.setAttribute('autocomplete', 'email');
|
|
978
|
+
identity.setAttribute('placeholder', 'Google account email');
|
|
979
|
+
identity.setAttribute('aria-label', 'Google account email');
|
|
980
|
+
card.appendChild(identity);
|
|
981
|
+
|
|
982
|
+
const profileId = el(doc, 'input', 'sub-profile-input');
|
|
983
|
+
profileId.setAttribute('type', 'text');
|
|
984
|
+
profileId.setAttribute('placeholder', 'Profile name, for example work-google');
|
|
985
|
+
profileId.setAttribute('aria-label', 'Profile name');
|
|
986
|
+
card.appendChild(profileId);
|
|
987
|
+
|
|
988
|
+
const method = el(doc, 'select', 'sub-profile-input');
|
|
989
|
+
method.setAttribute('aria-label', 'Sign-in method');
|
|
990
|
+
for (const [value, label] of [
|
|
991
|
+
['session-cookie', 'Already signed in / provider approval'],
|
|
992
|
+
['password', 'Password'],
|
|
993
|
+
['password+totp', 'Password + authenticator'],
|
|
994
|
+
]) {
|
|
995
|
+
const option = el(doc, 'option', '', label);
|
|
996
|
+
option.setAttribute('value', value);
|
|
997
|
+
method.appendChild(option);
|
|
998
|
+
}
|
|
999
|
+
card.appendChild(method);
|
|
1000
|
+
|
|
1001
|
+
const button = el(doc, 'button', 'sub-profile-create', 'Create dedicated profile');
|
|
1002
|
+
button.setAttribute('type', 'button');
|
|
1003
|
+
card.appendChild(button);
|
|
1004
|
+
const status = el(doc, 'div', 'sub-profile-status', '');
|
|
1005
|
+
status.setAttribute('aria-live', 'polite');
|
|
1006
|
+
card.appendChild(status);
|
|
1007
|
+
target.replaceChildren(card);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
939
1010
|
export function createController(opts) {
|
|
940
1011
|
const {
|
|
941
1012
|
doc,
|
|
@@ -945,6 +1016,7 @@ export function createController(opts) {
|
|
|
945
1016
|
cadenceMs = 30_000,
|
|
946
1017
|
schedule = (fn, ms) => setTimeout(fn, ms),
|
|
947
1018
|
cancel = (id) => clearTimeout(id),
|
|
1019
|
+
getOperatorSessionToken = () => '',
|
|
948
1020
|
} = opts;
|
|
949
1021
|
|
|
950
1022
|
// matrixTransient: client-side last-attempt state per `${accountId}::${machineId}` cell (FD6 —
|
|
@@ -955,6 +1027,7 @@ export function createController(opts) {
|
|
|
955
1027
|
// add the D4 ceremony when the episode lands. recentOutcomes: client-observed terminal
|
|
956
1028
|
// outcomes rendered as explicit cards in the pending panel (D4 — never a vanishing line).
|
|
957
1029
|
const state = { timerId: null, active: false, inFlight: null, offers: [], approveWired: false,
|
|
1030
|
+
reloginWired: false, reloginEpisodes: [], profileProvisionWired: false,
|
|
958
1031
|
matrixWired: false, matrixTransient: {}, lastPoolBody: null, lastPendingBody: null,
|
|
959
1032
|
matrixEpisodes: {}, recentOutcomes: [] };
|
|
960
1033
|
|
|
@@ -988,17 +1061,18 @@ export function createController(opts) {
|
|
|
988
1061
|
if (state.inFlight) { try { state.inFlight.abort(); } catch { /* superseded */ } }
|
|
989
1062
|
const controller = typeof AbortController !== 'undefined' ? new AbortController() : { signal: undefined, abort() {} };
|
|
990
1063
|
state.inFlight = controller;
|
|
991
|
-
let accountsBody, pendingBody, inUseBody, scanBody, poolBody;
|
|
1064
|
+
let accountsBody, pendingBody, inUseBody, scanBody, poolBody, reloginBody;
|
|
992
1065
|
try {
|
|
993
1066
|
// in-use AND the follow-me scan are best-effort — their failure must not blank the
|
|
994
1067
|
// accounts list, so each is caught independently (in-use → "unknown"; scan → no card).
|
|
995
1068
|
// poolBody (scope=pool) feeds the account×machine matrix; best-effort (matrix is hidden if absent).
|
|
996
|
-
[accountsBody, pendingBody, inUseBody, scanBody, poolBody] = await Promise.all([
|
|
1069
|
+
[accountsBody, pendingBody, inUseBody, scanBody, poolBody, reloginBody] = await Promise.all([
|
|
997
1070
|
fetchJson(URLS.accounts, controller),
|
|
998
1071
|
fetchJson(URLS.pending, controller),
|
|
999
1072
|
fetchJson(URLS.inUse, controller).catch(() => null),
|
|
1000
1073
|
postJson(URLS.scan, {}, controller).then((r) => (r.ok ? r.json : null)).catch(() => null),
|
|
1001
1074
|
fetchJson(URLS.accountsPool, controller).catch(() => null),
|
|
1075
|
+
fetchJson(URLS.relogin, controller).catch(() => null),
|
|
1002
1076
|
]);
|
|
1003
1077
|
} catch {
|
|
1004
1078
|
if (controller.signal && controller.signal.aborted) return;
|
|
@@ -1015,10 +1089,57 @@ export function createController(opts) {
|
|
|
1015
1089
|
return;
|
|
1016
1090
|
}
|
|
1017
1091
|
state.offers = scanBody && Array.isArray(scanBody.offered) ? scanBody.offered : [];
|
|
1092
|
+
state.reloginEpisodes = reloginBody && Array.isArray(reloginBody.episodes) ? reloginBody.episodes : [];
|
|
1093
|
+
if (els.profileProvision && !state.profileProvisionWired) {
|
|
1094
|
+
renderProfileProvisioner(doc, els.profileProvision);
|
|
1095
|
+
wireProfileProvisioner();
|
|
1096
|
+
}
|
|
1018
1097
|
render(accountsBody, pendingBody, inUseBody, poolBody);
|
|
1019
1098
|
reschedule();
|
|
1020
1099
|
}
|
|
1021
1100
|
|
|
1101
|
+
function wireProfileProvisioner() {
|
|
1102
|
+
if (!els.profileProvision || state.profileProvisionWired) return;
|
|
1103
|
+
state.profileProvisionWired = true;
|
|
1104
|
+
els.profileProvision.addEventListener('click', (event) => {
|
|
1105
|
+
const button = event.target && event.target.closest ? event.target.closest('.sub-profile-create') : null;
|
|
1106
|
+
if (!button) return;
|
|
1107
|
+
const proof = getOperatorSessionToken();
|
|
1108
|
+
const identity = els.profileProvision.querySelector('[aria-label="Google account email"]')?.value?.trim() || '';
|
|
1109
|
+
const profileId = els.profileProvision.querySelector('[aria-label="Profile name"]')?.value?.trim() || '';
|
|
1110
|
+
const loginMethod = els.profileProvision.querySelector('[aria-label="Sign-in method"]')?.value || 'session-cookie';
|
|
1111
|
+
const status = els.profileProvision.querySelector('.sub-profile-status');
|
|
1112
|
+
if (!proof) { status.textContent = 'Unlock the dashboard again, then retry.'; return; }
|
|
1113
|
+
if (!identity || !profileId) { status.textContent = 'Enter the Google account and a profile name.'; return; }
|
|
1114
|
+
button.setAttribute('disabled', 'disabled');
|
|
1115
|
+
status.textContent = 'Preparing the private profile…';
|
|
1116
|
+
void (async () => {
|
|
1117
|
+
try {
|
|
1118
|
+
const response = await fetchImpl(URLS.provisionProfile, {
|
|
1119
|
+
method: 'POST',
|
|
1120
|
+
headers: { 'Content-Type': 'application/json', 'X-Instar-Operator-Session': proof },
|
|
1121
|
+
body: JSON.stringify({ profileId, identity, loginMethod }),
|
|
1122
|
+
});
|
|
1123
|
+
const body = await response.json().catch(() => ({}));
|
|
1124
|
+
if (!response.ok) {
|
|
1125
|
+
status.textContent = response.status === 401
|
|
1126
|
+
? 'Unlock the dashboard again, then retry.'
|
|
1127
|
+
: `Couldn’t create the profile: ${body.error || 'try again'}`;
|
|
1128
|
+
button.removeAttribute('disabled');
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
status.textContent = loginMethod === 'session-cookie'
|
|
1132
|
+
? 'Profile ready. Your agent can now open the provider sign-in and send you any required approval link.'
|
|
1133
|
+
: 'Profile ready. Your agent will send a secure credential link before the first sign-in.';
|
|
1134
|
+
button.textContent = 'Profile created';
|
|
1135
|
+
} catch {
|
|
1136
|
+
status.textContent = 'Couldn’t reach the server — try again.';
|
|
1137
|
+
button.removeAttribute('disabled');
|
|
1138
|
+
}
|
|
1139
|
+
})();
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1022
1143
|
// ── Episode + outcome bookkeeping (D1/D4/D5) ───────────────────────────────
|
|
1023
1144
|
function purgeTransients() {
|
|
1024
1145
|
const t = now();
|
|
@@ -1128,7 +1249,8 @@ export function createController(opts) {
|
|
|
1128
1249
|
const accounts = accountsBody && Array.isArray(accountsBody.accounts) ? accountsBody.accounts : [];
|
|
1129
1250
|
const logins = pendingBody && Array.isArray(pendingBody.logins) ? pendingBody.logins : [];
|
|
1130
1251
|
const inUseAccountId = inUseBody && inUseBody.activeAccountId ? inUseBody.activeAccountId : null;
|
|
1131
|
-
renderAccounts(doc, els.accounts, accounts, now(), inUseAccountId);
|
|
1252
|
+
renderAccounts(doc, els.accounts, accounts, now(), inUseAccountId, state.reloginEpisodes);
|
|
1253
|
+
wireReloginActions();
|
|
1132
1254
|
purgeTransients();
|
|
1133
1255
|
// Episode reconciliation runs FIRST so a terminal transition it derives (expired /
|
|
1134
1256
|
// landed) is visible to BOTH surfaces on this same tick (the panel card + the cell).
|
|
@@ -1169,6 +1291,44 @@ export function createController(opts) {
|
|
|
1169
1291
|
}
|
|
1170
1292
|
}
|
|
1171
1293
|
|
|
1294
|
+
function wireReloginActions() {
|
|
1295
|
+
if (!els.accounts || state.reloginWired) return;
|
|
1296
|
+
state.reloginWired = true;
|
|
1297
|
+
els.accounts.addEventListener('click', (event) => {
|
|
1298
|
+
const button = event.target && event.target.closest ? event.target.closest('[data-relogin-action]') : null;
|
|
1299
|
+
if (!button) return;
|
|
1300
|
+
const action = button.getAttribute('data-relogin-action');
|
|
1301
|
+
const episodeId = button.getAttribute('data-relogin-id');
|
|
1302
|
+
if (!['approve', 'cancel', 'retry'].includes(action) || !episodeId) return;
|
|
1303
|
+
const proof = getOperatorSessionToken();
|
|
1304
|
+
if (!proof) {
|
|
1305
|
+
button.textContent = 'Unlock the dashboard again';
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
button.setAttribute('disabled', 'disabled');
|
|
1309
|
+
void postReloginAction(episodeId, action, proof, button);
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
async function postReloginAction(episodeId, action, proof, button) {
|
|
1314
|
+
try {
|
|
1315
|
+
const response = await fetchImpl(`${URLS.relogin}/${encodeURIComponent(episodeId)}/${action}`, {
|
|
1316
|
+
method: 'POST',
|
|
1317
|
+
headers: { 'Content-Type': 'application/json', 'X-Instar-Operator-Session': proof },
|
|
1318
|
+
body: '{}',
|
|
1319
|
+
});
|
|
1320
|
+
if (!response.ok) {
|
|
1321
|
+
button.textContent = response.status === 401 ? 'Unlock the dashboard again' : 'Couldn’t start — try again';
|
|
1322
|
+
button.removeAttribute('disabled');
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
await tick();
|
|
1326
|
+
} catch {
|
|
1327
|
+
button.textContent = 'Couldn’t reach the server — try again';
|
|
1328
|
+
button.removeAttribute('disabled');
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1172
1332
|
// Delegated, wired ONCE: an Approve tap reads the card's PIN, builds the issue-for-machine
|
|
1173
1333
|
// payload from the held offers (FD2 agents resolved server-side — never typed), and POSTs the
|
|
1174
1334
|
// PIN-gated mandate. The card carries only non-sensitive ids; the PIN is sent once, never stored.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqFH,OAAO,EAAE,cAAc,EAAwD,MAAM,2BAA2B,CAAC;AAUjH,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AA4BjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAiI7D,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;AA0ID,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAouDD;;;;;;;;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,CAivBN;AAgmBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA48sBtE;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
|
@@ -74,6 +74,7 @@ import { gapBEligibleForTopic, recentUserMessageFromHistory, recentUserMessageAt
|
|
|
74
74
|
import { TopicProfileTransferCarrier, createTopicProfilePullHandler } from '../core/TopicProfileTransferCarrier.js';
|
|
75
75
|
import { resolveFrameworkBinaryPath, computeGrokInteractiveOptIn } from '../core/frameworkSessionLaunch.js';
|
|
76
76
|
import { closeAllSqlite } from '../core/SqliteRegistry.js';
|
|
77
|
+
import { DP_SUBSCRIPTION_RELOGIN_ACTION } from '../data/provenanceCoverage.js';
|
|
77
78
|
import { SessionManager, IDLE_PROMPT_PATTERNS } from '../core/SessionManager.js';
|
|
78
79
|
import { configureSyncOpMarker, defaultInflightMarkerReader } from '../core/InFlightSyncOpMarker.js';
|
|
79
80
|
import { StateManager } from '../core/StateManager.js';
|
|
@@ -419,7 +420,10 @@ async function handleFixCommand(topicId, text, deps) {
|
|
|
419
420
|
else {
|
|
420
421
|
for (const s of stale) {
|
|
421
422
|
try {
|
|
422
|
-
|
|
423
|
+
// killSession() resolves BY ID; handing it a tmux name misses the
|
|
424
|
+
// lookup and returns false silently ("the stop that does not stop").
|
|
425
|
+
// killSessionByTmuxName is the single place that resolution lives.
|
|
426
|
+
deps.sessionManager.killSessionByTmuxName(s.tmuxSession);
|
|
423
427
|
}
|
|
424
428
|
catch { /* best effort */ }
|
|
425
429
|
}
|
|
@@ -13503,6 +13507,101 @@ export async function startServer(options) {
|
|
|
13503
13507
|
interactiveReservePct: 0.4,
|
|
13504
13508
|
maxDailyCents: promiseBeaconCfg.maxDailyLlmSpendCents ?? 100,
|
|
13505
13509
|
});
|
|
13510
|
+
// Assisted subscription re-login — explicit opt-in, dry-run/observe by default.
|
|
13511
|
+
// Constructed from the existing authority owners; no credential/token is copied.
|
|
13512
|
+
let subscriptionReloginRuntime = null;
|
|
13513
|
+
const reloginCfg = config.subscriptionPool?.assistedRelogin;
|
|
13514
|
+
if (reloginCfg?.enabled === true && subscriptionLoginLedger && subscriptionPoolMachineId && sharedIntelligence) {
|
|
13515
|
+
try {
|
|
13516
|
+
const [{ createSubscriptionReloginRuntime }, { PlaywrightProfileRegistry }, { SecretStore }, { secretKeyPaths }, { ClaudePasteBackController }, { ChromeCdpReloginBrowser }] = await Promise.all([
|
|
13517
|
+
import('../core/SubscriptionReloginRuntime.js'), import('../core/PlaywrightProfileRegistry.js'),
|
|
13518
|
+
import('../core/SecretStore.js'), import('../core/SecretSync.js'),
|
|
13519
|
+
import('../core/ClaudePasteBackController.js'), import('../core/ChromeCdpReloginBrowser.js'),
|
|
13520
|
+
]);
|
|
13521
|
+
const vault = new SecretStore({ stateDir: config.stateDir, forceFileKey: config.secrets?.forceFileKey });
|
|
13522
|
+
const profiles = new PlaywrightProfileRegistry({ stateDir: config.stateDir, projectDir: config.projectDir,
|
|
13523
|
+
listVaultNames: () => {
|
|
13524
|
+
try {
|
|
13525
|
+
return secretKeyPaths(vault.read());
|
|
13526
|
+
}
|
|
13527
|
+
catch {
|
|
13528
|
+
return null;
|
|
13529
|
+
}
|
|
13530
|
+
} });
|
|
13531
|
+
const pasteBack = new ClaudePasteBackController({
|
|
13532
|
+
captureOutput: (session, lines) => sessionManager.captureOutput(session, lines),
|
|
13533
|
+
sendInput: (session, input) => sessionManager.sendInput(session, input),
|
|
13534
|
+
clearHistory: (session) => {
|
|
13535
|
+
const tmux = detectTmuxPath();
|
|
13536
|
+
if (!tmux)
|
|
13537
|
+
throw new Error('tmux-unavailable');
|
|
13538
|
+
execFileSync(tmux, ['clear-history', '-t', `=${session}:`], { stdio: 'ignore' });
|
|
13539
|
+
},
|
|
13540
|
+
credentialReady: async (login) => {
|
|
13541
|
+
if (!login.configHome)
|
|
13542
|
+
return false;
|
|
13543
|
+
const result = await subscriptionIdentityOracle.resolveSlotTenant(login.configHome);
|
|
13544
|
+
return !('unavailable' in result) && typeof result.email === 'string' && result.email.length > 0;
|
|
13545
|
+
},
|
|
13546
|
+
});
|
|
13547
|
+
const notify = telegram ? async (episode, deliveryKey, kind) => {
|
|
13548
|
+
const summary = kind === 'approval'
|
|
13549
|
+
? 'A Claude Code subscription sign-in needs repair. Open Subscriptions and tap Repair sign-in once.'
|
|
13550
|
+
: kind === 'operator-only'
|
|
13551
|
+
? 'Automated sign-in paused at a provider security challenge. Open Subscriptions to continue.'
|
|
13552
|
+
: episode.state === 'succeeded'
|
|
13553
|
+
? 'Claude Code subscription sign-in was repaired and verified.'
|
|
13554
|
+
: `Claude Code subscription repair ended in ${episode.state}. Open Subscriptions for the redacted audit.`;
|
|
13555
|
+
await telegram.createAttentionItem({ id: deliveryKey.replace(/:/g, '-'), title: 'Claude Code sign-in repair',
|
|
13556
|
+
summary, description: summary, category: 'subscription-relogin',
|
|
13557
|
+
priority: kind === 'terminal' && episode.state === 'succeeded' ? 'NORMAL' : 'HIGH',
|
|
13558
|
+
sourceContext: 'assisted-subscription-relogin' });
|
|
13559
|
+
} : undefined;
|
|
13560
|
+
subscriptionReloginRuntime = createSubscriptionReloginRuntime({ stateDir: config.stateDir,
|
|
13561
|
+
projectDir: config.projectDir, machineId: subscriptionPoolMachineId,
|
|
13562
|
+
mode: reloginCfg.dryRun !== false ? 'observe' : (reloginCfg.mode ?? 'approval'),
|
|
13563
|
+
pool: subscriptionPool, ledger: subscriptionLoginLedger, enrollment: enrollmentWizard,
|
|
13564
|
+
profiles, quotaPoller, identityOracle: subscriptionIdentityOracle, pasteBack,
|
|
13565
|
+
createBrowser: (userDataDir) => new ChromeCdpReloginBrowser({ userDataDir }),
|
|
13566
|
+
resolveSecret: async (name) => { const value = vault.get(name); return typeof value === 'string' ? value : null; },
|
|
13567
|
+
supervise: async ({ snapshot, allowedActions }) => {
|
|
13568
|
+
const prompt = [
|
|
13569
|
+
'You are a Tier-1 validator for a bounded Claude sign-in browser worker.',
|
|
13570
|
+
'Choose exactly one action from allowedActions. Output only that action token.',
|
|
13571
|
+
'You cannot authorize an account, broaden scopes/origins, retrieve secrets, or declare success.',
|
|
13572
|
+
`closedSnapshot=${JSON.stringify(snapshot)}`,
|
|
13573
|
+
`allowedActions=${JSON.stringify(allowedActions)}`,
|
|
13574
|
+
].join('\n');
|
|
13575
|
+
const raw = await sharedLlmQueue.enqueue('background', (signal) => sharedIntelligence.evaluate(prompt, {
|
|
13576
|
+
model: 'fast', maxTokens: 20, temperature: 0, signal,
|
|
13577
|
+
attribution: { component: 'subscription-relogin-supervisor' },
|
|
13578
|
+
provenance: {
|
|
13579
|
+
decisionPoint: DP_SUBSCRIPTION_RELOGIN_ACTION,
|
|
13580
|
+
context: { ...snapshot },
|
|
13581
|
+
optionsPresented: [...allowedActions],
|
|
13582
|
+
promptId: 'subscription-relogin-action-v1',
|
|
13583
|
+
},
|
|
13584
|
+
}), 1);
|
|
13585
|
+
const action = raw.trim();
|
|
13586
|
+
if (!allowedActions.includes(action))
|
|
13587
|
+
throw new Error('relogin-supervisor-invalid-action');
|
|
13588
|
+
return action;
|
|
13589
|
+
},
|
|
13590
|
+
onSuggested: notify ? (episode, key) => notify(episode, key, 'approval') : undefined,
|
|
13591
|
+
onOperatorOnly: notify ? (episode, key) => notify(episode, key, 'operator-only') : undefined,
|
|
13592
|
+
onTerminal: notify ? (episode, key) => notify(episode, key, 'terminal') : undefined,
|
|
13593
|
+
allowedScopes: reloginCfg.allowedScopes ?? [], tickMs: reloginCfg.tickMs,
|
|
13594
|
+
maxAttempts: reloginCfg.maxAttempts, retryBaseMs: reloginCfg.retryBaseMs, });
|
|
13595
|
+
subscriptionReloginRuntime.start();
|
|
13596
|
+
console.log(pc.green(` Assisted subscription re-login: ${reloginCfg.dryRun !== false ? 'observe' : (reloginCfg.mode ?? 'approval')}`));
|
|
13597
|
+
}
|
|
13598
|
+
catch (error) {
|
|
13599
|
+
// @silent-fallback-ok — bootstrap refusal is logged and the runtime is explicitly darkened; routes return typed 503.
|
|
13600
|
+
console.warn(`[subscription-relogin] bootstrap refused: ${error instanceof Error ? error.message : String(error)}`);
|
|
13601
|
+
subscriptionReloginRuntime?.close();
|
|
13602
|
+
subscriptionReloginRuntime = null;
|
|
13603
|
+
}
|
|
13604
|
+
}
|
|
13506
13605
|
// sharedLlmQueue is wired into both PromiseBeacon (background lane) and
|
|
13507
13606
|
// PresenceProxy (interactive lane) below.
|
|
13508
13607
|
// ── WS3 one-voice speaker election (MULTI-MACHINE-SEAMLESSNESS-SPEC) ──
|
|
@@ -24771,6 +24870,13 @@ export async function startServer(options) {
|
|
|
24771
24870
|
} if (peers.length === 0)
|
|
24772
24871
|
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, conversationRegistry, conversationBindAuth, conversationFollowThrough, whatsappBusinessBackend, messageBridge, hookEventReceiver, worktreeMonitor, subagentTracker, instructionsVerifier, handshakeManager: threadlineHandshake, threadlineRouter, conversationStore, threadLog, threadMessageRecorder, warrantsReplyGate, collaborationSurfacer, threadResumeMap, topicLinkageHandler: topicLinkageHandler ?? undefined, threadlineRelayClient, getLastRelayEvent: threadlineGetLastRelayEvent, 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, getMachineCoherence: () => _machineCoherenceSentinel, getSingleMachineFailoverGap: () => _singleMachineFailoverGap, getMissingLoginSession: () => _missingLoginSession, getSessionPoolFailoverRunner: () => _sessionPoolFailoverRunnerDriver?.status() ?? null, sessionPoolPromotionActivation: _sessionPoolPromotionActivation, meshRpcDispatcher, deliverA2aToMachine: _deliverA2aToMachine ?? undefined, workingSetPullCoordinator, workingSetArtifactManager, orchestratorPoller, commitmentReplicaStore, preferenceReplicaStore, replicatedRecordEmitter, conflictStore, rollbackUnmerge, droppedOriginRegistry, preferencesUnionReader, forwardCommitmentMutate, sessionOwnershipRegistry, sendDrain: _sendDrain ?? undefined, topicPinStore: _topicPinStore ?? undefined, topicPinSkewQuarantine: _topicPinSkewQuarantine ?? undefined, topicPinFoldView: _topicPinFoldView ?? undefined, ownershipReconciler: _ownershipReconciler ?? undefined, staleOwnerEngine: _staleOwnerEngine ?? undefined, duplicateReconciler: _duplicateReconciler ?? undefined, ownerDarkLadder: _ownerDarkLadder ?? undefined, spawnAdmission: _spawnAdmission ?? undefined, judgmentProvenance: _judgmentProvenance ?? 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, externalHogSentinel, orphanedWorkSentinel, mcpProcessReaper, geminiLoopRunner, sleepController, agentActivityState, reapLog, resumeQueue, resumeDrainer, autonomousLivenessReconciler, enforcedTerminationStatus: () => enforcedTerminationWatchdog?.guardStatus() ?? null, prHandLease: prHandLease ?? undefined, standDownRegistry: _standDownRegistry ?? undefined, standDownAudit: _standDownAudit ?? undefined, operatorStopRecorder: recordOperatorStop, sleepWakeDetector, unjustifiedStopGate, stopGateDb, stopNotifier, liveTestGate, liveTestGateMode, liveTestRunnerCtx }); // Resolve the late-bound topic-operator getter (increment 2e): routing was
|
|
24773
24872
|
server.setSubscriptionLoginLedger(subscriptionLoginLedger);
|
|
24873
|
+
server.setSubscriptionRelogin(subscriptionReloginRuntime ? {
|
|
24874
|
+
store: subscriptionReloginRuntime.store,
|
|
24875
|
+
approve: (episodeId) => subscriptionReloginRuntime.service.approve(episodeId),
|
|
24876
|
+
cancel: (episodeId) => subscriptionReloginRuntime.service.cancel(episodeId),
|
|
24877
|
+
retry: (episodeId) => subscriptionReloginRuntime.service.retry(episodeId),
|
|
24878
|
+
close: () => subscriptionReloginRuntime.close(),
|
|
24879
|
+
} : null);
|
|
24774
24880
|
server.setWorkQueue(workQueue);
|
|
24775
24881
|
if (_stateSyncStoresResolved?.classReview?.enabled && replicatedPeerStreamReader) {
|
|
24776
24882
|
const { CLASS_REVIEW_STORE_KEY, classReviewFromOriginRecord } = await import('../core/ClassReviewReplicatedStore.js');
|
|
@@ -24909,14 +25015,29 @@ export async function startServer(options) {
|
|
|
24909
25015
|
return 'unconfirmed';
|
|
24910
25016
|
return paneIdleWithEmptyInput(tail) ? 'confirmed-idle' : 'busy';
|
|
24911
25017
|
},
|
|
24912
|
-
|
|
25018
|
+
// NB: listTopicSessions() supplies `sessionName` as the session's
|
|
25019
|
+
// TMUX NAME. killSession() resolves BY ID (state/sessions/<id>.json),
|
|
25020
|
+
// so passing a name here missed the lookup and returned false with no
|
|
25021
|
+
// kill — "the stop that does not stop". The respawn then found the old
|
|
25022
|
+
// session still alive and merely injected into it, so a framework swap
|
|
25023
|
+
// silently never happened. killSessionByTmuxName is the single place
|
|
25024
|
+
// that name→id resolution lives; its boolean is the REAL outcome and
|
|
25025
|
+
// the orchestrator now aborts the respawn when it is false.
|
|
25026
|
+
killForResume: async (sessionName) => sessionManager.killSessionByTmuxName(sessionName),
|
|
24913
25027
|
killFresh: async (sessionName) => {
|
|
24914
|
-
// Fresh respawn:
|
|
24915
|
-
//
|
|
24916
|
-
|
|
24917
|
-
|
|
24918
|
-
|
|
24919
|
-
|
|
25028
|
+
// Fresh respawn: the orchestrator has ALREADY parked this topic's
|
|
25029
|
+
// resume entry (claudeResume.park -> _topicResumeMap.park) before
|
|
25030
|
+
// calling us, and a parked entry is invisible to get() /
|
|
25031
|
+
// getForFramework() / getProvenance() — so the next spawn cannot
|
|
25032
|
+
// --resume the cross-framework transcript. This port used to ALSO
|
|
25033
|
+
// call _topicResumeMap.remove(), which DELETES rather than parks;
|
|
25034
|
+
// that was redundant with the park and destroyed the cheap
|
|
25035
|
+
// recovery TopicResumeMap's own §8 rule protects ("'remove' means
|
|
25036
|
+
// PARK, not delete"). It also made the kill-failure abort
|
|
25037
|
+
// unrecoverable: unpark() finds no entry and restores nothing, so
|
|
25038
|
+
// a still-live session lost its resume id to an attempt that was
|
|
25039
|
+
// supposed to be a no-op.
|
|
25040
|
+
return sessionManager.killSessionByTmuxName(sessionName);
|
|
24920
25041
|
},
|
|
24921
25042
|
spawn: async (topicKey, _resolved, _directive) => {
|
|
24922
25043
|
// The orchestrator already killed; respawn re-resolves the (now
|