instar 1.2.37 → 1.2.39

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 (50) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +19 -1
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/TopicIntent.d.ts +162 -0
  5. package/dist/core/TopicIntent.d.ts.map +1 -0
  6. package/dist/core/TopicIntent.js +355 -0
  7. package/dist/core/TopicIntent.js.map +1 -0
  8. package/dist/core/TopicIntentArcCheck.d.ts +101 -0
  9. package/dist/core/TopicIntentArcCheck.d.ts.map +1 -0
  10. package/dist/core/TopicIntentArcCheck.js +146 -0
  11. package/dist/core/TopicIntentArcCheck.js.map +1 -0
  12. package/dist/core/TopicIntentBriefing.d.ts +44 -0
  13. package/dist/core/TopicIntentBriefing.d.ts.map +1 -0
  14. package/dist/core/TopicIntentBriefing.js +88 -0
  15. package/dist/core/TopicIntentBriefing.js.map +1 -0
  16. package/dist/core/TopicIntentExtractor.d.ts +90 -0
  17. package/dist/core/TopicIntentExtractor.d.ts.map +1 -0
  18. package/dist/core/TopicIntentExtractor.js +153 -0
  19. package/dist/core/TopicIntentExtractor.js.map +1 -0
  20. package/dist/core/TopicIntentPendingConfirm.d.ts +96 -0
  21. package/dist/core/TopicIntentPendingConfirm.d.ts.map +1 -0
  22. package/dist/core/TopicIntentPendingConfirm.js +223 -0
  23. package/dist/core/TopicIntentPendingConfirm.js.map +1 -0
  24. package/dist/messaging/TelegramAdapter.d.ts +45 -0
  25. package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
  26. package/dist/messaging/TelegramAdapter.js +52 -0
  27. package/dist/messaging/TelegramAdapter.js.map +1 -1
  28. package/dist/server/AgentServer.d.ts +3 -0
  29. package/dist/server/AgentServer.d.ts.map +1 -1
  30. package/dist/server/AgentServer.js +10 -0
  31. package/dist/server/AgentServer.js.map +1 -1
  32. package/dist/server/topicIntentRoutes.d.ts +27 -0
  33. package/dist/server/topicIntentRoutes.d.ts.map +1 -0
  34. package/dist/server/topicIntentRoutes.js +189 -0
  35. package/dist/server/topicIntentRoutes.js.map +1 -0
  36. package/dist/tunnel/TunnelManager.d.ts +32 -1
  37. package/dist/tunnel/TunnelManager.d.ts.map +1 -1
  38. package/dist/tunnel/TunnelManager.js +36 -0
  39. package/dist/tunnel/TunnelManager.js.map +1 -1
  40. package/dist/tunnel/TunnelNotifier.d.ts +28 -0
  41. package/dist/tunnel/TunnelNotifier.d.ts.map +1 -1
  42. package/dist/tunnel/TunnelNotifier.js +38 -4
  43. package/dist/tunnel/TunnelNotifier.js.map +1 -1
  44. package/package.json +1 -1
  45. package/src/data/builtin-manifest.json +4 -4
  46. package/src/templates/hooks/telegram-topic-context.sh +19 -0
  47. package/upgrades/1.2.38.md +103 -0
  48. package/upgrades/1.2.39.md +54 -0
  49. package/upgrades/side-effects/owner-dm-channel.md +152 -0
  50. package/upgrades/side-effects/topic-intent-layer.md +86 -0
