@remnic/core 9.3.736 → 9.3.737

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/dist/access-boundary.d.ts +17 -6
  2. package/dist/access-cli.js +1 -1
  3. package/dist/access-http.d.ts +17 -6
  4. package/dist/access-mcp.d.ts +17 -6
  5. package/dist/access-operations.d.ts +17 -6
  6. package/dist/{access-service-CdC0q4pr.d.ts → access-service-DwQ6BIQw.d.ts} +3 -3
  7. package/dist/access-service.d.ts +16 -5
  8. package/dist/access-surface-catalog.d.ts +17 -6
  9. package/dist/action-confidence.d.ts +1 -1
  10. package/dist/active-memory-bridge.d.ts +1 -1
  11. package/dist/active-recall.d.ts +1 -1
  12. package/dist/behavior-learner.d.ts +1 -1
  13. package/dist/behavior-signals.d.ts +1 -1
  14. package/dist/bootstrap.d.ts +16 -4
  15. package/dist/briefing.d.ts +1 -1
  16. package/dist/buffer-surprise-report.d.ts +1 -1
  17. package/dist/buffer.d.ts +1 -1
  18. package/dist/calibration.d.ts +1 -1
  19. package/dist/capabilities.d.ts +1 -1
  20. package/dist/{catalog-D2NLDg4S.d.ts → catalog-_Bap6vfD.d.ts} +1 -1
  21. package/dist/causal-behavior.d.ts +1 -1
  22. package/dist/causal-consolidation.d.ts +1 -1
  23. package/dist/causal-trajectory-graph.d.ts +1 -1
  24. package/dist/{chunk-AE2HPUP7.js → chunk-PB5YON2G.js} +352 -297
  25. package/dist/chunk-PB5YON2G.js.map +1 -0
  26. package/dist/{cli-BHy6Bo6u.d.ts → cli-BlEIWDQw.d.ts} +3 -3
  27. package/dist/cli.d.ts +9 -7
  28. package/dist/compounding/engine.d.ts +1 -1
  29. package/dist/compounding/preference-consolidator.d.ts +1 -1
  30. package/dist/compression-optimizer.d.ts +1 -1
  31. package/dist/config.d.ts +1 -1
  32. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  33. package/dist/connectors/codex-materialize.d.ts +1 -1
  34. package/dist/connectors/index.d.ts +1 -1
  35. package/dist/consolidation-provenance-check.d.ts +1 -1
  36. package/dist/consolidation-undo.d.ts +1 -1
  37. package/dist/contradiction/index.d.ts +1 -1
  38. package/dist/conversation-index/backend.d.ts +1 -1
  39. package/dist/conversation-index/chunker.d.ts +1 -1
  40. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  41. package/dist/conversation-index/indexer.d.ts +1 -1
  42. package/dist/conversation-index/search.d.ts +1 -1
  43. package/dist/day-summary.d.ts +1 -1
  44. package/dist/delinearize.d.ts +1 -1
  45. package/dist/direct-answer-wiring.d.ts +1 -1
  46. package/dist/direct-answer.d.ts +1 -1
  47. package/dist/embedding-fallback.d.ts +1 -1
  48. package/dist/enrichment/index.d.ts +1 -1
  49. package/dist/entity-retrieval.d.ts +1 -1
  50. package/dist/entity-schema.d.ts +1 -1
  51. package/dist/explicit-capture.d.ts +16 -4
  52. package/dist/extraction-faithfulness.d.ts +1 -1
  53. package/dist/extraction-judge-telemetry.d.ts +1 -1
  54. package/dist/extraction-judge-training.d.ts +1 -1
  55. package/dist/extraction-judge.d.ts +1 -1
  56. package/dist/extraction.d.ts +1 -1
  57. package/dist/fallback-llm.d.ts +1 -1
  58. package/dist/graph-dashboard-diff.d.ts +1 -1
  59. package/dist/graph-dashboard-key.d.ts +1 -1
  60. package/dist/graph-dashboard-parser.d.ts +1 -1
  61. package/dist/graph-edge-reinforcement.d.ts +1 -1
  62. package/dist/graph-snapshot.d.ts +1 -1
  63. package/dist/graph.d.ts +1 -1
  64. package/dist/identity-continuity.d.ts +1 -1
  65. package/dist/importance.d.ts +1 -1
  66. package/dist/index.d.ts +20 -18
  67. package/dist/index.js +1 -1
  68. package/dist/intent.d.ts +1 -1
  69. package/dist/lcm/engine.d.ts +1 -1
  70. package/dist/lcm/index.d.ts +1 -1
  71. package/dist/lcm/tools.d.ts +1 -1
  72. package/dist/lifecycle.d.ts +1 -1
  73. package/dist/live-connectors-runner.d.ts +1 -1
  74. package/dist/local-llm.d.ts +1 -1
  75. package/dist/local-model-endpoint.d.ts +1 -1
  76. package/dist/maintenance/memory-governance.d.ts +1 -1
  77. package/dist/mcp-memory-inspector-app.d.ts +17 -6
  78. package/dist/memory-action-policy.d.ts +1 -1
  79. package/dist/memory-cache.d.ts +1 -1
  80. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  81. package/dist/memory-projection-store.d.ts +1 -1
  82. package/dist/memory-provenance.d.ts +1 -1
  83. package/dist/memory-worth-outcomes.d.ts +1 -1
  84. package/dist/models-json.d.ts +1 -1
  85. package/dist/namespaces/migrate.d.ts +2 -2
  86. package/dist/namespaces/principal.d.ts +1 -1
  87. package/dist/namespaces/search.d.ts +1 -1
  88. package/dist/namespaces/storage.d.ts +2 -2
  89. package/dist/native-knowledge.d.ts +1 -1
  90. package/dist/operator-toolkit.d.ts +1 -1
  91. package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
  92. package/dist/orchestration/maintenance.d.ts +2 -2
  93. package/dist/{orchestrator-C-xp_rtZ.d.ts → orchestrator-CJZ21ZiB.d.ts} +78 -12
  94. package/dist/orchestrator.d.ts +16 -4
  95. package/dist/orchestrator.js +1 -1
  96. package/dist/patterns-cli.d.ts +1 -1
  97. package/dist/policy-runtime.d.ts +1 -1
  98. package/dist/provenance.d.ts +1 -1
  99. package/dist/qmd-recall-cache.d.ts +1 -1
  100. package/dist/qmd.d.ts +1 -1
  101. package/dist/recall-disclosure-escalation.d.ts +1 -1
  102. package/dist/recall-explain-renderer.d.ts +1 -1
  103. package/dist/recall-planner-llm.d.ts +1 -1
  104. package/dist/recall-state.d.ts +1 -1
  105. package/dist/recall-tag-filter.d.ts +1 -1
  106. package/dist/recall-xray-cli.d.ts +1 -1
  107. package/dist/recall-xray-renderer.d.ts +1 -1
  108. package/dist/recall-xray.d.ts +1 -1
  109. package/dist/resolve-auth-token.d.ts +1 -1
  110. package/dist/retrieval-agents.d.ts +1 -1
  111. package/dist/retrieval-tiers.d.ts +1 -1
  112. package/dist/routing/engine.d.ts +1 -1
  113. package/dist/routing/store.d.ts +1 -1
  114. package/dist/search/embed-helper.d.ts +1 -1
  115. package/dist/search/factory.d.ts +1 -1
  116. package/dist/search/index.d.ts +1 -1
  117. package/dist/search/lancedb-backend.d.ts +1 -1
  118. package/dist/search/meilisearch-backend.d.ts +1 -1
  119. package/dist/search/noop-backend.d.ts +1 -1
  120. package/dist/search/orama-backend.d.ts +1 -1
  121. package/dist/search/port.d.ts +1 -1
  122. package/dist/search/remote-backend.d.ts +1 -1
  123. package/dist/{semantic-consolidation-COdwU-me.d.ts → semantic-consolidation-DsIEiCka.d.ts} +1 -1
  124. package/dist/semantic-consolidation.d.ts +2 -2
  125. package/dist/semantic-rule-verifier.d.ts +1 -1
  126. package/dist/session-observer-bands.d.ts +1 -1
  127. package/dist/session-observer-state.d.ts +1 -1
  128. package/dist/shared-context/manager.d.ts +1 -1
  129. package/dist/signal.d.ts +1 -1
  130. package/dist/storage.d.ts +1 -1
  131. package/dist/summarizer.d.ts +1 -1
  132. package/dist/summary-snapshot.d.ts +1 -1
  133. package/dist/temporal-supersession.d.ts +1 -1
  134. package/dist/temporal-validity.d.ts +1 -1
  135. package/dist/threading.d.ts +1 -1
  136. package/dist/tier-migration.d.ts +1 -1
  137. package/dist/tier-routing.d.ts +1 -1
  138. package/dist/topics.d.ts +1 -1
  139. package/dist/transcript.d.ts +1 -1
  140. package/dist/trust-score-stage.d.ts +2 -2
  141. package/dist/trust-score.d.ts +1 -1
  142. package/dist/{types-w3Y232yi.d.ts → types-CglNn2V-.d.ts} +1 -1
  143. package/dist/types.d.ts +1 -1
  144. package/dist/utility-runtime.d.ts +1 -1
  145. package/package.json +2 -2
  146. package/src/orchestration/recall-result-formatter.ts +438 -0
  147. package/src/orchestrator.ts +22 -305
  148. package/dist/chunk-AE2HPUP7.js.map +0 -1
