instar 1.2.67 → 1.2.68

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.
Files changed (45) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +50 -1
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/PostUpdateMigrator.d.ts +12 -0
  5. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  6. package/dist/core/PostUpdateMigrator.js +138 -0
  7. package/dist/core/PostUpdateMigrator.js.map +1 -1
  8. package/dist/core/SessionManager.d.ts.map +1 -1
  9. package/dist/core/SessionManager.js +3 -0
  10. package/dist/core/SessionManager.js.map +1 -1
  11. package/dist/providers/adapters/anthropic-headless/transport/agenticSessionHeadless.d.ts.map +1 -1
  12. package/dist/providers/adapters/anthropic-headless/transport/agenticSessionHeadless.js +1 -0
  13. package/dist/providers/adapters/anthropic-headless/transport/agenticSessionHeadless.js.map +1 -1
  14. package/dist/providers/adapters/openai-codex/transport/agenticSessionHeadless.d.ts.map +1 -1
  15. package/dist/providers/adapters/openai-codex/transport/agenticSessionHeadless.js +1 -0
  16. package/dist/providers/adapters/openai-codex/transport/agenticSessionHeadless.js.map +1 -1
  17. package/dist/providers/adapters/openai-codex/transport/codexSpawn.d.ts +2 -0
  18. package/dist/providers/adapters/openai-codex/transport/codexSpawn.d.ts.map +1 -1
  19. package/dist/providers/adapters/openai-codex/transport/codexSpawn.js +4 -0
  20. package/dist/providers/adapters/openai-codex/transport/codexSpawn.js.map +1 -1
  21. package/dist/server/AgentServer.d.ts +4 -0
  22. package/dist/server/AgentServer.d.ts.map +1 -1
  23. package/dist/server/AgentServer.js +2 -0
  24. package/dist/server/AgentServer.js.map +1 -1
  25. package/dist/server/routes.d.ts +6 -0
  26. package/dist/server/routes.d.ts.map +1 -1
  27. package/dist/server/routes.js +60 -1
  28. package/dist/server/routes.js.map +1 -1
  29. package/dist/threadline/ConversationStore.d.ts +158 -0
  30. package/dist/threadline/ConversationStore.d.ts.map +1 -0
  31. package/dist/threadline/ConversationStore.js +341 -0
  32. package/dist/threadline/ConversationStore.js.map +1 -0
  33. package/dist/threadline/ThreadlineRouter.d.ts.map +1 -1
  34. package/dist/threadline/ThreadlineRouter.js +24 -0
  35. package/dist/threadline/ThreadlineRouter.js.map +1 -1
  36. package/dist/threadline/WarrantsReplyGate.d.ts +110 -0
  37. package/dist/threadline/WarrantsReplyGate.d.ts.map +1 -0
  38. package/dist/threadline/WarrantsReplyGate.js +263 -0
  39. package/dist/threadline/WarrantsReplyGate.js.map +1 -0
  40. package/dist/threadline/mcp-http-client.d.ts.map +1 -1
  41. package/dist/threadline/mcp-http-client.js +6 -0
  42. package/dist/threadline/mcp-http-client.js.map +1 -1
  43. package/package.json +1 -1
  44. package/src/data/builtin-manifest.json +63 -63
  45. package/upgrades/1.2.68.md +97 -0
