@volter/supercode-teams 0.3.94 → 0.3.96
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 +3 -0
- package/machine-records.mjs +8 -4
- package/machine.mjs +51 -8
- package/package.json +1 -1
package/live-sessions.mjs
CHANGED
|
@@ -440,6 +440,9 @@ export class LiveSessions extends EventEmitter {
|
|
|
440
440
|
if (had) this.#changed();
|
|
441
441
|
}
|
|
442
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
|
+
|
|
443
446
|
/** The session index's first answer is read: its Codex sessions' activity is followed next (even when it has none). */
|
|
444
447
|
codexIndexed() {
|
|
445
448
|
if (!this.codexIndexRead) this.codexIndexedAt = Date.now();
|
package/machine-records.mjs
CHANGED
|
@@ -21,8 +21,10 @@ 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
|
|
25
|
-
|
|
24
|
+
/** How often a machine whose daemon is too old to serve records is asked again: its update is what answers it, and the
|
|
25
|
+
* server says nothing when its link comes back within the grace (machine-events.mjs records a change of linked only),
|
|
26
|
+
* so it is asked each minute and said once. */
|
|
27
|
+
const TOO_OLD_RETRY_MS = 60_000;
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
30
|
* The producer: `readers[section](detail)` reads one section (async), `detail` naming what changed when the event knows
|
|
@@ -236,11 +238,13 @@ export class RecordHolder {
|
|
|
236
238
|
// opened again after a backoff, 2 s doubling to 5 min: an obligation of this machine, never a sweep. A daemon too
|
|
237
239
|
// old to serve records is asked again every TOO_OLD_RETRY_MS: only its update answers it (asked every 8-16 s, it
|
|
238
240
|
// filled the log)
|
|
241
|
+
const saidBefore = state === 'too-old' && held.saidTooOld;
|
|
239
242
|
held.backoff = state === 'too-old' ? TOO_OLD_RETRY_MS : backoff(held.backoff);
|
|
243
|
+
held.saidTooOld = state === 'too-old';
|
|
240
244
|
if (this.closed) return;
|
|
241
245
|
held.retry = setTimeout(() => { held.retry = null; if ((held.state === 'closed' || held.state === 'too-old') && !this.closed) this.subscribe(machine); }, held.backoff);
|
|
242
246
|
held.retry.unref?.();
|
|
243
|
-
this.log(`machine records from ${machine}: ${held.why}; read again in ${Math.round(held.backoff / 1000)} s`);
|
|
247
|
+
if (!saidBefore) this.log(`machine records from ${machine}: ${held.why}; read again in ${Math.round(held.backoff / 1000)} s${state === 'too-old' ? ' (and each minute after, said again only once it changes)' : ''}`);
|
|
244
248
|
};
|
|
245
249
|
// the peer answers within STALE_MS (any frame, its beat included) or its record is stale, with why, and reopened
|
|
246
250
|
const heard = () => { clearTimeout(held.quiet); held.quiet = setTimeout(() => closed(`no frame from ${machine} within ${STALE_MS / 1000} s (its record stream went quiet)`), STALE_MS); held.quiet.unref?.(); };
|
|
@@ -307,7 +311,7 @@ export class RecordHolder {
|
|
|
307
311
|
}
|
|
308
312
|
held.record = record;
|
|
309
313
|
} else return null;
|
|
310
|
-
Object.assign(held, { state: 'open', since: held.state === 'open' ? held.since : Date.now(), why: null, received_at: Date.now(), backoff: 0 });
|
|
314
|
+
Object.assign(held, { state: 'open', since: held.state === 'open' ? held.since : Date.now(), why: null, received_at: Date.now(), backoff: 0, saidTooOld: false });
|
|
311
315
|
this.save();
|
|
312
316
|
return null;
|
|
313
317
|
}
|
package/machine.mjs
CHANGED
|
@@ -617,8 +617,37 @@ 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
|
+
this.emit('codex-index', { changes: params.changes ?? [] });
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
// The Codex sessions' own index (Codex rollouts only): the live map knows them from its first answer, never waiting on
|
|
629
|
+
// the whole index of every harness, which the machine records keep (on yuerans that took 25.6 s to answer: grok's
|
|
630
|
+
// 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).
|
|
631
|
+
let codexIndexBackoff = 0, codexIndexRetry = null;
|
|
632
|
+
const subscribeCodex = () => this.#serveRequest('harness.v1.sessions.index.subscribe', { harnesses: ['codex'], limit: 0 }, 60_000)
|
|
633
|
+
.then((answer) => {
|
|
634
|
+
this.codexIndex = answer?.subscription ?? null; codexIndexBackoff = 0;
|
|
635
|
+
this.live.codexRowsReplaced();
|
|
636
|
+
for (const descriptor of answer?.initial ?? []) if (descriptor?.locator) this.live.codexIndex({ descriptor });
|
|
637
|
+
this.live.codexIndexed();
|
|
638
|
+
// the records take their Codex rows from this index too (their own whole index leaves Codex out: one watch and
|
|
639
|
+
// one read of the rollouts)
|
|
640
|
+
this.emit('codex-index', { initial: answer?.initial ?? [] });
|
|
641
|
+
}, (error) => {
|
|
642
|
+
codexIndexBackoff = Math.min(300_000, Math.max(2000, (codexIndexBackoff || 1000) * 2));
|
|
643
|
+
this.emit('records-log', `live sessions: the Codex sessions' index could not be subscribed (${error.message}); tried again in ${Math.round(codexIndexBackoff / 1000)} s`);
|
|
644
|
+
clearTimeout(codexIndexRetry); codexIndexRetry = setTimeout(() => void subscribeCodex(), codexIndexBackoff); codexIndexRetry.unref?.();
|
|
645
|
+
});
|
|
646
|
+
void subscribeCodex();
|
|
647
|
+
this.on('serve-exit', () => {
|
|
648
|
+
this.liveActivity = null; this.codexIndex = null; followed = ''; this.live.codexForget(); this.live.emit('codex-followed');
|
|
649
|
+
clearTimeout(codexIndexRetry); codexIndexRetry = setTimeout(() => void subscribeCodex(), 2000); codexIndexRetry.unref?.();
|
|
620
650
|
});
|
|
621
|
-
this.on('serve-exit', () => { this.liveActivity = null; followed = ''; this.live.codexForget(); this.live.emit('codex-followed'); });
|
|
622
651
|
}
|
|
623
652
|
|
|
624
653
|
// ---- this machine's mail (mail/; docs/architecture/overview.md, "The mail operations") ---------------------------
|
|
@@ -769,7 +798,22 @@ export class TeamsMachine extends MachineCore {
|
|
|
769
798
|
// is back, and holds its own retry (2 s doubling to 5 min) until it is (MR4); nothing reads its fields as current.
|
|
770
799
|
const backoff = (previous) => Math.min(300_000, Math.max(2000, (previous || 1000) * 2));
|
|
771
800
|
const both = () => { producer.touch('sessions'); producer.touch('panes'); };
|
|
772
|
-
|
|
801
|
+
// every harness but Codex, whose rows come from the live map's Codex-only index ('codex-index' below)
|
|
802
|
+
const HARNESSES = ['claude-code', 'pi', 'opencode', 'goose', 'gemini', 'grok', 'openclaw', 'hermes', 'supercode'];
|
|
803
|
+
const isCodex = (key) => key.startsWith('codex:') || key.includes(':codex:');
|
|
804
|
+
const applyRow = (change) => {
|
|
805
|
+
if (change.kind === 'removed' && change.key) { this.sessionDescriptors?.delete(`sc:${machineName(this.env)}:${change.key.harness}:${change.key.session_id}`); locators.delete(`${change.key.harness}:${change.key.session_id}`); }
|
|
806
|
+
else if (change.descriptor?.locator) { const { harness, session_id } = change.descriptor.locator; this.sessionDescriptors?.set(`sc:${machineName(this.env)}:${harness}:${session_id}`, change.descriptor); locators.set(`${harness}:${session_id}`, change.descriptor.locator); }
|
|
807
|
+
};
|
|
808
|
+
this.on('codex-index', ({ initial = null, changes = [] }) => {
|
|
809
|
+
if (initial) {
|
|
810
|
+
for (const key of [...locators.keys()]) if (isCodex(key)) locators.delete(key);
|
|
811
|
+
for (const key of [...(this.sessionDescriptors?.keys() ?? [])]) if (isCodex(key)) this.sessionDescriptors.delete(key);
|
|
812
|
+
for (const descriptor of initial) applyRow({ descriptor });
|
|
813
|
+
}
|
|
814
|
+
for (const change of changes) applyRow(change);
|
|
815
|
+
both();
|
|
816
|
+
});
|
|
773
817
|
const locators = new Map(); // `${harness}:${session_id}` → the index's locator for it
|
|
774
818
|
const noteIndexParts = (failed, error) => {
|
|
775
819
|
// the index's own unread parts (a store, a folder, a registry it could not read): their sessions are unknown
|
|
@@ -782,8 +826,6 @@ export class TeamsMachine extends MachineCore {
|
|
|
782
826
|
for (const change of params.changes ?? []) {
|
|
783
827
|
if (change.kind === 'removed' && change.key) this.sessionDescriptors?.delete(`sc:${machineName(this.env)}:${change.key.harness}:${change.key.session_id}`);
|
|
784
828
|
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
829
|
if (change.kind === 'removed' && change.key) locators.delete(`${change.key.harness}:${change.key.session_id}`);
|
|
788
830
|
else if (change.descriptor?.locator) locators.set(`${change.descriptor.locator.harness}:${change.descriptor.locator.session_id}`, change.descriptor.locator);
|
|
789
831
|
}
|
|
@@ -794,11 +836,12 @@ export class TeamsMachine extends MachineCore {
|
|
|
794
836
|
let indexBackoff = 0;
|
|
795
837
|
const subscribe = () => this.#serveRequest('harness.v1.sessions.index.subscribe', { harnesses: HARNESSES, limit: 0 }, 60_000)
|
|
796
838
|
.then((answer) => {
|
|
797
|
-
this.recordsIndex = answer?.subscription ?? null; indexBackoff = 0;
|
|
798
|
-
|
|
799
|
-
for (const
|
|
839
|
+
this.recordsIndex = answer?.subscription ?? null; indexBackoff = 0;
|
|
840
|
+
// its own rows replaced; the Codex rows (the live map's index's) kept
|
|
841
|
+
for (const key of [...locators.keys()]) if (!isCodex(key)) locators.delete(key);
|
|
842
|
+
for (const key of [...(this.sessionDescriptors?.keys() ?? [])]) if (!isCodex(key)) this.sessionDescriptors.delete(key);
|
|
843
|
+
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
844
|
producer.note('sessions_index', null); noteIndexParts(answer?.failed, null); both();
|
|
801
|
-
this.live?.codexIndexed();
|
|
802
845
|
}, (error) => {
|
|
803
846
|
indexBackoff = backoff(indexBackoff);
|
|
804
847
|
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.96",
|
|
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",
|