@@ -0,0 +1,438 @@
1
+ /**
2
+ * Recall result formatter — extracted from the orchestrator (issue #1526).
3
+ *
4
+ * Owns the formatting of recall search results into display sections, plus the
5
+ * identity-continuity section assembly:
6
+ * - QMD result formatting (with memory handles + epistemic hedge rendering)
7
+ * - Specialized recall result formatters (objective-state, causal-trajectory,
8
+ * trust-zone, harmonic-retrieval, work-product, verified-episode,
9
+ * verified-semantic-rule)
10
+ * - Identity continuity helpers (summarizeIdentityText, formatOpenIncidentLine,
11
+ * trimIdentitySection) and the full buildIdentityContinuitySection builder
12
+ * - Module-level helpers hasIdentityRecoveryIntent and
13
+ * resolveEffectiveIdentityInjectionMode (only consumed here)
14
+ *
15
+ * Behavior-preserving move from orchestrator.ts. No logic changes — the
16
+ * orchestrator constructs one instance and keeps thin delegating methods so
17
+ * existing call sites and tests that exercise the private API continue to work.
18
+ */
19
+
20
+ import { buildHandleIndexForResults } from "../recall-handles.js";
21
+ import { renderEpistemicHedge } from "../trust-score.js";
22
+ import { resolveIdentityContinuityCapabilities } from "../capabilities.js";
23
+ import type { StorageManager } from "../index.js";
24
+ import type { ObjectiveStateSearchResult } from "../objective-state.js";
25
+ import type { CausalTrajectorySearchResult } from "../causal-trajectory.js";
26
+ import type { TrustZoneSearchResult } from "../trust-zones.js";
27
+ import type { HarmonicRetrievalResult } from "../harmonic-retrieval.js";
28
+ import type { VerifiedEpisodeResult } from "../verified-recall.js";
29
+ import type { VerifiedSemanticRuleResult } from "../semantic-rule-verifier.js";
30
+ import type { WorkProductLedgerSearchResult } from "../work-product-ledger.js";
31
+ import type { TrustStageResultItem } from "../trust-score-stage.js";
32
+ import type {
33
+ ContinuityIncidentRecord,
34
+ IdentityInjectionMode,
35
+ PluginConfig,
36
+ QmdSearchResult,
37
+ RecallPlanMode,
38
+ } from "../types.js";
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // Identity injection mode helpers (moved verbatim from orchestrator.ts)
42
+ // ---------------------------------------------------------------------------
43
+
44
+ export function hasIdentityRecoveryIntent(prompt: string): boolean {
45
+ const text = typeof prompt === "string" ? prompt.toLowerCase() : "";
46
+ if (!text) return false;
47
+ return /\b(identity|continuity|recover(?:y|ing|ed)?|incident|drift|restore|regress(?:ion|ed|ing)?)\b/i.test(
48
+ text,
49
+ );
50
+ }
51
+
52
+ export function resolveEffectiveIdentityInjectionMode(options: {
53
+ configuredMode: IdentityInjectionMode;
54
+ recallMode: RecallPlanMode;
55
+ prompt: string;
56
+ }): { mode: IdentityInjectionMode; shouldInject: boolean } {
57
+ if (
58
+ options.configuredMode === "recovery_only" &&
59
+ !hasIdentityRecoveryIntent(options.prompt)
60
+ ) {
61
+ return { mode: "recovery_only", shouldInject: false };
62
+ }
63
+ if (options.recallMode === "minimal" && options.configuredMode === "full") {
64
+ return { mode: "minimal", shouldInject: true };
65
+ }
66
+ return { mode: options.configuredMode, shouldInject: true };
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Coordinator
71
+ // ---------------------------------------------------------------------------
72
+
73
+ /**
74
+ * Formats recall search results into display sections and assembles the
75
+ * identity-continuity section. Holds a reference to config for feature-gated
76
+ * rendering (memory handles, epistemic hedges, identity injection mode).
77
+ */
78
+ export class RecallResultFormatter {
79
+ constructor(private readonly config: PluginConfig) {}
80
+
81
+ // ── QMD results (memory handles + epistemic hedge) ──────────────────────
82
+
83
+ formatQmdResults(
84
+ title: string,
85
+ results: QmdSearchResult[],
86
+ sessionKey?: string,
87
+ trustByPath?: Map<string, TrustStageResultItem> | null,
88
+ ): string {
89
+ // Issue #1582 — handles are only rendered when a session key is available:
90
+ // resolution requires the handle history to have been recorded for this
91
+ // session, which only happens when sessionKey is present. Rendering handles
92
+ // without recording would show tokens a user can never resolve (cursor
93
+ // review). The rendering logic itself lives in the pure handles module.
94
+ const handleByIndex = buildHandleIndexForResults(
95
+ results,
96
+ this.config.recallMemoryHandles === true && sessionKey != null,
97
+ );
98
+ // Issue #1577 — epistemic hedge. Append a deterministic, component-derived
99
+ // suffix so the downstream model knows each memory's trust status (the
100
+ // cheap lever against confident-stale-answer failures). Gated separately
101
+ // from scoring so rendering can ship after the stage is stable. High-band
102
+ // and neutral items get no suffix (don't waste tokens on the common case).
103
+ const renderHedge = this.config.trustScoreEpistemicRendering && trustByPath !== null && trustByPath !== undefined;
104
+ const hedgeMap = renderHedge ? trustByPath : null;
105
+ const lines = results.map((r, i) => {
106
+ const snippet = r.snippet
107
+ ? r.snippet.slice(0, 500).replace(/\n/g, " ")
108
+ : "(no preview)";
109
+ const source = typeof r.line === "number" ? `${r.path}:${r.line}` : r.path;
110
+ const head = `[${i + 1}] ${source} (score: ${r.score.toFixed(3)})\n${snippet}`;
111
+ const handle = handleByIndex.get(i);
112
+ const hedged = head.replace(/\s+$/, "");
113
+ const withHandle = handle ? `${hedged} ${handle}` : hedged;
114
+ if (hedgeMap) {
115
+ const item = hedgeMap.get(r.path);
116
+ if (item) {
117
+ const hedge = renderEpistemicHedge(item.trust);
118
+ if (hedge.length > 0) return `${withHandle} ${hedge}`;
119
+ }
120
+ }
121
+ return withHandle;
122
+ });
123
+ return `## ${title}\n\n${lines.join("\n\n")}`;
124
+ }
125
+
126
+ // ── Specialized recall result formatters ────────────────────────────────
127
+
128
+ formatObjectiveStateResults(
129
+ results: ObjectiveStateSearchResult[],
130
+ ): string {
131
+ const lines = results.map(({ snapshot }, index) => {
132
+ const parts = [
133
+ snapshot.recordedAt.replace("T", " ").slice(0, 16),
134
+ `${snapshot.kind}/${snapshot.changeKind}`,
135
+ ];
136
+ if (snapshot.outcome) parts.push(snapshot.outcome);
137
+ const header = `[${index + 1}] ${parts.join(" | ")} | ${snapshot.scope}`;
138
+ const detailParts = [snapshot.summary];
139
+ if (snapshot.command) detailParts.push(`command: ${snapshot.command}`);
140
+ else if (snapshot.toolName)
141
+ detailParts.push(`tool: ${snapshot.toolName}`);
142
+ return `${header}\n${detailParts.join(" | ")}`;
143
+ });
144
+ return `## Objective State\n\n${lines.join("\n\n")}`;
145
+ }
146
+
147
+ formatCausalTrajectoryResults(
148
+ results: CausalTrajectorySearchResult[],
149
+ ): string {
150
+ const lines = results.map(({ record, matchedFields }, index) => {
151
+ const header = [
152
+ `[${index + 1}] ${record.recordedAt.replace("T", " ").slice(0, 16)}`,
153
+ record.outcomeKind,
154
+ ].join(" | ");
155
+ const details = [
156
+ `goal: ${record.goal}`,
157
+ `action: ${record.actionSummary}`,
158
+ `observation: ${record.observationSummary}`,
159
+ `outcome: ${record.outcomeSummary}`,
160
+ ];
161
+ if (record.followUpSummary)
162
+ details.push(`follow-up: ${record.followUpSummary}`);
163
+ if (matchedFields.length > 0)
164
+ details.push(`matched: ${matchedFields.join(", ")}`);
165
+ return `${header}\n${details.join("\n")}`;
166
+ });
167
+
168
+ return `## Causal Trajectories\n\n${lines.join("\n\n")}`;
169
+ }
170
+
171
+ formatTrustZoneResults(results: TrustZoneSearchResult[]): string {
172
+ const lines = results.map(({ record, matchedFields }, index) => {
173
+ const header = [
174
+ `[${index + 1}] ${record.recordedAt.replace("T", " ").slice(0, 16)}`,
175
+ record.zone,
176
+ record.kind,
177
+ ].join(" | ");
178
+ const details = [
179
+ record.summary,
180
+ `provenance: ${record.provenance.sourceClass}`,
181
+ ];
182
+ if (record.entityRefs && record.entityRefs.length > 0) {
183
+ details.push(`entities: ${record.entityRefs.join(", ")}`);
184
+ }
185
+ if (record.tags && record.tags.length > 0) {
186
+ details.push(`tags: ${record.tags.join(", ")}`);
187
+ }
188
+ if (matchedFields.length > 0) {
189
+ details.push(`matched: ${matchedFields.join(", ")}`);
190
+ }
191
+ return `${header}\n${details.join("\n")}`;
192
+ });
193
+
194
+ return `## Trust Zones\n\n${lines.join("\n\n")}`;
195
+ }
196
+
197
+ formatHarmonicRetrievalResults(
198
+ results: HarmonicRetrievalResult[],
199
+ ): string {
200
+ const lines = results.map(
201
+ (
202
+ { node, matchedAnchors, matchedFields, nodeScore, anchorScore },
203
+ index,
204
+ ) => {
205
+ const header = [
206
+ `[${index + 1}] ${node.recordedAt.replace("T", " ").slice(0, 16)}`,
207
+ `${node.kind}/${node.abstractionLevel}`,
208
+ node.sessionKey,
209
+ ].join(" | ");
210
+ const details = [
211
+ node.title,
212
+ node.summary,
213
+ `scores: node=${nodeScore.toFixed(1)} anchor=${anchorScore.toFixed(1)}`,
214
+ ];
215
+ if (matchedAnchors.length > 0) {
216
+ details.push(
217
+ `anchors: ${matchedAnchors.map((anchor) => `${anchor.anchorType}:${anchor.anchorValue}`).join("; ")}`,
218
+ );
219
+ }
220
+ if (matchedFields.length > 0) {
221
+ details.push(`matched: ${matchedFields.join(", ")}`);
222
+ }
223
+ return `${header}\n${details.join("\n")}`;
224
+ },
225
+ );
226
+
227
+ return `## Harmonic Retrieval\n\n${lines.join("\n\n")}`;
228
+ }
229
+
230
+ formatWorkProductResults(
231
+ results: WorkProductLedgerSearchResult[],
232
+ ): string {
233
+ const lines = results.map(({ entry, matchedFields }, index) => {
234
+ const header = [
235
+ `[${index + 1}] ${entry.recordedAt.replace("T", " ").slice(0, 16)}`,
236
+ `${entry.kind}/${entry.action}`,
237
+ entry.sessionKey,
238
+ ].join(" | ");
239
+ const details = [entry.summary, `scope: ${entry.scope}`];
240
+ if (entry.artifactPath) details.push(`artifact: ${entry.artifactPath}`);
241
+ if (entry.tags && entry.tags.length > 0)
242
+ details.push(`tags: ${entry.tags.join(", ")}`);
243
+ if (matchedFields.length > 0)
244
+ details.push(`matched: ${matchedFields.join(", ")}`);
245
+ return `${header}\n${details.join("\n")}`;
246
+ });
247
+
248
+ return `## Work Products\n\n${lines.join("\n\n")}`;
249
+ }
250
+
251
+ formatVerifiedEpisodeResults(
252
+ results: VerifiedEpisodeResult[],
253
+ ): string {
254
+ const lines = results.map(
255
+ ({ box, verifiedEpisodeCount, matchedFields }, index) => {
256
+ const header = [
257
+ `[${index + 1}] ${box.sealedAt.replace("T", " ").slice(0, 16)}`,
258
+ box.traceId ? `trace:${box.traceId.slice(0, 12)}` : "trace:none",
259
+ ].join(" | ");
260
+ const details = [
261
+ box.goal ?? `topics: ${box.topics.join(", ")}`,
262
+ `verified episodes: ${verifiedEpisodeCount}`,
263
+ ];
264
+ if (box.toolsUsed && box.toolsUsed.length > 0) {
265
+ details.push(`tools: ${box.toolsUsed.join(", ")}`);
266
+ }
267
+ if (matchedFields.length > 0) {
268
+ details.push(`matched: ${matchedFields.join(", ")}`);
269
+ }
270
+ return `${header}\n${details.join("\n")}`;
271
+ },
272
+ );
273
+
274
+ return `## Verified Episodes\n\n${lines.join("\n\n")}`;
275
+ }
276
+
277
+ formatVerifiedSemanticRuleResults(
278
+ results: VerifiedSemanticRuleResult[],
279
+ ): string {
280
+ const lines = results.map(
281
+ (
282
+ {
283
+ rule,
284
+ sourceMemoryId,
285
+ verificationStatus,
286
+ effectiveConfidence,
287
+ matchedFields,
288
+ },
289
+ index,
290
+ ) => {
291
+ const header = [
292
+ `[${index + 1}] ${rule.frontmatter.updated.replace("T", " ").slice(0, 16)}`,
293
+ verificationStatus,
294
+ `confidence:${effectiveConfidence.toFixed(2)}`,
295
+ ].join(" | ");
296
+ const details = [rule.content, `source memory: ${sourceMemoryId}`];
297
+ if (matchedFields.length > 0) {
298
+ details.push(`matched: ${matchedFields.join(", ")}`);
299
+ }
300
+ return `${header}\n${details.join("\n")}`;
301
+ },
302
+ );
303
+
304
+ return `## Verified Rules\n\n${lines.join("\n\n")}`;
305
+ }
306
+
307
+ // ── Identity continuity helpers ─────────────────────────────────────────
308
+
309
+ summarizeIdentityText(
310
+ raw: string,
311
+ maxLines: number,
312
+ maxChars: number,
313
+ ): string {
314
+ const lines = raw
315
+ .replace(/\r/g, "")
316
+ .split("\n")
317
+ .map((line) => line.trim())
318
+ .filter((line) => line.length > 0 && !line.startsWith("#"));
319
+ const compact = lines.slice(0, Math.max(1, maxLines)).join(" ");
320
+ if (compact.length <= maxChars) return compact;
321
+ return `${compact.slice(0, Math.max(0, maxChars - 1))}…`;
322
+ }
323
+
324
+ formatOpenIncidentLine(
325
+ incident: ContinuityIncidentRecord,
326
+ includeDetails: boolean,
327
+ ): string {
328
+ const base = `[${incident.id}] ${incident.symptom.trim()}`;
329
+ if (!includeDetails) return `- ${base}`;
330
+ const parts = [base];
331
+ if (incident.suspectedCause)
332
+ parts.push(`cause: ${incident.suspectedCause.trim()}`);
333
+ if (incident.triggerWindow)
334
+ parts.push(`window: ${incident.triggerWindow.trim()}`);
335
+ return `- ${parts.join(" | ")}`;
336
+ }
337
+
338
+ trimIdentitySection(
339
+ content: string,
340
+ maxChars: number,
341
+ ): { text: string; truncated: boolean } {
342
+ if (maxChars <= 0) return { text: "", truncated: false };
343
+ if (content.length <= maxChars) return { text: content, truncated: false };
344
+ const suffix = "\n\n...(identity continuity trimmed)";
345
+ if (maxChars <= suffix.length) {
346
+ return { text: content.slice(0, maxChars), truncated: true };
347
+ }
348
+ const headroom = Math.max(0, maxChars - suffix.length);
349
+ return { text: `${content.slice(0, headroom)}${suffix}`, truncated: true };
350
+ }
351
+
352
+ async buildIdentityContinuitySection(options: {
353
+ storage: StorageManager;
354
+ recallMode: RecallPlanMode;
355
+ prompt: string;
356
+ }): Promise<{
357
+ section: string;
358
+ mode: IdentityInjectionMode;
359
+ injectedChars: number;
360
+ truncated: boolean;
361
+ } | null> {
362
+ if (!resolveIdentityContinuityCapabilities(this.config).identityContinuity) return null;
363
+ if (this.config.identityMaxInjectChars <= 0) return null;
364
+
365
+ const resolved = resolveEffectiveIdentityInjectionMode({
366
+ configuredMode: this.config.identityInjectionMode,
367
+ recallMode: options.recallMode,
368
+ prompt: options.prompt,
369
+ });
370
+ if (!resolved.shouldInject) return null;
371
+
372
+ const [anchorRaw, loopsRaw, incidents] = await Promise.all([
373
+ options.storage.readIdentityAnchor(),
374
+ options.storage.readIdentityImprovementLoops(),
375
+ options.storage.readContinuityIncidents(200),
376
+ ]);
377
+ const openIncidents = incidents.filter(
378
+ (incident) => incident.state === "open",
379
+ );
380
+
381
+ const lines: string[] = [];
382
+ if (resolved.mode === "full") {
383
+ lines.push("## Identity Continuity");
384
+ if (anchorRaw && anchorRaw.trim().length > 0) {
385
+ lines.push("", "### Anchor", "", anchorRaw.trim());
386
+ }
387
+ if (loopsRaw && loopsRaw.trim().length > 0) {
388
+ lines.push("", "### Improvement Loops", "", loopsRaw.trim());
389
+ }
390
+ lines.push("", "### Open Incidents", "");
391
+ if (openIncidents.length === 0) {
392
+ lines.push("- none");
393
+ } else {
394
+ lines.push(
395
+ ...openIncidents
396
+ .slice(0, 5)
397
+ .map((incident) => this.formatOpenIncidentLine(incident, true)),
398
+ );
399
+ }
400
+ } else {
401
+ const anchorSummary = anchorRaw
402
+ ? this.summarizeIdentityText(anchorRaw, 3, 320)
403
+ : "";
404
+ const loopsSummary = loopsRaw
405
+ ? this.summarizeIdentityText(loopsRaw, 2, 240)
406
+ : "";
407
+ lines.push("## Identity Continuity Signals", "");
408
+ if (anchorSummary) lines.push(`- anchor: ${anchorSummary}`);
409
+ if (loopsSummary) lines.push(`- loops: ${loopsSummary}`);
410
+ if (openIncidents.length === 0) {
411
+ lines.push("- incidents: 0 open");
412
+ } else {
413
+ lines.push(`- incidents: ${openIncidents.length} open`);
414
+ lines.push(
415
+ ...openIncidents
416
+ .slice(0, 2)
417
+ .map((incident) => this.formatOpenIncidentLine(incident, false)),
418
+ );
419
+ }
420
+ }
421
+
422
+ const body = lines.join("\n").trim();
423
+ if (!body) return null;
424
+
425
+ const { text, truncated } = this.trimIdentitySection(
426
+ body,
427
+ this.config.identityMaxInjectChars,
428
+ );
429
+ if (!text) return null;
430
+
431
+ return {
432
+ section: text,
433
+ mode: resolved.mode,
434
+ injectedChars: text.length,
435
+ truncated,
436
+ };
437
+ }
438
+ }