@@ -0,0 +1,158 @@
1
+ /**
2
+ * ConversationStore — the single source of truth for a Threadline conversation.
3
+ *
4
+ * Phase 1 of the Threadline re-assessment (THREADLINE-CONVERSATION-KEYSTONE-SPEC.md).
5
+ * Collapses the state previously smeared across `ThreadResumeMap`,
6
+ * `ContextThreadMap`, the in-memory peer-affinity map and `inbox.jsonl` into one
7
+ * durable record keyed by `threadId`. It is the ONLY place conversation turn
8
+ * state lives — the one-shot reply worker provably cannot self-police a loop, so
9
+ * the turn count + novelty hashes must live here, on the conversation.
10
+ *
11
+ * Concurrency: every write goes through `mutate(threadId, fn)`, a single-writer
12
+ * surface modeled on `CommitmentTracker.mutate()` — a per-threadId FIFO queue
13
+ * plus optimistic CAS on the record's `version`. This is the fix for the
14
+ * convergence-flagged race where two near-simultaneous inbound messages (or a
15
+ * live-inject racing a resume) would clobber `turnCount`/`lastOutboundHash` under
16
+ * the legacy last-writer-wins `load→mutate→persist`.
17
+ *
18
+ * Storage: {stateDir}/threadline/conversations.json (server-write-only, atomic
19
+ * tmp+rename). NOT relay-hosted — authoritative state stays local. The
20
+ * append-only SharedStateLedger remains the audit trail; this is the live
21
+ * mutable state it logs transitions from.
22
+ */
23
+ /**
24
+ * Conversation lifecycle state. Superset of the legacy `ThreadState`
25
+ * (`active|idle|resolved|failed|archived`) plus `open` (created, not yet
26
+ * worked) and `awaiting-reply` (we replied, waiting on the peer). `idle` is
27
+ * also the warrants-a-reply gate's "suppressed, do not spawn" terminal-ish
28
+ * state.
29
+ */
30
+ export type ConversationState = 'open' | 'active' | 'idle' | 'awaiting-reply' | 'resolved' | 'failed' | 'archived';
31
+ /**
32
+ * The durable conversation record. EXHAUSTIVE against the legacy stores — an
33
+ * incomplete field list silently drops live data on migration (convergence
34
+ * finding). Every field the four legacy stores held is preserved here.
35
+ */
36
+ export interface Conversation {
37
+ /** Primary key — the Threadline thread id. */
38
+ threadId: string;
39
+ /** Monotonic version for optimistic CAS in mutate(). */
40
+ version: number;
41
+ /** Conversation participants — self + peer fingerprint(s). */
42
+ participants: {
43
+ /** This agent's name/fingerprint (best-effort). */
44
+ self?: string;
45
+ /** Remote peer fingerprint(s). */
46
+ peers: string[];
47
+ };
48
+ /** Convenience display handle for the primary remote peer. */
49
+ remoteAgent?: string;
50
+ /** Lifecycle state. */
51
+ state: ConversationState;
52
+ /** When state became 'resolved' (grace-period clock). */
53
+ resolvedAt?: string;
54
+ /** The Claude/Codex session UUID the resume primitive depends on. */
55
+ sessionUuid?: string;
56
+ /** The live (or last-known) tmux session bound to this conversation. */
57
+ boundSessionName?: string;
58
+ /** Owning Telegram topic (formerly originTopicId). */
59
+ boundTopicId?: number;
60
+ /** Originating topic-session name at send time (fast-path resume cache). */
61
+ originSessionName?: string;
62
+ /** Spawn mode of the worker handling this conversation. */
63
+ spawnMode?: 'interactive' | 'pipe';
64
+ /** Thread subject. */
65
+ subject?: string;
66
+ /** A2A contextId mapped to this thread, if any. */
67
+ contextId?: string;
68
+ /**
69
+ * The authenticated agent identity that owns the contextId binding. Dropping
70
+ * this reopens the session-smuggling vector ContextThreadMap defends.
71
+ */
72
+ agentIdentity?: string;
73
+ pinned: boolean;
74
+ messageCount: number;
75
+ machineOrigin?: string;
76
+ migratedTo?: string;
77
+ migrateFrom?: string;
78
+ turnCount: number;
79
+ lastInboundHash?: string;
80
+ lastOutboundHash?: string;
81
+ createdAt: string;
82
+ savedAt: string;
83
+ lastActivityAt: string;
84
+ /** Snapshot of the unified-trust level at last contact. */
85
+ trustLevel?: string;
86
+ /** Snapshot of the MoltBridge IQS band at last contact. */
87
+ iqsBand?: string;
88
+ }
89
+ /** Mutation function: receives a draft clone, returns the next record. */
90
+ export type ConversationMutateFn = (draft: Conversation) => Conversation | Promise<Conversation>;
91
+ export declare class ConversationStore {
92
+ private filePath;
93
+ private store;
94
+ /** Per-threadId FIFO mutate queues — serialize concurrent writers. */
95
+ private mutateQueues;
96
+ private mutateRunning;
97
+ /** Ephemeral verified-only peer-affinity hints (peerFingerprint → hint). */
98
+ private affinity;
99
+ constructor(stateDir: string);
100
+ /** Get a conversation by threadId. Returns null if missing or TTL-expired. */
101
+ get(threadId: string): Conversation | null;
102
+ /** True if a (non-expired) conversation exists for the threadId. */
103
+ has(threadId: string): boolean;
104
+ /** Find conversations whose peer set includes the given fingerprint/name. */
105
+ getByParticipant(participant: string): Conversation[];
106
+ /** Find the conversation bound to a Telegram topic, if any. */
107
+ getByTopicId(topicId: number): Conversation | null;
108
+ /** Reverse lookup: conversation owning an A2A contextId (identity-bound). */
109
+ getByContextId(contextId: string, agentIdentity: string): Conversation | null;
110
+ /** List active/idle conversations (not resolved/failed/archived). */
111
+ listActive(): Conversation[];
112
+ /** Total stored conversations (for monitoring). */
113
+ size(): number;
114
+ /**
115
+ * Single-writer mutate surface. Every write path routes through here so
116
+ * concurrent writers (the inbound funnel gate, the router resume/spawn, the
117
+ * relay-send binding stamp) can't clobber each other.
118
+ *
119
+ * Contract (mirrors CommitmentTracker.mutate):
120
+ * - FIFO queue per threadId, max depth 256.
121
+ * - Optimistic CAS on `version`: read → fn(clone) → write iff version
122
+ * unchanged, else retry (max 5). On success version is incremented and
123
+ * the store is persisted atomically.
124
+ * - If the conversation does not exist yet, the fn receives a fresh skeleton
125
+ * (state 'open', version 0) so callers can upsert in one call.
126
+ */
127
+ mutate(threadId: string, fn: ConversationMutateFn): Promise<Conversation>;
128
+ private drainMutateQueue;
129
+ private applyMutationWithCAS;
130
+ /**
131
+ * Direct upsert of a full record WITHOUT going through the CAS queue. ONLY
132
+ * for migration / bulk import where there are no concurrent writers. Runtime
133
+ * writers MUST use mutate().
134
+ */
135
+ importDirect(conversation: Conversation): void;
136
+ /** Persist after a batch of importDirect calls. */
137
+ flush(): void;
138
+ /** Remove a conversation. */
139
+ remove(threadId: string): void;
140
+ /**
141
+ * Record a short-lived, verified-only affinity hint (peer → most-recent
142
+ * thread). Deliberately NOT persisted (accepted loss on restart). Callers
143
+ * MUST only call this for VERIFIED peers — an unverified affinity would be a
144
+ * hijack vector.
145
+ */
146
+ recordAffinity(peerFingerprint: string, threadId: string): void;
147
+ /** Look up a fresh affinity hint, honoring the sliding + absolute windows. */
148
+ getAffinity(peerFingerprint: string): string | null;
149
+ /** Prune expired + resolved-past-grace + LRU-overflow (non-pinned). */
150
+ prune(): void;
151
+ private skeleton;
152
+ private isExpired;
153
+ private pruneIfNeeded;
154
+ private pruneMap;
155
+ private loadStore;
156
+ private saveStore;
157
+ }
158
+ //# sourceMappingURL=ConversationStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConversationStore.d.ts","sourceRoot":"","sources":["../../src/threadline/ConversationStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,341 @@
1
+ /**
2
+ * ConversationStore — the single source of truth for a Threadline conversation.
3
+ *
4
+ * Phase 1 of the Threadline re-assessment (THREADLINE-CONVERSATION-KEYSTONE-SPEC.md).
5
+ * Collapses the state previously smeared across `ThreadResumeMap`,
6
+ * `ContextThreadMap`, the in-memory peer-affinity map and `inbox.jsonl` into one
7
+ * durable record keyed by `threadId`. It is the ONLY place conversation turn
8
+ * state lives — the one-shot reply worker provably cannot self-police a loop, so
9
+ * the turn count + novelty hashes must live here, on the conversation.
10
+ *
11
+ * Concurrency: every write goes through `mutate(threadId, fn)`, a single-writer
12
+ * surface modeled on `CommitmentTracker.mutate()` — a per-threadId FIFO queue
13
+ * plus optimistic CAS on the record's `version`. This is the fix for the
14
+ * convergence-flagged race where two near-simultaneous inbound messages (or a
15
+ * live-inject racing a resume) would clobber `turnCount`/`lastOutboundHash` under
16
+ * the legacy last-writer-wins `load→mutate→persist`.
17
+ *
18
+ * Storage: {stateDir}/threadline/conversations.json (server-write-only, atomic
19
+ * tmp+rename). NOT relay-hosted — authoritative state stays local. The
20
+ * append-only SharedStateLedger remains the audit trail; this is the live
21
+ * mutable state it logs transitions from.
22
+ */
23
+ import fs from 'node:fs';
24
+ import path from 'node:path';
25
+ // ── Constants ───────────────────────────────────────────────────
26
+ /** Entries older than 7 days are pruned (non-pinned only) — matches legacy TTL. */
27
+ const MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
28
+ /** Resolved conversations get a 7-day grace before removal — matches legacy. */
29
+ const RESOLVED_GRACE_MS = 7 * 24 * 60 * 60 * 1000;
30
+ /** Cap before LRU eviction of non-pinned entries — matches ThreadResumeMap. */
31
+ const MAX_ENTRIES = 1000;
32
+ /** Max depth of the per-id mutate queue. Enqueue beyond this rejects. */
33
+ const MUTATE_QUEUE_MAX_DEPTH = 256;
34
+ /** Max CAS retries when the version drifts under an apply. */
35
+ const MUTATE_CAS_MAX_RETRIES = 5;
36
+ // ── Ephemeral verified-only peer-affinity (NOT persisted) ────────
37
+ //
38
+ // The legacy peer-affinity map is a SHORT (10-min sliding / 2-hr absolute),
39
+ // verified-only, deliberately ephemeral hint — promoting it to a durable,
40
+ // unverified binding would reopen a hijack vector (spec §1). We keep it in
41
+ // memory on the store so it is lost on restart by design (accepted loss).
42
+ const AFFINITY_SLIDING_MS = 10 * 60 * 1000;
43
+ const AFFINITY_ABSOLUTE_MS = 2 * 60 * 60 * 1000;
44
+ // ── Implementation ──────────────────────────────────────────────
45
+ export class ConversationStore {
46
+ filePath;
47
+ store;
48
+ /** Per-threadId FIFO mutate queues — serialize concurrent writers. */
49
+ mutateQueues = new Map();
50
+ mutateRunning = new Set();
51
+ /** Ephemeral verified-only peer-affinity hints (peerFingerprint → hint). */
52
+ affinity = new Map();
53
+ constructor(stateDir) {
54
+ const threadlineDir = path.join(stateDir, 'threadline');
55
+ fs.mkdirSync(threadlineDir, { recursive: true });
56
+ this.filePath = path.join(threadlineDir, 'conversations.json');
57
+ this.store = this.loadStore();
58
+ }
59
+ // ── Reads (synchronous, from in-memory store) ──────────────────
60
+ /** Get a conversation by threadId. Returns null if missing or TTL-expired. */
61
+ get(threadId) {
62
+ const c = this.store.conversations[threadId];
63
+ if (!c)
64
+ return null;
65
+ if (!c.pinned && this.isExpired(c))
66
+ return null;
67
+ return c;
68
+ }
69
+ /** True if a (non-expired) conversation exists for the threadId. */
70
+ has(threadId) {
71
+ return this.get(threadId) !== null;
72
+ }
73
+ /** Find conversations whose peer set includes the given fingerprint/name. */
74
+ getByParticipant(participant) {
75
+ const out = [];
76
+ for (const c of Object.values(this.store.conversations)) {
77
+ if (c.pinned || !this.isExpired(c)) {
78
+ if (c.participants.peers.includes(participant) || c.remoteAgent === participant) {
79
+ out.push(c);
80
+ }
81
+ }
82
+ }
83
+ return out;
84
+ }
85
+ /** Find the conversation bound to a Telegram topic, if any. */
86
+ getByTopicId(topicId) {
87
+ for (const c of Object.values(this.store.conversations)) {
88
+ if (c.boundTopicId === topicId && (c.pinned || !this.isExpired(c)))
89
+ return c;
90
+ }
91
+ return null;
92
+ }
93
+ /** Reverse lookup: conversation owning an A2A contextId (identity-bound). */
94
+ getByContextId(contextId, agentIdentity) {
95
+ for (const c of Object.values(this.store.conversations)) {
96
+ if (c.contextId === contextId && (c.pinned || !this.isExpired(c))) {
97
+ // Identity binding — prevents session smuggling (ContextThreadMap parity).
98
+ if (c.agentIdentity && c.agentIdentity !== agentIdentity)
99
+ return null;
100
+ return c;
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+ /** List active/idle conversations (not resolved/failed/archived). */
106
+ listActive() {
107
+ const out = [];
108
+ for (const c of Object.values(this.store.conversations)) {
109
+ if ((c.state === 'active' || c.state === 'idle' || c.state === 'open' || c.state === 'awaiting-reply') &&
110
+ (c.pinned || !this.isExpired(c))) {
111
+ out.push(c);
112
+ }
113
+ }
114
+ return out;
115
+ }
116
+ /** Total stored conversations (for monitoring). */
117
+ size() {
118
+ return Object.keys(this.store.conversations).length;
119
+ }
120
+ // ── Writes (single-writer CAS) ─────────────────────────────────
121
+ /**
122
+ * Single-writer mutate surface. Every write path routes through here so
123
+ * concurrent writers (the inbound funnel gate, the router resume/spawn, the
124
+ * relay-send binding stamp) can't clobber each other.
125
+ *
126
+ * Contract (mirrors CommitmentTracker.mutate):
127
+ * - FIFO queue per threadId, max depth 256.
128
+ * - Optimistic CAS on `version`: read → fn(clone) → write iff version
129
+ * unchanged, else retry (max 5). On success version is incremented and
130
+ * the store is persisted atomically.
131
+ * - If the conversation does not exist yet, the fn receives a fresh skeleton
132
+ * (state 'open', version 0) so callers can upsert in one call.
133
+ */
134
+ async mutate(threadId, fn) {
135
+ return new Promise((resolve, reject) => {
136
+ let queue = this.mutateQueues.get(threadId);
137
+ if (!queue) {
138
+ queue = [];
139
+ this.mutateQueues.set(threadId, queue);
140
+ }
141
+ if (queue.length >= MUTATE_QUEUE_MAX_DEPTH) {
142
+ reject(new Error(`ConversationStore.mutate: queue full for ${threadId} (depth ${queue.length} >= ${MUTATE_QUEUE_MAX_DEPTH})`));
143
+ return;
144
+ }
145
+ queue.push({ fn, resolve, reject });
146
+ void this.drainMutateQueue(threadId);
147
+ });
148
+ }
149
+ async drainMutateQueue(threadId) {
150
+ if (this.mutateRunning.has(threadId))
151
+ return;
152
+ this.mutateRunning.add(threadId);
153
+ try {
154
+ const queue = this.mutateQueues.get(threadId);
155
+ while (queue && queue.length > 0) {
156
+ const entry = queue.shift();
157
+ try {
158
+ const result = await this.applyMutationWithCAS(threadId, entry.fn);
159
+ entry.resolve(result);
160
+ }
161
+ catch (err) {
162
+ entry.reject(err instanceof Error ? err : new Error(String(err)));
163
+ }
164
+ }
165
+ if (queue && queue.length === 0)
166
+ this.mutateQueues.delete(threadId);
167
+ }
168
+ finally {
169
+ this.mutateRunning.delete(threadId);
170
+ }
171
+ }
172
+ async applyMutationWithCAS(threadId, fn) {
173
+ let attempt = 0;
174
+ while (attempt <= MUTATE_CAS_MAX_RETRIES) {
175
+ const current = this.store.conversations[threadId] ?? this.skeleton(threadId);
176
+ const observedVersion = current.version ?? 0;
177
+ const draft = { ...current, participants: { ...current.participants, peers: [...current.participants.peers] } };
178
+ const next = await fn(draft);
179
+ // CAS: the record's version must not have drifted underneath us.
180
+ const latest = this.store.conversations[threadId];
181
+ const latestVersion = latest?.version ?? (latest ? 0 : observedVersion);
182
+ if (latest && latestVersion !== observedVersion) {
183
+ attempt++;
184
+ continue;
185
+ }
186
+ const committed = {
187
+ ...next,
188
+ threadId,
189
+ version: observedVersion + 1,
190
+ savedAt: new Date().toISOString(),
191
+ };
192
+ this.store.conversations[threadId] = committed;
193
+ this.pruneIfNeeded();
194
+ this.saveStore();
195
+ return committed;
196
+ }
197
+ throw new Error(`ConversationStore.mutate: CAS retry budget exhausted for ${threadId} after ${MUTATE_CAS_MAX_RETRIES} retries`);
198
+ }
199
+ /**
200
+ * Direct upsert of a full record WITHOUT going through the CAS queue. ONLY
201
+ * for migration / bulk import where there are no concurrent writers. Runtime
202
+ * writers MUST use mutate().
203
+ */
204
+ importDirect(conversation) {
205
+ const existing = this.store.conversations[conversation.threadId];
206
+ this.store.conversations[conversation.threadId] = {
207
+ ...conversation,
208
+ version: existing ? Math.max(existing.version ?? 0, conversation.version ?? 0) : (conversation.version ?? 0),
209
+ savedAt: new Date().toISOString(),
210
+ };
211
+ }
212
+ /** Persist after a batch of importDirect calls. */
213
+ flush() {
214
+ this.saveStore();
215
+ }
216
+ /** Remove a conversation. */
217
+ remove(threadId) {
218
+ if (this.store.conversations[threadId]) {
219
+ delete this.store.conversations[threadId];
220
+ this.saveStore();
221
+ }
222
+ }
223
+ // ── Ephemeral verified-only peer affinity (in-memory, non-durable) ──
224
+ /**
225
+ * Record a short-lived, verified-only affinity hint (peer → most-recent
226
+ * thread). Deliberately NOT persisted (accepted loss on restart). Callers
227
+ * MUST only call this for VERIFIED peers — an unverified affinity would be a
228
+ * hijack vector.
229
+ */
230
+ recordAffinity(peerFingerprint, threadId) {
231
+ const now = Date.now();
232
+ const existing = this.affinity.get(peerFingerprint);
233
+ if (existing && existing.threadId === threadId) {
234
+ existing.lastSeen = now;
235
+ }
236
+ else {
237
+ this.affinity.set(peerFingerprint, { threadId, firstSeen: now, lastSeen: now });
238
+ }
239
+ }
240
+ /** Look up a fresh affinity hint, honoring the sliding + absolute windows. */
241
+ getAffinity(peerFingerprint) {
242
+ const hint = this.affinity.get(peerFingerprint);
243
+ if (!hint)
244
+ return null;
245
+ const now = Date.now();
246
+ if (now - hint.lastSeen > AFFINITY_SLIDING_MS || now - hint.firstSeen > AFFINITY_ABSOLUTE_MS) {
247
+ this.affinity.delete(peerFingerprint);
248
+ return null;
249
+ }
250
+ return hint.threadId;
251
+ }
252
+ // ── Maintenance ────────────────────────────────────────────────
253
+ /** Prune expired + resolved-past-grace + LRU-overflow (non-pinned). */
254
+ prune() {
255
+ this.pruneMap();
256
+ this.saveStore();
257
+ }
258
+ // ── Private helpers ────────────────────────────────────────────
259
+ skeleton(threadId) {
260
+ const now = new Date().toISOString();
261
+ return {
262
+ threadId,
263
+ version: 0,
264
+ participants: { peers: [] },
265
+ state: 'open',
266
+ pinned: false,
267
+ messageCount: 0,
268
+ turnCount: 0,
269
+ createdAt: now,
270
+ savedAt: now,
271
+ lastActivityAt: now,
272
+ };
273
+ }
274
+ isExpired(c) {
275
+ const now = Date.now();
276
+ if (c.state === 'resolved' && c.resolvedAt) {
277
+ return now - new Date(c.resolvedAt).getTime() > RESOLVED_GRACE_MS;
278
+ }
279
+ const ref = c.lastActivityAt || c.savedAt;
280
+ return now - new Date(ref).getTime() > MAX_AGE_MS;
281
+ }
282
+ pruneIfNeeded() {
283
+ if (Object.keys(this.store.conversations).length > MAX_ENTRIES)
284
+ this.pruneMap();
285
+ }
286
+ pruneMap() {
287
+ const map = this.store.conversations;
288
+ // Phase 1: drop expired / resolved-past-grace (non-pinned).
289
+ for (const key of Object.keys(map)) {
290
+ const c = map[key];
291
+ if (c.pinned)
292
+ continue;
293
+ if (this.isExpired(c))
294
+ delete map[key];
295
+ }
296
+ // Phase 2: LRU eviction if still over cap.
297
+ const keys = Object.keys(map);
298
+ if (keys.length <= MAX_ENTRIES)
299
+ return;
300
+ const unpinned = [];
301
+ for (const key of keys) {
302
+ const c = map[key];
303
+ if (c.pinned)
304
+ continue;
305
+ unpinned.push({ key, t: new Date(c.lastActivityAt || c.savedAt).getTime() });
306
+ }
307
+ unpinned.sort((a, b) => a.t - b.t);
308
+ const toEvict = keys.length - MAX_ENTRIES;
309
+ for (let i = 0; i < toEvict && i < unpinned.length; i++) {
310
+ delete map[unpinned[i].key];
311
+ }
312
+ }
313
+ loadStore() {
314
+ try {
315
+ if (fs.existsSync(this.filePath)) {
316
+ const data = JSON.parse(fs.readFileSync(this.filePath, 'utf-8'));
317
+ if (data && data.version === 1 && data.conversations && typeof data.conversations === 'object') {
318
+ return data;
319
+ }
320
+ }
321
+ }
322
+ catch {
323
+ // Corrupted — start fresh.
324
+ }
325
+ return { version: 1, conversations: {}, lastModified: new Date().toISOString() };
326
+ }
327
+ saveStore() {
328
+ this.store.lastModified = new Date().toISOString();
329
+ try {
330
+ const dir = path.dirname(this.filePath);
331
+ fs.mkdirSync(dir, { recursive: true });
332
+ const tmpPath = `${this.filePath}.${process.pid}.tmp`;
333
+ fs.writeFileSync(tmpPath, JSON.stringify(this.store, null, 2) + '\n');
334
+ fs.renameSync(tmpPath, this.filePath);
335
+ }
336
+ catch {
337
+ // @silent-fallback-ok — state persistence failure, retried on next write.
338
+ }
339
+ }
340
+ }
341
+ //# sourceMappingURL=ConversationStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConversationStore.js","sourceRoot":"","sources":["../../src/threadline/ConversationStore.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadlineRouter.d.ts","sourceRoot":"","sources":["../../src/threadline/ThreadlineRouter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ThreadlineRouter.d.ts","sourceRoot":"","sources":["../../src/threadline/ThreadlineRouter.ts"],"names":[],"mappings":"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"}
@@ -246,6 +246,30 @@ export class ThreadlineRouter {
246
246
  message.threadId = mintedThreadId;
247
247
  }
248
248
  }
249
+ // SECURITY (KEYSTONE §2 / acceptance #8): a threadId is NOT a bearer token.
250
+ // If an UNVERIFIED peer presents a threadId that already resolves to a
251
+ // conversation owned by a DIFFERENT participant, it must NOT be resumed into
252
+ // / routed to that owner session — that is the hijack surface
253
+ // ContextThreadMap.agentIdentity defends. Crypto-verified peers are exempt
254
+ // (identity is already established); for unverified peers the inbound
255
+ // identity must match the thread's known participant, else we isolate the
256
+ // sender to a fresh first-contact thread and the victim's conversation is
257
+ // left untouched.
258
+ {
259
+ const presented = this.threadResumeMap.get(message.threadId);
260
+ if (presented) {
261
+ const cryptoVerified = relayContext?.trust.kind === 'verified';
262
+ const inboundFp = relayContext?.senderFingerprint || message.from.agent || '';
263
+ const inboundName = relayContext?.senderName || '';
264
+ const peer = presented.remoteAgent || '';
265
+ const identityMatches = !!peer && (peer === inboundFp || peer === inboundName);
266
+ if (!cryptoVerified && !identityMatches) {
267
+ const freshId = crypto.randomUUID();
268
+ console.warn(`[ThreadlineRouter] Anti-hijack: unverified sender ${inboundName || inboundFp || 'unknown'} presented threadId ${message.threadId.slice(0, 8)} owned by ${peer.slice(0, 16)}; isolating to fresh thread ${freshId.slice(0, 8)}`);
269
+ message.threadId = freshId;
270
+ }
271
+ }
272
+ }
249
273
  const threadId = message.threadId;
250
274
  // Record affinity (no-op for non-verified trust kinds).
251
275
  this.recordAffinity(relayContext, threadId);