@volter/supercode-teams 0.3.93 → 0.3.95
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/live-sessions.mjs +8 -0
- package/machine-records.mjs +15 -2
- package/machine.mjs +27 -5
- package/package.json +1 -1
package/live-sessions.mjs
CHANGED
|
@@ -433,8 +433,16 @@ export class LiveSessions extends EventEmitter {
|
|
|
433
433
|
codexForget() {
|
|
434
434
|
if (!this.codexIndexRead && !this.codexKnown) return;
|
|
435
435
|
this.codexIndexRead = false; this.codexKnown = false; this.codexWaitedOut = false; this.startedAt = Date.now();
|
|
436
|
+
// what the ended serve said is not kept as what is true now: a Codex session that ended in the gap would be filed for
|
|
437
|
+
// a door it no longer has; until serve answers anew, a Codex session is not known yet (a send to it waits)
|
|
438
|
+
const had = this.codexRunning.size + this.codexPaneOf.size;
|
|
439
|
+
this.codexRunning.clear(); this.codexPaneOf = new Map();
|
|
440
|
+
if (had) this.#changed();
|
|
436
441
|
}
|
|
437
442
|
|
|
443
|
+
/** A new index subscription's first answer replaces the Codex rows the last one held (its rows follow this). */
|
|
444
|
+
codexRowsReplaced() { this.codexRows.clear(); }
|
|
445
|
+
|
|
438
446
|
/** The session index's first answer is read: its Codex sessions' activity is followed next (even when it has none). */
|
|
439
447
|
codexIndexed() {
|
|
440
448
|
if (!this.codexIndexRead) this.codexIndexedAt = Date.now();
|
package/machine-records.mjs
CHANGED
|
@@ -21,6 +21,8 @@ const MIN_READ_MS = 2000;
|
|
|
21
21
|
export const BEAT_MS = 15_000;
|
|
22
22
|
export const STALE_MS = 45_000;
|
|
23
23
|
const backoff = (previous) => Math.min(300_000, Math.max(2000, (previous || 1000) * 2));
|
|
24
|
+
/** How often a machine whose daemon is too old to serve records is asked again (its update is what answers it). */
|
|
25
|
+
const TOO_OLD_RETRY_MS = 600_000;
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* The producer: `readers[section](detail)` reads one section (async), `detail` naming what changed when the event knows
|
|
@@ -209,6 +211,15 @@ export class RecordHolder {
|
|
|
209
211
|
if (forgot) this.save();
|
|
210
212
|
for (const [machine, held] of this.held) if (!linked.has(machine) && held.state !== 'unlinked') this.let(machine, 'offline: not linked to the team\'s server');
|
|
211
213
|
for (const machine of linked) { const held = this.held.get(machine); if (!held || held.state === 'unlinked' || ((held.state === 'closed' || held.state === 'too-old') && !held.retry)) this.subscribe(machine); }
|
|
214
|
+
// a machine too old to serve records whose link changed since (it linked again: an update restarts its daemon) is
|
|
215
|
+
// asked again now, not at its ten-minute re-ask
|
|
216
|
+
const changedAt = new Map((items ?? []).filter((item) => item.linked).map((item) => [normal(item.name), item.changed_at ?? null]));
|
|
217
|
+
for (const [machine, held] of this.held) {
|
|
218
|
+
const at = changedAt.get(machine);
|
|
219
|
+
if (at == null) continue;
|
|
220
|
+
if (held.state === 'too-old' && held.linkChangedAt != null && at !== held.linkChangedAt) { held.linkChangedAt = at; this.subscribe(machine); continue; }
|
|
221
|
+
held.linkChangedAt = at;
|
|
222
|
+
}
|
|
212
223
|
}
|
|
213
224
|
let(machine, why) {
|
|
214
225
|
const held = this.held.get(machine); if (!held) return;
|
|
@@ -231,8 +242,10 @@ export class RecordHolder {
|
|
|
231
242
|
held.attempt = null;
|
|
232
243
|
Object.assign(held, { stream: null, state, since: Date.now(), why: String(why ?? 'the stream closed').slice(0, 300) });
|
|
233
244
|
try { stream?.close?.(); } catch { /* closing */ }
|
|
234
|
-
// opened again after a backoff, 2 s doubling to 5 min: an obligation of this machine, never a sweep
|
|
235
|
-
|
|
245
|
+
// opened again after a backoff, 2 s doubling to 5 min: an obligation of this machine, never a sweep. A daemon too
|
|
246
|
+
// old to serve records is asked again every TOO_OLD_RETRY_MS: only its update answers it (asked every 8-16 s, it
|
|
247
|
+
// filled the log)
|
|
248
|
+
held.backoff = state === 'too-old' ? TOO_OLD_RETRY_MS : backoff(held.backoff);
|
|
236
249
|
if (this.closed) return;
|
|
237
250
|
held.retry = setTimeout(() => { held.retry = null; if ((held.state === 'closed' || held.state === 'too-old') && !this.closed) this.subscribe(machine); }, held.backoff);
|
|
238
251
|
held.retry.unref?.();
|
package/machine.mjs
CHANGED
|
@@ -617,8 +617,33 @@ export class TeamsMachine extends MachineCore {
|
|
|
617
617
|
this.on('internal-notification', (message) => {
|
|
618
618
|
const params = message.params ?? {};
|
|
619
619
|
if (params.subscription && params.subscription === this.liveActivity) this.live.codexActivity(params.activities);
|
|
620
|
+
else if (params.subscription && params.subscription === this.codexIndex) {
|
|
621
|
+
for (const change of params.changes ?? []) {
|
|
622
|
+
if (change.kind === 'removed' && change.key?.harness === 'codex') this.live.codexIndex({ removed: change.key.session_id });
|
|
623
|
+
else if (change.descriptor) this.live.codexIndex({ descriptor: change.descriptor });
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
// The Codex sessions' own index (Codex rollouts only): the live map knows them from its first answer, never waiting on
|
|
628
|
+
// the whole index of every harness, which the machine records keep (on yuerans that took 25.6 s to answer: grok's
|
|
629
|
+
// 11,305 files and claude-code's 6,334 listed, a page of 5,340 rows built; Codex's 980 files listed in 25 ms).
|
|
630
|
+
let codexIndexBackoff = 0, codexIndexRetry = null;
|
|
631
|
+
const subscribeCodex = () => this.#serveRequest('harness.v1.sessions.index.subscribe', { harnesses: ['codex'], limit: 0 }, 60_000)
|
|
632
|
+
.then((answer) => {
|
|
633
|
+
this.codexIndex = answer?.subscription ?? null; codexIndexBackoff = 0;
|
|
634
|
+
this.live.codexRowsReplaced();
|
|
635
|
+
for (const descriptor of answer?.initial ?? []) if (descriptor?.locator) this.live.codexIndex({ descriptor });
|
|
636
|
+
this.live.codexIndexed();
|
|
637
|
+
}, (error) => {
|
|
638
|
+
codexIndexBackoff = Math.min(300_000, Math.max(2000, (codexIndexBackoff || 1000) * 2));
|
|
639
|
+
this.emit('records-log', `live sessions: the Codex sessions' index could not be subscribed (${error.message}); tried again in ${Math.round(codexIndexBackoff / 1000)} s`);
|
|
640
|
+
clearTimeout(codexIndexRetry); codexIndexRetry = setTimeout(() => void subscribeCodex(), codexIndexBackoff); codexIndexRetry.unref?.();
|
|
641
|
+
});
|
|
642
|
+
void subscribeCodex();
|
|
643
|
+
this.on('serve-exit', () => {
|
|
644
|
+
this.liveActivity = null; this.codexIndex = null; followed = ''; this.live.codexForget(); this.live.emit('codex-followed');
|
|
645
|
+
clearTimeout(codexIndexRetry); codexIndexRetry = setTimeout(() => void subscribeCodex(), 2000); codexIndexRetry.unref?.();
|
|
620
646
|
});
|
|
621
|
-
this.on('serve-exit', () => { this.liveActivity = null; followed = ''; this.live.codexForget(); this.live.emit('codex-followed'); });
|
|
622
647
|
}
|
|
623
648
|
|
|
624
649
|
// ---- this machine's mail (mail/; docs/architecture/overview.md, "The mail operations") ---------------------------
|
|
@@ -782,8 +807,6 @@ export class TeamsMachine extends MachineCore {
|
|
|
782
807
|
for (const change of params.changes ?? []) {
|
|
783
808
|
if (change.kind === 'removed' && change.key) this.sessionDescriptors?.delete(`sc:${machineName(this.env)}:${change.key.harness}:${change.key.session_id}`);
|
|
784
809
|
else if (change.descriptor?.locator) this.sessionDescriptors?.set(`sc:${machineName(this.env)}:${change.descriptor.locator.harness}:${change.descriptor.locator.session_id}`, change.descriptor);
|
|
785
|
-
if (change.kind === 'removed' && change.key?.harness === 'codex') this.live?.codexIndex({ removed: change.key.session_id });
|
|
786
|
-
else if (change.descriptor) this.live?.codexIndex({ descriptor: change.descriptor });
|
|
787
810
|
if (change.kind === 'removed' && change.key) locators.delete(`${change.key.harness}:${change.key.session_id}`);
|
|
788
811
|
else if (change.descriptor?.locator) locators.set(`${change.descriptor.locator.harness}:${change.descriptor.locator.session_id}`, change.descriptor.locator);
|
|
789
812
|
}
|
|
@@ -796,9 +819,8 @@ export class TeamsMachine extends MachineCore {
|
|
|
796
819
|
.then((answer) => {
|
|
797
820
|
this.recordsIndex = answer?.subscription ?? null; indexBackoff = 0; locators.clear();
|
|
798
821
|
this.sessionDescriptors?.clear();
|
|
799
|
-
for (const descriptor of answer?.initial ?? []) if (descriptor?.locator) { locators.set(`${descriptor.locator.harness}:${descriptor.locator.session_id}`, descriptor.locator); this.
|
|
822
|
+
for (const descriptor of answer?.initial ?? []) if (descriptor?.locator) { locators.set(`${descriptor.locator.harness}:${descriptor.locator.session_id}`, descriptor.locator); this.sessionDescriptors?.set(`sc:${machineName(this.env)}:${descriptor.locator.harness}:${descriptor.locator.session_id}`, descriptor); }
|
|
800
823
|
producer.note('sessions_index', null); noteIndexParts(answer?.failed, null); both();
|
|
801
|
-
this.live?.codexIndexed();
|
|
802
824
|
}, (error) => {
|
|
803
825
|
indexBackoff = backoff(indexBackoff);
|
|
804
826
|
const why = `the session index could not be subscribed: ${error.message}; sessions and panes change on pane and mail events only until it is`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-teams",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.95",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A machine daemon serving this machine's doors, and a local or remote Teams server for shared session discovery, OpenTelemetry ingestion and access to enrolled machines.",
|
|
6
6
|
"license": "MIT",
|