instar 1.3.323 → 1.3.325
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/init.d.ts.map +1 -1
- package/dist/commands/init.js +30 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +4 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/core/AgentPassport.d.ts +50 -0
- package/dist/core/AgentPassport.d.ts.map +1 -0
- package/dist/core/AgentPassport.js +70 -0
- package/dist/core/AgentPassport.js.map +1 -0
- package/dist/core/LiveTailSource.d.ts +62 -3
- package/dist/core/LiveTailSource.d.ts.map +1 -1
- package/dist/core/LiveTailSource.js +89 -9
- package/dist/core/LiveTailSource.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +20 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/handoffSentinelBootWiring.d.ts +3 -1
- package/dist/core/handoffSentinelBootWiring.d.ts.map +1 -1
- package/dist/core/handoffSentinelBootWiring.js +3 -1
- package/dist/core/handoffSentinelBootWiring.js.map +1 -1
- package/dist/messaging/TelegramAdapter.d.ts +46 -1
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +131 -3
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +5 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +9 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +55 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +63 -63
- package/src/scaffold/templates.ts +5 -0
- package/upgrades/1.3.324.md +51 -0
- package/upgrades/1.3.325.md +59 -0
- package/upgrades/side-effects/exo3-g3-ci-greening.md +31 -0
- package/upgrades/side-effects/live-tail-event-loop-guards.md +77 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentPassport — the EXO 3.0 "digital passport" (Salim Ismail, "The 80-Year
|
|
3
|
+
* Business Rule AI Just Broke"): "every AI agent gets a digital passport with
|
|
4
|
+
* metadata saying what it's allowed to do and what it's not allowed to do, and
|
|
5
|
+
* other AI agents watching that it's complying."
|
|
6
|
+
*
|
|
7
|
+
* Instar already has the primitives — agent identity (name + routing
|
|
8
|
+
* fingerprint), a trust level, and ORG-INTENT constraints (forbidden actions).
|
|
9
|
+
* This module PACKAGES them into one explicit, portable passport object plus a
|
|
10
|
+
* deterministic `permits()` check, so a peer agent can read a passport and
|
|
11
|
+
* decide whether to trust a proposed action BEFORE it happens. Deterministic +
|
|
12
|
+
* advisory; the caller decides what to do with the verdict.
|
|
13
|
+
*/
|
|
14
|
+
export type TrustLevel = 'untrusted' | 'supervised' | 'collaborative' | 'autonomous';
|
|
15
|
+
export interface PassportInput {
|
|
16
|
+
agent: string;
|
|
17
|
+
fingerprint: string;
|
|
18
|
+
trustLevel?: TrustLevel;
|
|
19
|
+
/** Capabilities this agent is scoped to (empty = unrestricted by capability). */
|
|
20
|
+
allowedCapabilities?: string[];
|
|
21
|
+
/** Actions this agent must never do (e.g. ORG-INTENT constraints). */
|
|
22
|
+
forbiddenActions?: string[];
|
|
23
|
+
/** ISO timestamp the passport was issued (caller-supplied to stay pure). */
|
|
24
|
+
issuedAt: string;
|
|
25
|
+
}
|
|
26
|
+
export interface AgentPassport {
|
|
27
|
+
version: 1;
|
|
28
|
+
agent: string;
|
|
29
|
+
fingerprint: string;
|
|
30
|
+
trustLevel: TrustLevel;
|
|
31
|
+
allowedCapabilities: string[];
|
|
32
|
+
forbiddenActions: string[];
|
|
33
|
+
issuedAt: string;
|
|
34
|
+
}
|
|
35
|
+
export interface PermitVerdict {
|
|
36
|
+
permitted: boolean;
|
|
37
|
+
/** 'forbidden-action' | 'out-of-scope' | 'trust-floor' | 'ok' */
|
|
38
|
+
basis: 'forbidden-action' | 'out-of-scope' | 'trust-floor' | 'ok';
|
|
39
|
+
reason: string;
|
|
40
|
+
/** The forbidden action that matched, if any. */
|
|
41
|
+
matched?: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function buildPassport(input: PassportInput): AgentPassport;
|
|
44
|
+
/**
|
|
45
|
+
* The compliance check a peer runs against a passport before trusting an action.
|
|
46
|
+
* Order: forbidden-action (hard no) → trust-floor (untrusted can't act) →
|
|
47
|
+
* capability-scope (if scoped, the action must be in scope) → ok.
|
|
48
|
+
*/
|
|
49
|
+
export declare function permits(passport: AgentPassport, action: string): PermitVerdict;
|
|
50
|
+
//# sourceMappingURL=AgentPassport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentPassport.d.ts","sourceRoot":"","sources":["../../src/core/AgentPassport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,YAAY,GAAG,eAAe,GAAG,YAAY,CAAC;AAErF,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,iEAAiE;IACjE,KAAK,EAAE,kBAAkB,GAAG,cAAc,GAAG,aAAa,GAAG,IAAI,CAAC;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA4BD,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CAUjE;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAgB9E"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentPassport — the EXO 3.0 "digital passport" (Salim Ismail, "The 80-Year
|
|
3
|
+
* Business Rule AI Just Broke"): "every AI agent gets a digital passport with
|
|
4
|
+
* metadata saying what it's allowed to do and what it's not allowed to do, and
|
|
5
|
+
* other AI agents watching that it's complying."
|
|
6
|
+
*
|
|
7
|
+
* Instar already has the primitives — agent identity (name + routing
|
|
8
|
+
* fingerprint), a trust level, and ORG-INTENT constraints (forbidden actions).
|
|
9
|
+
* This module PACKAGES them into one explicit, portable passport object plus a
|
|
10
|
+
* deterministic `permits()` check, so a peer agent can read a passport and
|
|
11
|
+
* decide whether to trust a proposed action BEFORE it happens. Deterministic +
|
|
12
|
+
* advisory; the caller decides what to do with the verdict.
|
|
13
|
+
*/
|
|
14
|
+
// ── Helpers (keyword overlap, shared shape with IntentTestHarness) ───
|
|
15
|
+
const STOP = new Set(['the', 'a', 'an', 'to', 'of', 'for', 'with', 'and', 'or', 'any', 'that', 'this', 'is', 'are', 'be', 'on', 'in', 'it', 'its', 'our', 'your', 'their', 'all', 'from', 'by']);
|
|
16
|
+
function normalize(t) {
|
|
17
|
+
return t.toLowerCase().replace(/[^a-z0-9\s]/g, ' ').replace(/\s+/g, ' ').trim();
|
|
18
|
+
}
|
|
19
|
+
function words(s) {
|
|
20
|
+
return normalize(s).split(' ').filter((w) => w.length > 2 && !STOP.has(w));
|
|
21
|
+
}
|
|
22
|
+
function overlap(a, b) {
|
|
23
|
+
const wa = words(a), wb = words(b);
|
|
24
|
+
if (!wa.length || !wb.length)
|
|
25
|
+
return 0;
|
|
26
|
+
const setB = new Set(wb);
|
|
27
|
+
return wa.filter((w) => setB.has(w)).length / Math.min(wa.length, wb.length);
|
|
28
|
+
}
|
|
29
|
+
const MATCH = 0.6;
|
|
30
|
+
// Trust floors: an untrusted passport may only read/observe; supervised+ may act.
|
|
31
|
+
const ACTING_VERBS = ['wire', 'send', 'delete', 'deploy', 'pay', 'publish', 'transfer', 'modify', 'write', 'execute', 'purchase', 'sign'];
|
|
32
|
+
function isActing(action) {
|
|
33
|
+
const w = new Set(words(action));
|
|
34
|
+
return ACTING_VERBS.some((v) => w.has(v));
|
|
35
|
+
}
|
|
36
|
+
// ── Public API ───────────────────────────────────────────────────────
|
|
37
|
+
export function buildPassport(input) {
|
|
38
|
+
return {
|
|
39
|
+
version: 1,
|
|
40
|
+
agent: input.agent,
|
|
41
|
+
fingerprint: input.fingerprint,
|
|
42
|
+
trustLevel: input.trustLevel ?? 'supervised',
|
|
43
|
+
allowedCapabilities: input.allowedCapabilities ?? [],
|
|
44
|
+
forbiddenActions: input.forbiddenActions ?? [],
|
|
45
|
+
issuedAt: input.issuedAt,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The compliance check a peer runs against a passport before trusting an action.
|
|
50
|
+
* Order: forbidden-action (hard no) → trust-floor (untrusted can't act) →
|
|
51
|
+
* capability-scope (if scoped, the action must be in scope) → ok.
|
|
52
|
+
*/
|
|
53
|
+
export function permits(passport, action) {
|
|
54
|
+
for (const f of passport.forbiddenActions) {
|
|
55
|
+
if (overlap(action, f) >= MATCH) {
|
|
56
|
+
return { permitted: false, basis: 'forbidden-action', reason: `Forbidden by the passport: "${f}".`, matched: f };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (passport.trustLevel === 'untrusted' && isActing(action)) {
|
|
60
|
+
return { permitted: false, basis: 'trust-floor', reason: 'Untrusted passport may observe but not act.' };
|
|
61
|
+
}
|
|
62
|
+
if (passport.allowedCapabilities.length > 0) {
|
|
63
|
+
const inScope = passport.allowedCapabilities.some((c) => overlap(action, c) >= MATCH);
|
|
64
|
+
if (!inScope) {
|
|
65
|
+
return { permitted: false, basis: 'out-of-scope', reason: 'Action is outside the passport\'s allowed capabilities.' };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { permitted: true, basis: 'ok', reason: 'Permitted: violates no forbidden action and is within scope.' };
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=AgentPassport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentPassport.js","sourceRoot":"","sources":["../../src/core/AgentPassport.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,30 @@
|
|
|
13
13
|
* topic sequence numbers so HandoffSentinel can build the manifest the incoming
|
|
14
14
|
* machine must echo.
|
|
15
15
|
*
|
|
16
|
+
* EVENT-LOOP DISCIPLINE (the 2026-06-05 Laptop-meltdown fix): building a topic's
|
|
17
|
+
* content is the expensive step — the provider may serialize hundreds of history
|
|
18
|
+
* entries. Doing that for EVERY known topic on EVERY tick blocked the server's
|
|
19
|
+
* event loop for seconds at a stretch, which made our mesh RPC timestamps go
|
|
20
|
+
* stale, which made the standby reject flushes, which caused hot retries — a
|
|
21
|
+
* self-amplifying storm. Three guards now bound the work:
|
|
22
|
+
*
|
|
23
|
+
* 1. VERSION GATE — when the deps provide getTopicVersion (a cheap, monotonic
|
|
24
|
+
* per-topic message counter), a topic whose version is unchanged since its
|
|
25
|
+
* last successful flush is skipped WITHOUT building its content. Idle topics
|
|
26
|
+
* cost one Map lookup per tick instead of a full history serialization.
|
|
27
|
+
* 2. FAILURE BACKOFF — a topic whose flush was rejected/unreachable is not
|
|
28
|
+
* retried every tick. Consecutive failures back off exponentially (base
|
|
29
|
+
* doubling, capped), so a rejecting peer (clock-skew 403s, auth break) is
|
|
30
|
+
* never hammered with full-tail resends at tick rate.
|
|
31
|
+
* 3. CONTENT CAP — a single flush's content is capped (maxFlushBytes); an
|
|
32
|
+
* oversized delta/full-resend sends only the freshest suffix. The standby's
|
|
33
|
+
* buffer caps per-topic bytes anyway (LiveTailBuffer.maxBytesPerTopic), so
|
|
34
|
+
* an unbounded send is pure cost with no retention benefit.
|
|
35
|
+
*
|
|
36
|
+
* The handoff path passes { force: true }, which bypasses the version gate and
|
|
37
|
+
* the backoff window (a handoff is a deliberate one-shot that must try NOW) —
|
|
38
|
+
* but still sends nothing when content is genuinely unchanged.
|
|
39
|
+
*
|
|
16
40
|
* The content provider (getTopicContent) and the transport are injected, so this
|
|
17
41
|
* is unit-testable without a network or a live message store, and so the same
|
|
18
42
|
* producer works for any channel (Telegram, Slack) — the source is channel-
|
|
@@ -31,6 +55,26 @@ export interface LiveTailSourceDeps {
|
|
|
31
55
|
content: string;
|
|
32
56
|
}) => Promise<boolean>;
|
|
33
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* Cheap, monotonic per-topic content version (e.g. a message counter bumped on
|
|
60
|
+
* every logged message). When provided, an unchanged version skips the topic
|
|
61
|
+
* without calling getTopicContent — the event-loop guard that keeps idle
|
|
62
|
+
* topics at O(1) per tick. Omitted → every tick builds every topic's content
|
|
63
|
+
* (the pre-fix behavior; fine for tests and tiny installs).
|
|
64
|
+
*/
|
|
65
|
+
getTopicVersion?: (topic: string) => number;
|
|
66
|
+
/**
|
|
67
|
+
* Cap on a single flush's content size, in UTF-16 chars (≈ bytes for our
|
|
68
|
+
* ASCII-heavy logs; heavily non-ASCII content can be up to ~3× larger on the
|
|
69
|
+
* wire — acceptable: the standby buffer enforces its own independent byte
|
|
70
|
+
* cap, this is purely a sender-side cost bound). Default 256 KiB.
|
|
71
|
+
*/
|
|
72
|
+
maxFlushBytes?: number;
|
|
73
|
+
/** First-failure retry delay; doubles per consecutive failure. Default 5s. */
|
|
74
|
+
failureBackoffBaseMs?: number;
|
|
75
|
+
/** Backoff ceiling. Default 5 min. */
|
|
76
|
+
failureBackoffMaxMs?: number;
|
|
77
|
+
now?: () => number;
|
|
34
78
|
logger?: (msg: string) => void;
|
|
35
79
|
}
|
|
36
80
|
export interface FlushOutcome {
|
|
@@ -40,14 +84,29 @@ export interface FlushOutcome {
|
|
|
40
84
|
/** Whether new content was actually flushed (false = nothing new). */
|
|
41
85
|
flushed: boolean;
|
|
42
86
|
}
|
|
87
|
+
export interface FlushOpts {
|
|
88
|
+
/**
|
|
89
|
+
* Bypass the version gate and the failure-backoff window (handoff path — a
|
|
90
|
+
* deliberate one-shot that must attempt NOW). Unchanged content still sends
|
|
91
|
+
* nothing.
|
|
92
|
+
*/
|
|
93
|
+
force?: boolean;
|
|
94
|
+
}
|
|
43
95
|
export declare class LiveTailSource {
|
|
44
96
|
private readonly d;
|
|
45
97
|
/** Per-topic content already streamed (the prefix the standby has). */
|
|
46
98
|
private streamed;
|
|
47
99
|
/** Per-topic monotonic sequence (matches the standby's applied seq). */
|
|
48
100
|
private seq;
|
|
101
|
+
/** Per-topic provider version at the last successful flush / confirmed no-op. */
|
|
102
|
+
private lastSeenVersion;
|
|
103
|
+
/** Per-topic consecutive broadcast failures (drives the backoff). */
|
|
104
|
+
private failures;
|
|
105
|
+
/** Per-topic earliest next attempt (ms epoch) while backing off. */
|
|
106
|
+
private nextAttemptAt;
|
|
49
107
|
constructor(deps: LiveTailSourceDeps);
|
|
50
108
|
private log;
|
|
109
|
+
private now;
|
|
51
110
|
/** Current high-water sequence for a topic (for the handoff manifest). */
|
|
52
111
|
currentSeq(topic: string): number;
|
|
53
112
|
/**
|
|
@@ -56,10 +115,10 @@ export declare class LiveTailSource {
|
|
|
56
115
|
* rewrite), we resend the whole content as a fresh delta. No new content → no
|
|
57
116
|
* flush, no sequence bump (so duplicate ticks don't inflate the standby's seq).
|
|
58
117
|
*/
|
|
59
|
-
flushTopic(topic: string): Promise<FlushOutcome>;
|
|
118
|
+
flushTopic(topic: string, opts?: FlushOpts): Promise<FlushOutcome>;
|
|
60
119
|
/** Flush every active topic; returns each topic's outcome (used by the handoff). */
|
|
61
|
-
flushAll(): Promise<FlushOutcome[]>;
|
|
120
|
+
flushAll(opts?: FlushOpts): Promise<FlushOutcome[]>;
|
|
62
121
|
/** The cadence driver — flush whatever has new content. Returns count actually flushed. */
|
|
63
|
-
pushTick(): Promise<number>;
|
|
122
|
+
pushTick(opts?: FlushOpts): Promise<number>;
|
|
64
123
|
}
|
|
65
124
|
//# sourceMappingURL=LiveTailSource.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveTailSource.d.ts","sourceRoot":"","sources":["../../src/core/LiveTailSource.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"LiveTailSource.d.ts","sourceRoot":"","sources":["../../src/core/LiveTailSource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,MAAM,WAAW,kBAAkB;IACjC,+EAA+E;IAC/E,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,oEAAoE;IACpE,YAAY,EAAE,MAAM,MAAM,EAAE,CAAC;IAC7B,oFAAoF;IACpF,SAAS,EAAE;QACT,SAAS,EAAE,CAAC,KAAK,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;KACzF,CAAC;IACF;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8EAA8E;IAC9E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,sCAAsC;IACtC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,6FAA6F;IAC7F,GAAG,EAAE,MAAM,CAAC;IACZ,sEAAsE;IACtE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAMD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAqB;IACvC,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAA6B;IAC7C,wEAAwE;IACxE,OAAO,CAAC,GAAG,CAA6B;IACxC,iFAAiF;IACjF,OAAO,CAAC,eAAe,CAA6B;IACpD,qEAAqE;IACrE,OAAO,CAAC,QAAQ,CAA6B;IAC7C,oEAAoE;IACpE,OAAO,CAAC,aAAa,CAA6B;gBAEtC,IAAI,EAAE,kBAAkB;IAIpC,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,GAAG;IAIX,0EAA0E;IAC1E,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIjC;;;;;OAKG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC;IA+ExE,oFAAoF;IAC9E,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAQzD,2FAA2F;IACrF,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;CAIlD"}
|
|
@@ -13,23 +13,59 @@
|
|
|
13
13
|
* topic sequence numbers so HandoffSentinel can build the manifest the incoming
|
|
14
14
|
* machine must echo.
|
|
15
15
|
*
|
|
16
|
+
* EVENT-LOOP DISCIPLINE (the 2026-06-05 Laptop-meltdown fix): building a topic's
|
|
17
|
+
* content is the expensive step — the provider may serialize hundreds of history
|
|
18
|
+
* entries. Doing that for EVERY known topic on EVERY tick blocked the server's
|
|
19
|
+
* event loop for seconds at a stretch, which made our mesh RPC timestamps go
|
|
20
|
+
* stale, which made the standby reject flushes, which caused hot retries — a
|
|
21
|
+
* self-amplifying storm. Three guards now bound the work:
|
|
22
|
+
*
|
|
23
|
+
* 1. VERSION GATE — when the deps provide getTopicVersion (a cheap, monotonic
|
|
24
|
+
* per-topic message counter), a topic whose version is unchanged since its
|
|
25
|
+
* last successful flush is skipped WITHOUT building its content. Idle topics
|
|
26
|
+
* cost one Map lookup per tick instead of a full history serialization.
|
|
27
|
+
* 2. FAILURE BACKOFF — a topic whose flush was rejected/unreachable is not
|
|
28
|
+
* retried every tick. Consecutive failures back off exponentially (base
|
|
29
|
+
* doubling, capped), so a rejecting peer (clock-skew 403s, auth break) is
|
|
30
|
+
* never hammered with full-tail resends at tick rate.
|
|
31
|
+
* 3. CONTENT CAP — a single flush's content is capped (maxFlushBytes); an
|
|
32
|
+
* oversized delta/full-resend sends only the freshest suffix. The standby's
|
|
33
|
+
* buffer caps per-topic bytes anyway (LiveTailBuffer.maxBytesPerTopic), so
|
|
34
|
+
* an unbounded send is pure cost with no retention benefit.
|
|
35
|
+
*
|
|
36
|
+
* The handoff path passes { force: true }, which bypasses the version gate and
|
|
37
|
+
* the backoff window (a handoff is a deliberate one-shot that must try NOW) —
|
|
38
|
+
* but still sends nothing when content is genuinely unchanged.
|
|
39
|
+
*
|
|
16
40
|
* The content provider (getTopicContent) and the transport are injected, so this
|
|
17
41
|
* is unit-testable without a network or a live message store, and so the same
|
|
18
42
|
* producer works for any channel (Telegram, Slack) — the source is channel-
|
|
19
43
|
* agnostic; only the content provider differs.
|
|
20
44
|
*/
|
|
45
|
+
const DEFAULT_MAX_FLUSH_BYTES = 256 * 1024;
|
|
46
|
+
const DEFAULT_BACKOFF_BASE_MS = 5_000;
|
|
47
|
+
const DEFAULT_BACKOFF_MAX_MS = 300_000;
|
|
21
48
|
export class LiveTailSource {
|
|
22
49
|
d;
|
|
23
50
|
/** Per-topic content already streamed (the prefix the standby has). */
|
|
24
51
|
streamed = new Map();
|
|
25
52
|
/** Per-topic monotonic sequence (matches the standby's applied seq). */
|
|
26
53
|
seq = new Map();
|
|
54
|
+
/** Per-topic provider version at the last successful flush / confirmed no-op. */
|
|
55
|
+
lastSeenVersion = new Map();
|
|
56
|
+
/** Per-topic consecutive broadcast failures (drives the backoff). */
|
|
57
|
+
failures = new Map();
|
|
58
|
+
/** Per-topic earliest next attempt (ms epoch) while backing off. */
|
|
59
|
+
nextAttemptAt = new Map();
|
|
27
60
|
constructor(deps) {
|
|
28
61
|
this.d = deps;
|
|
29
62
|
}
|
|
30
63
|
log(m) {
|
|
31
64
|
this.d.logger?.(`[live-tail-source] ${m}`);
|
|
32
65
|
}
|
|
66
|
+
now() {
|
|
67
|
+
return (this.d.now ?? Date.now)();
|
|
68
|
+
}
|
|
33
69
|
/** Current high-water sequence for a topic (for the handoff manifest). */
|
|
34
70
|
currentSeq(topic) {
|
|
35
71
|
return this.seq.get(topic) ?? 0;
|
|
@@ -40,12 +76,37 @@ export class LiveTailSource {
|
|
|
40
76
|
* rewrite), we resend the whole content as a fresh delta. No new content → no
|
|
41
77
|
* flush, no sequence bump (so duplicate ticks don't inflate the standby's seq).
|
|
42
78
|
*/
|
|
43
|
-
async flushTopic(topic) {
|
|
79
|
+
async flushTopic(topic, opts) {
|
|
80
|
+
const noFlush = { topic, seq: this.currentSeq(topic), flushed: false };
|
|
81
|
+
const now = this.now();
|
|
82
|
+
if (!opts?.force) {
|
|
83
|
+
// Guard 2 — failure backoff: a topic mid-backoff is skipped outright.
|
|
84
|
+
const retryAt = this.nextAttemptAt.get(topic);
|
|
85
|
+
if (retryAt !== undefined && now < retryAt)
|
|
86
|
+
return noFlush;
|
|
87
|
+
// Guard 1 — version gate: unchanged version + no pending retry → skip
|
|
88
|
+
// WITHOUT building the content. (A pending retry means content changed but
|
|
89
|
+
// the send failed — the version is unchanged since that attempt, yet we
|
|
90
|
+
// still owe the retry.)
|
|
91
|
+
if (this.d.getTopicVersion && (this.failures.get(topic) ?? 0) === 0) {
|
|
92
|
+
const v = this.d.getTopicVersion(topic);
|
|
93
|
+
if (this.lastSeenVersion.get(topic) === v)
|
|
94
|
+
return noFlush;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
44
97
|
const full = this.d.getTopicContent(topic) ?? '';
|
|
45
98
|
const prior = this.streamed.get(topic) ?? '';
|
|
99
|
+
const version = this.d.getTopicVersion?.(topic);
|
|
100
|
+
const recordNoNewContent = () => {
|
|
101
|
+
// Content is confirmed identical — record the version so the gate skips
|
|
102
|
+
// this topic until it actually changes again.
|
|
103
|
+
if (version !== undefined)
|
|
104
|
+
this.lastSeenVersion.set(topic, version);
|
|
105
|
+
return noFlush;
|
|
106
|
+
};
|
|
46
107
|
let delta;
|
|
47
108
|
if (full === prior) {
|
|
48
|
-
return
|
|
109
|
+
return recordNoNewContent();
|
|
49
110
|
}
|
|
50
111
|
if (full.startsWith(prior)) {
|
|
51
112
|
delta = full.slice(prior.length);
|
|
@@ -56,31 +117,50 @@ export class LiveTailSource {
|
|
|
56
117
|
delta = full;
|
|
57
118
|
}
|
|
58
119
|
if (delta.length === 0) {
|
|
59
|
-
return
|
|
120
|
+
return recordNoNewContent();
|
|
121
|
+
}
|
|
122
|
+
// Guard 3 — content cap: keep the freshest suffix. The standby's buffer caps
|
|
123
|
+
// per-topic bytes anyway; an oversized send is cost without retention.
|
|
124
|
+
const maxBytes = this.d.maxFlushBytes ?? DEFAULT_MAX_FLUSH_BYTES;
|
|
125
|
+
if (delta.length > maxBytes) {
|
|
126
|
+
this.log(`topic ${topic} flush capped: ${delta.length} → ${maxBytes} chars (freshest suffix kept)`);
|
|
127
|
+
delta = delta.slice(-maxBytes);
|
|
60
128
|
}
|
|
61
129
|
const nextSeq = this.currentSeq(topic) + 1;
|
|
62
130
|
const ok = await this.d.transport.broadcast({ topic, seq: nextSeq, content: delta });
|
|
63
131
|
if (!ok) {
|
|
64
132
|
// The standby was unreachable — do NOT advance our streamed/seq state, so
|
|
65
|
-
//
|
|
66
|
-
|
|
133
|
+
// a later attempt retries the same delta (the buffer dedups on seq anyway).
|
|
134
|
+
// Back off exponentially so a persistently-rejecting peer is not hammered
|
|
135
|
+
// at tick rate (the 403 retry-storm guard).
|
|
136
|
+
const failures = (this.failures.get(topic) ?? 0) + 1;
|
|
137
|
+
this.failures.set(topic, failures);
|
|
138
|
+
const base = this.d.failureBackoffBaseMs ?? DEFAULT_BACKOFF_BASE_MS;
|
|
139
|
+
const cap = this.d.failureBackoffMaxMs ?? DEFAULT_BACKOFF_MAX_MS;
|
|
140
|
+
const backoff = Math.min(base * 2 ** (failures - 1), cap);
|
|
141
|
+
this.nextAttemptAt.set(topic, this.now() + backoff);
|
|
142
|
+
this.log(`flush of topic ${topic} seq ${nextSeq} not acknowledged — retry in ${Math.round(backoff / 1000)}s (failure #${failures})`);
|
|
67
143
|
return { topic, seq: this.currentSeq(topic), flushed: false };
|
|
68
144
|
}
|
|
145
|
+
this.failures.delete(topic);
|
|
146
|
+
this.nextAttemptAt.delete(topic);
|
|
69
147
|
this.seq.set(topic, nextSeq);
|
|
70
148
|
this.streamed.set(topic, full);
|
|
149
|
+
if (version !== undefined)
|
|
150
|
+
this.lastSeenVersion.set(topic, version);
|
|
71
151
|
return { topic, seq: nextSeq, flushed: true };
|
|
72
152
|
}
|
|
73
153
|
/** Flush every active topic; returns each topic's outcome (used by the handoff). */
|
|
74
|
-
async flushAll() {
|
|
154
|
+
async flushAll(opts) {
|
|
75
155
|
const outcomes = [];
|
|
76
156
|
for (const topic of this.d.activeTopics()) {
|
|
77
|
-
outcomes.push(await this.flushTopic(topic));
|
|
157
|
+
outcomes.push(await this.flushTopic(topic, opts));
|
|
78
158
|
}
|
|
79
159
|
return outcomes;
|
|
80
160
|
}
|
|
81
161
|
/** The cadence driver — flush whatever has new content. Returns count actually flushed. */
|
|
82
|
-
async pushTick() {
|
|
83
|
-
const outcomes = await this.flushAll();
|
|
162
|
+
async pushTick(opts) {
|
|
163
|
+
const outcomes = await this.flushAll(opts);
|
|
84
164
|
return outcomes.filter((o) => o.flushed).length;
|
|
85
165
|
}
|
|
86
166
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveTailSource.js","sourceRoot":"","sources":["../../src/core/LiveTailSource.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"LiveTailSource.js","sourceRoot":"","sources":["../../src/core/LiveTailSource.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostUpdateMigrator.d.ts","sourceRoot":"","sources":["../../src/core/PostUpdateMigrator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"PostUpdateMigrator.d.ts","sourceRoot":"","sources":["../../src/core/PostUpdateMigrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -2721,6 +2721,21 @@ Rule: I do not state that work landed inside another agent's state unless I have
|
|
|
2721
2721
|
patched = true;
|
|
2722
2722
|
result.upgraded.push('CLAUDE.md: added Agent-Readiness Scoring section');
|
|
2723
2723
|
}
|
|
2724
|
+
// Agent Digital Passport (EXO 3.0 G3): identity + trust + ORG-INTENT
|
|
2725
|
+
// constraints packaged portably, with a peer compliance check. Existing
|
|
2726
|
+
// agents need /passport + /passport/verify awareness before trusting a
|
|
2727
|
+
// peer's proposed action. Content-sniffed on a distinctive marker.
|
|
2728
|
+
if (!content.includes('Agent Digital Passport (EXO 3.0')) {
|
|
2729
|
+
const agentPassportSection = `
|
|
2730
|
+
**Agent Digital Passport (EXO 3.0).** Your identity (name + routing fingerprint), trust level, and ORG-INTENT constraints packaged into one portable passport — "every agent carries metadata saying what it's allowed and forbidden to do, and other agents watch compliance" (Salim Ismail).
|
|
2731
|
+
- Your passport: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/passport\` → \`{ agent, fingerprint, trustLevel, allowedCapabilities, forbiddenActions, issuedAt }\` (forbiddenActions = your ORG-INTENT constraints).
|
|
2732
|
+
- Verify a peer's action against their passport: \`curl -X POST -H "Authorization: Bearer $AUTH" -H 'Content-Type: application/json' -d '{"passport":{...},"action":"..."}' http://localhost:${port}/passport/verify\` → \`{ permitted, basis, reason }\` (basis: forbidden-action / trust-floor / out-of-scope / ok).
|
|
2733
|
+
- **When to use** (PROACTIVE): before trusting another agent's proposed action, verify it against their passport; hand peers your passport so they know your scope. Skill: \`/agent-passport\`.
|
|
2734
|
+
`;
|
|
2735
|
+
content += '\n' + agentPassportSection;
|
|
2736
|
+
patched = true;
|
|
2737
|
+
result.upgraded.push('CLAUDE.md: added Agent Digital Passport section');
|
|
2738
|
+
}
|
|
2724
2739
|
// Apprenticeship Program (Step 1, APPRENTICESHIP-STEP1-PROGRAM-SCAFFOLD-SPEC.md).
|
|
2725
2740
|
// Existing agents need to know the program registry + lifecycle gates exist —
|
|
2726
2741
|
// an agent that doesn't know about a capability effectively doesn't have it.
|
|
@@ -4320,6 +4335,11 @@ Create worktrees for collaborator repos with \`instar worktree create <branch>\`
|
|
|
4320
4335
|
// /agent-readiness/score can't run the task-decomposition matrix before
|
|
4321
4336
|
// delegating work.
|
|
4322
4337
|
'**Agent-Readiness Scoring (EXO 3.0',
|
|
4338
|
+
// Agent Digital Passport (EXO 3.0 G3): portable identity + trust +
|
|
4339
|
+
// constraints, with a peer compliance check. A Codex/Gemini agent that
|
|
4340
|
+
// never learns /passport/verify can't check a peer's proposed action
|
|
4341
|
+
// against its passport before trusting it.
|
|
4342
|
+
'**Agent Digital Passport (EXO 3.0',
|
|
4323
4343
|
];
|
|
4324
4344
|
for (const shadowName of ['AGENTS.md', 'GEMINI.md']) {
|
|
4325
4345
|
const shadowPath = path.join(this.config.projectDir, shadowName);
|