instar 1.3.676 → 1.3.678
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/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +100 -9
- package/dist/commands/server.js.map +1 -1
- package/dist/core/SpawningTopicsRegistry.d.ts +65 -0
- package/dist/core/SpawningTopicsRegistry.d.ts.map +1 -0
- package/dist/core/SpawningTopicsRegistry.js +75 -0
- package/dist/core/SpawningTopicsRegistry.js.map +1 -0
- package/dist/core/internalFrameworkDefault.d.ts +23 -2
- package/dist/core/internalFrameworkDefault.d.ts.map +1 -1
- package/dist/core/internalFrameworkDefault.js +41 -3
- package/dist/core/internalFrameworkDefault.js.map +1 -1
- package/dist/monitoring/TopicReachabilityVerifier.d.ts +92 -0
- package/dist/monitoring/TopicReachabilityVerifier.d.ts.map +1 -0
- package/dist/monitoring/TopicReachabilityVerifier.js +173 -0
- package/dist/monitoring/TopicReachabilityVerifier.js.map +1 -0
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.678.md +110 -0
- package/upgrades/side-effects/latency-sensitive-gate-framework.md +79 -0
- package/upgrades/side-effects/verify-after-reachability.md +65 -0
- package/upgrades/1.3.676.md +0 -45
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpawningTopicsRegistry — F7 Piece 1
|
|
3
|
+
* (docs/specs/verify-after-reachability.md §Piece 1).
|
|
4
|
+
*
|
|
5
|
+
* Replaces the closure-local `Set<number>` that `onTopicMessage` used to guard against
|
|
6
|
+
* double-spawning a topic. The Set was cleared ONLY in the spawn promise's `.finally`,
|
|
7
|
+
* so a HUNG spawn left the flag set forever → every subsequent inbound for that topic
|
|
8
|
+
* was silently skipped (the single-machine black-hole F7 surfaces). It carried no
|
|
9
|
+
* timestamp and no token.
|
|
10
|
+
*
|
|
11
|
+
* This component is the SAFE minimum (round-2 review proved any external auto-CLEAR of
|
|
12
|
+
* the flag relocates the double-spawn race, because the spawn body is non-cancellable):
|
|
13
|
+
* - `add(topic)` returns a unique TOKEN and stamps `startedAtMs`.
|
|
14
|
+
* - `clear(topic, token)` is TOKEN-GUARDED: it deletes ONLY if the live entry's token
|
|
15
|
+
* still matches — so a late `.finally` from a superseded spawn cannot delete a newer
|
|
16
|
+
* entry (the ABA fix). The `.finally` on a spawn's own settle remains the SOLE
|
|
17
|
+
* clearer; NO timeout and NO sweep clear the flag here.
|
|
18
|
+
* - `stuckSinceMs(topic, now)` lets the TopicReachabilityVerifier SURFACE a spawn that
|
|
19
|
+
* has been in flight past a threshold (it is never cleared — surfaced, not raced).
|
|
20
|
+
*
|
|
21
|
+
* The mechanical auto-recovery of a hung spawn (cancellable spawn) is a tracked
|
|
22
|
+
* follow-up — see the spec.
|
|
23
|
+
*/
|
|
24
|
+
export interface SpawningEntry {
|
|
25
|
+
/** Unique per-add token (NOT the topic — the ABA guard). */
|
|
26
|
+
token: string;
|
|
27
|
+
/** ms epoch when this spawn entered the registry. */
|
|
28
|
+
startedAtMs: number;
|
|
29
|
+
}
|
|
30
|
+
export declare class SpawningTopicsRegistry {
|
|
31
|
+
private readonly map;
|
|
32
|
+
private readonly now;
|
|
33
|
+
private seq;
|
|
34
|
+
constructor(deps?: {
|
|
35
|
+
now?: () => number;
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Mark `topic` as spawning. Returns a unique token the caller passes back to
|
|
39
|
+
* `clear`. If an entry already exists (a retry that already landed), it is REPLACED
|
|
40
|
+
* with a fresh token+timestamp — the new spawn supersedes the old, and the old
|
|
41
|
+
* spawn's later `clear(old token)` becomes a no-op (ABA-safe).
|
|
42
|
+
*/
|
|
43
|
+
add(topic: number): string;
|
|
44
|
+
/**
|
|
45
|
+
* Token-guarded clear. Deletes the entry ONLY if its live token equals `token`.
|
|
46
|
+
* A late `.finally` from a superseded spawn (whose token no longer matches) is a
|
|
47
|
+
* no-op, so it can never delete a newer spawn's entry.
|
|
48
|
+
*/
|
|
49
|
+
clear(topic: number, token: string): void;
|
|
50
|
+
/** Is `topic` currently marked spawning? (The hot-path double-spawn guard read.) */
|
|
51
|
+
has(topic: number): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* If `topic` has been spawning since longer than now-startedAtMs, return that age in
|
|
54
|
+
* ms; else undefined. The verifier uses this to detect a wedged spawn. NEVER clears.
|
|
55
|
+
*/
|
|
56
|
+
stuckSinceMs(topic: number, nowMs?: number): number | undefined;
|
|
57
|
+
/** Snapshot of currently-spawning topics (for the verifier / status). */
|
|
58
|
+
entries(): Array<{
|
|
59
|
+
topic: number;
|
|
60
|
+
startedAtMs: number;
|
|
61
|
+
}>;
|
|
62
|
+
/** Count of in-flight spawns (naturally tiny — one per concurrent spawn). */
|
|
63
|
+
size(): number;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=SpawningTopicsRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpawningTopicsRegistry.d.ts","sourceRoot":"","sources":["../../src/core/SpawningTopicsRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoC;IACxD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,GAAG,CAAK;gBAEJ,IAAI,GAAE;QAAE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;KAAO;IAI7C;;;;;OAKG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAM1B;;;;OAIG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKzC,oFAAoF;IACpF,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI3B;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAmB,GAAG,MAAM,GAAG,SAAS;IAM3E,yEAAyE;IACzE,OAAO,IAAI,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAIxD,6EAA6E;IAC7E,IAAI,IAAI,MAAM;CAGf"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpawningTopicsRegistry — F7 Piece 1
|
|
3
|
+
* (docs/specs/verify-after-reachability.md §Piece 1).
|
|
4
|
+
*
|
|
5
|
+
* Replaces the closure-local `Set<number>` that `onTopicMessage` used to guard against
|
|
6
|
+
* double-spawning a topic. The Set was cleared ONLY in the spawn promise's `.finally`,
|
|
7
|
+
* so a HUNG spawn left the flag set forever → every subsequent inbound for that topic
|
|
8
|
+
* was silently skipped (the single-machine black-hole F7 surfaces). It carried no
|
|
9
|
+
* timestamp and no token.
|
|
10
|
+
*
|
|
11
|
+
* This component is the SAFE minimum (round-2 review proved any external auto-CLEAR of
|
|
12
|
+
* the flag relocates the double-spawn race, because the spawn body is non-cancellable):
|
|
13
|
+
* - `add(topic)` returns a unique TOKEN and stamps `startedAtMs`.
|
|
14
|
+
* - `clear(topic, token)` is TOKEN-GUARDED: it deletes ONLY if the live entry's token
|
|
15
|
+
* still matches — so a late `.finally` from a superseded spawn cannot delete a newer
|
|
16
|
+
* entry (the ABA fix). The `.finally` on a spawn's own settle remains the SOLE
|
|
17
|
+
* clearer; NO timeout and NO sweep clear the flag here.
|
|
18
|
+
* - `stuckSinceMs(topic, now)` lets the TopicReachabilityVerifier SURFACE a spawn that
|
|
19
|
+
* has been in flight past a threshold (it is never cleared — surfaced, not raced).
|
|
20
|
+
*
|
|
21
|
+
* The mechanical auto-recovery of a hung spawn (cancellable spawn) is a tracked
|
|
22
|
+
* follow-up — see the spec.
|
|
23
|
+
*/
|
|
24
|
+
export class SpawningTopicsRegistry {
|
|
25
|
+
map = new Map();
|
|
26
|
+
now;
|
|
27
|
+
seq = 0;
|
|
28
|
+
constructor(deps = {}) {
|
|
29
|
+
this.now = deps.now ?? (() => Date.now());
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Mark `topic` as spawning. Returns a unique token the caller passes back to
|
|
33
|
+
* `clear`. If an entry already exists (a retry that already landed), it is REPLACED
|
|
34
|
+
* with a fresh token+timestamp — the new spawn supersedes the old, and the old
|
|
35
|
+
* spawn's later `clear(old token)` becomes a no-op (ABA-safe).
|
|
36
|
+
*/
|
|
37
|
+
add(topic) {
|
|
38
|
+
const token = `spawn:${topic}:${this.now().toString(36)}:${(this.seq++).toString(36)}`;
|
|
39
|
+
this.map.set(topic, { token, startedAtMs: this.now() });
|
|
40
|
+
return token;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Token-guarded clear. Deletes the entry ONLY if its live token equals `token`.
|
|
44
|
+
* A late `.finally` from a superseded spawn (whose token no longer matches) is a
|
|
45
|
+
* no-op, so it can never delete a newer spawn's entry.
|
|
46
|
+
*/
|
|
47
|
+
clear(topic, token) {
|
|
48
|
+
const e = this.map.get(topic);
|
|
49
|
+
if (e && e.token === token)
|
|
50
|
+
this.map.delete(topic);
|
|
51
|
+
}
|
|
52
|
+
/** Is `topic` currently marked spawning? (The hot-path double-spawn guard read.) */
|
|
53
|
+
has(topic) {
|
|
54
|
+
return this.map.has(topic);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* If `topic` has been spawning since longer than now-startedAtMs, return that age in
|
|
58
|
+
* ms; else undefined. The verifier uses this to detect a wedged spawn. NEVER clears.
|
|
59
|
+
*/
|
|
60
|
+
stuckSinceMs(topic, nowMs = this.now()) {
|
|
61
|
+
const e = this.map.get(topic);
|
|
62
|
+
if (!e)
|
|
63
|
+
return undefined;
|
|
64
|
+
return Math.max(0, nowMs - e.startedAtMs);
|
|
65
|
+
}
|
|
66
|
+
/** Snapshot of currently-spawning topics (for the verifier / status). */
|
|
67
|
+
entries() {
|
|
68
|
+
return [...this.map.entries()].map(([topic, e]) => ({ topic, startedAtMs: e.startedAtMs }));
|
|
69
|
+
}
|
|
70
|
+
/** Count of in-flight spawns (naturally tiny — one per concurrent spawn). */
|
|
71
|
+
size() {
|
|
72
|
+
return this.map.size;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=SpawningTopicsRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpawningTopicsRegistry.js","sourceRoot":"","sources":["../../src/core/SpawningTopicsRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AASH,MAAM,OAAO,sBAAsB;IAChB,GAAG,GAAG,IAAI,GAAG,EAAyB,CAAC;IACvC,GAAG,CAAe;IAC3B,GAAG,GAAG,CAAC,CAAC;IAEhB,YAAY,OAA+B,EAAE;QAC3C,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,KAAa;QACf,MAAM,KAAK,GAAG,SAAS,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;QACvF,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,KAAa,EAAE,KAAa;QAChC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK;YAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrD,CAAC;IAED,oFAAoF;IACpF,GAAG,CAAC,KAAa;QACf,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,KAAa,EAAE,QAAgB,IAAI,CAAC,GAAG,EAAE;QACpD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,CAAC;YAAE,OAAO,SAAS,CAAC;QACzB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC;IAC5C,CAAC;IAED,yEAAyE;IACzE,OAAO;QACL,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC9F,CAAC;IAED,6EAA6E;IAC7E,IAAI;QACF,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;IACvB,CAAC;CACF"}
|
|
@@ -28,6 +28,23 @@ import type { ComponentFrameworksConfig } from './IntelligenceRouter.js';
|
|
|
28
28
|
* entry against the real `IntelligenceFramework` enum so an unknown name never ships.
|
|
29
29
|
*/
|
|
30
30
|
export declare const INTERNAL_FRAMEWORK_PREFERENCE: readonly IntelligenceFramework[];
|
|
31
|
+
/**
|
|
32
|
+
* The LATENCY-SENSITIVE preference chain — used for the `gate` category ONLY.
|
|
33
|
+
*
|
|
34
|
+
* A `gate` is a SYNCHRONOUS, action-blocking check (the user-facing
|
|
35
|
+
* `MessagingToneGate` is the canonical one — a human is waiting for their reply).
|
|
36
|
+
* The general `INTERNAL_FRAMEWORK_PREFERENCE` puts `codex-cli` first by operator
|
|
37
|
+
* directive (spread background LOAD off Claude), but codex-cli is the SLOWEST
|
|
38
|
+
* off-Claude framework (~30s, which exceeds the 20s outbound-gate review budget
|
|
39
|
+
* and times the gate out — the 2026-06-25 silent-outbound class). For a
|
|
40
|
+
* latency-sensitive gate the right default is the FASTEST available off-Claude
|
|
41
|
+
* framework, not the load-spreading order. Ranked fastest→slowest, Claude last.
|
|
42
|
+
*
|
|
43
|
+
* This does NOT override the codex-first directive for the BACKGROUND categories
|
|
44
|
+
* (`sentinel` / `reflector`) — their latency does not block a human, so they keep
|
|
45
|
+
* the load-spreading order. Only `gate` (where a user waits) goes fastest-first.
|
|
46
|
+
*/
|
|
47
|
+
export declare const LATENCY_SENSITIVE_FRAMEWORK_PREFERENCE: readonly IntelligenceFramework[];
|
|
31
48
|
/**
|
|
32
49
|
* Compute the default `componentFrameworks` from the active-framework set.
|
|
33
50
|
*
|
|
@@ -35,8 +52,12 @@ export declare const INTERNAL_FRAMEWORK_PREFERENCE: readonly IntelligenceFramewo
|
|
|
35
52
|
* agent, IN PREFERENCE ORDER (the caller filters `INTERNAL_FRAMEWORK_PREFERENCE`
|
|
36
53
|
* by `buildProvider(fw) !== null`). MUST already be ordered + de-duplicated.
|
|
37
54
|
* @returns the effective `ComponentFrameworksConfig`:
|
|
38
|
-
* - `categories.{sentinel,
|
|
39
|
-
* or claude-code if that's all that's active)
|
|
55
|
+
* - `categories.{sentinel,reflector}` = `active[0]` (first active off-Claude in the
|
|
56
|
+
* codex-first load-spreading order, or claude-code if that's all that's active)
|
|
57
|
+
* - `categories.gate` = the FASTEST active off-Claude framework
|
|
58
|
+
* (`LATENCY_SENSITIVE_FRAMEWORK_PREFERENCE`: pi → gemini → codex → claude) — the
|
|
59
|
+
* gate is synchronous + user-blocking, so it prefers speed over load-spreading.
|
|
60
|
+
* Equals `active[0]` when only one off-Claude framework is active (no-op then).
|
|
40
61
|
* - `failureSwap` = `active.slice(1)` (the ordered tail, claude-code last)
|
|
41
62
|
* - `fallback: 'default'`
|
|
42
63
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internalFrameworkDefault.d.ts","sourceRoot":"","sources":["../../src/core/internalFrameworkDefault.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,EAAE,SAAS,qBAAqB,EAKhE,CAAC;AAEX
|
|
1
|
+
{"version":3,"file":"internalFrameworkDefault.d.ts","sourceRoot":"","sources":["../../src/core/internalFrameworkDefault.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,EAAE,SAAS,qBAAqB,EAKhE,CAAC;AAEX;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sCAAsC,EAAE,SAAS,qBAAqB,EAKzE,CAAC;AAUX;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,+BAA+B,CAC7C,gBAAgB,EAAE,SAAS,qBAAqB,EAAE,GACjD,yBAAyB,CA6B3B"}
|
|
@@ -31,6 +31,32 @@ export const INTERNAL_FRAMEWORK_PREFERENCE = [
|
|
|
31
31
|
'gemini-cli',
|
|
32
32
|
'claude-code',
|
|
33
33
|
];
|
|
34
|
+
/**
|
|
35
|
+
* The LATENCY-SENSITIVE preference chain — used for the `gate` category ONLY.
|
|
36
|
+
*
|
|
37
|
+
* A `gate` is a SYNCHRONOUS, action-blocking check (the user-facing
|
|
38
|
+
* `MessagingToneGate` is the canonical one — a human is waiting for their reply).
|
|
39
|
+
* The general `INTERNAL_FRAMEWORK_PREFERENCE` puts `codex-cli` first by operator
|
|
40
|
+
* directive (spread background LOAD off Claude), but codex-cli is the SLOWEST
|
|
41
|
+
* off-Claude framework (~30s, which exceeds the 20s outbound-gate review budget
|
|
42
|
+
* and times the gate out — the 2026-06-25 silent-outbound class). For a
|
|
43
|
+
* latency-sensitive gate the right default is the FASTEST available off-Claude
|
|
44
|
+
* framework, not the load-spreading order. Ranked fastest→slowest, Claude last.
|
|
45
|
+
*
|
|
46
|
+
* This does NOT override the codex-first directive for the BACKGROUND categories
|
|
47
|
+
* (`sentinel` / `reflector`) — their latency does not block a human, so they keep
|
|
48
|
+
* the load-spreading order. Only `gate` (where a user waits) goes fastest-first.
|
|
49
|
+
*/
|
|
50
|
+
export const LATENCY_SENSITIVE_FRAMEWORK_PREFERENCE = [
|
|
51
|
+
'pi-cli',
|
|
52
|
+
'gemini-cli',
|
|
53
|
+
'codex-cli',
|
|
54
|
+
'claude-code',
|
|
55
|
+
];
|
|
56
|
+
/** The first framework in `order` that is also present in `active`, else undefined. */
|
|
57
|
+
function firstActiveIn(order, active) {
|
|
58
|
+
return order.find((fw) => active.includes(fw));
|
|
59
|
+
}
|
|
34
60
|
/**
|
|
35
61
|
* Compute the default `componentFrameworks` from the active-framework set.
|
|
36
62
|
*
|
|
@@ -38,8 +64,12 @@ export const INTERNAL_FRAMEWORK_PREFERENCE = [
|
|
|
38
64
|
* agent, IN PREFERENCE ORDER (the caller filters `INTERNAL_FRAMEWORK_PREFERENCE`
|
|
39
65
|
* by `buildProvider(fw) !== null`). MUST already be ordered + de-duplicated.
|
|
40
66
|
* @returns the effective `ComponentFrameworksConfig`:
|
|
41
|
-
* - `categories.{sentinel,
|
|
42
|
-
* or claude-code if that's all that's active)
|
|
67
|
+
* - `categories.{sentinel,reflector}` = `active[0]` (first active off-Claude in the
|
|
68
|
+
* codex-first load-spreading order, or claude-code if that's all that's active)
|
|
69
|
+
* - `categories.gate` = the FASTEST active off-Claude framework
|
|
70
|
+
* (`LATENCY_SENSITIVE_FRAMEWORK_PREFERENCE`: pi → gemini → codex → claude) — the
|
|
71
|
+
* gate is synchronous + user-blocking, so it prefers speed over load-spreading.
|
|
72
|
+
* Equals `active[0]` when only one off-Claude framework is active (no-op then).
|
|
43
73
|
* - `failureSwap` = `active.slice(1)` (the ordered tail, claude-code last)
|
|
44
74
|
* - `fallback: 'default'`
|
|
45
75
|
*
|
|
@@ -58,10 +88,18 @@ export function resolveInternalFrameworkDefault(activeFrameworks) {
|
|
|
58
88
|
return { failureSwap: [], fallback: 'default' };
|
|
59
89
|
}
|
|
60
90
|
const primary = active[0];
|
|
91
|
+
// The `gate` category is LATENCY-SENSITIVE: a synchronous, user-blocking check.
|
|
92
|
+
// It gets the FASTEST active off-Claude framework (pi → gemini → codex → claude),
|
|
93
|
+
// NOT the load-spreading codex-first order. `active` is in INTERNAL_FRAMEWORK_
|
|
94
|
+
// PREFERENCE order, so we re-rank it by the latency order to find the fastest.
|
|
95
|
+
// Falls back to `primary` if (somehow) no latency-ranked match — keeping the gate
|
|
96
|
+
// never worse than today. When only one off-Claude framework is active, gatePrimary
|
|
97
|
+
// === primary (byte-identical to the old behavior).
|
|
98
|
+
const gatePrimary = firstActiveIn(LATENCY_SENSITIVE_FRAMEWORK_PREFERENCE, active) ?? primary;
|
|
61
99
|
return {
|
|
62
100
|
categories: {
|
|
63
101
|
sentinel: primary,
|
|
64
|
-
gate:
|
|
102
|
+
gate: gatePrimary,
|
|
65
103
|
reflector: primary,
|
|
66
104
|
// `job` and `other` are deliberately ABSENT (§4.1).
|
|
67
105
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internalFrameworkDefault.js","sourceRoot":"","sources":["../../src/core/internalFrameworkDefault.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAqC;IAC7E,WAAW;IACX,QAAQ;IACR,YAAY;IACZ,aAAa;CACL,CAAC;AAEX
|
|
1
|
+
{"version":3,"file":"internalFrameworkDefault.js","sourceRoot":"","sources":["../../src/core/internalFrameworkDefault.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAqC;IAC7E,WAAW;IACX,QAAQ;IACR,YAAY;IACZ,aAAa;CACL,CAAC;AAEX;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,sCAAsC,GAAqC;IACtF,QAAQ;IACR,YAAY;IACZ,WAAW;IACX,aAAa;CACL,CAAC;AAEX,uFAAuF;AACvF,SAAS,aAAa,CACpB,KAAuC,EACvC,MAAwC;IAExC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,+BAA+B,CAC7C,gBAAkD;IAElD,MAAM,MAAM,GAAG,gBAAgB,CAAC;IAEhC,iFAAiF;IACjF,8EAA8E;IAC9E,kFAAkF;IAClF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,aAAa,CAAC,EAAE,CAAC;QAChF,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IAClD,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1B,gFAAgF;IAChF,kFAAkF;IAClF,+EAA+E;IAC/E,+EAA+E;IAC/E,kFAAkF;IAClF,oFAAoF;IACpF,oDAAoD;IACpD,MAAM,WAAW,GAAG,aAAa,CAAC,sCAAsC,EAAE,MAAM,CAAC,IAAI,OAAO,CAAC;IAC7F,OAAO;QACL,UAAU,EAAE;YACV,QAAQ,EAAE,OAAO;YACjB,IAAI,EAAE,WAAW;YACjB,SAAS,EAAE,OAAO;YAClB,oDAAoD;SACrD;QACD,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5B,QAAQ,EAAE,SAAS;KACpB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TopicReachabilityVerifier — F7 Piece 2 (PURE SIGNAL)
|
|
3
|
+
* (docs/specs/verify-after-reachability.md §Piece 2).
|
|
4
|
+
*
|
|
5
|
+
* After a destructive session/routing mutation (a `sessionReaped(terminal)` or an
|
|
6
|
+
* ownership release/transfer), it verifies the affected topic is still inbound-reachable
|
|
7
|
+
* and SURFACES a genuine orphan as ONE NORMAL-priority attention item. It mutates
|
|
8
|
+
* NOTHING — no clear, spawn, kill, transfer, or re-place. It is a smoke alarm.
|
|
9
|
+
*
|
|
10
|
+
* Honesty guard: the dominant single-machine path already self-heals (the next inbound
|
|
11
|
+
* auto-spawns), so a topic that simply has no session now but WILL spawn on the next
|
|
12
|
+
* message is REACHABLE, not orphaned — the verifier must not scream on every idle kill.
|
|
13
|
+
* Only the specific defeats of the self-heal (stuck-spawn, at-capacity, released-no-
|
|
14
|
+
* placement, stalled inbound-queue) are orphans.
|
|
15
|
+
*
|
|
16
|
+
* This module is the DECISION CORE (deterministic, tick-driven, injected deps). The
|
|
17
|
+
* server wires the triggers (events) + the live `probe` (reads session/placement state)
|
|
18
|
+
* + the attention sink; this class owns grace/coalescing/pressure/suppression/dedup.
|
|
19
|
+
*/
|
|
20
|
+
export type OrphanReason = 'stuck-spawn' | 'at-capacity' | 'released-no-placement' | 'inbound-queue-stalled' | 'partition-suspected';
|
|
21
|
+
export type Reachability = {
|
|
22
|
+
reachable: true;
|
|
23
|
+
} | {
|
|
24
|
+
reachable: false;
|
|
25
|
+
reason: OrphanReason;
|
|
26
|
+
};
|
|
27
|
+
export interface AttentionSurface {
|
|
28
|
+
/** A single attention item (NORMAL priority, stable sourceContext, deduped by key). */
|
|
29
|
+
key: string;
|
|
30
|
+
topics: number[];
|
|
31
|
+
reason: string;
|
|
32
|
+
rolledUp: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface VerifierDeps {
|
|
35
|
+
/** Live reachability classification for a topic (reads local session/placement state). */
|
|
36
|
+
probe: (topic: number) => Reachability;
|
|
37
|
+
/** Raise one NORMAL attention item. */
|
|
38
|
+
surface: (item: AttentionSurface) => void;
|
|
39
|
+
/** True ⇒ skip per-topic verify churn (mass-reap is the pressure; don't amplify). */
|
|
40
|
+
pressureCritical: () => boolean;
|
|
41
|
+
/** True ⇒ an operator emergency-stop / halt is active; suppress surfacing. */
|
|
42
|
+
emergencyStopActive: () => boolean;
|
|
43
|
+
now: () => number;
|
|
44
|
+
/** Verify delay after a mutation (default 30s; > normal respawn so a healthy bounce isn't flagged). */
|
|
45
|
+
graceMs?: number;
|
|
46
|
+
/** Orphan count in a flush past which a single rolled-up item is emitted (default 10). */
|
|
47
|
+
burstThreshold?: number;
|
|
48
|
+
/** Per-topic minimum re-surface interval, exponential floor (default 1h). */
|
|
49
|
+
resurfaceFloorMs?: number;
|
|
50
|
+
/** Hard cap on pending verifies (overflow counted). */
|
|
51
|
+
maxPendingVerifies?: number;
|
|
52
|
+
}
|
|
53
|
+
export declare class TopicReachabilityVerifier {
|
|
54
|
+
private readonly d;
|
|
55
|
+
/** topic → earliest time the verify is due (now + grace at record time). Coalesced. */
|
|
56
|
+
private readonly pending;
|
|
57
|
+
/** Topics whose verify was SKIPPED under pressure / SUPPRESSED under halt — re-swept on clear. */
|
|
58
|
+
private readonly deferredWindow;
|
|
59
|
+
private readonly dedup;
|
|
60
|
+
private _overflow;
|
|
61
|
+
private _orphansSurfaced;
|
|
62
|
+
private _verifiedReachable;
|
|
63
|
+
private _lastTickAt;
|
|
64
|
+
constructor(deps: VerifierDeps);
|
|
65
|
+
/** A destructive mutation hit `topic` — schedule a coalesced post-grace verify. */
|
|
66
|
+
recordMutation(topic: number): void;
|
|
67
|
+
/**
|
|
68
|
+
* Process verifies whose grace has elapsed. Pure-signal: classifies + surfaces.
|
|
69
|
+
* Returns a report (for the status route + tests).
|
|
70
|
+
*/
|
|
71
|
+
tick(): {
|
|
72
|
+
surfaced: number;
|
|
73
|
+
reachable: number;
|
|
74
|
+
skipped: number;
|
|
75
|
+
overflow: number;
|
|
76
|
+
pending: number;
|
|
77
|
+
};
|
|
78
|
+
private surfaceOrphans;
|
|
79
|
+
/** Backoff gate: re-surface only past an exponentially-widening floor, regardless of re-arm. */
|
|
80
|
+
private shouldSurface;
|
|
81
|
+
private bumpDedup;
|
|
82
|
+
private markReachable;
|
|
83
|
+
status(): {
|
|
84
|
+
pending: number;
|
|
85
|
+
deferred: number;
|
|
86
|
+
orphansSurfaced: number;
|
|
87
|
+
verifiedReachable: number;
|
|
88
|
+
overflow: number;
|
|
89
|
+
lastTickAt: number;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=TopicReachabilityVerifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TopicReachabilityVerifier.d.ts","sourceRoot":"","sources":["../../src/monitoring/TopicReachabilityVerifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,YAAY,GACpB,aAAa,GACb,aAAa,GACb,uBAAuB,GACvB,uBAAuB,GACvB,qBAAqB,CAAC;AAE1B,MAAM,MAAM,YAAY,GAAG;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,CAAC;AAE5F,MAAM,WAAW,gBAAgB;IAC/B,uFAAuF;IACvF,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,0FAA0F;IAC1F,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,YAAY,CAAC;IACvC,uCAAuC;IACvC,OAAO,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC1C,qFAAqF;IACrF,gBAAgB,EAAE,MAAM,OAAO,CAAC;IAChC,8EAA8E;IAC9E,mBAAmB,EAAE,MAAM,OAAO,CAAC;IACnC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,uGAAuG;IACvG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0FAA0F;IAC1F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAgBD,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAC6E;IAC/F,uFAAuF;IACvF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,kGAAkG;IAClG,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,WAAW,CAAK;gBAEZ,IAAI,EAAE,YAAY;IAU9B,mFAAmF;IACnF,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAYnC;;;OAGG;IACH,IAAI,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAgDnG,OAAO,CAAC,cAAc;IAqCtB,gGAAgG;IAChG,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,aAAa;IAQrB,MAAM,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;CAU1I"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TopicReachabilityVerifier — F7 Piece 2 (PURE SIGNAL)
|
|
3
|
+
* (docs/specs/verify-after-reachability.md §Piece 2).
|
|
4
|
+
*
|
|
5
|
+
* After a destructive session/routing mutation (a `sessionReaped(terminal)` or an
|
|
6
|
+
* ownership release/transfer), it verifies the affected topic is still inbound-reachable
|
|
7
|
+
* and SURFACES a genuine orphan as ONE NORMAL-priority attention item. It mutates
|
|
8
|
+
* NOTHING — no clear, spawn, kill, transfer, or re-place. It is a smoke alarm.
|
|
9
|
+
*
|
|
10
|
+
* Honesty guard: the dominant single-machine path already self-heals (the next inbound
|
|
11
|
+
* auto-spawns), so a topic that simply has no session now but WILL spawn on the next
|
|
12
|
+
* message is REACHABLE, not orphaned — the verifier must not scream on every idle kill.
|
|
13
|
+
* Only the specific defeats of the self-heal (stuck-spawn, at-capacity, released-no-
|
|
14
|
+
* placement, stalled inbound-queue) are orphans.
|
|
15
|
+
*
|
|
16
|
+
* This module is the DECISION CORE (deterministic, tick-driven, injected deps). The
|
|
17
|
+
* server wires the triggers (events) + the live `probe` (reads session/placement state)
|
|
18
|
+
* + the attention sink; this class owns grace/coalescing/pressure/suppression/dedup.
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_GRACE_MS = 30_000;
|
|
21
|
+
const DEFAULT_BURST = 10;
|
|
22
|
+
const DEFAULT_RESURFACE_FLOOR_MS = 3_600_000;
|
|
23
|
+
const DEFAULT_MAX_PENDING = 500;
|
|
24
|
+
export class TopicReachabilityVerifier {
|
|
25
|
+
d;
|
|
26
|
+
/** topic → earliest time the verify is due (now + grace at record time). Coalesced. */
|
|
27
|
+
pending = new Map();
|
|
28
|
+
/** Topics whose verify was SKIPPED under pressure / SUPPRESSED under halt — re-swept on clear. */
|
|
29
|
+
deferredWindow = new Set();
|
|
30
|
+
dedup = new Map();
|
|
31
|
+
_overflow = 0;
|
|
32
|
+
_orphansSurfaced = 0;
|
|
33
|
+
_verifiedReachable = 0;
|
|
34
|
+
_lastTickAt = 0;
|
|
35
|
+
constructor(deps) {
|
|
36
|
+
this.d = {
|
|
37
|
+
...deps,
|
|
38
|
+
graceMs: deps.graceMs ?? DEFAULT_GRACE_MS,
|
|
39
|
+
burstThreshold: deps.burstThreshold ?? DEFAULT_BURST,
|
|
40
|
+
resurfaceFloorMs: deps.resurfaceFloorMs ?? DEFAULT_RESURFACE_FLOOR_MS,
|
|
41
|
+
maxPendingVerifies: deps.maxPendingVerifies ?? DEFAULT_MAX_PENDING,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** A destructive mutation hit `topic` — schedule a coalesced post-grace verify. */
|
|
45
|
+
recordMutation(topic) {
|
|
46
|
+
const due = this.d.now() + this.d.graceMs;
|
|
47
|
+
if (!this.pending.has(topic)) {
|
|
48
|
+
if (this.pending.size >= this.d.maxPendingVerifies) {
|
|
49
|
+
this._overflow++;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this.pending.set(topic, due);
|
|
53
|
+
}
|
|
54
|
+
// coalesced: an existing pending verify keeps its (earlier) due time.
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Process verifies whose grace has elapsed. Pure-signal: classifies + surfaces.
|
|
58
|
+
* Returns a report (for the status route + tests).
|
|
59
|
+
*/
|
|
60
|
+
tick() {
|
|
61
|
+
const now = this.d.now();
|
|
62
|
+
this._lastTickAt = now;
|
|
63
|
+
const due = [];
|
|
64
|
+
for (const [topic, dueAt] of this.pending) {
|
|
65
|
+
if (dueAt <= now)
|
|
66
|
+
due.push(topic);
|
|
67
|
+
}
|
|
68
|
+
const halt = this.d.emergencyStopActive();
|
|
69
|
+
const pressure = this.d.pressureCritical();
|
|
70
|
+
let skipped = 0;
|
|
71
|
+
const orphans = [];
|
|
72
|
+
for (const topic of due) {
|
|
73
|
+
this.pending.delete(topic);
|
|
74
|
+
// Under halt or critical pressure we do NOT churn per-topic — defer to re-sweep.
|
|
75
|
+
if (halt || pressure) {
|
|
76
|
+
this.deferredWindow.add(topic);
|
|
77
|
+
skipped++;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const r = this.d.probe(topic);
|
|
81
|
+
if (r.reachable) {
|
|
82
|
+
this._verifiedReachable++;
|
|
83
|
+
this.markReachable(topic); // re-arm dedup
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
orphans.push({ topic, reason: r.reason });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// A window just cleared (no halt + no pressure) → re-sweep deferred topics once.
|
|
90
|
+
if (!halt && !pressure && this.deferredWindow.size > 0) {
|
|
91
|
+
for (const topic of [...this.deferredWindow]) {
|
|
92
|
+
this.deferredWindow.delete(topic);
|
|
93
|
+
const r = this.d.probe(topic);
|
|
94
|
+
if (r.reachable) {
|
|
95
|
+
this._verifiedReachable++;
|
|
96
|
+
this.markReachable(topic);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
orphans.push({ topic, reason: r.reason });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const surfaced = this.surfaceOrphans(orphans, now, pressure);
|
|
104
|
+
return { surfaced, reachable: this._verifiedReachable, skipped, overflow: this._overflow, pending: this.pending.size };
|
|
105
|
+
}
|
|
106
|
+
surfaceOrphans(orphans, now, pressure) {
|
|
107
|
+
if (orphans.length === 0)
|
|
108
|
+
return 0;
|
|
109
|
+
// Burst roll-up: a mass-orphan (or partition) → ONE rolled-up item, never N.
|
|
110
|
+
if (orphans.length >= this.d.burstThreshold || pressure) {
|
|
111
|
+
const topics = orphans.map((o) => o.topic);
|
|
112
|
+
this.d.surface({
|
|
113
|
+
key: 'topic-reachability:burst',
|
|
114
|
+
topics,
|
|
115
|
+
reason: `${topics.length} topics may be unreachable`,
|
|
116
|
+
rolledUp: true,
|
|
117
|
+
});
|
|
118
|
+
for (const o of orphans)
|
|
119
|
+
this.bumpDedup(o.topic, now);
|
|
120
|
+
this._orphansSurfaced += 1;
|
|
121
|
+
return 1;
|
|
122
|
+
}
|
|
123
|
+
// Per-topic with backoff (a single flapper cannot mint per cycle).
|
|
124
|
+
let count = 0;
|
|
125
|
+
for (const o of orphans) {
|
|
126
|
+
if (this.shouldSurface(o.topic, now)) {
|
|
127
|
+
this.d.surface({
|
|
128
|
+
key: `topic-reachability:${o.topic}`,
|
|
129
|
+
topics: [o.topic],
|
|
130
|
+
reason: `topic ${o.topic} may be unreachable (${o.reason})`,
|
|
131
|
+
rolledUp: false,
|
|
132
|
+
});
|
|
133
|
+
this.bumpDedup(o.topic, now);
|
|
134
|
+
this._orphansSurfaced++;
|
|
135
|
+
count++;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return count;
|
|
139
|
+
}
|
|
140
|
+
/** Backoff gate: re-surface only past an exponentially-widening floor, regardless of re-arm. */
|
|
141
|
+
shouldSurface(topic, now) {
|
|
142
|
+
const s = this.dedup.get(topic);
|
|
143
|
+
if (!s || s.armed)
|
|
144
|
+
return true; // first time, or re-armed by a verified-reachable obs
|
|
145
|
+
const wait = this.d.resurfaceFloorMs * Math.pow(2, Math.max(0, s.surfaceCount - 1));
|
|
146
|
+
return now - s.lastSurfacedMs >= wait;
|
|
147
|
+
}
|
|
148
|
+
bumpDedup(topic, now) {
|
|
149
|
+
const s = this.dedup.get(topic) ?? { lastSurfacedMs: 0, surfaceCount: 0, armed: true };
|
|
150
|
+
s.lastSurfacedMs = now;
|
|
151
|
+
s.surfaceCount = s.armed ? 1 : s.surfaceCount + 1;
|
|
152
|
+
s.armed = false;
|
|
153
|
+
this.dedup.set(topic, s);
|
|
154
|
+
}
|
|
155
|
+
markReachable(topic) {
|
|
156
|
+
const s = this.dedup.get(topic);
|
|
157
|
+
if (s) {
|
|
158
|
+
s.armed = true; // a genuine re-orphan after heal may surface again (not suppressed forever)
|
|
159
|
+
s.surfaceCount = 0;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
status() {
|
|
163
|
+
return {
|
|
164
|
+
pending: this.pending.size,
|
|
165
|
+
deferred: this.deferredWindow.size,
|
|
166
|
+
orphansSurfaced: this._orphansSurfaced,
|
|
167
|
+
verifiedReachable: this._verifiedReachable,
|
|
168
|
+
overflow: this._overflow,
|
|
169
|
+
lastTickAt: this._lastTickAt,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=TopicReachabilityVerifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TopicReachabilityVerifier.js","sourceRoot":"","sources":["../../src/monitoring/TopicReachabilityVerifier.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-06-26T13:
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-06-26T13:16:49.510Z",
|
|
5
|
+
"instarVersion": "1.3.678",
|
|
6
6
|
"entryCount": 202,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|