agent-working-memory 0.14.1 → 0.14.5
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/README.md +244 -653
- package/dist/core/whoami.d.ts +9 -1
- package/dist/core/whoami.d.ts.map +1 -1
- package/dist/core/whoami.js +12 -2
- package/dist/core/whoami.js.map +1 -1
- package/dist/engine/activation.d.ts +2 -0
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +18 -3
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/eval.d.ts +15 -0
- package/dist/engine/eval.d.ts.map +1 -1
- package/dist/engine/eval.js +23 -0
- package/dist/engine/eval.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +18 -2
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +30 -4
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/mcp.js +43 -7
- package/dist/mcp.js.map +1 -1
- package/dist/storage/pglite.d.ts +7 -0
- package/dist/storage/pglite.d.ts.map +1 -1
- package/dist/storage/pglite.js +17 -3
- package/dist/storage/pglite.js.map +1 -1
- package/dist/storage/postgres.d.ts +7 -0
- package/dist/storage/postgres.d.ts.map +1 -1
- package/dist/storage/postgres.js +17 -3
- package/dist/storage/postgres.js.map +1 -1
- package/dist/storage/sqlite.d.ts +12 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +24 -3
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types/engram.d.ts +16 -0
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/eval.d.ts +2 -0
- package/dist/types/eval.d.ts.map +1 -1
- package/package.json +20 -2
- package/src/core/whoami.ts +11 -1
- package/src/engine/activation.ts +18 -3
- package/src/engine/eval.ts +34 -0
- package/src/hooks/sidecar.ts +50 -6
- package/src/mcp.ts +45 -7
- package/src/storage/pglite.ts +22 -4
- package/src/storage/postgres.ts +22 -4
- package/src/storage/sqlite.ts +26 -4
- package/src/types/engram.ts +16 -0
- package/src/types/eval.ts +3 -1
package/src/mcp.ts
CHANGED
|
@@ -124,6 +124,15 @@ function deriveAgentFromDir(): string {
|
|
|
124
124
|
}
|
|
125
125
|
const AGENT_ID = process.env.AWM_AGENT_ID ?? process.env.WORKER_NAME ?? deriveAgentFromDir();
|
|
126
126
|
const HOOK_PORT = parseInt(process.env.AWM_HOOK_PORT ?? '8401', 10);
|
|
127
|
+
// 0.14.2: ports to try upward from HOOK_PORT when it is busy (see sidecar.ts).
|
|
128
|
+
const HOOK_PORT_RANGE = Math.max(1, parseInt(process.env.AWM_HOOK_PORT_RANGE ?? '10', 10) || 10);
|
|
129
|
+
// Set once the sidecar starts; whoami reads the bound port from it.
|
|
130
|
+
let sidecarHandle: { boundPort: () => number | null } | null = null;
|
|
131
|
+
function sidecarPortLabel(): string {
|
|
132
|
+
const p = sidecarHandle?.boundPort() ?? null;
|
|
133
|
+
if (p === null) return `not bound (preferred ${HOOK_PORT}; hooks disabled)`;
|
|
134
|
+
return p === HOOK_PORT ? `127.0.0.1:${p}` : `127.0.0.1:${p} (preferred ${HOOK_PORT} was busy)`;
|
|
135
|
+
}
|
|
127
136
|
const HOOK_SECRET = process.env.AWM_HOOK_SECRET ?? null;
|
|
128
137
|
|
|
129
138
|
initLogger(DB_PATH);
|
|
@@ -521,10 +530,15 @@ Returns the most relevant memories ranked by text relevance, temporal recency, a
|
|
|
521
530
|
};
|
|
522
531
|
}
|
|
523
532
|
|
|
533
|
+
// 0.14.3: surface the activation event id once, so memory_feedback can join
|
|
534
|
+
// to this recall. Rendered after the token footer; cheap (one short line).
|
|
535
|
+
const evId = results[0]?.activationEventId;
|
|
536
|
+
const evFooter = evId ? `\n[recall_id: ${evId}]` : '';
|
|
537
|
+
|
|
524
538
|
return {
|
|
525
539
|
content: [{
|
|
526
540
|
type: 'text' as const,
|
|
527
|
-
text: packed.lines.join('\n') + peerSuffix + formatTokenFooter(packed, params.max_tokens),
|
|
541
|
+
text: packed.lines.join('\n') + peerSuffix + formatTokenFooter(packed, params.max_tokens) + evFooter,
|
|
528
542
|
}],
|
|
529
543
|
};
|
|
530
544
|
}
|
|
@@ -539,9 +553,19 @@ Always call this after using a recalled memory so the system learns what's valua
|
|
|
539
553
|
engram_id: z.string().describe('ID of the memory (from memory_recall results)'),
|
|
540
554
|
useful: z.boolean().describe('Was this memory actually helpful?'),
|
|
541
555
|
context: z.string().optional().describe('Brief note on why it was/wasn\'t useful'),
|
|
556
|
+
recall_id: z.string().optional().describe(
|
|
557
|
+
'The [recall_id: …] printed at the end of the memory_recall output that returned this memory. '
|
|
558
|
+
+ 'Pass it so the feedback is joined to that recall; omitted, it defaults to the most recent recall in this session.',
|
|
559
|
+
),
|
|
542
560
|
},
|
|
543
561
|
async (params) => {
|
|
544
|
-
|
|
562
|
+
// 0.14.3: join feedback to the recall that produced it. Explicit recall_id
|
|
563
|
+
// wins; otherwise fall back to the engine's most recent logged activation —
|
|
564
|
+
// the common case is "recall, use, feedback" in one turn. Before this the
|
|
565
|
+
// MCP path hardcoded null (the HTTP route already accepted the id), which
|
|
566
|
+
// is why every retrieval_feedback row in the live store was orphaned.
|
|
567
|
+
const eventId = params.recall_id ?? activationEngine.lastActivationEventId ?? null;
|
|
568
|
+
await store.logRetrievalFeedback(eventId, params.engram_id, params.useful, params.context ?? '');
|
|
545
569
|
|
|
546
570
|
const engram = await store.getEngram(params.engram_id);
|
|
547
571
|
if (engram) {
|
|
@@ -645,7 +669,7 @@ server.tool(
|
|
|
645
669
|
`Identify THIS AWM instance — agent id, mode (standalone/hive), backend, store path, code provenance, ports, and the sibling agent spaces present in the same store. Call when unsure which AWM instance or memory space you are talking to.`,
|
|
646
670
|
{},
|
|
647
671
|
async () => {
|
|
648
|
-
const info = await buildWhoami(store, AGENT_ID, 'mcp');
|
|
672
|
+
const info = await buildWhoami(store, AGENT_ID, 'mcp', sidecarHandle ? sidecarHandle.boundPort() : undefined);
|
|
649
673
|
return { content: [{ type: 'text', text: formatWhoami(info) }] };
|
|
650
674
|
},
|
|
651
675
|
);
|
|
@@ -658,6 +682,11 @@ Also shows the activity log path so the user can tail it to see what's happening
|
|
|
658
682
|
async () => {
|
|
659
683
|
const metrics = await evalEngine.computeMetrics(AGENT_ID);
|
|
660
684
|
const checkpoint = await store.getCheckpoint(AGENT_ID);
|
|
685
|
+
// 0.14.3: outcome numbers, not activity counters. "Edge utility" (share of
|
|
686
|
+
// edges ever activated) was monotone — it could only rise — and read as
|
|
687
|
+
// health when it was not. Latency was a mean over a column that mixes cold
|
|
688
|
+
// loads and stalls (live store: mean 20 s, median 1.6 s); medians only now.
|
|
689
|
+
const usage = await evalEngine.computeUsage(AGENT_ID);
|
|
661
690
|
const lines = [
|
|
662
691
|
`Agent: ${AGENT_ID}`,
|
|
663
692
|
`Active memories: ${metrics.activeEngramCount}`,
|
|
@@ -665,9 +694,12 @@ Also shows the activity log path so the user can tail it to see what's happening
|
|
|
665
694
|
`Retracted: ${metrics.retractedCount}`,
|
|
666
695
|
`Avg confidence: ${metrics.avgConfidence.toFixed(3)}`,
|
|
667
696
|
`Total edges: ${metrics.totalEdges}`,
|
|
668
|
-
|
|
697
|
+
``,
|
|
698
|
+
`Write:recall (30d): 1 : ${usage.recallsPerWrite30d.toFixed(2)} (${usage.writes30d} writes, ${usage.recalls30d} recalls)`,
|
|
699
|
+
`Never recalled: ${(usage.neverRecalledShare * 100).toFixed(0)}% of active memories`,
|
|
700
|
+
`Recall→use (7d): ${usage.feedbackLinked7d > 0 ? (usage.usefulShare7d * 100).toFixed(0) + '% useful of ' + usage.feedbackLinked7d + ' judged' : 'no linked feedback yet'}`,
|
|
669
701
|
`Activations (24h): ${metrics.activationCount}`,
|
|
670
|
-
`
|
|
702
|
+
`Recall latency (24h): p50 ${metrics.p50LatencyMs.toFixed(0)}ms p90 ${metrics.p90LatencyMs.toFixed(0)}ms`,
|
|
671
703
|
``,
|
|
672
704
|
`Session writes: ${checkpoint?.auto.writeCountSinceConsolidation ?? 0}`,
|
|
673
705
|
`Session recalls: ${checkpoint?.auto.recallCountSinceConsolidation ?? 0}`,
|
|
@@ -675,7 +707,10 @@ Also shows the activity log path so the user can tail it to see what's happening
|
|
|
675
707
|
`Checkpoint: ${checkpoint?.executionState ? checkpoint.executionState.currentTask : 'none'}`,
|
|
676
708
|
``,
|
|
677
709
|
`Activity log: ${getLogPath() ?? 'not configured'}`,
|
|
678
|
-
|
|
710
|
+
// Report the port actually BOUND, not the one configured. Before 0.14.2
|
|
711
|
+
// this line said 8401 in every session, including the ones whose sidecar
|
|
712
|
+
// had lost the port and silently disabled hooks.
|
|
713
|
+
`Hook sidecar: ${sidecarPortLabel()}`,
|
|
679
714
|
];
|
|
680
715
|
|
|
681
716
|
return {
|
|
@@ -1336,6 +1371,8 @@ async function main() {
|
|
|
1336
1371
|
agentId: AGENT_ID,
|
|
1337
1372
|
secret: HOOK_SECRET,
|
|
1338
1373
|
port: HOOK_PORT,
|
|
1374
|
+
portRange: HOOK_PORT_RANGE,
|
|
1375
|
+
version: VERSION,
|
|
1339
1376
|
// 0.12.2: warm recall for hooks — the sidecar shares this process's
|
|
1340
1377
|
// activation engine and loaded models, so a UserPromptSubmit hook can get
|
|
1341
1378
|
// warm-latency recall without any standing server. Trimmed result shape
|
|
@@ -1419,7 +1456,8 @@ async function main() {
|
|
|
1419
1456
|
|
|
1420
1457
|
// Log to stderr (stdout is reserved for MCP protocol)
|
|
1421
1458
|
console.error(`AgentWorkingMemory MCP server started (agent: ${AGENT_ID}, db: ${DB_PATH})`);
|
|
1422
|
-
|
|
1459
|
+
sidecarHandle = sidecar;
|
|
1460
|
+
console.error(`Hook sidecar preferred 127.0.0.1:${HOOK_PORT} (range ${HOOK_PORT_RANGE})${HOOK_SECRET ? ' (auth enabled)' : ' (no auth — set AWM_HOOK_SECRET)'}`);
|
|
1423
1461
|
|
|
1424
1462
|
// Clean shutdown
|
|
1425
1463
|
const cleanup = async () => {
|
package/src/storage/pglite.ts
CHANGED
|
@@ -1020,7 +1020,7 @@ export class PGliteEngramStore {
|
|
|
1020
1020
|
};
|
|
1021
1021
|
}
|
|
1022
1022
|
|
|
1023
|
-
async getActivationStats(agentId: string, windowHours: number = 24): Promise<{ count: number; avgLatencyMs: number; p95LatencyMs: number }> {
|
|
1023
|
+
async getActivationStats(agentId: string, windowHours: number = 24): Promise<{ count: number; avgLatencyMs: number; p50LatencyMs: number; p90LatencyMs: number; p95LatencyMs: number }> {
|
|
1024
1024
|
await this.readyPromise;
|
|
1025
1025
|
// Flush any buffered activation events so stats reflect the latest writes.
|
|
1026
1026
|
await this.flushActivationEvents();
|
|
@@ -1031,17 +1031,35 @@ export class PGliteEngramStore {
|
|
|
1031
1031
|
ORDER BY latency_ms ASC`,
|
|
1032
1032
|
[agentId, since],
|
|
1033
1033
|
);
|
|
1034
|
-
if (result.rows.length === 0) return { count: 0, avgLatencyMs: 0, p95LatencyMs: 0 };
|
|
1034
|
+
if (result.rows.length === 0) return { count: 0, avgLatencyMs: 0, p50LatencyMs: 0, p90LatencyMs: 0, p95LatencyMs: 0 };
|
|
1035
1035
|
const latencies = result.rows.map((r) => Number(r.latency_ms));
|
|
1036
1036
|
const total = latencies.reduce((s, l) => s + l, 0);
|
|
1037
|
-
|
|
1037
|
+
// 0.14.3: p50/p90 — see sqlite.ts for why the mean is not usable here.
|
|
1038
|
+
const pct = (q: number) => latencies[Math.min(Math.floor(latencies.length * q), latencies.length - 1)];
|
|
1038
1039
|
return {
|
|
1039
1040
|
count: latencies.length,
|
|
1040
1041
|
avgLatencyMs: total / latencies.length,
|
|
1041
|
-
|
|
1042
|
+
p50LatencyMs: pct(0.5),
|
|
1043
|
+
p90LatencyMs: pct(0.9),
|
|
1044
|
+
p95LatencyMs: pct(0.95),
|
|
1042
1045
|
};
|
|
1043
1046
|
}
|
|
1044
1047
|
|
|
1048
|
+
/** 0.14.3: feedback rows joined to an activation event — see sqlite.ts. */
|
|
1049
|
+
async getLinkedFeedbackStats(agentId: string, windowHours: number = 24 * 7): Promise<{ total: number; useful: number }> {
|
|
1050
|
+
await this.readyPromise;
|
|
1051
|
+
const since = new Date(Date.now() - windowHours * 3600_000).toISOString();
|
|
1052
|
+
const result = await this.db.query<any>(
|
|
1053
|
+
`SELECT COUNT(*) AS total, COUNT(CASE WHEN rf.useful = TRUE THEN 1 END) AS useful
|
|
1054
|
+
FROM retrieval_feedback rf
|
|
1055
|
+
JOIN activation_events ae ON ae.id = rf.activation_event_id
|
|
1056
|
+
WHERE ae.agent_id = $1 AND rf.timestamp > $2`,
|
|
1057
|
+
[agentId, since],
|
|
1058
|
+
);
|
|
1059
|
+
const row = result.rows[0] ?? { total: 0, useful: 0 };
|
|
1060
|
+
return { total: Number(row.total), useful: Number(row.useful) };
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1045
1063
|
async getConsolidatedCount(agentId: string): Promise<number> {
|
|
1046
1064
|
await this.readyPromise;
|
|
1047
1065
|
const result = await this.db.query<any>(
|
package/src/storage/postgres.ts
CHANGED
|
@@ -1119,7 +1119,7 @@ export class PostgresEngramStore {
|
|
|
1119
1119
|
};
|
|
1120
1120
|
}
|
|
1121
1121
|
|
|
1122
|
-
async getActivationStats(agentId: string, windowHours: number = 24): Promise<{ count: number; avgLatencyMs: number; p95LatencyMs: number }> {
|
|
1122
|
+
async getActivationStats(agentId: string, windowHours: number = 24): Promise<{ count: number; avgLatencyMs: number; p50LatencyMs: number; p90LatencyMs: number; p95LatencyMs: number }> {
|
|
1123
1123
|
await this.readyPromise;
|
|
1124
1124
|
// Flush any buffered activation events so stats reflect the latest writes.
|
|
1125
1125
|
await this.flushActivationEvents();
|
|
@@ -1130,17 +1130,35 @@ export class PostgresEngramStore {
|
|
|
1130
1130
|
ORDER BY latency_ms ASC`,
|
|
1131
1131
|
[agentId, since],
|
|
1132
1132
|
);
|
|
1133
|
-
if (result.rows.length === 0) return { count: 0, avgLatencyMs: 0, p95LatencyMs: 0 };
|
|
1133
|
+
if (result.rows.length === 0) return { count: 0, avgLatencyMs: 0, p50LatencyMs: 0, p90LatencyMs: 0, p95LatencyMs: 0 };
|
|
1134
1134
|
const latencies = result.rows.map((r) => Number(r.latency_ms));
|
|
1135
1135
|
const total = latencies.reduce((s, l) => s + l, 0);
|
|
1136
|
-
|
|
1136
|
+
// 0.14.3: p50/p90 — see sqlite.ts for why the mean is not usable here.
|
|
1137
|
+
const pct = (q: number) => latencies[Math.min(Math.floor(latencies.length * q), latencies.length - 1)];
|
|
1137
1138
|
return {
|
|
1138
1139
|
count: latencies.length,
|
|
1139
1140
|
avgLatencyMs: total / latencies.length,
|
|
1140
|
-
|
|
1141
|
+
p50LatencyMs: pct(0.5),
|
|
1142
|
+
p90LatencyMs: pct(0.9),
|
|
1143
|
+
p95LatencyMs: pct(0.95),
|
|
1141
1144
|
};
|
|
1142
1145
|
}
|
|
1143
1146
|
|
|
1147
|
+
/** 0.14.3: feedback rows joined to an activation event — see sqlite.ts. */
|
|
1148
|
+
async getLinkedFeedbackStats(agentId: string, windowHours: number = 24 * 7): Promise<{ total: number; useful: number }> {
|
|
1149
|
+
await this.readyPromise;
|
|
1150
|
+
const since = new Date(Date.now() - windowHours * 3600_000).toISOString();
|
|
1151
|
+
const result = await this.q<any>(
|
|
1152
|
+
`SELECT COUNT(*) AS total, COUNT(CASE WHEN rf.useful = TRUE THEN 1 END) AS useful
|
|
1153
|
+
FROM retrieval_feedback rf
|
|
1154
|
+
JOIN activation_events ae ON ae.id = rf.activation_event_id
|
|
1155
|
+
WHERE ae.agent_id = $1 AND rf.timestamp > $2`,
|
|
1156
|
+
[agentId, since],
|
|
1157
|
+
);
|
|
1158
|
+
const row = result.rows[0] ?? { total: 0, useful: 0 };
|
|
1159
|
+
return { total: Number(row.total), useful: Number(row.useful) };
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1144
1162
|
async getConsolidatedCount(agentId: string): Promise<number> {
|
|
1145
1163
|
await this.readyPromise;
|
|
1146
1164
|
const result = await this.q<any>(
|
package/src/storage/sqlite.ts
CHANGED
|
@@ -1348,7 +1348,7 @@ export class EngramStore {
|
|
|
1348
1348
|
}
|
|
1349
1349
|
|
|
1350
1350
|
getActivationStats(agentId: string, windowHours: number = 24): {
|
|
1351
|
-
count: number; avgLatencyMs: number; p95LatencyMs: number;
|
|
1351
|
+
count: number; avgLatencyMs: number; p50LatencyMs: number; p90LatencyMs: number; p95LatencyMs: number;
|
|
1352
1352
|
} {
|
|
1353
1353
|
const since = new Date(Date.now() - windowHours * 3600_000).toISOString();
|
|
1354
1354
|
const rows = this.db.prepare(`
|
|
@@ -1357,17 +1357,39 @@ export class EngramStore {
|
|
|
1357
1357
|
ORDER BY latency_ms ASC
|
|
1358
1358
|
`).all(agentId, since) as { latency_ms: number }[];
|
|
1359
1359
|
|
|
1360
|
-
if (rows.length === 0) return { count: 0, avgLatencyMs: 0, p95LatencyMs: 0 };
|
|
1360
|
+
if (rows.length === 0) return { count: 0, avgLatencyMs: 0, p50LatencyMs: 0, p90LatencyMs: 0, p95LatencyMs: 0 };
|
|
1361
1361
|
|
|
1362
|
+
// 0.14.3: p50/p90 added. The column mixes warm recalls with cold model loads
|
|
1363
|
+
// and stalls (live store: mean 20 s, median 1.6 s, max 38 min), so the mean
|
|
1364
|
+
// is not a usable number; percentiles are. Rows are already sorted ASC.
|
|
1365
|
+
const pct = (q: number) => rows[Math.min(Math.floor(rows.length * q), rows.length - 1)].latency_ms;
|
|
1362
1366
|
const total = rows.reduce((s, r) => s + r.latency_ms, 0);
|
|
1363
|
-
const p95Index = Math.min(Math.floor(rows.length * 0.95), rows.length - 1);
|
|
1364
1367
|
return {
|
|
1365
1368
|
count: rows.length,
|
|
1366
1369
|
avgLatencyMs: total / rows.length,
|
|
1367
|
-
|
|
1370
|
+
p50LatencyMs: pct(0.5),
|
|
1371
|
+
p90LatencyMs: pct(0.9),
|
|
1372
|
+
p95LatencyMs: pct(0.95),
|
|
1368
1373
|
};
|
|
1369
1374
|
}
|
|
1370
1375
|
|
|
1376
|
+
/**
|
|
1377
|
+
* 0.14.3: feedback rows in the window that are JOINED to an activation event.
|
|
1378
|
+
* Unlinked rows (activation_event_id IS NULL — all 872 rows written before
|
|
1379
|
+
* 0.14.3) are excluded on purpose: this is the recall→use signal, and a row
|
|
1380
|
+
* that cannot be traced to a recall does not measure it.
|
|
1381
|
+
*/
|
|
1382
|
+
getLinkedFeedbackStats(agentId: string, windowHours: number = 24 * 7): { total: number; useful: number } {
|
|
1383
|
+
const since = new Date(Date.now() - windowHours * 3600_000).toISOString();
|
|
1384
|
+
const row = this.db.prepare(`
|
|
1385
|
+
SELECT COUNT(*) AS total, COUNT(CASE WHEN rf.useful = 1 THEN 1 END) AS useful
|
|
1386
|
+
FROM retrieval_feedback rf
|
|
1387
|
+
JOIN activation_events ae ON ae.id = rf.activation_event_id
|
|
1388
|
+
WHERE ae.agent_id = ? AND rf.timestamp > ?
|
|
1389
|
+
`).get(agentId, since) as { total: number; useful: number };
|
|
1390
|
+
return { total: row.total, useful: row.useful };
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1371
1393
|
getConsolidatedCount(agentId: string): number {
|
|
1372
1394
|
const row = this.db.prepare(
|
|
1373
1395
|
`SELECT COUNT(*) as cnt FROM engrams WHERE agent_id = ? AND stage = 'consolidated'`
|
package/src/types/engram.ts
CHANGED
|
@@ -219,6 +219,13 @@ export interface ActivationResult {
|
|
|
219
219
|
* See `src/engine/confidence.ts` for the formula.
|
|
220
220
|
*/
|
|
221
221
|
confidence?: number;
|
|
222
|
+
/**
|
|
223
|
+
* Id of the activation_events row this recall logged (0.14.3). Same value on
|
|
224
|
+
* every result in the same recall. Hand it back on memory_feedback so the
|
|
225
|
+
* feedback joins to the recall that produced it — before this, the id was
|
|
226
|
+
* generated and dropped, and every retrieval_feedback row was orphaned.
|
|
227
|
+
*/
|
|
228
|
+
activationEventId?: string;
|
|
222
229
|
/**
|
|
223
230
|
* Confidence-adaptive content preview (Paper 3: cognitive teaming).
|
|
224
231
|
* Set when the query opts in via `granularity: 'compact' | 'auto'`.
|
|
@@ -310,6 +317,15 @@ export interface ActivationQuery {
|
|
|
310
317
|
* something different on every run.
|
|
311
318
|
*/
|
|
312
319
|
asOf?: number;
|
|
320
|
+
/**
|
|
321
|
+
* 0.14.4: clock for ACT-R decay. Defaults to Date.now(). `asOf` pinned the
|
|
322
|
+
* temporal PARSER but decay still read the wall clock, so a "frozen" eval
|
|
323
|
+
* snapshot scored differently every day — a 300-query benchmark read 70.0%
|
|
324
|
+
* s@1 one evening and 67.0% the next afternoon on byte-identical data and
|
|
325
|
+
* code. Evals must pass the snapshot's own timestamp here. Production leaves
|
|
326
|
+
* it unset.
|
|
327
|
+
*/
|
|
328
|
+
now?: number;
|
|
313
329
|
bm25Only?: boolean; // Skip embedding — fast text-only retrieval for bulk/benchmark scenarios
|
|
314
330
|
/**
|
|
315
331
|
* Output granularity (Paper 3: cognitive teaming, Brill 2018 ACT-R collaboration).
|
package/src/types/eval.ts
CHANGED
|
@@ -54,7 +54,9 @@ export interface EvalMetrics {
|
|
|
54
54
|
// Retrieval quality
|
|
55
55
|
activationCount: number;
|
|
56
56
|
avgPrecisionAtK: number; // Of returned results, % judged useful
|
|
57
|
-
avgLatencyMs: number;
|
|
57
|
+
avgLatencyMs: number; // kept for callers; misleading on the live store (mixes cold loads) — prefer p50/p90
|
|
58
|
+
p50LatencyMs: number; // 0.14.3
|
|
59
|
+
p90LatencyMs: number; // 0.14.3
|
|
58
60
|
p95LatencyMs: number;
|
|
59
61
|
|
|
60
62
|
// Connection quality
|