@agencer/reverie-loop 0.1.0-alpha.0 → 0.1.0

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.
@@ -0,0 +1,216 @@
1
+ // @agencer/reverie-loop reverie-loop module.
2
+ //
3
+ // Leg G-4a: extracted from packages/server/src/services/reverie-loop.ts
4
+ // (350 LOC). The post-session self-reflection orchestrator.
5
+ //
6
+ // Two execution paths share this module:
7
+ //
8
+ // 1. Real path: production. When deps.forceMock is false/undefined,
9
+ // executeReverie composes executeReflection (opus-client) +
10
+ // splitResultByCategory + appendReverieResult / appendSafetyFlag
11
+ // (pair-sink) for the triple-discriminated jsonl audit trail.
12
+ // Records cost via deps.meter (Law 16). Returns ReverieResultReal
13
+ // (the new pairs[] schema).
14
+ //
15
+ // 2. Legacy mock path: test scaffolding. When deps.forceMock is true,
16
+ // executeReverie generates a ReverieResultLegacy (the old
17
+ // observations[] schema) and writes to deps.legacyReverieLogPath.
18
+ // Retained ONLY for pre-existing test scaffolding; production
19
+ // callers always pass forceMock: false (or omit it).
20
+ //
21
+ // Sacred constraint (Law 8, declared in README): No process.env or
22
+ // os.homedir() reads inside this module. The agencer-ox composition
23
+ // root (and the compatibility shim until G-6) reads
24
+ // process.env["MOCK_REVERIE"] and threads it as deps.forceMock; same
25
+ // pattern for the three jsonl paths.
26
+ //
27
+ // CRITICAL: All public functions NEVER throw. Reverie is
28
+ // observability + training-corpus infrastructure; it cannot block
29
+ // the user-facing teardown path.
30
+ import fs from "node:fs";
31
+ import path from "node:path";
32
+ import { executeReflection, } from "./opus-client.js";
33
+ import { splitResultByCategory, appendReverieResult, appendSafetyFlag, } from "./pair-sink.js";
34
+ // ── Module state ──────────────────────────────────────────────────
35
+ const _pendingTimers = new Map();
36
+ let _lastReverie = null;
37
+ let _lastReverieReal = null;
38
+ const DEBOUNCE_MS = 10_000; // 10 seconds (Leg 1b contract)
39
+ // ── Public API ────────────────────────────────────────────────────
40
+ /**
41
+ * Trigger a reverie for a session. Debounced by 10 seconds to avoid
42
+ * firing on quick reconnects.
43
+ *
44
+ * Always takes a `deps` argument (forceMock toggles the path). The
45
+ * pre-Leg-G no-deps overload is preserved on the agencer-ox
46
+ * compatibility shim only.
47
+ */
48
+ export function triggerReverie(sessionId, getHistory, deps) {
49
+ try {
50
+ const existing = _pendingTimers.get(sessionId);
51
+ if (existing)
52
+ clearTimeout(existing);
53
+ const timer = setTimeout(() => {
54
+ _pendingTimers.delete(sessionId);
55
+ void executeReverie(sessionId, getHistory(), deps);
56
+ }, DEBOUNCE_MS);
57
+ _pendingTimers.set(sessionId, timer);
58
+ }
59
+ catch {
60
+ // NEVER throw
61
+ }
62
+ }
63
+ /**
64
+ * Execute reverie immediately.
65
+ *
66
+ * Real path when deps.forceMock is false/undefined; legacy mock path
67
+ * when deps.forceMock is true. Return type is the union; consumers
68
+ * with specific knowledge can narrow via the forceMock flag or by
69
+ * runtime shape check (`"pairs" in result` discriminates real vs
70
+ * legacy).
71
+ */
72
+ export async function executeReverie(sessionId, history, deps) {
73
+ // Real path: forceMock not set.
74
+ if (!deps.forceMock) {
75
+ return executeRealReverie(sessionId, history, deps);
76
+ }
77
+ // Legacy path: mock observations[] schema, writes
78
+ // deps.legacyReverieLogPath.
79
+ return executeLegacyMockReverie(sessionId, history, deps.legacyReverieLogPath);
80
+ }
81
+ /**
82
+ * Most recent reverie result (legacy shape; test scaffolding).
83
+ */
84
+ export function getLastReverie() {
85
+ return _lastReverie;
86
+ }
87
+ /**
88
+ * Most recent real-path reverie result. Used by tests of the real
89
+ * path; production callers should consume the jsonl audit logs.
90
+ */
91
+ export function getLastReverieReal() {
92
+ return _lastReverieReal;
93
+ }
94
+ /**
95
+ * Cancel all pending reverie timers (shutdown / test teardown).
96
+ */
97
+ export function cancelAllReveries() {
98
+ for (const timer of _pendingTimers.values()) {
99
+ clearTimeout(timer);
100
+ }
101
+ _pendingTimers.clear();
102
+ }
103
+ // ── Real path ─────────────────────────────────────────────────────
104
+ async function executeRealReverie(cohortId, history, deps) {
105
+ try {
106
+ const result = await executeReflection({
107
+ cohortId,
108
+ userContext: deps.userContext,
109
+ history,
110
+ inferenceProvider: deps.inferenceProvider,
111
+ meter: deps.meter,
112
+ costCalculator: deps.costCalculator,
113
+ logger: deps.logger,
114
+ });
115
+ // Split for triple-discriminated routing. Always emit at least
116
+ // one record (audit trail per Anthropic call).
117
+ const split = splitResultByCategory(result, deps.reveriePairsPath);
118
+ if (split.behavioral) {
119
+ await appendReverieResult(deps.reveriePairsPath, split.behavioral, deps.logger);
120
+ }
121
+ if (split.safety) {
122
+ await appendSafetyFlag(deps.reverieSafetyFlagsPath, split.safety, deps.logger);
123
+ }
124
+ if (split.behavioral) {
125
+ deps.logger.info({
126
+ cohortId,
127
+ pairs_total: result.pairs.length,
128
+ pairs_behavioral: split.behavioral.pairs.length,
129
+ pairs_safety: split.safety?.pairs.length ?? 0,
130
+ cost_usd: result.cost_usd,
131
+ model: result.model,
132
+ error_kind: result.error_kind,
133
+ validation_error: result.validation_error,
134
+ }, "reverie-loop.executeRealReverie complete");
135
+ }
136
+ else {
137
+ deps.logger.info({
138
+ cohortId,
139
+ pairs_safety: split.safety?.pairs.length ?? 0,
140
+ cost_usd: result.cost_usd,
141
+ model: result.model,
142
+ }, "reverie-loop.executeRealReverie complete (pure safety cohort)");
143
+ }
144
+ _lastReverieReal = result;
145
+ return result;
146
+ }
147
+ catch (err) {
148
+ // executeReflection is contract-bound to never throw; if it does,
149
+ // log + swallow per the always-resolves contract.
150
+ deps.logger.warn({ err: err instanceof Error ? err.message : String(err), cohortId }, "reverie-loop.executeRealReverie unexpected throw (executeReflection contract violation)");
151
+ return null;
152
+ }
153
+ }
154
+ // ── Legacy mock path (test scaffolding only) ──────────────────────
155
+ async function executeLegacyMockReverie(sessionId, history, legacyReverieLogPath) {
156
+ try {
157
+ const exchanges = pairExchanges(history);
158
+ if (exchanges.length === 0)
159
+ return null;
160
+ const result = generateMockReverie(sessionId, exchanges.length);
161
+ _lastReverie = result;
162
+ writeLegacyReverieLog(result, legacyReverieLogPath);
163
+ return result;
164
+ }
165
+ catch {
166
+ return null;
167
+ }
168
+ }
169
+ function pairExchanges(history) {
170
+ const pairs = [];
171
+ for (let i = 0; i < history.length - 1; i++) {
172
+ const current = history[i];
173
+ const next = history[i + 1];
174
+ if (current && next && current.role === "user" && next.role === "assistant") {
175
+ pairs.push({ user: current.content, assistant: next.content });
176
+ }
177
+ }
178
+ return pairs;
179
+ }
180
+ function generateMockReverie(sessionId, exchangeCount) {
181
+ const observations = [];
182
+ for (let i = 0; i < exchangeCount; i++) {
183
+ const isLast = i === exchangeCount - 1 && exchangeCount > 1;
184
+ observations.push({
185
+ exchange_index: i,
186
+ helpfulness: isLast ? 5 : 8 + Math.floor(Math.random() * 2),
187
+ in_character: isLast ? 4 : 8 + Math.floor(Math.random() * 2),
188
+ user_corrected: isLast,
189
+ correction: isLast ? "User corrected a factual claim" : null,
190
+ lesson: isLast
191
+ ? "Next time I should verify facts before stating them confidently"
192
+ : "Continue responding in this style",
193
+ });
194
+ }
195
+ return {
196
+ sessionId,
197
+ timestamp: new Date().toISOString(),
198
+ observations,
199
+ summary: `Reviewed ${exchangeCount} exchanges. ${exchangeCount - 1} were well-received, 1 needed correction.`,
200
+ };
201
+ }
202
+ function writeLegacyReverieLog(result, legacyReverieLogPath) {
203
+ // Legacy mock writes to reverie-log.jsonl (the OLD file, schema
204
+ // ReverieResultLegacy). The real path (executeRealReverie) writes
205
+ // to reverie-pairs.jsonl + reverie-safety-flags.jsonl instead.
206
+ // This legacy file remains an orphan artifact for test scaffolding;
207
+ // ox-data-factory consumes only reverie-pairs.jsonl going forward.
208
+ try {
209
+ fs.mkdirSync(path.dirname(legacyReverieLogPath), { recursive: true });
210
+ fs.appendFileSync(legacyReverieLogPath, JSON.stringify(result) + "\n");
211
+ }
212
+ catch {
213
+ // NEVER let logging break anything
214
+ }
215
+ }
216
+ //# sourceMappingURL=reverie-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reverie-loop.js","sourceRoot":"","sources":["../src/reverie-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import type { CostCalculator, LoggerLike, ReverieInferenceProvider, ReverieUserContext, UsageMeter } from "./contracts.js";
2
+ import { type ConversationExchange } from "./reverie-loop.js";
3
+ export interface ReverieTriggerOptions {
4
+ meter: UsageMeter;
5
+ logger: LoggerLike;
6
+ /** Cost calculator (injected). UsageAccountant's calculateCost
7
+ * satisfies the signature byte-for-byte. */
8
+ costCalculator: CostCalculator;
9
+ /** Real Anthropic Opus client. When provided, fireForCohort routes
10
+ * triggerReverie through the real-Opus + dual-jsonl-sink path.
11
+ * When absent (test scaffolding), the legacy mock observations[]
12
+ * schema is produced. */
13
+ inferenceProvider?: ReverieInferenceProvider;
14
+ /** Absolute path. Behavioral correction-pair JSONL audit log
15
+ * written by the real path. */
16
+ reveriePairsPath: string;
17
+ /** Absolute path. Safety-concern JSONL audit log written by the
18
+ * real path. */
19
+ reverieSafetyFlagsPath: string;
20
+ /** Absolute path. Legacy mock JSONL audit log written by the
21
+ * legacy mock path (test scaffolding). */
22
+ legacyReverieLogPath: string;
23
+ /** Idle window before reverie fires from inactivity. Default 5min. */
24
+ idleMs?: number;
25
+ /** Per-user daily reverie cost ceiling. Default $1.00. */
26
+ dailyBudgetUsd?: number;
27
+ /** Force legacy mock path even when inferenceProvider is wired
28
+ * (composition root reads process.env["MOCK_REVERIE"] and threads
29
+ * it). Default false. */
30
+ forceMock?: boolean;
31
+ }
32
+ export declare class ReverieTrigger {
33
+ private readonly meter;
34
+ private readonly logger;
35
+ private readonly costCalculator;
36
+ private readonly idleMs;
37
+ private readonly dailyBudgetUsd;
38
+ private readonly inferenceProvider;
39
+ private readonly reveriePairsPath;
40
+ private readonly reverieSafetyFlagsPath;
41
+ private readonly legacyReverieLogPath;
42
+ private readonly forceMockBase;
43
+ private currentCohort;
44
+ constructor(opts: ReverieTriggerOptions);
45
+ /**
46
+ * Mark the active cohort. Starts a cohort if none exists, refreshes
47
+ * the userContext + getHistory snapshot if one does, resets the
48
+ * idle timer either way. Called per turn from brain/route-and-log.ts.
49
+ */
50
+ markActive(userContext: ReverieUserContext, getHistory: () => ConversationExchange[]): void;
51
+ /**
52
+ * Fire reverie for the active cohort and clear it. Called from
53
+ * index.ts's onClientDisconnect when wsContext.clients.size === 0.
54
+ * Detached via setImmediate so the WebSocket teardown call stack
55
+ * returns immediately.
56
+ */
57
+ endCohort(): void;
58
+ /**
59
+ * Cancel any in-flight idle timer and clear cohort state. For
60
+ * server shutdown / test teardown. Does NOT fire reverie.
61
+ */
62
+ cancelAll(): void;
63
+ private fireForCohort;
64
+ private recordCounter;
65
+ private dailyReverieCost;
66
+ }
67
+ //# sourceMappingURL=reverie-trigger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reverie-trigger.d.ts","sourceRoot":"","sources":["../src/reverie-trigger.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,wBAAwB,EACxB,kBAAkB,EAClB,UAAU,EACX,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,mBAAmB,CAAC;AAc3B,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB;iDAC6C;IAC7C,cAAc,EAAE,cAAc,CAAC;IAC/B;;;8BAG0B;IAC1B,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAE7C;oCACgC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB;qBACiB;IACjB,sBAAsB,EAAE,MAAM,CAAC;IAC/B;+CAC2C;IAC3C,oBAAoB,EAAE,MAAM,CAAC;IAE7B,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;8BAE0B;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAuBD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAuC;IACzE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;IAChD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,aAAa,CAA4B;gBAErC,IAAI,EAAE,qBAAqB;IAavC;;;;OAIG;IACH,UAAU,CACR,WAAW,EAAE,kBAAkB,EAC/B,UAAU,EAAE,MAAM,oBAAoB,EAAE,GACvC,IAAI;IAiCP;;;;;OAKG;IACH,SAAS,IAAI,IAAI;IAqBjB;;;OAGG;IACH,SAAS,IAAI,IAAI;IASjB,OAAO,CAAC,aAAa;IAwFrB,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,gBAAgB;CAyBzB"}
@@ -0,0 +1,271 @@
1
+ // @agencer/reverie-loop reverie-trigger module.
2
+ //
3
+ // Leg G-4b: extracted from packages/server/src/brain/reverie-trigger.ts
4
+ // (350 LOC). Session-end orchestrator that fires reverie-loop reflection
5
+ // on last-WebSocket-disconnect or 5-min idle, with dedup, fire-and-forget
6
+ // detachment, and a per-user daily-cost ceiling.
7
+ //
8
+ // Wiring sites preserved (production caller in agencer-ox composition
9
+ // root rewires at G-5, currently routed via the compatibility shim):
10
+ // - markActive(userContext, getHistory) called per turn from
11
+ // brain/route-and-log.ts. Resets the cohort idle timer.
12
+ // - endCohort() called from index.ts's wsContext.onClientDisconnect
13
+ // when wsContext.clients.size === 0 (last client gone).
14
+ //
15
+ // Cohort model: OX is local-first / single-user (CLAUDE.md §5).
16
+ // Multiple WebSockets may auth simultaneously (web tab + voice client).
17
+ // They share one "cohort" — a conversation lifetime that starts on
18
+ // first markActive after no-cohort state and ends on last-client
19
+ // disconnect or 5-min idle. Each cohort fires reverie at most once.
20
+ //
21
+ // Sub-meter constants (Law 16 Meter Everything): six observable
22
+ // reverie.* component strings. The pre-Leg-G code branded them as
23
+ // UsageComponentType (from @agencer/usage-accountant); the Lego
24
+ // passes them as plain strings to the injected UsageMeter, which
25
+ // preserves runtime semantics bit-for-bit while removing the
26
+ // @agencer/* type coupling.
27
+ //
28
+ // reverie.opus.reflection is the canonical paid-Opus row written by
29
+ // opus-client's executeReflection. The trigger's daily-cost guard
30
+ // reads only this row's totalCostUsd. The other five are zero-cost
31
+ // counters distinguishing why a cohort fired (or didn't).
32
+ //
33
+ // Sacred constraint (Law 8, declared in README): No process.env or
34
+ // os.homedir() reads inside this module. ReverieTriggerOptions takes
35
+ // explicit path config (reveriePairsPath, reverieSafetyFlagsPath,
36
+ // legacyReverieLogPath) which is threaded through to the Lego's
37
+ // ReverieDeps via reverie-loop.ts. The agencer-ox composition root
38
+ // resolves ~/.operative-x/logs/* and passes the paths in.
39
+ //
40
+ // CRITICAL: All public methods NEVER throw. Reverie is observability
41
+ // infrastructure; it cannot block the user-facing teardown path or
42
+ // any per-turn handler.
43
+ import { randomUUID } from "node:crypto";
44
+ import { triggerReverie, } from "./reverie-loop.js";
45
+ // ── Sub-meter component strings ──────────────────────────────────
46
+ const COMP_REVERIE_OPUS_REFLECTION = "reverie.opus.reflection";
47
+ const COMP_REVERIE_TRIGGER_OPUS_ATTEMPT = "reverie.trigger.opus.attempt";
48
+ const COMP_REVERIE_TRIGGER_DISCONNECT = "reverie.trigger.disconnect";
49
+ const COMP_REVERIE_TRIGGER_IDLE_TIMER = "reverie.trigger.idle_timer";
50
+ const COMP_REVERIE_SKIPPED_BUDGET_EXCEEDED = "reverie.skipped.budget_exceeded";
51
+ const COMP_REVERIE_SKIPPED_DEDUPED = "reverie.skipped.deduped";
52
+ const DEFAULT_IDLE_MS = 5 * 60 * 1000;
53
+ const DEFAULT_DAILY_BUDGET_USD = 1.0;
54
+ // No-op stubs used when inferenceProvider is unset. The legacy mock
55
+ // branch of executeReverie never invokes inferenceProvider, meter, or
56
+ // costCalculator — but ReverieDeps requires them present, so we
57
+ // thread these inert placeholders.
58
+ const stubInferenceProvider = {
59
+ async messagesCreate() {
60
+ throw new Error("reverie-trigger: stubInferenceProvider invoked; the legacy mock path should never call it");
61
+ },
62
+ };
63
+ const stubCostCalculator = () => 0;
64
+ export class ReverieTrigger {
65
+ meter;
66
+ logger;
67
+ costCalculator;
68
+ idleMs;
69
+ dailyBudgetUsd;
70
+ inferenceProvider;
71
+ reveriePairsPath;
72
+ reverieSafetyFlagsPath;
73
+ legacyReverieLogPath;
74
+ forceMockBase;
75
+ currentCohort = null;
76
+ constructor(opts) {
77
+ this.meter = opts.meter;
78
+ this.logger = opts.logger;
79
+ this.costCalculator = opts.costCalculator;
80
+ this.idleMs = opts.idleMs ?? DEFAULT_IDLE_MS;
81
+ this.dailyBudgetUsd = opts.dailyBudgetUsd ?? DEFAULT_DAILY_BUDGET_USD;
82
+ this.inferenceProvider = opts.inferenceProvider;
83
+ this.reveriePairsPath = opts.reveriePairsPath;
84
+ this.reverieSafetyFlagsPath = opts.reverieSafetyFlagsPath;
85
+ this.legacyReverieLogPath = opts.legacyReverieLogPath;
86
+ this.forceMockBase = opts.forceMock ?? false;
87
+ }
88
+ /**
89
+ * Mark the active cohort. Starts a cohort if none exists, refreshes
90
+ * the userContext + getHistory snapshot if one does, resets the
91
+ * idle timer either way. Called per turn from brain/route-and-log.ts.
92
+ */
93
+ markActive(userContext, getHistory) {
94
+ try {
95
+ if (!this.currentCohort) {
96
+ this.currentCohort = {
97
+ id: randomUUID(),
98
+ userContext,
99
+ getHistory,
100
+ idleTimer: null,
101
+ reflected: false,
102
+ };
103
+ }
104
+ else {
105
+ this.currentCohort.userContext = userContext;
106
+ this.currentCohort.getHistory = getHistory;
107
+ }
108
+ if (this.currentCohort.idleTimer) {
109
+ clearTimeout(this.currentCohort.idleTimer);
110
+ }
111
+ // Capture cohort reference so the timer callback acts on this
112
+ // specific cohort even if currentCohort is replaced before the
113
+ // timer fires (race-safe).
114
+ const cohort = this.currentCohort;
115
+ const t = setTimeout(() => this.fireForCohort(cohort, "idle_timer"), this.idleMs);
116
+ // unref so the idle timer doesn't keep node alive on its own.
117
+ t.unref?.();
118
+ this.currentCohort.idleTimer = t;
119
+ }
120
+ catch (err) {
121
+ this.logger.warn({ err }, "ReverieTrigger.markActive failed");
122
+ }
123
+ }
124
+ /**
125
+ * Fire reverie for the active cohort and clear it. Called from
126
+ * index.ts's onClientDisconnect when wsContext.clients.size === 0.
127
+ * Detached via setImmediate so the WebSocket teardown call stack
128
+ * returns immediately.
129
+ */
130
+ endCohort() {
131
+ try {
132
+ const cohort = this.currentCohort;
133
+ // Visible at info level so future production smoke surfaces
134
+ // the orchestrator path without needing log-level overrides.
135
+ // Fires at most once per cohort lifetime — not per turn.
136
+ this.logger.info({
137
+ hasCohort: !!cohort,
138
+ cohortId: cohort?.id ?? null,
139
+ reflected: cohort?.reflected ?? null,
140
+ }, "ReverieTrigger.endCohort invoked");
141
+ if (!cohort)
142
+ return;
143
+ setImmediate(() => this.fireForCohort(cohort, "disconnect"));
144
+ }
145
+ catch (err) {
146
+ this.logger.warn({ err }, "ReverieTrigger.endCohort failed");
147
+ }
148
+ }
149
+ /**
150
+ * Cancel any in-flight idle timer and clear cohort state. For
151
+ * server shutdown / test teardown. Does NOT fire reverie.
152
+ */
153
+ cancelAll() {
154
+ if (this.currentCohort?.idleTimer) {
155
+ clearTimeout(this.currentCohort.idleTimer);
156
+ }
157
+ this.currentCohort = null;
158
+ }
159
+ // ── Internal ───────────────────────────────────────────────────
160
+ fireForCohort(cohort, reason) {
161
+ // Always clear the idle timer for this cohort; this fire claims it.
162
+ if (cohort.idleTimer) {
163
+ clearTimeout(cohort.idleTimer);
164
+ cohort.idleTimer = null;
165
+ }
166
+ // Dedup: a cohort fires at most once. Subsequent triggers (e.g.,
167
+ // disconnect arriving after an idle-timer fire, or two disconnect
168
+ // events racing) record a counter and exit.
169
+ if (cohort.reflected) {
170
+ this.recordCounter(cohort.userContext, COMP_REVERIE_SKIPPED_DEDUPED);
171
+ return;
172
+ }
173
+ cohort.reflected = true;
174
+ // If this cohort is still the current cohort, clear it so a future
175
+ // markActive starts a fresh cohort. If currentCohort was already
176
+ // replaced (e.g., user reconnected during the setImmediate window),
177
+ // leave it alone.
178
+ if (this.currentCohort === cohort) {
179
+ this.currentCohort = null;
180
+ }
181
+ // Budget guard. dailyReverieCost filters by reverie.opus.reflection
182
+ // and sums cost. Real-cost rows are written by opus-client's
183
+ // executeReflection (single source of truth for billing).
184
+ if (this.dailyReverieCost(cohort.userContext.userId) >= this.dailyBudgetUsd) {
185
+ this.recordCounter(cohort.userContext, COMP_REVERIE_SKIPPED_BUDGET_EXCEEDED);
186
+ return;
187
+ }
188
+ const triggerComp = reason === "disconnect"
189
+ ? COMP_REVERIE_TRIGGER_DISCONNECT
190
+ : COMP_REVERIE_TRIGGER_IDLE_TIMER;
191
+ this.recordCounter(cohort.userContext, triggerComp);
192
+ // Trigger-attempt counter. Records "this cohort fired and intended
193
+ // to invoke reverie reflection" without overlapping the canonical
194
+ // reverie.opus.reflection sub-meter owned by opus-client. Distinct
195
+ // sub-meter restores 1:1 totalCalls rollup against actual paid API
196
+ // calls. Cost is always 0 here; real cost is recorded inside
197
+ // executeReflection at the SDK boundary.
198
+ this.recordCounter(cohort.userContext, COMP_REVERIE_TRIGGER_OPUS_ATTEMPT);
199
+ try {
200
+ // Snapshot history synchronously at trigger time. reverie-loop's
201
+ // triggerReverie schedules a 10s debounce that internally calls
202
+ // `getHistory()` AT FIRE TIME. Without this snapshot, a quick
203
+ // reconnect during the 10s window means executeReverie reads the
204
+ // conversation state 10s LATER — potentially the NEXT cohort's
205
+ // data — while the sessionId stamped on the reverie-log row is
206
+ // still THIS cohort's UUID. Cohort UUID and reflected content
207
+ // must move together.
208
+ const historySnapshot = [...cohort.getHistory()];
209
+ // Build deps for the Lego's triggerReverie. forceMock is the
210
+ // OR of construct-time base (composition root threads from
211
+ // process.env["MOCK_REVERIE"]) and inferenceProvider absence
212
+ // (test scaffolding without DI).
213
+ const noProvider = !this.inferenceProvider;
214
+ const deps = {
215
+ inferenceProvider: this.inferenceProvider ?? stubInferenceProvider,
216
+ meter: this.meter,
217
+ costCalculator: noProvider ? stubCostCalculator : this.costCalculator,
218
+ userContext: cohort.userContext,
219
+ logger: this.logger,
220
+ reveriePairsPath: this.reveriePairsPath,
221
+ reverieSafetyFlagsPath: this.reverieSafetyFlagsPath,
222
+ legacyReverieLogPath: this.legacyReverieLogPath,
223
+ forceMock: this.forceMockBase || noProvider,
224
+ };
225
+ triggerReverie(cohort.id, () => historySnapshot, deps);
226
+ }
227
+ catch (err) {
228
+ this.logger.warn({ err, cohortId: cohort.id }, "ReverieTrigger.fireForCohort: getHistory or triggerReverie threw");
229
+ }
230
+ }
231
+ recordCounter(userContext, component) {
232
+ try {
233
+ this.meter.recordCall({
234
+ userId: userContext.userId,
235
+ sessionId: userContext.sessionId,
236
+ component,
237
+ model: "n/a",
238
+ inputTokens: 0,
239
+ outputTokens: 0,
240
+ latencyMs: 0,
241
+ });
242
+ }
243
+ catch (err) {
244
+ this.logger.warn({ err, component }, "ReverieTrigger.recordCounter failed");
245
+ }
246
+ }
247
+ dailyReverieCost(userId) {
248
+ try {
249
+ // SQLite's datetime('now') stores rows as "YYYY-MM-DD HH:MM:SS"
250
+ // (space separator). We must compare against the same format
251
+ // so the lexicographic >= filter actually matches today's rows.
252
+ // `Date.toISOString().slice(0, 10)` gives "YYYY-MM-DD" in UTC,
253
+ // which is a prefix-safe lower bound: every row from today is
254
+ // >= "YYYY-MM-DD" because the row's space-after-date sorts
255
+ // higher than the empty string after the date portion.
256
+ const fromUtcDate = new Date().toISOString().slice(0, 10);
257
+ const breakdown = this.meter.getUserUsage(userId, {
258
+ from: fromUtcDate,
259
+ groupBy: "component",
260
+ });
261
+ return breakdown
262
+ .filter((b) => b.component === COMP_REVERIE_OPUS_REFLECTION)
263
+ .reduce((sum, b) => sum + b.totalCostUsd, 0);
264
+ }
265
+ catch (err) {
266
+ this.logger.warn({ err, userId }, "ReverieTrigger.dailyReverieCost failed");
267
+ return 0;
268
+ }
269
+ }
270
+ }
271
+ //# sourceMappingURL=reverie-trigger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reverie-trigger.js","sourceRoot":"","sources":["../src/reverie-trigger.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,wEAAwE;AACxE,yEAAyE;AACzE,0EAA0E;AAC1E,iDAAiD;AACjD,EAAE;AACF,sEAAsE;AACtE,qEAAqE;AACrE,+DAA+D;AAC/D,4DAA4D;AAC5D,sEAAsE;AACtE,4DAA4D;AAC5D,EAAE;AACF,gEAAgE;AAChE,wEAAwE;AACxE,mEAAmE;AACnE,iEAAiE;AACjE,oEAAoE;AACpE,EAAE;AACF,gEAAgE;AAChE,kEAAkE;AAClE,gEAAgE;AAChE,iEAAiE;AACjE,6DAA6D;AAC7D,4BAA4B;AAC5B,EAAE;AACF,oEAAoE;AACpE,kEAAkE;AAClE,mEAAmE;AACnE,0DAA0D;AAC1D,EAAE;AACF,mEAAmE;AACnE,qEAAqE;AACrE,kEAAkE;AAClE,gEAAgE;AAChE,mEAAmE;AACnE,0DAA0D;AAC1D,EAAE;AACF,qEAAqE;AACrE,mEAAmE;AACnE,wBAAwB;AAExB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQzC,OAAO,EACL,cAAc,GAGf,MAAM,mBAAmB,CAAC;AAE3B,oEAAoE;AAEpE,MAAM,4BAA4B,GAAG,yBAAyB,CAAC;AAC/D,MAAM,iCAAiC,GAAG,8BAA8B,CAAC;AACzE,MAAM,+BAA+B,GAAG,4BAA4B,CAAC;AACrE,MAAM,+BAA+B,GAAG,4BAA4B,CAAC;AACrE,MAAM,oCAAoC,GAAG,iCAAiC,CAAC;AAC/E,MAAM,4BAA4B,GAAG,yBAAyB,CAAC;AAE/D,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AACtC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AA0CrC,oEAAoE;AACpE,sEAAsE;AACtE,gEAAgE;AAChE,mCAAmC;AACnC,MAAM,qBAAqB,GAA6B;IACtD,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,KAAK,CACb,2FAA2F,CAC5F,CAAC;IACJ,CAAC;CACF,CAAC;AACF,MAAM,kBAAkB,GAAmB,GAAG,EAAE,CAAC,CAAC,CAAC;AAEnD,MAAM,OAAO,cAAc;IACR,KAAK,CAAa;IAClB,MAAM,CAAa;IACnB,cAAc,CAAiB;IAC/B,MAAM,CAAS;IACf,cAAc,CAAS;IACvB,iBAAiB,CAAuC;IACxD,gBAAgB,CAAS;IACzB,sBAAsB,CAAS;IAC/B,oBAAoB,CAAS;IAC7B,aAAa,CAAU;IAChC,aAAa,GAAuB,IAAI,CAAC;IAEjD,YAAY,IAA2B;QACrC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;QAC1C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC;QAC7C,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,wBAAwB,CAAC;QACtE,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC;QAChD,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC;QAC9C,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC,sBAAsB,CAAC;QAC1D,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,oBAAoB,CAAC;QACtD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IAC/C,CAAC;IAED;;;;OAIG;IACH,UAAU,CACR,WAA+B,EAC/B,UAAwC;QAExC,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;gBACxB,IAAI,CAAC,aAAa,GAAG;oBACnB,EAAE,EAAE,UAAU,EAAE;oBAChB,WAAW;oBACX,UAAU;oBACV,SAAS,EAAE,IAAI;oBACf,SAAS,EAAE,KAAK;iBACjB,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,aAAa,CAAC,WAAW,GAAG,WAAW,CAAC;gBAC7C,IAAI,CAAC,aAAa,CAAC,UAAU,GAAG,UAAU,CAAC;YAC7C,CAAC;YACD,IAAI,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,CAAC;gBACjC,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;YAC7C,CAAC;YACD,8DAA8D;YAC9D,+DAA+D;YAC/D,2BAA2B;YAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC;YAClC,MAAM,CAAC,GAAG,UAAU,CAClB,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,YAAY,CAAC,EAC9C,IAAI,CAAC,MAAM,CACZ,CAAC;YACF,8DAA8D;YAC9D,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;YACZ,IAAI,CAAC,aAAa,CAAC,SAAS,GAAG,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,kCAAkC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,SAAS;QACP,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC;YAClC,4DAA4D;YAC5D,6DAA6D;YAC7D,yDAAyD;YACzD,IAAI,CAAC,MAAM,CAAC,IAAI,CACd;gBACE,SAAS,EAAE,CAAC,CAAC,MAAM;gBACnB,QAAQ,EAAE,MAAM,EAAE,EAAE,IAAI,IAAI;gBAC5B,SAAS,EAAE,MAAM,EAAE,SAAS,IAAI,IAAI;aACrC,EACD,kCAAkC,CACnC,CAAC;YACF,IAAI,CAAC,MAAM;gBAAE,OAAO;YACpB,YAAY,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,iCAAiC,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,SAAS;QACP,IAAI,IAAI,CAAC,aAAa,EAAE,SAAS,EAAE,CAAC;YAClC,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC5B,CAAC;IAED,kEAAkE;IAE1D,aAAa,CACnB,MAAmB,EACnB,MAAmC;QAEnC,oEAAoE;QACpE,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC/B,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;QAC1B,CAAC;QAED,iEAAiE;QACjE,kEAAkE;QAClE,4CAA4C;QAC5C,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,WAAW,EAAE,4BAA4B,CAAC,CAAC;YACrE,OAAO;QACT,CAAC;QACD,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;QAExB,mEAAmE;QACnE,iEAAiE;QACjE,oEAAoE;QACpE,kBAAkB;QAClB,IAAI,IAAI,CAAC,aAAa,KAAK,MAAM,EAAE,CAAC;YAClC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;QAED,oEAAoE;QACpE,6DAA6D;QAC7D,0DAA0D;QAC1D,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC5E,IAAI,CAAC,aAAa,CAChB,MAAM,CAAC,WAAW,EAClB,oCAAoC,CACrC,CAAC;YACF,OAAO;QACT,CAAC;QAED,MAAM,WAAW,GACf,MAAM,KAAK,YAAY;YACrB,CAAC,CAAC,+BAA+B;YACjC,CAAC,CAAC,+BAA+B,CAAC;QACtC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QAEpD,mEAAmE;QACnE,kEAAkE;QAClE,mEAAmE;QACnE,mEAAmE;QACnE,6DAA6D;QAC7D,yCAAyC;QACzC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,WAAW,EAAE,iCAAiC,CAAC,CAAC;QAE1E,IAAI,CAAC;YACH,iEAAiE;YACjE,gEAAgE;YAChE,8DAA8D;YAC9D,iEAAiE;YACjE,+DAA+D;YAC/D,+DAA+D;YAC/D,8DAA8D;YAC9D,sBAAsB;YACtB,MAAM,eAAe,GAAG,CAAC,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;YAEjD,6DAA6D;YAC7D,2DAA2D;YAC3D,6DAA6D;YAC7D,iCAAiC;YACjC,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC;YAC3C,MAAM,IAAI,GAAgB;gBACxB,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,IAAI,qBAAqB;gBAClE,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,cAAc,EAAE,UAAU,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc;gBACrE,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;gBACvC,sBAAsB,EAAE,IAAI,CAAC,sBAAsB;gBACnD,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;gBAC/C,SAAS,EAAE,IAAI,CAAC,aAAa,IAAI,UAAU;aAC5C,CAAC;YACF,cAAc,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,EAC5B,kEAAkE,CACnE,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,aAAa,CACnB,WAA+B,EAC/B,SAAiB;QAEjB,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;gBACpB,MAAM,EAAE,WAAW,CAAC,MAAM;gBAC1B,SAAS,EAAE,WAAW,CAAC,SAAS;gBAChC,SAAS;gBACT,KAAK,EAAE,KAAK;gBACZ,WAAW,EAAE,CAAC;gBACd,YAAY,EAAE,CAAC;gBACf,SAAS,EAAE,CAAC;aACb,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,GAAG,EAAE,SAAS,EAAE,EAClB,qCAAqC,CACtC,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,gBAAgB,CAAC,MAAc;QACrC,IAAI,CAAC;YACH,gEAAgE;YAChE,6DAA6D;YAC7D,gEAAgE;YAChE,+DAA+D;YAC/D,8DAA8D;YAC9D,2DAA2D;YAC3D,uDAAuD;YACvD,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE;gBAChD,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,WAAW;aACrB,CAAC,CAAC;YACH,OAAO,SAAS;iBACb,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,4BAA4B,CAAC;iBAC3D,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,GAAG,EAAE,MAAM,EAAE,EACf,wCAAwC,CACzC,CAAC;YACF,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;CACF"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agencer/reverie-loop",
3
- "version": "0.1.0-alpha.0",
4
- "description": "Append-only JSONL writer for reverie training signals (whisper-failure deltas + whisper model outcomes). One of three signal sources for the Agencer Learning Loop (alongside session-logger and whisper-router). Path-configurable, NEVER-throws discipline.",
3
+ "version": "0.1.0",
4
+ "description": "Reverie Loop Strange Loop training-signal capture for AI agents. Post-session Opus reflection produces correction pairs split into behavioral + safety JSONL files for fine-tune corpus generation. Portable Lego with zero @agencer/* runtime dependencies and zero Anthropic-SDK coupling at the public API.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -14,26 +14,57 @@
14
14
  "files": [
15
15
  "dist",
16
16
  "README.md",
17
- "CHANGELOG.md",
18
- "LICENSE"
17
+ "LICENSE",
18
+ "CHANGELOG.md"
19
19
  ],