@@ -0,0 +1,162 @@
1
+ /**
2
+ * TopicIntent — per-topic semantic state tracking via continuous confidence.
3
+ *
4
+ * Layer 1 of the Topic Intent Layer spec (v14 CLEAN, approved 2026-05-22).
5
+ * See docs/specs/topic-intent-layer.md.
6
+ *
7
+ * Tracks candidate facts and decisions extracted from conversation. Each
8
+ * EstablishedRef accumulates evidence over multi-turn exchange; confidence
9
+ * is computed on read as a deterministic projection over the append-only
10
+ * event log.
11
+ *
12
+ * Framework-agnostic: pure JSON persistence, pure math projection. Reachable
13
+ * from Claude Code and Codex sessions alike.
14
+ *
15
+ * Storage: {stateDir}/topic-intent/<topicId>.json
16
+ */
17
+ export type RefKind = 'fact' | 'decision';
18
+ export type RefStatus = 'live' | 'conflicted';
19
+ export type EvidenceKind = 'extract-user' | 'extract-agent' | 'user-reref' | 'agent-reref' | 'user-affirm' | 'pending-confirm-positive' | 'pending-confirm-negative' | 'contradiction' | 'conflict-mark' | 'sharpen-retry-issued';
20
+ export interface EvidenceEvent {
21
+ eventId: string;
22
+ refId: string;
23
+ kind: EvidenceKind;
24
+ sourceMessageId: string;
25
+ userAuthored: boolean;
26
+ at: string;
27
+ delta: number;
28
+ meta?: Record<string, unknown>;
29
+ }
30
+ export interface EstablishedRef {
31
+ refId: string;
32
+ arcId: string;
33
+ topicId: number;
34
+ kind: RefKind;
35
+ text: string;
36
+ confidence: number;
37
+ evidence: EvidenceEvent[];
38
+ lastReinforcedAt: string;
39
+ status: RefStatus;
40
+ createdAt: string;
41
+ updatedAt: string;
42
+ }
43
+ export interface PendingConfirmation {
44
+ pendingId: string;
45
+ topicId: number;
46
+ arcId: string;
47
+ refId: string;
48
+ propositionText: string;
49
+ questionText: string;
50
+ sentAtTurn: number;
51
+ sentAtTime: string;
52
+ ttl: {
53
+ turns: number;
54
+ hours: number;
55
+ };
56
+ retries: number;
57
+ maxRetries: number;
58
+ status: 'pending' | 'answered' | 'expired' | 'abandoned';
59
+ queuedAtTime?: string;
60
+ dequeuedAtTime?: string;
61
+ answeredAtTime?: string;
62
+ answerVerdict?: 'positive' | 'negative' | 'ambiguous' | 'non-responsive';
63
+ }
64
+ export interface TopicIntentFile {
65
+ topicId: number;
66
+ refs: Record<string, EstablishedRef>;
67
+ pending: {
68
+ outstanding: PendingConfirmation | null;
69
+ queue: PendingConfirmation[];
70
+ };
71
+ telemetry: TelemetryCounters;
72
+ schemaVersion: 1;
73
+ }
74
+ export interface TelemetryCounters {
75
+ extraction_total: Record<string, number>;
76
+ evidence_event_total: Record<string, number>;
77
+ confidence_clamp_authority_total: number;
78
+ pending_confirm_created_total: number;
79
+ pending_confirm_queue_dropped_total: number;
80
+ pending_confirm_abandoned_total: number;
81
+ pending_confirm_expired_total: number;
82
+ pending_confirm_answered_total: Record<string, number>;
83
+ }
84
+ export declare const SIGNAL_DELTA: Record<EvidenceKind, number>;
85
+ export declare const USER_AUTHORED_BY_DEFAULT: Record<EvidenceKind, boolean>;
86
+ export interface ProjectionResult {
87
+ confidence: number;
88
+ tier: 'observation' | 'tentative' | 'authoritative';
89
+ authorityClampApplied: boolean;
90
+ decayApplied: number;
91
+ evidenceCount: number;
92
+ userAuthoredEpisodes: number;
93
+ }
94
+ /**
95
+ * Compute the confidence projection for a single EstablishedRef from its
96
+ * evidence array. Pure function — no I/O, no state, fully deterministic.
97
+ *
98
+ * Order of operations:
99
+ * 1. Per-message dedup by (refId, sourceMessageId): on collision, keep the
100
+ * single largest applicable delta. (Multiple signals from the same user
101
+ * message about the same refId count as ONE episode.)
102
+ * 2. Apply signal-specific caps (user-reref cumulative <= +0.30, etc.)
103
+ * 3. Apply affirmation caps (per-refId per 24h, per single user message).
104
+ * 4. Sum applicable deltas (with caps).
105
+ * 5. Apply time decay if (now - lastReinforcedAt) > 30 days.
106
+ * 6. Authority hard clamp: if would-be >= 0.7 and no qualifying
107
+ * user-authored episode exists, clamp at 0.69.
108
+ * 7. Clamp final to [0.0, 1.0].
109
+ */
110
+ export declare function projectConfidence(evidence: EvidenceEvent[], lastReinforcedAt: string, nowMs?: number): ProjectionResult;
111
+ /**
112
+ * Which evidence kinds count as user-authored EPISODES that qualify for
113
+ * authority? (Distinct from the userAuthored boolean, which is broader.)
114
+ *
115
+ * Per spec: "user-authored episodes are the unit of evidence." Only
116
+ * specific kinds qualify — extraction from user, user re-reference,
117
+ * anchored affirmation, positive pending-confirm answer, contradiction
118
+ * (which is also a user-authored episode in the negative direction).
119
+ */
120
+ export declare function qualifiesAsUserAuthoredEpisode(kind: EvidenceKind): boolean;
121
+ export declare function buildEvent(refId: string, kind: EvidenceKind, sourceMessageId: string, opts?: {
122
+ at?: string;
123
+ userAuthored?: boolean;
124
+ delta?: number;
125
+ meta?: Record<string, unknown>;
126
+ }): EvidenceEvent;
127
+ export declare class TopicIntentStore {
128
+ private dir;
129
+ constructor(stateDir: string);
130
+ private filePath;
131
+ /** Load a topic's intent file, returning an empty skeleton if missing or corrupt. */
132
+ load(topicId: number): TopicIntentFile;
133
+ /** Persist the topic's intent file. */
134
+ save(file: TopicIntentFile): void;
135
+ /**
136
+ * Append an evidence event to a refId. Creates the ref if it doesn't exist.
137
+ * Updates lastReinforcedAt if the event has positive delta.
138
+ * Updates telemetry counters.
139
+ */
140
+ appendEvidence(topicId: number, refId: string, ev: EvidenceEvent, refInit?: Partial<EstablishedRef>): TopicIntentFile;
141
+ /** Get the live projection for a refId (recomputed from evidence). */
142
+ getProjection(topicId: number, refId: string, nowMs?: number): ProjectionResult | null;
143
+ /** Get all refs for a topic at current tier or above. */
144
+ getRefsAtOrAbove(topicId: number, minTier: 'observation' | 'tentative' | 'authoritative', nowMs?: number): Array<EstablishedRef & {
145
+ projection: ProjectionResult;
146
+ }>;
147
+ /** Full read of a topic's file (for diagnostics endpoint). */
148
+ read(topicId: number): TopicIntentFile;
149
+ }
150
+ export declare const TOPIC_INTENT_CONSTANTS: {
151
+ DECAY_HALF_LIFE_DAYS: number;
152
+ DECAY_GRACE_DAYS: number;
153
+ DECAY_LAMBDA: number;
154
+ AUTHORITY_THRESHOLD: number;
155
+ AUTHORITY_CLAMP: number;
156
+ TENTATIVE_THRESHOLD: number;
157
+ SIGNAL_CAPS: Partial<Record<EvidenceKind, number>>;
158
+ AFFIRM_PER_REF_PER_24H_LIMIT: number;
159
+ AFFIRM_PER_MESSAGE_REF_LIMIT: number;
160
+ MS_PER_DAY: number;
161
+ };
162
+ //# sourceMappingURL=TopicIntent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicIntent.d.ts","sourceRoot":"","sources":["../../src/core/TopicIntent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;AAC1C,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,YAAY,CAAC;AAE9C,MAAM,MAAM,YAAY,GACpB,cAAc,GACd,eAAe,GACf,YAAY,GACZ,aAAa,GACb,aAAa,GACb,0BAA0B,GAC1B,0BAA0B,GAC1B,eAAe,GACf,eAAe,GACf,sBAAsB,CAAC;AAE3B,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,YAAY,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,SAAS,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,WAAW,CAAC;IAEzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG,gBAAgB,CAAC;CAC1E;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACrC,OAAO,EAAE;QACP,WAAW,EAAE,mBAAmB,GAAG,IAAI,CAAC;QACxC,KAAK,EAAE,mBAAmB,EAAE,CAAC;KAC9B,CAAC;IACF,SAAS,EAAE,iBAAiB,CAAC;IAC7B,aAAa,EAAE,CAAC,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,gCAAgC,EAAE,MAAM,CAAC;IACzC,6BAA6B,EAAE,MAAM,CAAC;IACtC,mCAAmC,EAAE,MAAM,CAAC;IAC5C,+BAA+B,EAAE,MAAM,CAAC;IACxC,6BAA6B,EAAE,MAAM,CAAC;IACtC,8BAA8B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxD;AA4BD,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAWrD,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,YAAY,EAAE,OAAO,CAWlE,CAAC;AAIF,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,aAAa,GAAG,WAAW,GAAG,eAAe,CAAC;IACpD,qBAAqB,EAAE,OAAO,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,aAAa,EAAE,EACzB,gBAAgB,EAAE,MAAM,EACxB,KAAK,GAAE,MAAmB,GACzB,gBAAgB,CA+FlB;AAED;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAS1E;AAID,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,YAAY,EAClB,eAAe,EAAE,MAAM,EACvB,IAAI,CAAC,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC7F,aAAa,CAWf;AAID,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,GAAG,CAAS;gBAER,QAAQ,EAAE,MAAM;IAS5B,OAAO,CAAC,QAAQ;IAIhB,qFAAqF;IACrF,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe;IAkBtC,uCAAuC;IACvC,IAAI,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI;IASjC;;;;OAIG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,eAAe;IA0CrH,sEAAsE;IACtE,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAOtF,yDAAyD;IACzD,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,WAAW,GAAG,eAAe,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,cAAc,GAAG;QAAE,UAAU,EAAE,gBAAgB,CAAA;KAAE,CAAC;IAcnK,8DAA8D;IAC9D,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe;CAGvC;AA6BD,eAAO,MAAM,sBAAsB;;;;;;;;;;;CAWlC,CAAC"}
@@ -0,0 +1,355 @@
1
+ /**
2
+ * TopicIntent — per-topic semantic state tracking via continuous confidence.
3
+ *
4
+ * Layer 1 of the Topic Intent Layer spec (v14 CLEAN, approved 2026-05-22).
5
+ * See docs/specs/topic-intent-layer.md.
6
+ *
7
+ * Tracks candidate facts and decisions extracted from conversation. Each
8
+ * EstablishedRef accumulates evidence over multi-turn exchange; confidence
9
+ * is computed on read as a deterministic projection over the append-only
10
+ * event log.
11
+ *
12
+ * Framework-agnostic: pure JSON persistence, pure math projection. Reachable
13
+ * from Claude Code and Codex sessions alike.
14
+ *
15
+ * Storage: {stateDir}/topic-intent/<topicId>.json
16
+ */
17
+ import fs from 'node:fs';
18
+ import path from 'node:path';
19
+ import { randomUUID } from 'node:crypto';
20
+ // ── Constants from spec ──────────────────────────────────────────────────
21
+ const DECAY_HALF_LIFE_DAYS = 180;
22
+ const DECAY_GRACE_DAYS = 30;
23
+ const DECAY_LAMBDA = Math.log(2) / DECAY_HALF_LIFE_DAYS;
24
+ const AUTHORITY_THRESHOLD = 0.7;
25
+ const AUTHORITY_CLAMP = 0.69;
26
+ const TENTATIVE_THRESHOLD = 0.3;
27
+ /** Signal-specific caps. Numeric = max cumulative contribution from that kind. */
28
+ const SIGNAL_CAPS = {
29
+ 'user-reref': 0.30,
30
+ 'agent-reref': 0.05,
31
+ 'extract-agent': 0.10, // initial agent-origin extraction capped at single occurrence value
32
+ };
33
+ /** Affirmation safety: per-refId per 24h cap. */
34
+ const AFFIRM_PER_REF_PER_24H_LIMIT = 1;
35
+ /** Per single user message, max number of distinct refIds that may receive affirmation bonus. */
36
+ const AFFIRM_PER_MESSAGE_REF_LIMIT = 3;
37
+ const MS_PER_DAY = 24 * 60 * 60 * 1000;
38
+ // ── Default signal deltas (used by helpers; raw spec values) ─────────────
39
+ export const SIGNAL_DELTA = {
40
+ 'extract-user': 0.40,
41
+ 'extract-agent': 0.10,
42
+ 'user-reref': 0.10,
43
+ 'agent-reref': 0.01,
44
+ 'user-affirm': 0.30,
45
+ 'pending-confirm-positive': 0.50,
46
+ 'pending-confirm-negative': -0.70,
47
+ 'contradiction': -0.60,
48
+ 'conflict-mark': 0, // marker-only, no delta
49
+ 'sharpen-retry-issued': 0, // bookkeeping, no delta
50
+ };
51
+ export const USER_AUTHORED_BY_DEFAULT = {
52
+ 'extract-user': true,
53
+ 'extract-agent': false,
54
+ 'user-reref': true,
55
+ 'agent-reref': false,
56
+ 'user-affirm': true,
57
+ 'pending-confirm-positive': true,
58
+ 'pending-confirm-negative': true,
59
+ 'contradiction': true,
60
+ 'conflict-mark': false,
61
+ 'sharpen-retry-issued': false,
62
+ };
63
+ /**
64
+ * Compute the confidence projection for a single EstablishedRef from its
65
+ * evidence array. Pure function — no I/O, no state, fully deterministic.
66
+ *
67
+ * Order of operations:
68
+ * 1. Per-message dedup by (refId, sourceMessageId): on collision, keep the
69
+ * single largest applicable delta. (Multiple signals from the same user
70
+ * message about the same refId count as ONE episode.)
71
+ * 2. Apply signal-specific caps (user-reref cumulative <= +0.30, etc.)
72
+ * 3. Apply affirmation caps (per-refId per 24h, per single user message).
73
+ * 4. Sum applicable deltas (with caps).
74
+ * 5. Apply time decay if (now - lastReinforcedAt) > 30 days.
75
+ * 6. Authority hard clamp: if would-be >= 0.7 and no qualifying
76
+ * user-authored episode exists, clamp at 0.69.
77
+ * 7. Clamp final to [0.0, 1.0].
78
+ */
79
+ export function projectConfidence(evidence, lastReinforcedAt, nowMs = Date.now()) {
80
+ // Step 1: per-message dedup — keep largest applicable delta per (refId, sourceMessageId)
81
+ const dedupedByMsg = new Map();
82
+ for (const ev of evidence) {
83
+ const key = `${ev.refId}::${ev.sourceMessageId}`;
84
+ const existing = dedupedByMsg.get(key);
85
+ if (!existing || Math.abs(ev.delta) > Math.abs(existing.delta)) {
86
+ dedupedByMsg.set(key, ev);
87
+ }
88
+ }
89
+ const deduped = Array.from(dedupedByMsg.values());
90
+ // Step 2-3: bucket by kind, apply caps
91
+ const bucketedByKind = new Map();
92
+ for (const ev of deduped) {
93
+ if (!bucketedByKind.has(ev.kind))
94
+ bucketedByKind.set(ev.kind, []);
95
+ bucketedByKind.get(ev.kind).push(ev);
96
+ }
97
+ let runningSum = 0;
98
+ let userAuthoredEpisodes = 0;
99
+ for (const [kind, events] of bucketedByKind) {
100
+ const cap = SIGNAL_CAPS[kind];
101
+ if (cap !== undefined && events.length > 0 && events[0].delta > 0) {
102
+ // Positive-delta capped signal: sum and clamp at cap
103
+ const raw = events.reduce((s, e) => s + e.delta, 0);
104
+ runningSum += Math.min(raw, cap);
105
+ }
106
+ else if (kind === 'user-affirm') {
107
+ // Affirm safety: enforce per-refId per 24h limit
108
+ // and per single source-message limit of distinct refIds (handled at INSERT time,
109
+ // but defensive in projection too)
110
+ const affirmsByDay = new Map();
111
+ for (const ev of events) {
112
+ const dayKey = ev.at.slice(0, 10); // YYYY-MM-DD coarse bucket
113
+ if (!affirmsByDay.has(dayKey))
114
+ affirmsByDay.set(dayKey, []);
115
+ affirmsByDay.get(dayKey).push(ev);
116
+ }
117
+ let appliedAffirms = 0;
118
+ for (const dayEvents of affirmsByDay.values()) {
119
+ // Sort by time and take only the first AFFIRM_PER_REF_PER_24H_LIMIT
120
+ dayEvents.sort((a, b) => a.at.localeCompare(b.at));
121
+ const allowed = dayEvents.slice(0, AFFIRM_PER_REF_PER_24H_LIMIT);
122
+ appliedAffirms += allowed.reduce((s, e) => s + e.delta, 0);
123
+ }
124
+ runningSum += appliedAffirms;
125
+ }
126
+ else {
127
+ // Uncapped signal: just sum
128
+ runningSum += events.reduce((s, e) => s + e.delta, 0);
129
+ }
130
+ // Count user-authored episodes (those that qualify for authority)
131
+ for (const ev of events) {
132
+ if (ev.userAuthored && qualifiesAsUserAuthoredEpisode(ev.kind)) {
133
+ userAuthoredEpisodes++;
134
+ }
135
+ }
136
+ }
137
+ // Step 5: time decay
138
+ let preDecaySum = Math.max(0, Math.min(1, runningSum));
139
+ const daysSince = Math.max(0, (nowMs - new Date(lastReinforcedAt).getTime()) / MS_PER_DAY);
140
+ let decayApplied = 0;
141
+ if (daysSince > DECAY_GRACE_DAYS) {
142
+ const decayDays = daysSince - DECAY_GRACE_DAYS;
143
+ const decayed = preDecaySum * Math.exp(-DECAY_LAMBDA * decayDays);
144
+ decayApplied = preDecaySum - decayed;
145
+ preDecaySum = decayed;
146
+ }
147
+ // Step 6: authority hard clamp
148
+ let authorityClampApplied = false;
149
+ let finalConf = preDecaySum;
150
+ if (finalConf >= AUTHORITY_THRESHOLD && userAuthoredEpisodes === 0) {
151
+ finalConf = AUTHORITY_CLAMP;
152
+ authorityClampApplied = true;
153
+ }
154
+ // Step 7: final clamp
155
+ finalConf = Math.max(0, Math.min(1, finalConf));
156
+ // Tier classification (emergent)
157
+ let tier;
158
+ if (finalConf < TENTATIVE_THRESHOLD)
159
+ tier = 'observation';
160
+ else if (finalConf < AUTHORITY_THRESHOLD)
161
+ tier = 'tentative';
162
+ else
163
+ tier = 'authoritative';
164
+ return {
165
+ confidence: finalConf,
166
+ tier,
167
+ authorityClampApplied,
168
+ decayApplied,
169
+ evidenceCount: deduped.length,
170
+ userAuthoredEpisodes,
171
+ };
172
+ }
173
+ /**
174
+ * Which evidence kinds count as user-authored EPISODES that qualify for
175
+ * authority? (Distinct from the userAuthored boolean, which is broader.)
176
+ *
177
+ * Per spec: "user-authored episodes are the unit of evidence." Only
178
+ * specific kinds qualify — extraction from user, user re-reference,
179
+ * anchored affirmation, positive pending-confirm answer, contradiction
180
+ * (which is also a user-authored episode in the negative direction).
181
+ */
182
+ export function qualifiesAsUserAuthoredEpisode(kind) {
183
+ return (kind === 'extract-user' ||
184
+ kind === 'user-reref' ||
185
+ kind === 'user-affirm' ||
186
+ kind === 'pending-confirm-positive' ||
187
+ kind === 'pending-confirm-negative' ||
188
+ kind === 'contradiction');
189
+ }
190
+ // ── Helpers for building events ──────────────────────────────────────────
191
+ export function buildEvent(refId, kind, sourceMessageId, opts) {
192
+ return {
193
+ eventId: randomUUID(),
194
+ refId,
195
+ kind,
196
+ sourceMessageId,
197
+ userAuthored: opts?.userAuthored ?? USER_AUTHORED_BY_DEFAULT[kind],
198
+ at: opts?.at ?? new Date().toISOString(),
199
+ delta: opts?.delta ?? SIGNAL_DELTA[kind],
200
+ meta: opts?.meta,
201
+ };
202
+ }
203
+ // ── Store (file-based, framework-agnostic) ───────────────────────────────
204
+ export class TopicIntentStore {
205
+ dir;
206
+ constructor(stateDir) {
207
+ this.dir = path.join(stateDir, 'topic-intent');
208
+ try {
209
+ fs.mkdirSync(this.dir, { recursive: true });
210
+ }
211
+ catch (err) {
212
+ console.error(`[TopicIntentStore] Failed to create dir ${this.dir}: ${err}`);
213
+ }
214
+ }
215
+ filePath(topicId) {
216
+ return path.join(this.dir, `${topicId}.json`);
217
+ }
218
+ /** Load a topic's intent file, returning an empty skeleton if missing or corrupt. */
219
+ load(topicId) {
220
+ const fp = this.filePath(topicId);
221
+ try {
222
+ if (fs.existsSync(fp)) {
223
+ const parsed = JSON.parse(fs.readFileSync(fp, 'utf-8'));
224
+ // Ensure required fields exist (defensive for older files)
225
+ if (!parsed.refs)
226
+ parsed.refs = {};
227
+ if (!parsed.pending)
228
+ parsed.pending = { outstanding: null, queue: [] };
229
+ if (!parsed.telemetry)
230
+ parsed.telemetry = emptyTelemetry();
231
+ if (parsed.schemaVersion === undefined)
232
+ parsed.schemaVersion = 1;
233
+ return parsed;
234
+ }
235
+ }
236
+ catch (err) {
237
+ console.error(`[TopicIntentStore] Corrupt file ${fp}, starting fresh: ${err}`);
238
+ }
239
+ return emptyFile(topicId);
240
+ }
241
+ /** Persist the topic's intent file. */
242
+ save(file) {
243
+ const fp = this.filePath(file.topicId);
244
+ try {
245
+ fs.writeFileSync(fp, JSON.stringify(file, null, 2));
246
+ }
247
+ catch (err) {
248
+ console.error(`[TopicIntentStore] Failed to save ${fp}: ${err}`);
249
+ }
250
+ }
251
+ /**
252
+ * Append an evidence event to a refId. Creates the ref if it doesn't exist.
253
+ * Updates lastReinforcedAt if the event has positive delta.
254
+ * Updates telemetry counters.
255
+ */
256
+ appendEvidence(topicId, refId, ev, refInit) {
257
+ const file = this.load(topicId);
258
+ let ref = file.refs[refId];
259
+ if (!ref) {
260
+ ref = {
261
+ refId,
262
+ arcId: refInit?.arcId ?? `arc-${topicId}`,
263
+ topicId,
264
+ kind: refInit?.kind ?? 'fact',
265
+ text: refInit?.text ?? '',
266
+ confidence: 0,
267
+ evidence: [],
268
+ lastReinforcedAt: ev.at,
269
+ status: 'live',
270
+ createdAt: ev.at,
271
+ updatedAt: ev.at,
272
+ };
273
+ file.refs[refId] = ref;
274
+ }
275
+ ref.evidence.push(ev);
276
+ if (ev.delta > 0) {
277
+ ref.lastReinforcedAt = ev.at;
278
+ }
279
+ ref.updatedAt = ev.at;
280
+ // Recompute confidence + tier snapshot for visibility (projection runs on read regardless)
281
+ const proj = projectConfidence(ref.evidence, ref.lastReinforcedAt);
282
+ ref.confidence = proj.confidence;
283
+ // Telemetry
284
+ const extractKey = `${ev.kind}:${ev.userAuthored}`;
285
+ file.telemetry.extraction_total[extractKey] = (file.telemetry.extraction_total[extractKey] ?? 0) + 1;
286
+ file.telemetry.evidence_event_total[ev.kind] = (file.telemetry.evidence_event_total[ev.kind] ?? 0) + 1;
287
+ if (proj.authorityClampApplied) {
288
+ file.telemetry.confidence_clamp_authority_total++;
289
+ }
290
+ this.save(file);
291
+ return file;
292
+ }
293
+ /** Get the live projection for a refId (recomputed from evidence). */
294
+ getProjection(topicId, refId, nowMs) {
295
+ const file = this.load(topicId);
296
+ const ref = file.refs[refId];
297
+ if (!ref)
298
+ return null;
299
+ return projectConfidence(ref.evidence, ref.lastReinforcedAt, nowMs);
300
+ }
301
+ /** Get all refs for a topic at current tier or above. */
302
+ getRefsAtOrAbove(topicId, minTier, nowMs) {
303
+ const file = this.load(topicId);
304
+ const tierOrder = { observation: 0, tentative: 1, authoritative: 2 };
305
+ const minRank = tierOrder[minTier];
306
+ const out = [];
307
+ for (const ref of Object.values(file.refs)) {
308
+ const proj = projectConfidence(ref.evidence, ref.lastReinforcedAt, nowMs);
309
+ if (tierOrder[proj.tier] >= minRank) {
310
+ out.push({ ...ref, projection: proj });
311
+ }
312
+ }
313
+ return out;
314
+ }
315
+ /** Full read of a topic's file (for diagnostics endpoint). */
316
+ read(topicId) {
317
+ return this.load(topicId);
318
+ }
319
+ }
320
+ // ── Helpers for empty state ──────────────────────────────────────────────
321
+ function emptyTelemetry() {
322
+ return {
323
+ extraction_total: {},
324
+ evidence_event_total: {},
325
+ confidence_clamp_authority_total: 0,
326
+ pending_confirm_created_total: 0,
327
+ pending_confirm_queue_dropped_total: 0,
328
+ pending_confirm_abandoned_total: 0,
329
+ pending_confirm_expired_total: 0,
330
+ pending_confirm_answered_total: {},
331
+ };
332
+ }
333
+ function emptyFile(topicId) {
334
+ return {
335
+ topicId,
336
+ refs: {},
337
+ pending: { outstanding: null, queue: [] },
338
+ telemetry: emptyTelemetry(),
339
+ schemaVersion: 1,
340
+ };
341
+ }
342
+ // ── Re-exports for convenience ───────────────────────────────────────────
343
+ export const TOPIC_INTENT_CONSTANTS = {
344
+ DECAY_HALF_LIFE_DAYS,
345
+ DECAY_GRACE_DAYS,
346
+ DECAY_LAMBDA,
347
+ AUTHORITY_THRESHOLD,
348
+ AUTHORITY_CLAMP,
349
+ TENTATIVE_THRESHOLD,
350
+ SIGNAL_CAPS,
351
+ AFFIRM_PER_REF_PER_24H_LIMIT,
352
+ AFFIRM_PER_MESSAGE_REF_LIMIT,
353
+ MS_PER_DAY,
354
+ };
355
+ //# sourceMappingURL=TopicIntent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicIntent.js","sourceRoot":"","sources":["../../src/core/TopicIntent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * TopicIntentArcCheck — Layer 3 of the Topic Intent Layer.
3
+ *
4
+ * Pre-send classifier that scans an agent's draft reply against the
5
+ * topic's current tentative + settled items. Fires signals (not blocks)
6
+ * when the draft would:
7
+ * - Act on a tentative item without conversational confirmation
8
+ * - Contradict an item already at authoritative tier
9
+ *
10
+ * Per spec v14 sections "How the agent asks for confirmation" and
11
+ * "Authority on send" — ArcCheck is SIGNAL-only. The existing outbound
12
+ * gate retains authority. The fired signal includes a suggested rewrite
13
+ * hint, but never blocks. The agent itself decides whether to redraft.
14
+ *
15
+ * Per `feedback_gate_latency_vs_client_timeout`: ArcCheck is designed
16
+ * to run concurrent with send-prep, not as a serial second gate. The
17
+ * timeout is enforced at the HTTP route layer (route caller sets the
18
+ * deadline; classifier itself is pure async).
19
+ *
20
+ * Per `feedback_signal_vs_authority`: ArcCheck is the brittle/low-context
21
+ * filter that detects and emits a signal. The higher-level outbound gate
22
+ * (tone-gate, response-review) has blocking authority. Two-layer split.
23
+ *
24
+ * Framework-agnostic: the LLM call is injected. Production wires it to
25
+ * Instar's LlmQueue + IntelligenceProvider; tests stub it.
26
+ */
27
+ import { TopicIntentStore, type EstablishedRef, type ProjectionResult } from './TopicIntent.js';
28
+ export interface ArcCheckInput {
29
+ topicId: number;
30
+ draftText: string;
31
+ /** Optional: which user turn this draft is in reply to (for record-keeping). */
32
+ forUserTurn?: number;
33
+ }
34
+ export type ArcCheckVerdict = {
35
+ fire: false;
36
+ } | {
37
+ fire: true;
38
+ kind: 'acting-on-tentative' | 'contradicts-settled';
39
+ refId: string;
40
+ refText: string;
41
+ currentTier: 'tentative' | 'authoritative';
42
+ currentConfidence: number;
43
+ reason: string;
44
+ /** Suggested rewrite hint the agent can include in its redraft. */
45
+ suggestedRewriteHint: string;
46
+ };
47
+ /**
48
+ * The injectable LLM step. Receives the draft + tracked refs and returns
49
+ * a structured classification. Production wires this to a Haiku-class
50
+ * call via LlmQueue; tests stub it.
51
+ *
52
+ * Return value shape: which refs (if any) the draft engages with, and
53
+ * whether the engagement is "act on it" or "contradict it."
54
+ */
55
+ export interface ArcCheckClassification {
56
+ /** refIds the draft appears to ACT ON (decisions baked in, facts assumed). */
57
+ actsOn: string[];
58
+ /** refIds the draft appears to CONTRADICT. */
59
+ contradicts: string[];
60
+ }
61
+ export type ArcCheckClassifyFn = (draftText: string, refs: Array<EstablishedRef & {
62
+ projection: ProjectionResult;
63
+ }>) => Promise<ArcCheckClassification>;
64
+ export declare class ArcCheck {
65
+ private store;
66
+ private classifyFn;
67
+ constructor(store: TopicIntentStore, classifyFn: ArcCheckClassifyFn);
68
+ /**
69
+ * Run ArcCheck on a draft. Returns a verdict object suitable for direct
70
+ * return from an HTTP route or consumption by a hook.
71
+ *
72
+ * Verdict priority (when multiple conditions fire):
73
+ * 1. contradicts-settled (highest — wrong direction on a decided item)
74
+ * 2. acting-on-tentative (lower — uncertain, but not wrong)
75
+ *
76
+ * If the draft engages multiple refs, the first matching one of the
77
+ * highest-priority kind wins. Subsequent fires are deferred to a
78
+ * future ArcCheck call after the agent's redraft.
79
+ */
80
+ check(input: ArcCheckInput): Promise<ArcCheckVerdict>;
81
+ }
82
+ /**
83
+ * Build the ArcCheck prompt for production use. Separated so prompt
84
+ * tuning can iterate without touching the classifier logic.
85
+ *
86
+ * The actual LLM provider call is wired in by the caller; this returns
87
+ * the prompt string + structured response schema description.
88
+ */
89
+ export declare function buildArcCheckPrompt(draftText: string, refs: Array<EstablishedRef & {
90
+ projection: ProjectionResult;
91
+ }>): {
92
+ systemPrompt: string;
93
+ userPrompt: string;
94
+ };
95
+ /**
96
+ * Parse the LLM's response into an ArcCheckClassification. Tolerates
97
+ * code fences and prose preamble; degrades to empty arrays on parse
98
+ * failure so a busted LLM response never falsely fires ArcCheck.
99
+ */
100
+ export declare function parseArcCheckResponse(raw: string): ArcCheckClassification;
101
+ //# sourceMappingURL=TopicIntentArcCheck.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicIntentArcCheck.d.ts","sourceRoot":"","sources":["../../src/core/TopicIntentArcCheck.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EACL,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GACf;IACE,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,qBAAqB,GAAG,qBAAqB,CAAC;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,WAAW,GAAG,eAAe,CAAC;IAC3C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEN;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,8CAA8C;IAC9C,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,MAAM,kBAAkB,GAAG,CAC/B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,KAAK,CAAC,cAAc,GAAG;IAAE,UAAU,EAAE,gBAAgB,CAAA;CAAE,CAAC,KAC3D,OAAO,CAAC,sBAAsB,CAAC,CAAC;AAErC,qBAAa,QAAQ;IAEjB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,UAAU;gBADV,KAAK,EAAE,gBAAgB,EACvB,UAAU,EAAE,kBAAkB;IAGxC;;;;;;;;;;;OAWG;IACG,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC;CAgD5D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,KAAK,CAAC,cAAc,GAAG;IAAE,UAAU,EAAE,gBAAgB,CAAA;CAAE,CAAC,GAC7D;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CA2B9C;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,sBAAsB,CAiBzE"}