@remnic/core 9.16.3 → 9.17.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/dist/access-admin-ops-surface.d.ts +8 -8
- package/dist/access-boundary.d.ts +8 -8
- package/dist/access-cli.js +8 -8
- package/dist/access-http.d.ts +8 -8
- package/dist/access-identity-continuity-surface.d.ts +6 -6
- package/dist/access-lcm-surface.d.ts +8 -8
- package/dist/access-mcp.d.ts +8 -8
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-DwhEFYy3.d.ts → access-namespace-preflight-_yxq3PYy.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-observe-write-surface.d.ts +8 -8
- package/dist/access-operations.d.ts +11 -11
- package/dist/access-recall-concurrency.d.ts +8 -8
- package/dist/access-recall-response.d.ts +8 -8
- package/dist/access-recall-surface.d.ts +8 -8
- package/dist/access-schema.d.ts +76 -76
- package/dist/{access-service-CSq1Wp2Z.d.ts → access-service-CSm242ks.d.ts} +6 -6
- package/dist/access-service.d.ts +8 -8
- package/dist/access-surface-catalog.d.ts +8 -8
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +2 -2
- package/dist/{auto-sync-UZG3XQZ5.js → auto-sync-IH3G25MT.js} +2 -2
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +6 -6
- package/dist/briefing.d.ts +2 -2
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-B9uga4sG.d.ts → catalog-9qLG_2OV.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-5EKOBRCA.js → chunk-BCNVNUQG.js} +2 -2
- package/dist/{chunk-V6LYAXGW.js → chunk-F7BWFIDK.js} +2 -2
- package/dist/{chunk-BTK2PIFL.js → chunk-GBT4TFV7.js} +2 -2
- package/dist/{chunk-JOCXI2O5.js → chunk-K2B2TMF3.js} +2 -2
- package/dist/{chunk-GSQASASV.js → chunk-MONWCSVL.js} +65 -4
- package/dist/chunk-MONWCSVL.js.map +1 -0
- package/dist/{chunk-VLIY55Q5.js → chunk-TXXP7WAM.js} +4 -4
- package/dist/{chunk-YHURQ3M5.js → chunk-UH7VNQ7O.js} +268 -5
- package/dist/chunk-UH7VNQ7O.js.map +1 -0
- package/dist/{chunk-5SB7SC2G.js → chunk-UX3ZXN34.js} +3 -1
- package/dist/{cli-DYLGXxuu.d.ts → cli-BLZHYY2A.d.ts} +5 -5
- package/dist/cli.d.ts +10 -10
- package/dist/cli.js +5 -5
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +2 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +6 -6
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/index.d.ts +502 -427
- package/dist/index.js +14 -8
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/mcp-memory-inspector-app.d.ts +8 -8
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +3 -3
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +2 -2
- package/dist/operator-toolkit.js +3 -3
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +3 -3
- package/dist/orchestration/maintenance.js +2 -2
- package/dist/{orchestrator-BHx-DWCa.d.ts → orchestrator-CplS9U3U.d.ts} +5 -5
- package/dist/orchestrator.d.ts +6 -6
- package/dist/orchestrator.js +8 -8
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-DDHSE876.d.ts → pipeline-DB4CbFqo.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{qmd-C4WVh6BM.d.ts → qmd-BxZmOLHq.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/recall-concurrency-config.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +3 -3
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/schemas.d.ts +76 -76
- package/dist/{scope-profiles-EqMXwzCz.d.ts → scope-profiles-NsQQo1Lt.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/index.d.ts +1 -1
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-CYpvwmQc.d.ts → semantic-consolidation-DW85h30n.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +9 -9
- package/dist/signal.d.ts +1 -1
- package/dist/{storage-Dxd7zVZH.d.ts → storage-o5BTAa8d.d.ts} +1 -1
- package/dist/storage.d.ts +2 -2
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/types.d.ts +66 -66
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-OUCJALBK.d.ts → types-V3dT-pen.d.ts} +14 -2
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/activity/config.test.ts +42 -4
- package/src/activity/config.ts +83 -8
- package/src/activity/index.ts +5 -3
- package/src/activity/memory-gen.test.ts +436 -0
- package/src/activity/memory-gen.ts +409 -0
- package/src/activity/runner.test.ts +3 -0
- package/src/activity/scheduler.test.ts +3 -0
- package/src/activity/sync-registration.test.ts +2 -1
- package/src/activity/types.ts +15 -1
- package/src/config.test.ts +7 -0
- package/dist/chunk-GSQASASV.js.map +0 -1
- package/dist/chunk-YHURQ3M5.js.map +0 -1
- /package/dist/{auto-sync-UZG3XQZ5.js.map → auto-sync-IH3G25MT.js.map} +0 -0
- /package/dist/{chunk-5EKOBRCA.js.map → chunk-BCNVNUQG.js.map} +0 -0
- /package/dist/{chunk-V6LYAXGW.js.map → chunk-F7BWFIDK.js.map} +0 -0
- /package/dist/{chunk-BTK2PIFL.js.map → chunk-GBT4TFV7.js.map} +0 -0
- /package/dist/{chunk-JOCXI2O5.js.map → chunk-K2B2TMF3.js.map} +0 -0
- /package/dist/{chunk-VLIY55Q5.js.map → chunk-TXXP7WAM.js.map} +0 -0
- /package/dist/{chunk-5SB7SC2G.js.map → chunk-UX3ZXN34.js.map} +0 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import { defaultActivityConfig } from "./config.js";
|
|
5
|
+
import {
|
|
6
|
+
generateActivityMemories,
|
|
7
|
+
isEligibleActivityFact,
|
|
8
|
+
type ActivityMemoryGenerationDeps,
|
|
9
|
+
type ActivityMemoryWriter,
|
|
10
|
+
} from "./memory-gen.js";
|
|
11
|
+
import type { ExtractedFact, ImportanceScore, MemoryStatus } from "../types.js";
|
|
12
|
+
import type { JudgeCandidate, JudgeVerdict, JudgeVerdictKind } from "../extraction-judge.js";
|
|
13
|
+
import { scoreImportance } from "../importance.js";
|
|
14
|
+
|
|
15
|
+
const DATE = "2026-03-10";
|
|
16
|
+
const DAY_START = "2026-03-10T00:00:00.000Z";
|
|
17
|
+
|
|
18
|
+
type ActFact = Pick<ExtractedFact, "category" | "content" | "confidence" | "tags" | "entityRef" | "structuredAttributes">;
|
|
19
|
+
|
|
20
|
+
const ownDecision: ActFact = {
|
|
21
|
+
category: "decision",
|
|
22
|
+
content: "I decided to consolidate the account settings.",
|
|
23
|
+
confidence: 0.95,
|
|
24
|
+
tags: ["settings"],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function depsFor(
|
|
28
|
+
facts: ActFact[],
|
|
29
|
+
opts: {
|
|
30
|
+
existing?: { id: string; status: MemoryStatus | undefined; key?: string; startUtc?: string } | null;
|
|
31
|
+
promoteResult?: boolean;
|
|
32
|
+
demoteResult?: boolean;
|
|
33
|
+
dayCount?: number;
|
|
34
|
+
judgeThrows?: boolean;
|
|
35
|
+
judgeRejects?: boolean;
|
|
36
|
+
judgeVerdictByIndex?: Record<number, JudgeVerdictKind>;
|
|
37
|
+
extractThrows?: boolean;
|
|
38
|
+
extractionFailure?: string;
|
|
39
|
+
} = {},
|
|
40
|
+
) {
|
|
41
|
+
const writes: Array<{
|
|
42
|
+
status: string;
|
|
43
|
+
content: string;
|
|
44
|
+
validAt?: string;
|
|
45
|
+
confidence?: number;
|
|
46
|
+
structuredAttributes?: Readonly<Record<string, string>>;
|
|
47
|
+
importance?: ImportanceScore;
|
|
48
|
+
}> = [];
|
|
49
|
+
const promotions: Array<{ id: string; attrs: Record<string, string>; confidence?: number }> = [];
|
|
50
|
+
const demotions: Array<{ id: string; attrs: Record<string, string> }> = [];
|
|
51
|
+
let extractCalls = 0;
|
|
52
|
+
const writer: ActivityMemoryWriter = {
|
|
53
|
+
findActivityMemoryByContent: async (key, startUtc) => {
|
|
54
|
+
const e = opts.existing;
|
|
55
|
+
if (e === undefined || e === null) return null;
|
|
56
|
+
if (e.key !== undefined && e.key !== key) return null;
|
|
57
|
+
if (e.startUtc !== undefined && e.startUtc !== startUtc) return null;
|
|
58
|
+
return { id: e.id, status: e.status };
|
|
59
|
+
},
|
|
60
|
+
countActivityMemoriesForDay: async () => opts.dayCount ?? 0,
|
|
61
|
+
promoteActivityMemory: async (id, attrs, confidence) => {
|
|
62
|
+
promotions.push({ id, attrs, confidence });
|
|
63
|
+
return opts.promoteResult ?? true;
|
|
64
|
+
},
|
|
65
|
+
demoteActivityMemory: async (id, attrs) => {
|
|
66
|
+
demotions.push({ id, attrs });
|
|
67
|
+
return opts.demoteResult ?? true;
|
|
68
|
+
},
|
|
69
|
+
writeSealedMemory: async (envelope, extras) => {
|
|
70
|
+
writes.push({
|
|
71
|
+
content: envelope.content,
|
|
72
|
+
status: extras.status,
|
|
73
|
+
validAt: envelope.validAt,
|
|
74
|
+
confidence: envelope.confidence,
|
|
75
|
+
structuredAttributes: envelope.structuredAttributes,
|
|
76
|
+
importance: extras.importance,
|
|
77
|
+
});
|
|
78
|
+
return {};
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
const deps: ActivityMemoryGenerationDeps = {
|
|
82
|
+
extract: async () => {
|
|
83
|
+
extractCalls += 1;
|
|
84
|
+
if (opts.extractThrows) throw new Error("extract exploded");
|
|
85
|
+
return { facts, profileUpdates: [], entities: [], questions: [], extractionFailure: opts.extractionFailure };
|
|
86
|
+
},
|
|
87
|
+
judge: async (candidates: JudgeCandidate[]) => {
|
|
88
|
+
if (opts.judgeThrows) throw new Error("judge exploded");
|
|
89
|
+
return new Map<number, JudgeVerdict>(
|
|
90
|
+
candidates.map((_c, i): [number, JudgeVerdict] => {
|
|
91
|
+
const kind: JudgeVerdictKind = opts.judgeVerdictByIndex?.[i] ?? (opts.judgeRejects ? "reject" : "accept");
|
|
92
|
+
return [i, { durable: kind === "accept", reason: kind, kind }];
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
},
|
|
96
|
+
writer,
|
|
97
|
+
};
|
|
98
|
+
return { writes, promotions, demotions, extractCalls: () => extractCalls, deps };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
test("isEligibleActivityFact rejects attributed third-party first-person text", () => {
|
|
102
|
+
// A first-person pronoun quoted from someone else is not the user's own claim.
|
|
103
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Alice wrote: I decided to leave." }), false);
|
|
104
|
+
// Names that merely begin with a pronoun prefix ("Wendy", "Ian") are still
|
|
105
|
+
// third parties — the excluded pronouns are word-anchored.
|
|
106
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Wendy said: I will refactor the parser." }), false);
|
|
107
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Ian posted: I merged the release branch." }), false);
|
|
108
|
+
// The user's own first-person voice stays eligible, including team "we/our".
|
|
109
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "We decided to ship on Friday." }), true);
|
|
110
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "I updated our deploy runbook." }), true);
|
|
111
|
+
// Case-insensitive verbs and non-Titlecase names still count as attribution.
|
|
112
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Alice Wrote: I decided to leave." }), false);
|
|
113
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "ALICE wrote: I decided to leave." }), false);
|
|
114
|
+
// Colon-style chat/comment sender headers are attribution too.
|
|
115
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Bob: I decided to refactor the parser." }), false);
|
|
116
|
+
// Common document labels are not senders — the user's own note stays eligible.
|
|
117
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Note: I decided to refactor the parser." }), true);
|
|
118
|
+
// A chat self-label ("Me:") is the user, not a third party — stays eligible.
|
|
119
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Me: I decided to consolidate the settings." }), true);
|
|
120
|
+
// "Us:" is a first-person plural self-label, like "Me:".
|
|
121
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Us: we decided to ship on Friday." }), true);
|
|
122
|
+
// Third-person speakers are attribution, not the user — must be rejected.
|
|
123
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "They wrote: I decided to leave." }), false);
|
|
124
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "He said: I will handle the migration." }), false);
|
|
125
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "She posted: I closed the incident." }), false);
|
|
126
|
+
// Multi-token sender labels ("Alice Smith:") are attribution.
|
|
127
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Alice Smith: I decided to leave." }), false);
|
|
128
|
+
// Multi-word document labels stay eligible via the first-token check.
|
|
129
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Action items: I will refactor the parser." }), true);
|
|
130
|
+
// Colon labels with no space after the colon are still attribution.
|
|
131
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Bob:I decided to refactor the parser." }), false);
|
|
132
|
+
// An allowlisted label that names a sender ("Update from Alice:", "Note by Bob:") rejects.
|
|
133
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Update from Alice: I decided to cancel the migration." }), false);
|
|
134
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Note by Bob: I will handle it." }), false);
|
|
135
|
+
// Verb attribution only fires at the leading position: a first-person decision
|
|
136
|
+
// that mentions a named person's past action in passing stays eligible, while a
|
|
137
|
+
// leading "Name wrote/said" header still rejects.
|
|
138
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "I approved the plan Bob wrote last week." }), true);
|
|
139
|
+
assert.equal(isEligibleActivityFact({ ...ownDecision, content: "Bob wrote: I decided to leave." }), false);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("activity smart mode rejects attributed third-party first-person content before judging", async () => {
|
|
143
|
+
// Genuine first-person ("I decided") but attributed to a named third party,
|
|
144
|
+
// so it must be dropped at the eligibility gate rather than written.
|
|
145
|
+
const attributed = { ...ownDecision, content: "Alice wrote: I decided to leave the project." };
|
|
146
|
+
assert.equal(isEligibleActivityFact(attributed), false);
|
|
147
|
+
const { deps, writes } = depsFor([attributed]);
|
|
148
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
149
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart",
|
|
150
|
+
}, deps);
|
|
151
|
+
assert.deepEqual(result, { created: 0, promoted: 0, demoted: 0, pendingReview: 0, rejectedDisplayedContent: 1, rejectedByJudge: 0, skipped: 0 });
|
|
152
|
+
assert.deepEqual(writes, []);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("activity smart mode writes an accepted first-person decision bound to the digest day", async () => {
|
|
156
|
+
const { deps, writes } = depsFor([ownDecision]);
|
|
157
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
158
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
159
|
+
}, deps);
|
|
160
|
+
assert.equal(result.created, 1);
|
|
161
|
+
// validAt is pinned to the digest's local day, not the write instant.
|
|
162
|
+
assert.equal(writes.length, 1);
|
|
163
|
+
assert.equal(writes[0]?.content, ownDecision.content);
|
|
164
|
+
assert.equal(writes[0]?.status, "active");
|
|
165
|
+
assert.equal(writes[0]?.validAt, DAY_START);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("activity extraction remains inactive unless smart mode is explicitly enabled", async () => {
|
|
169
|
+
const { deps, writes, extractCalls } = depsFor([ownDecision]);
|
|
170
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", defaultActivityConfig(), deps);
|
|
171
|
+
assert.deepEqual(result, { created: 0, promoted: 0, demoted: 0, pendingReview: 0, rejectedDisplayedContent: 0, rejectedByJudge: 0, skipped: 0 });
|
|
172
|
+
assert.equal(extractCalls(), 0);
|
|
173
|
+
assert.deepEqual(writes, []);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("activity day cap seeds from persisted totals and keeps the highest-trust survivor", async () => {
|
|
177
|
+
const strong: ActFact = { category: "decision", content: "I decided to migrate the database.", confidence: 0.99, tags: ["infra"] };
|
|
178
|
+
const weak: ActFact = { category: "decision", content: "I decided to rename the worker pool.", confidence: 0.72, tags: ["infra"] };
|
|
179
|
+
// Two memories already persisted today; cap 3 leaves room for exactly one more.
|
|
180
|
+
const { deps, writes } = depsFor([weak, strong], { dayCount: 2 });
|
|
181
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
182
|
+
...defaultActivityConfig(),
|
|
183
|
+
enabled: true,
|
|
184
|
+
extractionMode: "smart",
|
|
185
|
+
sourceTrust: 1,
|
|
186
|
+
autoApproveTrust: 0.8,
|
|
187
|
+
maxMemoriesPerDay: 3,
|
|
188
|
+
}, deps);
|
|
189
|
+
assert.equal(result.created, 1);
|
|
190
|
+
assert.equal(result.skipped, 1);
|
|
191
|
+
// The higher-confidence (higher-trust) fact wins the remaining slot.
|
|
192
|
+
assert.deepEqual(writes.map((w) => w.content), [strong.content]);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("activity smart mode salvages a malformed optional field instead of aborting the day", async () => {
|
|
196
|
+
const overlongTag = "x".repeat(300);
|
|
197
|
+
const malformed: ActFact = {
|
|
198
|
+
category: "decision",
|
|
199
|
+
content: "I decided to archive stale branches.",
|
|
200
|
+
confidence: 0.95,
|
|
201
|
+
tags: [overlongTag],
|
|
202
|
+
};
|
|
203
|
+
const { deps, writes } = depsFor([malformed, ownDecision]);
|
|
204
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
205
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
206
|
+
}, deps);
|
|
207
|
+
// Strict compose would throw on the 300-char tag and abort both writes;
|
|
208
|
+
// salvage drops the bad tag so both eligible facts are still persisted.
|
|
209
|
+
assert.equal(result.created, 2);
|
|
210
|
+
assert.deepEqual(writes.map((w) => w.status), ["active", "active"]);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("activity smart mode degrades to trust scoring when the judge throws", async () => {
|
|
214
|
+
const { deps, writes } = depsFor([ownDecision], { judgeThrows: true });
|
|
215
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
216
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
217
|
+
}, deps);
|
|
218
|
+
// A judge failure must not abort the day: with no verdict the fact is scored
|
|
219
|
+
// on confidence x sourceTrust alone (0.95) and still clears autoApproveTrust.
|
|
220
|
+
assert.equal(result.created, 1);
|
|
221
|
+
assert.deepEqual(writes.map((w) => w.status), ["active"]);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("activity smart mode returns a zero result when extraction throws", async () => {
|
|
225
|
+
const { deps, writes } = depsFor([ownDecision], { extractThrows: true });
|
|
226
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
227
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
228
|
+
}, deps);
|
|
229
|
+
// An extraction failure must not throw out of the day's pass.
|
|
230
|
+
assert.deepEqual(result, { created: 0, promoted: 0, demoted: 0, pendingReview: 0, rejectedDisplayedContent: 0, rejectedByJudge: 0, skipped: 0 });
|
|
231
|
+
assert.deepEqual(writes, []);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("activity smart mode persists the scored importance on the write", async () => {
|
|
235
|
+
const { deps, writes } = depsFor([ownDecision]);
|
|
236
|
+
await generateActivityMemories(DATE, "## Notable activity", {
|
|
237
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
238
|
+
}, deps);
|
|
239
|
+
assert.equal(writes.length, 1);
|
|
240
|
+
const importance = writes[0]?.importance;
|
|
241
|
+
assert.ok(importance, "write must carry the scored importance");
|
|
242
|
+
const expected = scoreImportance(ownDecision.content, ownDecision.category, ownDecision.tags);
|
|
243
|
+
assert.equal(importance.level, expected.level);
|
|
244
|
+
assert.equal(importance.score, expected.score);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("activity smart mode returns a zero result on an in-band extraction failure", async () => {
|
|
248
|
+
const { deps, writes } = depsFor([ownDecision], { extractionFailure: "provider timeout" });
|
|
249
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
250
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
251
|
+
}, deps);
|
|
252
|
+
assert.deepEqual(result, { created: 0, promoted: 0, demoted: 0, pendingReview: 0, rejectedDisplayedContent: 0, rejectedByJudge: 0, skipped: 0 });
|
|
253
|
+
assert.deepEqual(writes, []);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("activity smart mode persists trust score, decision, and judge verdict on the write", async () => {
|
|
257
|
+
const { deps, writes } = depsFor([ownDecision]);
|
|
258
|
+
await generateActivityMemories(DATE, "## Notable activity", {
|
|
259
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
260
|
+
}, deps);
|
|
261
|
+
assert.equal(writes.length, 1);
|
|
262
|
+
const w = writes[0];
|
|
263
|
+
// Decision-derived trust is persisted as confidence (0.95 conf x 1 sourceTrust
|
|
264
|
+
// + 0.15 judge-accept boost, capped at 1), with the rationale in attributes.
|
|
265
|
+
assert.equal(w?.confidence, 1);
|
|
266
|
+
// Structured-attribute keys are canonicalized to lowercase on the envelope.
|
|
267
|
+
assert.equal(w?.structuredAttributes?.trustscore, "1.000");
|
|
268
|
+
assert.equal(w?.structuredAttributes?.trustdecision, "auto-approved");
|
|
269
|
+
assert.equal(w?.structuredAttributes?.judgeverdict, "accept");
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("activity smart mode preserves extracted attributes without overriding trust keys", async () => {
|
|
273
|
+
const fact: ActFact = { ...ownDecision, structuredAttributes: { chosen: "option B", trustScore: "0.001" } };
|
|
274
|
+
const { deps, writes } = depsFor([fact]);
|
|
275
|
+
await generateActivityMemories(DATE, "## Notable activity", {
|
|
276
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
277
|
+
}, deps);
|
|
278
|
+
assert.equal(writes.length, 1);
|
|
279
|
+
const sa = writes[0]?.structuredAttributes;
|
|
280
|
+
// Extractor attribute is preserved; the extractor's stray trustScore does not
|
|
281
|
+
// override the path-owned trust key (canonical lowercase).
|
|
282
|
+
assert.equal(sa?.chosen, "option B");
|
|
283
|
+
assert.equal(sa?.trustscore, "1.000");
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test("activity smart mode promotes a pending_review duplicate on a stronger reassessment", async () => {
|
|
287
|
+
const { deps, writes, promotions } = depsFor([ownDecision], { existing: { id: "mem-1", status: "pending_review" } });
|
|
288
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
289
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
290
|
+
}, deps);
|
|
291
|
+
assert.equal(result.promoted, 1);
|
|
292
|
+
assert.equal(result.created, 0);
|
|
293
|
+
assert.deepEqual(writes, []);
|
|
294
|
+
assert.equal(promotions.length, 1);
|
|
295
|
+
assert.equal(promotions[0]?.id, "mem-1");
|
|
296
|
+
assert.equal(promotions[0]?.attrs.trustDecision, "promoted-by-reassessment");
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("activity smart mode demotes a pending_review duplicate on a fresh judge reject", async () => {
|
|
300
|
+
const { deps, writes, demotions } = depsFor([ownDecision], {
|
|
301
|
+
existing: { id: "mem-2", status: "pending_review" },
|
|
302
|
+
judgeRejects: true,
|
|
303
|
+
});
|
|
304
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
305
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
306
|
+
}, deps);
|
|
307
|
+
assert.equal(result.demoted, 1);
|
|
308
|
+
assert.equal(result.rejectedByJudge, 0);
|
|
309
|
+
assert.deepEqual(writes, []);
|
|
310
|
+
assert.equal(demotions.length, 1);
|
|
311
|
+
assert.equal(demotions[0]?.id, "mem-2");
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test("activity smart mode skips an existing active duplicate without promoting", async () => {
|
|
315
|
+
const { deps, writes, promotions } = depsFor([ownDecision], { existing: { id: "mem-3", status: "active" } });
|
|
316
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
317
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
318
|
+
}, deps);
|
|
319
|
+
assert.equal(result.skipped, 1);
|
|
320
|
+
assert.equal(result.promoted, 0);
|
|
321
|
+
assert.deepEqual(writes, []);
|
|
322
|
+
assert.equal(promotions.length, 0);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("activity smart mode suppresses intra-run duplicate content", async () => {
|
|
326
|
+
// Two identical eligible facts in one batch: storage cannot see the first
|
|
327
|
+
// before the second is processed, so only one is written.
|
|
328
|
+
const { deps, writes } = depsFor([ownDecision, { ...ownDecision }]);
|
|
329
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
330
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
331
|
+
}, deps);
|
|
332
|
+
assert.equal(result.created, 1);
|
|
333
|
+
assert.equal(result.skipped, 1);
|
|
334
|
+
assert.equal(writes.length, 1);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("activity smart mode rejects a non-finite confidence before the floor", async () => {
|
|
338
|
+
// A NaN/absent confidence must fail the gate rather than slipping through to
|
|
339
|
+
// computeTrustScore's 0.7 default.
|
|
340
|
+
const fact: ActFact = { ...ownDecision, confidence: Number.NaN };
|
|
341
|
+
const { deps, writes } = depsFor([fact]);
|
|
342
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
343
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
344
|
+
}, deps);
|
|
345
|
+
assert.equal(result.created, 0);
|
|
346
|
+
assert.equal(result.skipped, 1);
|
|
347
|
+
assert.deepEqual(writes, []);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test("activity smart mode dedups whitespace/case content variants within a pass", async () => {
|
|
351
|
+
const a: ActFact = { ...ownDecision, content: "I decided to consolidate the settings." };
|
|
352
|
+
const b: ActFact = { ...ownDecision, content: " I DECIDED to Consolidate the Settings. " };
|
|
353
|
+
const { deps, writes } = depsFor([a, b]);
|
|
354
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
355
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
356
|
+
}, deps);
|
|
357
|
+
assert.equal(result.created, 1);
|
|
358
|
+
assert.equal(result.skipped, 1);
|
|
359
|
+
assert.equal(writes.length, 1);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test("activity smart mode keeps the stronger duplicate over a weaker earlier reject", async () => {
|
|
363
|
+
const first: ActFact = { ...ownDecision, confidence: 0.8 };
|
|
364
|
+
const second: ActFact = { ...ownDecision, confidence: 0.99 };
|
|
365
|
+
const { deps, writes } = depsFor([first, second], { judgeVerdictByIndex: { 0: "reject", 1: "accept" } });
|
|
366
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
367
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
368
|
+
}, deps);
|
|
369
|
+
// The later accepted copy wins; the earlier reject must not block it.
|
|
370
|
+
assert.equal(result.created, 1);
|
|
371
|
+
assert.equal(result.rejectedByJudge, 0);
|
|
372
|
+
assert.equal(writes.length, 1);
|
|
373
|
+
assert.equal(writes[0]?.status, "active");
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test("activity smart mode treats same-day duplicate content as a duplicate", async () => {
|
|
377
|
+
const { deps, writes } = depsFor([ownDecision], { existing: { id: "m-sameday", status: "active", startUtc: DAY_START } });
|
|
378
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
379
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
380
|
+
}, deps);
|
|
381
|
+
assert.equal(result.created, 0);
|
|
382
|
+
assert.equal(result.skipped, 1);
|
|
383
|
+
assert.deepEqual(writes, []);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test("activity smart mode writes recurring content on a different day (day-scoped dedup)", async () => {
|
|
387
|
+
const { deps, writes } = depsFor([ownDecision], { existing: { id: "m-day1", status: "active", startUtc: DAY_START } });
|
|
388
|
+
const result = await generateActivityMemories("2026-03-11", "## Notable activity", {
|
|
389
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
390
|
+
}, deps);
|
|
391
|
+
// The existing row is on 2026-03-10; the same text on 2026-03-11 is not a duplicate.
|
|
392
|
+
assert.equal(result.created, 1);
|
|
393
|
+
assert.equal(writes.length, 1);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("activity smart mode keeps the durable duplicate over a higher-confidence reject", async () => {
|
|
397
|
+
// Same content twice: a high-confidence reject vs a lower-confidence accept.
|
|
398
|
+
// The durable (accepted) copy must win even though the reject has higher raw trust.
|
|
399
|
+
const rejectHiConf: ActFact = { ...ownDecision, confidence: 0.99 };
|
|
400
|
+
const acceptLoConf: ActFact = { ...ownDecision, confidence: 0.72 };
|
|
401
|
+
const { deps, writes } = depsFor([rejectHiConf, acceptLoConf], { judgeVerdictByIndex: { 0: "reject", 1: "accept" } });
|
|
402
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
403
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
404
|
+
}, deps);
|
|
405
|
+
assert.equal(result.created, 1);
|
|
406
|
+
assert.equal(result.rejectedByJudge, 0);
|
|
407
|
+
assert.equal(writes.length, 1);
|
|
408
|
+
assert.equal(writes[0]?.status, "active");
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
test("activity smart extraction runs on extractionMode alone, not the enabled ingestion gate", async () => {
|
|
412
|
+
// enabled gates ingestion; extractionMode independently gates extraction of a
|
|
413
|
+
// supplied digest, so smart extraction runs even when enabled is false.
|
|
414
|
+
const { deps, writes } = depsFor([ownDecision]);
|
|
415
|
+
const result = await generateActivityMemories(DATE, "## Notable activity", {
|
|
416
|
+
...defaultActivityConfig(), enabled: false, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
417
|
+
}, deps);
|
|
418
|
+
assert.equal(result.created, 1);
|
|
419
|
+
assert.equal(writes.length, 1);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
test("activity smart mode retains trust fields even when the extractor floods structured attributes", async () => {
|
|
423
|
+
const many: Record<string, string> = {};
|
|
424
|
+
for (let i = 0; i < 64; i += 1) many[`k${i}`] = `v${i}`;
|
|
425
|
+
const fact: ActFact = { ...ownDecision, structuredAttributes: many };
|
|
426
|
+
const { deps, writes } = depsFor([fact]);
|
|
427
|
+
await generateActivityMemories(DATE, "## Notable activity", {
|
|
428
|
+
...defaultActivityConfig(), enabled: true, extractionMode: "smart", sourceTrust: 1, autoApproveTrust: 0.8,
|
|
429
|
+
}, deps);
|
|
430
|
+
assert.equal(writes.length, 1);
|
|
431
|
+
// Trust fields are inserted first, so the salvage 64-entry cap never trims them.
|
|
432
|
+
const sa = writes[0]?.structuredAttributes;
|
|
433
|
+
assert.equal(sa?.trustscore, "1.000");
|
|
434
|
+
assert.equal(sa?.trustdecision, "auto-approved");
|
|
435
|
+
assert.equal(sa?.judgeverdict, "accept");
|
|
436
|
+
});
|