20
20
  "scripts": {
21
21
  "build": "tsc",
22
22
  "prepack": "tsc",
23
- "test": "vitest run --passWithNoTests",
23
+ "test": "vitest run",
24
24
  "typecheck": "tsc --noEmit"
25
25
  },
26
- "publishConfig": {
27
- "registry": "https://registry.npmjs.org"
26
+ "dependencies": {},
27
+ "peerDependencies": {
28
+ "pino": ">=8.0.0",
29
+ "@agencer/usage-accountant": "0.1.0"
30
+ },
31
+ "peerDependenciesMeta": {
32
+ "@agencer/usage-accountant": {
33
+ "optional": true
34
+ }
28
35
  },
29
36
  "devDependencies": {
30
- "@types/node": "^20.0.0",
31
- "typescript": "^5.5.0",
32
- "vitest": "^1.4.0"
37
+ "@agencer/usage-accountant": "0.1.0",
38
+ "@anthropic-ai/sdk": "^0.30.0",
39
+ "@types/better-sqlite3": "^7.6.0",
40
+ "@types/node": "^22.0.0",
41
+ "better-sqlite3": "^12.8.0",
42
+ "pino": "^9.6.0",
43
+ "typescript": "^5.7.0",
44
+ "vitest": "^3.0.0"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public",
48
+ "registry": "https://registry.npmjs.org"
33
49
  },
34
50
  "repository": {
35
51
  "type": "git",
36
- "url": "git+https://github.com/Agencer-Inc/operative-shared.git"
52
+ "url": "https://github.com/Agencer-Inc/agencer-ox.git",
53
+ "directory": "packages/reverie-loop"
37
54
  },
38
- "license": "Apache-2.0"
55
+ "homepage": "https://github.com/Agencer-Inc/agencer-ox/tree/main/packages/reverie-loop#readme",
56
+ "bugs": {
57
+ "url": "https://github.com/Agencer-Inc/agencer-ox/issues"
58
+ },
59
+ "author": "Agencer, Inc.",
60
+ "keywords": [
61
+ "reverie-loop",
62
+ "strange-loop",
63
+ "training-signal",
64
+ "self-reflection",
65
+ "fine-tune-corpus",
66
+ "correction-pairs",
67
+ "agencer"
68
+ ],
69
+ "license": "SEE LICENSE IN LICENSE"
39
70
  }