@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.
- package/CHANGELOG.md +148 -69
- package/LICENSE +8 -199
- package/README.md +118 -56
- package/dist/contracts.d.ts +161 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +28 -0
- package/dist/contracts.js.map +1 -0
- package/dist/index.d.ts +12 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -4
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +47 -21
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +44 -107
- package/dist/logger.js.map +1 -0
- package/dist/opus-client.d.ts +60 -0
- package/dist/opus-client.d.ts.map +1 -0
- package/dist/opus-client.js +301 -0
- package/dist/opus-client.js.map +1 -0
- package/dist/pair-sink.d.ts +36 -0
- package/dist/pair-sink.d.ts.map +1 -0
- package/dist/pair-sink.js +132 -0
- package/dist/pair-sink.js.map +1 -0
- package/dist/reverie-loop.d.ts +83 -0
- package/dist/reverie-loop.d.ts.map +1 -0
- package/dist/reverie-loop.js +216 -0
- package/dist/reverie-loop.js.map +1 -0
- package/dist/reverie-trigger.d.ts +67 -0
- package/dist/reverie-trigger.d.ts.map +1 -0
- package/dist/reverie-trigger.js +271 -0
- package/dist/reverie-trigger.js.map +1 -0
- package/package.json +43 -12
- package/dist/compat.d.ts +0 -22
- package/dist/compat.js +0 -81
- package/dist/types.d.ts +0 -34
- package/dist/types.js +0 -8
|
@@ -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":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agencer/reverie-loop",
|
|
3
|
-
"version": "0.1.0
|
|
4
|
-
"description": "
|
|
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
|
-
"
|
|
18
|
-
"
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"CHANGELOG.md"
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsc",
|
|
22
22
|
"prepack": "tsc",
|
|
23
|
-
"test": "vitest run
|
|
23
|
+
"test": "vitest run",
|
|
24
24
|
"typecheck": "tsc --noEmit"
|
|
25
25
|
},
|
|
26
|
-
"
|
|
27
|
-
|
|
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
|
-
"@
|
|
31
|
-
"
|
|
32
|
-
"
|
|
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": "
|
|
52
|
+
"url": "https://github.com/Agencer-Inc/agencer-ox.git",
|
|
53
|
+
"directory": "packages/reverie-loop"
|
|
37
54
|
},
|
|
38
|
-
"
|
|
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
|
}
|