@remnic/core 9.3.653 → 9.3.654
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-cli.js +17 -17
- package/dist/access-http.d.ts +4 -4
- package/dist/access-http.js +10 -10
- package/dist/access-mcp.d.ts +4 -4
- package/dist/access-mcp.js +9 -9
- package/dist/access-schema.d.ts +12 -12
- package/dist/{access-service-CdJFd3_b.d.ts → access-service-C8A5hoXJ.d.ts} +11 -2
- package/dist/access-service.d.ts +4 -4
- package/dist/access-service.js +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 +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +3 -3
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +3 -3
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/calibration.d.ts +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +4 -4
- package/dist/{chunk-KJDKZVF3.js → chunk-2DSTAWNZ.js} +3 -3
- package/dist/chunk-3RACUBII.js +212 -0
- package/dist/chunk-3RACUBII.js.map +1 -0
- package/dist/{chunk-Y7NWBBHV.js → chunk-6CVI6BP6.js} +2 -2
- package/dist/{chunk-R3PQUPQ4.js → chunk-6IMKOIZ6.js} +85 -3
- package/dist/chunk-6IMKOIZ6.js.map +1 -0
- package/dist/{chunk-WTI35CVJ.js → chunk-BJA6DQOC.js} +5 -5
- package/dist/{chunk-GI45G4BK.js → chunk-BP2EV6W5.js} +3 -3
- package/dist/{chunk-WLGE6KEO.js → chunk-DBM2BD22.js} +3 -3
- package/dist/{chunk-IENGGY2C.js → chunk-ENV6RDTD.js} +2 -2
- package/dist/{chunk-BEMWL2FZ.js → chunk-FVRBLJP6.js} +2 -2
- package/dist/{chunk-H3PHZLMF.js → chunk-GKKAXVAJ.js} +20 -11
- package/dist/chunk-GKKAXVAJ.js.map +1 -0
- package/dist/{chunk-NOBL7OUP.js → chunk-GPW2E4LN.js} +12 -5
- package/dist/{chunk-NOBL7OUP.js.map → chunk-GPW2E4LN.js.map} +1 -1
- package/dist/{chunk-KWM33SPU.js → chunk-JMQSYGXS.js} +2 -2
- package/dist/{chunk-QQHIQ7JD.js → chunk-JYN7QNTA.js} +87 -18
- package/dist/chunk-JYN7QNTA.js.map +1 -0
- package/dist/{chunk-AJE7FJVE.js → chunk-K6X553JB.js} +2 -2
- package/dist/{chunk-E3J6O6N7.js → chunk-LJCEWTG3.js} +19 -8
- package/dist/{chunk-E3J6O6N7.js.map → chunk-LJCEWTG3.js.map} +1 -1
- package/dist/{chunk-EW52H5EM.js → chunk-NAZWHTYV.js} +12 -5
- package/dist/chunk-NAZWHTYV.js.map +1 -0
- package/dist/{chunk-XMN6MMTU.js → chunk-NCGWXCSW.js} +2 -2
- package/dist/{chunk-C43KEWEV.js → chunk-NE2JBMLN.js} +1 -1
- package/dist/chunk-NE2JBMLN.js.map +1 -0
- package/dist/{chunk-SPMZZUEJ.js → chunk-OL2364SB.js} +2020 -368
- package/dist/chunk-OL2364SB.js.map +1 -0
- package/dist/{chunk-JF7SFXTG.js → chunk-QKK64Z6M.js} +2 -2
- package/dist/{chunk-IVYSVAC6.js → chunk-QW6JZO5P.js} +2 -2
- package/dist/{chunk-EHQLDFSH.js → chunk-RGPUQ66K.js} +2 -2
- package/dist/{chunk-CFOCZPIQ.js → chunk-T2C6QJG2.js} +2 -2
- package/dist/{chunk-V4UDXYGG.js → chunk-XWQ6ERUG.js} +2 -2
- package/dist/{chunk-BNFRL6QW.js → chunk-Y2RIIF6H.js} +2 -2
- package/dist/{chunk-C63WC454.js → chunk-YLZLPVKK.js} +22 -1
- package/dist/chunk-YLZLPVKK.js.map +1 -0
- package/dist/{chunk-RZOBQ23O.js → chunk-Z5MQI7K2.js} +2 -2
- package/dist/{chunk-PRQXUSQV.js → chunk-ZCORQM74.js} +2 -2
- package/dist/{cli-DDo7Qgs-.d.ts → cli-uQgvDFNE.d.ts} +3 -3
- package/dist/cli.d.ts +5 -5
- package/dist/cli.js +22 -22
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +3 -3
- 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 +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +3 -3
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +3 -3
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +19 -1
- package/dist/contradiction/index.js +1 -1
- 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 +1 -1
- package/dist/entity-retrieval.js +3 -3
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +3 -3
- package/dist/explicit-capture.js +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/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +30 -28
- package/dist/index.js.map +1 -1
- 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/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +3 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
- package/dist/maintenance/rebuild-memory-projection.js +4 -4
- package/dist/mcp-memory-inspector-app.d.ts +4 -4
- 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 +1 -1
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +1 -1
- package/dist/namespaces/migrate.js +4 -4
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +52 -3
- package/dist/namespaces/storage.js +9 -5
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +7 -7
- package/dist/{orchestrator-8fTZsa0y.d.ts → orchestrator-B4Y4sWQH.d.ts} +503 -3
- package/dist/orchestrator.d.ts +3 -3
- package/dist/orchestrator.js +13 -13
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.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-explain-renderer.js +3 -3
- 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-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/{resolution-3SAP4SH2.js → resolution-IDTEBJFS.js} +2 -2
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +2 -2
- package/dist/retrieval-agents.d.ts +1 -1
- 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/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-DKdYzQOg.d.ts → semantic-consolidation-BKd0Pype.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +4 -4
- package/dist/semantic-rule-promotion.js +3 -3
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +3 -3
- 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 +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +2 -2
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.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 +1 -1
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/{types-D8yUmSik.d.ts → types-BgChEr0M.d.ts} +11 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +3 -3
- package/package.json +1 -1
- package/src/access-http.ts +7 -0
- package/src/access-mcp.ts +7 -0
- package/src/access-service.ts +12 -0
- package/src/cli.ts +104 -0
- package/src/config.test.ts +40 -0
- package/src/config.ts +29 -0
- package/src/contradiction/contradiction.test.ts +284 -0
- package/src/contradiction/resolution.ts +151 -4
- package/src/explicit-capture.ts +31 -10
- package/src/index.ts +10 -0
- package/src/namespaces/catalog.test.ts +3356 -0
- package/src/namespaces/catalog.ts +2123 -0
- package/src/namespaces/storage.ts +210 -30
- package/src/orchestrator-flush.test.ts +300 -0
- package/src/orchestrator.ts +851 -240
- package/src/types.ts +11 -0
- package/dist/chunk-C43KEWEV.js.map +0 -1
- package/dist/chunk-C63WC454.js.map +0 -1
- package/dist/chunk-EW52H5EM.js.map +0 -1
- package/dist/chunk-H3PHZLMF.js.map +0 -1
- package/dist/chunk-ORGWWNJG.js +0 -131
- package/dist/chunk-ORGWWNJG.js.map +0 -1
- package/dist/chunk-QQHIQ7JD.js.map +0 -1
- package/dist/chunk-R3PQUPQ4.js.map +0 -1
- package/dist/chunk-SPMZZUEJ.js.map +0 -1
- /package/dist/{chunk-KJDKZVF3.js.map → chunk-2DSTAWNZ.js.map} +0 -0
- /package/dist/{chunk-Y7NWBBHV.js.map → chunk-6CVI6BP6.js.map} +0 -0
- /package/dist/{chunk-WTI35CVJ.js.map → chunk-BJA6DQOC.js.map} +0 -0
- /package/dist/{chunk-GI45G4BK.js.map → chunk-BP2EV6W5.js.map} +0 -0
- /package/dist/{chunk-WLGE6KEO.js.map → chunk-DBM2BD22.js.map} +0 -0
- /package/dist/{chunk-IENGGY2C.js.map → chunk-ENV6RDTD.js.map} +0 -0
- /package/dist/{chunk-BEMWL2FZ.js.map → chunk-FVRBLJP6.js.map} +0 -0
- /package/dist/{chunk-KWM33SPU.js.map → chunk-JMQSYGXS.js.map} +0 -0
- /package/dist/{chunk-AJE7FJVE.js.map → chunk-K6X553JB.js.map} +0 -0
- /package/dist/{chunk-XMN6MMTU.js.map → chunk-NCGWXCSW.js.map} +0 -0
- /package/dist/{chunk-JF7SFXTG.js.map → chunk-QKK64Z6M.js.map} +0 -0
- /package/dist/{chunk-IVYSVAC6.js.map → chunk-QW6JZO5P.js.map} +0 -0
- /package/dist/{chunk-EHQLDFSH.js.map → chunk-RGPUQ66K.js.map} +0 -0
- /package/dist/{chunk-CFOCZPIQ.js.map → chunk-T2C6QJG2.js.map} +0 -0
- /package/dist/{chunk-V4UDXYGG.js.map → chunk-XWQ6ERUG.js.map} +0 -0
- /package/dist/{chunk-BNFRL6QW.js.map → chunk-Y2RIIF6H.js.map} +0 -0
- /package/dist/{chunk-RZOBQ23O.js.map → chunk-Z5MQI7K2.js.map} +0 -0
- /package/dist/{chunk-PRQXUSQV.js.map → chunk-ZCORQM74.js.map} +0 -0
- /package/dist/{resolution-3SAP4SH2.js.map → resolution-IDTEBJFS.js.map} +0 -0
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import './types-ByK7T3L6.js';
|
|
2
2
|
import './index-DJ9QWMw-.js';
|
|
3
|
-
export { y as AccessTrackingEntry, A as ActiveRecallChatType, d as ActiveRecallModelFallbackPolicy, b as ActiveRecallPromptStyle, a as ActiveRecallQueryMode, c as ActiveRecallThinking, a7 as AgentAccessAuthToken, b2 as AgentAccessHttpConfig, b3 as AgentDefaultsConfig, b4 as AgentPersona, f as AgentPersonaModelConfig, b5 as BehaviorLoopAdjustment, e as BehaviorLoopPolicyState, b6 as BehaviorSignalDirection, B as BehaviorSignalEvent, b7 as BehaviorSignalType, aH as BootstrapOptions, aI as BootstrapResult, V as BriefingActiveThread, b8 as BriefingCalendarSourceError, aO as BriefingConfig, Q as BriefingFocus, O as BriefingFollowup, aP as BriefingOpenCommitment, X as BriefingRecentEntity, W as BriefingResult, N as BriefingSections, aQ as BriefingWindow, b9 as BufferEntryState, k as BufferState, o as BufferSurpriseEvent, Y as BufferTurn, U as CalendarEvent, S as CalendarSource, ba as CaptureMode, $ as Checkpoint, bb as CodexCliReasoningEffort, bc as CodexCompactionFlushMode, aR as CodexCompatConfig, bd as CodexConnectorConfig, aG as CodingContext, aL as CodingModeConfig, be as CompressionGuidelineActivationState, a9 as CompressionGuidelineOptimizerActionSummary, a8 as CompressionGuidelineOptimizerEventCounts, aa as CompressionGuidelineOptimizerRuleUpdate, bf as CompressionGuidelineOptimizerSourceWindow, C as CompressionGuidelineOptimizerState, bg as ConfidenceTier, bh as ConsolidationAction, bi as ConsolidationItem, aJ as ConsolidationObservation, ag as ConsolidationResult, s as ContinuityImprovementLoop, r as ContinuityIncidentCloseInput, p as ContinuityIncidentOpenInput, q as ContinuityIncidentRecord, bj as ContinuityIncidentState, bk as ContinuityLoopCadence, u as ContinuityLoopReviewInput, bl as ContinuityLoopStatus, t as ContinuityLoopUpsertInput, bm as ContradictionScanConfig, as as ConversationThread, bn as CronConversationRecallMode, bo as CronRecallMode, aS as DEFAULT_RECALL_DISCLOSURE, ah as DaySummaryResult, bp as DreamingConfig, bq as DreamingNarrativePromptStyle, br as DreamsDeepSleepConfig, bs as DreamsLightSleepConfig, au as DreamsPhase, bt as DreamsPhaseStatus, bu as DreamsPhasesConfig, bv as DreamsRemConfig, av as DreamsRunResult, at as DreamsStatusResult, bw as EngramTraceEvent, w as EntityActivityEntry, x as EntityFile, ac as EntityMention, v as EntityRelationship, ad as EntitySchemaDefinition, ae as EntitySchemaSectionDefinition, E as EntityStructuredSection, aK as EntityTimelineEntry, aT as ExtractedFact, bx as ExtractedProcedureStep, by as ExtractedQuestion, bz as ExtractedReasoningTrace, bA as ExtractedReasoningTraceStep, bB as ExtractedRelationship, bC as ExtractionPassSource, af as ExtractionResult, F as FileHygieneConfig, G as GatewayConfig, bD as GitHubLiveConnectorConfig, bE as GmailLiveConnectorConfig, bF as GoogleDriveLiveConnectorConfig, bG as HeartbeatConfig, bH as HeartbeatDetectionMode, a0 as HourlySummary, J as IdentityInjectionMode, a5 as ImportanceLevel, I as ImportanceScore, a2 as LifecycleState, L as LiveConnectorsConfig, bI as LlmTraceCallback, bJ as LlmTraceEvent, ak as MemoryActionEligibilityContext, aU as MemoryActionEligibilitySource, m as MemoryActionEvent, bK as MemoryActionOutcome, bL as MemoryActionPolicyDecision, al as MemoryActionPolicyResult, bM as MemoryActionStatus, ab as MemoryActionType, M as MemoryCategory, g as MemoryFile, j as MemoryFrontmatter, ai as MemoryIntent, n as MemoryLifecycleEvent, am as MemoryLifecycleEventType, an as MemoryLifecycleStateSummary, i as MemoryLink, bN as MemoryLinkType, aV as MemoryObservation, bO as MemoryOsPresetName, z as MemoryProjectionCurrentState, aW as MemoryScope, h as MemoryStatus, D as MemorySummary, l as MetaState, bP as ModelApi, bQ as ModelDefinitionConfig, bR as ModelProviderAuthMode, ao as ModelProviderConfig, bS as NamespacePolicy, ap as NativeKnowledgeConfig, bT as NativeKnowledgeFolderRuleConfig, bU as NativeKnowledgeObsidianVaultConfig, bV as NativeKnowledgeOpenClawWorkspaceConfig, bW as NotionLiveConnectorConfig, P as PluginConfig, a4 as PolicyClass, bX as PrincipalFromSessionKeyMode, bY as PrincipalRule, bZ as ProceduralConfig, aj as QmdSearchExplain, Z as QmdSearchResult, b_ as QuestionEntry, aX as RECALL_DISCLOSURE_LEVELS, b$ as ReasoningEffort, K as RecallDisclosure, c0 as RecallPipelineConfig, H as RecallPlanMode, c1 as RecallSectionConfig, R as RecallTierExplain, c2 as RecallTraceEvent, c3 as RelevanceFeedback, aq as RetrievalTier, c4 as SPECULATIVE_TTL_DAYS, c5 as ScoredEntity, a6 as SecretRef, c6 as SemanticChunkingConfigShape, a1 as SessionObserverBandConfig, c7 as SignalLevel, ar as SignalScanResult, c8 as SlotBehaviorConfig, c9 as SlotMismatchMode, T as TopicScore, _ as TranscriptEntry, ca as TriggerMode, a3 as VerificationState, cb as confidenceTier, b1 as isRecallDisclosure } from './types-
|
|
3
|
+
export { y as AccessTrackingEntry, A as ActiveRecallChatType, d as ActiveRecallModelFallbackPolicy, b as ActiveRecallPromptStyle, a as ActiveRecallQueryMode, c as ActiveRecallThinking, a7 as AgentAccessAuthToken, b2 as AgentAccessHttpConfig, b3 as AgentDefaultsConfig, b4 as AgentPersona, f as AgentPersonaModelConfig, b5 as BehaviorLoopAdjustment, e as BehaviorLoopPolicyState, b6 as BehaviorSignalDirection, B as BehaviorSignalEvent, b7 as BehaviorSignalType, aH as BootstrapOptions, aI as BootstrapResult, V as BriefingActiveThread, b8 as BriefingCalendarSourceError, aO as BriefingConfig, Q as BriefingFocus, O as BriefingFollowup, aP as BriefingOpenCommitment, X as BriefingRecentEntity, W as BriefingResult, N as BriefingSections, aQ as BriefingWindow, b9 as BufferEntryState, k as BufferState, o as BufferSurpriseEvent, Y as BufferTurn, U as CalendarEvent, S as CalendarSource, ba as CaptureMode, $ as Checkpoint, bb as CodexCliReasoningEffort, bc as CodexCompactionFlushMode, aR as CodexCompatConfig, bd as CodexConnectorConfig, aG as CodingContext, aL as CodingModeConfig, be as CompressionGuidelineActivationState, a9 as CompressionGuidelineOptimizerActionSummary, a8 as CompressionGuidelineOptimizerEventCounts, aa as CompressionGuidelineOptimizerRuleUpdate, bf as CompressionGuidelineOptimizerSourceWindow, C as CompressionGuidelineOptimizerState, bg as ConfidenceTier, bh as ConsolidationAction, bi as ConsolidationItem, aJ as ConsolidationObservation, ag as ConsolidationResult, s as ContinuityImprovementLoop, r as ContinuityIncidentCloseInput, p as ContinuityIncidentOpenInput, q as ContinuityIncidentRecord, bj as ContinuityIncidentState, bk as ContinuityLoopCadence, u as ContinuityLoopReviewInput, bl as ContinuityLoopStatus, t as ContinuityLoopUpsertInput, bm as ContradictionScanConfig, as as ConversationThread, bn as CronConversationRecallMode, bo as CronRecallMode, aS as DEFAULT_RECALL_DISCLOSURE, ah as DaySummaryResult, bp as DreamingConfig, bq as DreamingNarrativePromptStyle, br as DreamsDeepSleepConfig, bs as DreamsLightSleepConfig, au as DreamsPhase, bt as DreamsPhaseStatus, bu as DreamsPhasesConfig, bv as DreamsRemConfig, av as DreamsRunResult, at as DreamsStatusResult, bw as EngramTraceEvent, w as EntityActivityEntry, x as EntityFile, ac as EntityMention, v as EntityRelationship, ad as EntitySchemaDefinition, ae as EntitySchemaSectionDefinition, E as EntityStructuredSection, aK as EntityTimelineEntry, aT as ExtractedFact, bx as ExtractedProcedureStep, by as ExtractedQuestion, bz as ExtractedReasoningTrace, bA as ExtractedReasoningTraceStep, bB as ExtractedRelationship, bC as ExtractionPassSource, af as ExtractionResult, F as FileHygieneConfig, G as GatewayConfig, bD as GitHubLiveConnectorConfig, bE as GmailLiveConnectorConfig, bF as GoogleDriveLiveConnectorConfig, bG as HeartbeatConfig, bH as HeartbeatDetectionMode, a0 as HourlySummary, J as IdentityInjectionMode, a5 as ImportanceLevel, I as ImportanceScore, a2 as LifecycleState, L as LiveConnectorsConfig, bI as LlmTraceCallback, bJ as LlmTraceEvent, ak as MemoryActionEligibilityContext, aU as MemoryActionEligibilitySource, m as MemoryActionEvent, bK as MemoryActionOutcome, bL as MemoryActionPolicyDecision, al as MemoryActionPolicyResult, bM as MemoryActionStatus, ab as MemoryActionType, M as MemoryCategory, g as MemoryFile, j as MemoryFrontmatter, ai as MemoryIntent, n as MemoryLifecycleEvent, am as MemoryLifecycleEventType, an as MemoryLifecycleStateSummary, i as MemoryLink, bN as MemoryLinkType, aV as MemoryObservation, bO as MemoryOsPresetName, z as MemoryProjectionCurrentState, aW as MemoryScope, h as MemoryStatus, D as MemorySummary, l as MetaState, bP as ModelApi, bQ as ModelDefinitionConfig, bR as ModelProviderAuthMode, ao as ModelProviderConfig, bS as NamespacePolicy, ap as NativeKnowledgeConfig, bT as NativeKnowledgeFolderRuleConfig, bU as NativeKnowledgeObsidianVaultConfig, bV as NativeKnowledgeOpenClawWorkspaceConfig, bW as NotionLiveConnectorConfig, P as PluginConfig, a4 as PolicyClass, bX as PrincipalFromSessionKeyMode, bY as PrincipalRule, bZ as ProceduralConfig, aj as QmdSearchExplain, Z as QmdSearchResult, b_ as QuestionEntry, aX as RECALL_DISCLOSURE_LEVELS, b$ as ReasoningEffort, K as RecallDisclosure, c0 as RecallPipelineConfig, H as RecallPlanMode, c1 as RecallSectionConfig, R as RecallTierExplain, c2 as RecallTraceEvent, c3 as RelevanceFeedback, aq as RetrievalTier, c4 as SPECULATIVE_TTL_DAYS, c5 as ScoredEntity, a6 as SecretRef, c6 as SemanticChunkingConfigShape, a1 as SessionObserverBandConfig, c7 as SignalLevel, ar as SignalScanResult, c8 as SlotBehaviorConfig, c9 as SlotMismatchMode, T as TopicScore, _ as TranscriptEntry, ca as TriggerMode, a3 as VerificationState, cb as confidenceTier, b1 as isRecallDisclosure } from './types-BgChEr0M.js';
|
package/dist/types.js
CHANGED
package/dist/verified-recall.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
searchVerifiedEpisodes
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QKK64Z6M.js";
|
|
4
4
|
import "./chunk-HQ6NIBL6.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-6CVI6BP6.js";
|
|
6
6
|
import "./chunk-M7XQSUBB.js";
|
|
7
7
|
import "./chunk-5UZXUTVO.js";
|
|
8
8
|
import "./chunk-J6A3CX5N.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-NE2JBMLN.js";
|
|
10
10
|
import "./chunk-CPPS65WS.js";
|
|
11
11
|
import "./chunk-RULE4VG5.js";
|
|
12
12
|
import "./chunk-SCU65EZI.js";
|
package/package.json
CHANGED
package/src/access-http.ts
CHANGED
|
@@ -1692,6 +1692,13 @@ export class EngramAccessHttpServer {
|
|
|
1692
1692
|
const resolved = await this.service.getWritableStorageForNamespace(namespace, principal);
|
|
1693
1693
|
return resolved.storage;
|
|
1694
1694
|
},
|
|
1695
|
+
// Catalog write touch (issue #1499 sweep): a contradiction merge writes a
|
|
1696
|
+
// new memory directly to the resolved namespace storage, bypassing the
|
|
1697
|
+
// extraction write path. Record it so QMD maintenance / writtenSince
|
|
1698
|
+
// don't miss the write. Best-effort and failure-tolerant.
|
|
1699
|
+
onMergedMemoryWritten: (namespace, storageDir) => {
|
|
1700
|
+
this.service.recordCatalogWrite(namespace, storageDir);
|
|
1701
|
+
},
|
|
1695
1702
|
});
|
|
1696
1703
|
this.respondJson(res, 200, result);
|
|
1697
1704
|
return;
|
package/src/access-mcp.ts
CHANGED
|
@@ -3220,6 +3220,13 @@ export class EngramMcpServer {
|
|
|
3220
3220
|
const resolved = await this.service.getWritableStorageForNamespace(namespace, effectivePrincipal);
|
|
3221
3221
|
return resolved.storage;
|
|
3222
3222
|
},
|
|
3223
|
+
// Catalog write touch (issue #1499 sweep): a contradiction merge writes
|
|
3224
|
+
// a new memory directly to the resolved namespace storage, bypassing the
|
|
3225
|
+
// extraction write path. Record it so QMD maintenance / writtenSince
|
|
3226
|
+
// don't miss the write. Best-effort and failure-tolerant.
|
|
3227
|
+
onMergedMemoryWritten: (namespace, storageDir) => {
|
|
3228
|
+
this.service.recordCatalogWrite(namespace, storageDir);
|
|
3229
|
+
},
|
|
3223
3230
|
});
|
|
3224
3231
|
}
|
|
3225
3232
|
case "engram.contradiction_scan_run":
|
package/src/access-service.ts
CHANGED
|
@@ -7348,6 +7348,18 @@ export class EngramAccessService {
|
|
|
7348
7348
|
return this.orchestrator.storage;
|
|
7349
7349
|
}
|
|
7350
7350
|
|
|
7351
|
+
/**
|
|
7352
|
+
* Best-effort catalog write touch delegate (issue #1499 sweep). HTTP/MCP
|
|
7353
|
+
* surfaces resolve a per-namespace storage and write through it directly (e.g.
|
|
7354
|
+
* a contradiction merge), bypassing the extraction write path that owns
|
|
7355
|
+
* `markCatalogWrite`. They call this to record the write so a (possibly
|
|
7356
|
+
* dynamic) namespace's `lastWriteAt` stays accurate and QMD maintenance does
|
|
7357
|
+
* not miss it. Fire-and-forget and failure-tolerant on the orchestrator side.
|
|
7358
|
+
*/
|
|
7359
|
+
recordCatalogWrite(namespace?: string, storageDir?: string): void {
|
|
7360
|
+
this.orchestrator.recordCatalogWrite(namespace, storageDir);
|
|
7361
|
+
}
|
|
7362
|
+
|
|
7351
7363
|
get configRef(): PluginConfig {
|
|
7352
7364
|
return this.orchestrator.config;
|
|
7353
7365
|
}
|
package/src/cli.ts
CHANGED
|
@@ -4155,6 +4155,103 @@ export function registerCli(
|
|
|
4155
4155
|
console.log("\nOK");
|
|
4156
4156
|
});
|
|
4157
4157
|
|
|
4158
|
+
namespacesCmd
|
|
4159
|
+
.command("rebuild")
|
|
4160
|
+
.description(
|
|
4161
|
+
"Rebuild the namespace catalog from disk (issue #1499). Scans configured + dynamic namespace roots safely and rewrites <memoryDir>/state/namespaces.jsonl",
|
|
4162
|
+
)
|
|
4163
|
+
.option("--dry-run", "Show the rebuilt catalog without writing it")
|
|
4164
|
+
.option("--apply", "Write the rebuilt catalog to disk")
|
|
4165
|
+
.option("--json", "Emit the rebuild result as JSON")
|
|
4166
|
+
.action(async (optionsRaw: unknown) => {
|
|
4167
|
+
const options =
|
|
4168
|
+
optionsRaw && typeof optionsRaw === "object"
|
|
4169
|
+
? (optionsRaw as { dryRun?: boolean; apply?: boolean; json?: boolean })
|
|
4170
|
+
: {};
|
|
4171
|
+
// Default to a non-destructive dry run unless --apply is given.
|
|
4172
|
+
// Reject contradictory flags rather than silently choosing one.
|
|
4173
|
+
if (options.dryRun === true && options.apply === true) {
|
|
4174
|
+
throw new Error("Pass only one of --dry-run or --apply, not both.");
|
|
4175
|
+
}
|
|
4176
|
+
const dryRun = options.apply !== true;
|
|
4177
|
+
|
|
4178
|
+
if (!orchestrator.namespaceCatalog.enabled) {
|
|
4179
|
+
const note =
|
|
4180
|
+
"Namespace catalog is inert: it only runs when namespacesEnabled is true and namespaceCatalogEnabled is not false.";
|
|
4181
|
+
if (options.json === true) {
|
|
4182
|
+
// Include `applied: false` so the inert JSON payload carries the SAME
|
|
4183
|
+
// shape as the enabled path (cursor Low — NFDBM). An inert catalog
|
|
4184
|
+
// rewrote nothing, so a `--apply` did NOT apply; automation that keys
|
|
4185
|
+
// on `applied` must not misread a missing field as a completed rebuild.
|
|
4186
|
+
console.log(
|
|
4187
|
+
JSON.stringify(
|
|
4188
|
+
{ dryRun, enabled: false, applied: false, records: [], skipped: [], note },
|
|
4189
|
+
null,
|
|
4190
|
+
2,
|
|
4191
|
+
),
|
|
4192
|
+
);
|
|
4193
|
+
} else {
|
|
4194
|
+
console.log(note);
|
|
4195
|
+
}
|
|
4196
|
+
// Exit-code parity with the enabled path (cursor Medium — NFb5W): an
|
|
4197
|
+
// inert catalog rewrote NOTHING, so a `--apply` (dryRun=false) did NOT
|
|
4198
|
+
// apply. The enabled path sets `exitCode = 1` whenever a non-dry-run
|
|
4199
|
+
// rebuild reports `applied: false`; an inert `--apply` is exactly that
|
|
4200
|
+
// case, so exit-code-only automation must see a non-zero exit rather
|
|
4201
|
+
// than read a no-op apply as a completed rebuild. A `--dry-run` (or the
|
|
4202
|
+
// default dry run) inert call stays exit 0 — it never promised to write.
|
|
4203
|
+
if (!dryRun) {
|
|
4204
|
+
process.exitCode = 1;
|
|
4205
|
+
}
|
|
4206
|
+
return;
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4209
|
+
const result = await orchestrator.namespaceCatalog.rebuildFromDisk({ dryRun });
|
|
4210
|
+
|
|
4211
|
+
if (options.json === true) {
|
|
4212
|
+
console.log(JSON.stringify({ enabled: true, ...result }, null, 2));
|
|
4213
|
+
// Mirror the non-JSON failure signal (round 6, codex P2 — NEFoa): an
|
|
4214
|
+
// `--apply` that could not acquire the lock returns `applied: false`
|
|
4215
|
+
// and rewrote nothing, so automation must see a non-zero exit even in
|
|
4216
|
+
// JSON mode rather than treating a no-op apply as a successful rebuild.
|
|
4217
|
+
if (!dryRun && result.applied === false) {
|
|
4218
|
+
process.exitCode = 1;
|
|
4219
|
+
}
|
|
4220
|
+
return;
|
|
4221
|
+
}
|
|
4222
|
+
|
|
4223
|
+
console.log("=== Namespace Catalog Rebuild ===\n");
|
|
4224
|
+
console.log(`mode: ${dryRun ? "dry-run" : "apply"}`);
|
|
4225
|
+
console.log(`namespaces: ${result.records.length}`);
|
|
4226
|
+
for (const record of result.records) {
|
|
4227
|
+
console.log(
|
|
4228
|
+
`${record.namespace}\n kind: ${record.kind}\n storage: ${record.storageDir}\n discovered-by: ${record.discoveredBy}`,
|
|
4229
|
+
);
|
|
4230
|
+
}
|
|
4231
|
+
|
|
4232
|
+
if (result.skipped.length > 0) {
|
|
4233
|
+
console.log("\nSkipped / ambiguous roots:");
|
|
4234
|
+
for (const skip of result.skipped) {
|
|
4235
|
+
console.log(`- ${skip.token} (${skip.reason})${skip.detail ? `: ${skip.detail}` : ""}`);
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
|
|
4239
|
+
if (dryRun) {
|
|
4240
|
+
console.log("\nDRY RUN");
|
|
4241
|
+
} else if (result.applied) {
|
|
4242
|
+
console.log("\nOK");
|
|
4243
|
+
} else {
|
|
4244
|
+
// An --apply that could not acquire the cross-process rebuild lock
|
|
4245
|
+
// ran compute-only and did NOT rewrite the catalog (NBn3n/NBsGG).
|
|
4246
|
+
// Report the non-mutation explicitly and exit non-zero so scripts
|
|
4247
|
+
// and operators retry rather than assume the rebuild persisted.
|
|
4248
|
+
console.log(
|
|
4249
|
+
"\nNOT APPLIED: another rebuild holds the lock; the catalog was NOT rewritten. Retry shortly.",
|
|
4250
|
+
);
|
|
4251
|
+
process.exitCode = 1;
|
|
4252
|
+
}
|
|
4253
|
+
});
|
|
4254
|
+
|
|
4158
4255
|
cmd
|
|
4159
4256
|
.command("export")
|
|
4160
4257
|
.description("Export Remnic memory to JSON, Markdown bundle, or SQLite")
|
|
@@ -8858,6 +8955,13 @@ export function registerCli(
|
|
|
8858
8955
|
}
|
|
8859
8956
|
return orchestrator.getStorageForNamespace(requested || orchestrator.config.defaultNamespace);
|
|
8860
8957
|
},
|
|
8958
|
+
// Catalog write touch (issue #1499 sweep): a contradiction merge can
|
|
8959
|
+
// write a new memory to a dynamic namespace via the CLI resolve path,
|
|
8960
|
+
// bypassing the extraction write path. Record it so QMD maintenance /
|
|
8961
|
+
// writtenSince don't miss the write. Best-effort and failure-tolerant.
|
|
8962
|
+
onMergedMemoryWritten: (namespace, storageDir) => {
|
|
8963
|
+
orchestrator.recordCatalogWrite(namespace, storageDir);
|
|
8964
|
+
},
|
|
8861
8965
|
});
|
|
8862
8966
|
console.log(result.message);
|
|
8863
8967
|
if (result.affectedIds.length > 0) {
|
package/src/config.test.ts
CHANGED
|
@@ -1291,3 +1291,43 @@ test("parseConfig rejects connectors.gmail.pollIntervalMs above maximum (25h)",
|
|
|
1291
1291
|
/pollIntervalMs/,
|
|
1292
1292
|
);
|
|
1293
1293
|
});
|
|
1294
|
+
|
|
1295
|
+
// ── issue #1499 / CLAUDE.md rule #36: namespaceCatalogEnabled opt-out coercion.
|
|
1296
|
+
// A string "false"/"0"/"no"/"off" from CLI/env/JSON config must disable the
|
|
1297
|
+
// catalog, not stay truthy. Defaults to enabled when absent/unrecognized.
|
|
1298
|
+
test("parseConfig namespaceCatalogEnabled defaults to true when absent", () => {
|
|
1299
|
+
assert.equal(parseConfig({}).namespaceCatalogEnabled, true);
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
test('parseConfig namespaceCatalogEnabled="false" (string) → false (rule #36)', () => {
|
|
1303
|
+
assert.equal(parseConfig({ namespaceCatalogEnabled: "false" }).namespaceCatalogEnabled, false);
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
test('parseConfig namespaceCatalogEnabled="0" (string) → false', () => {
|
|
1307
|
+
assert.equal(parseConfig({ namespaceCatalogEnabled: "0" }).namespaceCatalogEnabled, false);
|
|
1308
|
+
});
|
|
1309
|
+
|
|
1310
|
+
test('parseConfig namespaceCatalogEnabled="no"/"off" (strings) → false', () => {
|
|
1311
|
+
assert.equal(parseConfig({ namespaceCatalogEnabled: "no" }).namespaceCatalogEnabled, false);
|
|
1312
|
+
assert.equal(parseConfig({ namespaceCatalogEnabled: "off" }).namespaceCatalogEnabled, false);
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
test("parseConfig namespaceCatalogEnabled=false (boolean) → false", () => {
|
|
1316
|
+
assert.equal(parseConfig({ namespaceCatalogEnabled: false }).namespaceCatalogEnabled, false);
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1319
|
+
test('parseConfig namespaceCatalogEnabled="true" (string) → true', () => {
|
|
1320
|
+
assert.equal(parseConfig({ namespaceCatalogEnabled: "true" }).namespaceCatalogEnabled, true);
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
test("parseConfig namespaceCatalogEnabled present-but-unrecognized → throws (rule #51, codex NI42R)", () => {
|
|
1324
|
+
// A PRESENT but unrecognized value is rejected, not silently defaulted to
|
|
1325
|
+
// enabled — mirrors resolveEmitLegacyTools (CLAUDE.md rule #51: reject invalid
|
|
1326
|
+
// input instead of silently defaulting). Absent still defaults to true.
|
|
1327
|
+
assert.throws(
|
|
1328
|
+
() => parseConfig({ namespaceCatalogEnabled: "maybe" }),
|
|
1329
|
+
/namespaceCatalogEnabled must be a boolean-like value/,
|
|
1330
|
+
);
|
|
1331
|
+
assert.throws(() => parseConfig({ namespaceCatalogEnabled: "flase" }), /boolean-like value/);
|
|
1332
|
+
assert.throws(() => parseConfig({ namespaceCatalogEnabled: 2 }), /boolean-like value/);
|
|
1333
|
+
});
|
package/src/config.ts
CHANGED
|
@@ -384,6 +384,27 @@ function resolveEmitLegacyTools(configValue: unknown): boolean {
|
|
|
384
384
|
return true;
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
+
/**
|
|
388
|
+
* Resolve the `namespaceCatalogEnabled` opt-out (issue #1499). A boolean-like
|
|
389
|
+
* value ("false"/"0"/"no"/"off" etc.) is honored; a PRESENT but unrecognized
|
|
390
|
+
* value ("flase", 2) is REJECTED rather than silently defaulting to enabled
|
|
391
|
+
* (CLAUDE.md rule #51 — reject invalid input instead of silently defaulting),
|
|
392
|
+
* mirroring `resolveEmitLegacyTools`. Defaults to enabled only when absent.
|
|
393
|
+
*/
|
|
394
|
+
function resolveNamespaceCatalogEnabled(configValue: unknown): boolean {
|
|
395
|
+
const ACCEPTED = "true/false/1/0/yes/no/on/off";
|
|
396
|
+
if (configValue !== undefined && configValue !== null) {
|
|
397
|
+
const coerced = coerceBooleanLike(configValue);
|
|
398
|
+
if (coerced === undefined) {
|
|
399
|
+
throw new Error(
|
|
400
|
+
`namespaceCatalogEnabled must be a boolean-like value (${ACCEPTED}); got ${JSON.stringify(configValue)}`,
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
return coerced;
|
|
404
|
+
}
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
|
|
387
408
|
export function isOpenaiApiKeyDisabled(value: unknown): boolean {
|
|
388
409
|
return value === false || (typeof value === "string" && value.trim().toLowerCase() === "false");
|
|
389
410
|
}
|
|
@@ -2588,6 +2609,14 @@ export function parseConfig(raw: unknown): PluginConfig {
|
|
|
2588
2609
|
|
|
2589
2610
|
// v3.0 namespaces (default off)
|
|
2590
2611
|
namespacesEnabled: cfg.namespacesEnabled === true,
|
|
2612
|
+
// Namespace catalog (issue #1499): default ON, but only does anything when
|
|
2613
|
+
// namespacesEnabled is also true (the catalog is inert otherwise). Operators
|
|
2614
|
+
// opt out with namespaceCatalogEnabled: false. Boolean-like opt-outs from
|
|
2615
|
+
// CLI/env/JSON ("false", "0", "no", "off") are honored (CLAUDE.md rule #36);
|
|
2616
|
+
// a PRESENT but unrecognized value ("flase", 2) is REJECTED rather than
|
|
2617
|
+
// silently defaulting to enabled (CLAUDE.md rule #51); default to enabled
|
|
2618
|
+
// only when the value is absent.
|
|
2619
|
+
namespaceCatalogEnabled: resolveNamespaceCatalogEnabled(cfg.namespaceCatalogEnabled),
|
|
2591
2620
|
// NOTE: namespace identifiers are intentionally NOT sanitized here — the
|
|
2592
2621
|
// codebase rejects unsafe namespaces at the point of use (see
|
|
2593
2622
|
// codex-materialize-runner and NamespaceStorageRouter / resolveNamespaceDir),
|
|
@@ -91,6 +91,7 @@ function makeResolutionStorage(options: {
|
|
|
91
91
|
failRollbackFor?: string;
|
|
92
92
|
partialSupersedeBeforeFailureFor?: string;
|
|
93
93
|
onSupersede?: (oldId: string, newId: string) => void;
|
|
94
|
+
dir?: string;
|
|
94
95
|
} = {}) {
|
|
95
96
|
const memories = new Map<string, MemoryFile>([
|
|
96
97
|
["mem-a-001", makeMemory("mem-a-001")],
|
|
@@ -111,6 +112,7 @@ function makeResolutionStorage(options: {
|
|
|
111
112
|
supersedeCalls,
|
|
112
113
|
frontmatterWrites,
|
|
113
114
|
removedFactHashIds,
|
|
115
|
+
dir: options.dir ?? "/tmp/contradiction-namespace-storage",
|
|
114
116
|
async getMemoryById(id: string) {
|
|
115
117
|
const memory = memories.get(id);
|
|
116
118
|
return memory ? cloneMemory(memory) : null;
|
|
@@ -1819,6 +1821,213 @@ test("executeResolution rolls back memory changes when pair resolution persisten
|
|
|
1819
1821
|
}
|
|
1820
1822
|
});
|
|
1821
1823
|
|
|
1824
|
+
// ── Catalog-write touch ordering (NH1dX, rule #25) ──────────────────────────────
|
|
1825
|
+
// The merge catalog touch (onMergedMemoryWritten) must fire ONLY after the
|
|
1826
|
+
// resolution durably commits past the rollback point. If resolvePair fails and
|
|
1827
|
+
// the merge rolls back, NO catalog write may be recorded for a write that did
|
|
1828
|
+
// not survive.
|
|
1829
|
+
|
|
1830
|
+
test("executeResolution merge records NO catalog write touch when resolution persistence fails and rolls back", async () => {
|
|
1831
|
+
const { dir, cleanup } = await makeTempDir();
|
|
1832
|
+
try {
|
|
1833
|
+
const written = writePair(dir, makePair({ namespace: "work" }));
|
|
1834
|
+
const pairFile = path.join(dir, ".review", "contradictions", `${written.pairId}.json`);
|
|
1835
|
+
// Force resolvePair to fail by removing the pair file once supersession runs,
|
|
1836
|
+
// mirroring the existing rollback test. This drives the rollback path.
|
|
1837
|
+
const storage = makeResolutionStorage({
|
|
1838
|
+
dir: "/tmp/work-namespace-storage",
|
|
1839
|
+
onSupersede: () => {
|
|
1840
|
+
fs.rmSync(pairFile, { force: true });
|
|
1841
|
+
},
|
|
1842
|
+
});
|
|
1843
|
+
const catalogTouches: Array<{ namespace: string | undefined; storageDir: string }> = [];
|
|
1844
|
+
|
|
1845
|
+
const result = await executeResolution(dir, storage, written.pairId, "merge", {
|
|
1846
|
+
mergedContent: "merged canonical fact",
|
|
1847
|
+
storageForNamespace: () => storage,
|
|
1848
|
+
onMergedMemoryWritten: (namespace, storageDir) => {
|
|
1849
|
+
catalogTouches.push({ namespace, storageDir });
|
|
1850
|
+
},
|
|
1851
|
+
});
|
|
1852
|
+
|
|
1853
|
+
assert.match(result.message, /Resolution persistence failed; rolled back memory changes/);
|
|
1854
|
+
assert.deepEqual(result.affectedIds, []);
|
|
1855
|
+
// The merged memory was created then cleaned up by the rollback, and the
|
|
1856
|
+
// resolution never persisted — so the catalog touch must NOT have fired.
|
|
1857
|
+
assert.deepEqual(
|
|
1858
|
+
catalogTouches,
|
|
1859
|
+
[],
|
|
1860
|
+
"no catalog write touch may be recorded for a rolled-back merge",
|
|
1861
|
+
);
|
|
1862
|
+
assert.equal(readPair(dir, written.pairId), null);
|
|
1863
|
+
} finally {
|
|
1864
|
+
await cleanup();
|
|
1865
|
+
}
|
|
1866
|
+
});
|
|
1867
|
+
|
|
1868
|
+
test("executeResolution merge records exactly one catalog write touch for the pair namespace on success", async () => {
|
|
1869
|
+
const { dir, cleanup } = await makeTempDir();
|
|
1870
|
+
try {
|
|
1871
|
+
const written = writePair(dir, makePair({ namespace: "work" }));
|
|
1872
|
+
const storage = makeResolutionStorage({ dir: "/tmp/work-namespace-storage" });
|
|
1873
|
+
const catalogTouches: Array<{ namespace: string | undefined; storageDir: string }> = [];
|
|
1874
|
+
|
|
1875
|
+
const result = await executeResolution(dir, storage, written.pairId, "merge", {
|
|
1876
|
+
mergedContent: "merged canonical fact",
|
|
1877
|
+
storageForNamespace: () => storage,
|
|
1878
|
+
onMergedMemoryWritten: (namespace, storageDir) => {
|
|
1879
|
+
catalogTouches.push({ namespace, storageDir });
|
|
1880
|
+
},
|
|
1881
|
+
});
|
|
1882
|
+
|
|
1883
|
+
assert.match(result.message, /Both memories superseded by merged/);
|
|
1884
|
+
assert.deepEqual(result.affectedIds, ["mem-a-001", "mem-b-002"]);
|
|
1885
|
+
assert.equal(readPair(dir, written.pairId)?.resolution, "merge");
|
|
1886
|
+
// Exactly one touch, carrying the pair namespace and the routed storage dir.
|
|
1887
|
+
assert.deepEqual(catalogTouches, [
|
|
1888
|
+
{ namespace: "work", storageDir: "/tmp/work-namespace-storage" },
|
|
1889
|
+
]);
|
|
1890
|
+
} finally {
|
|
1891
|
+
await cleanup();
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
|
|
1895
|
+
// NH3X3: supersede-only resolutions (keep-a / keep-b) also mutate the namespace,
|
|
1896
|
+
// so they must record a catalog touch — post-commit, never on rollback.
|
|
1897
|
+
|
|
1898
|
+
test("executeResolution keep-a records exactly one catalog write touch on success", async () => {
|
|
1899
|
+
const { dir, cleanup } = await makeTempDir();
|
|
1900
|
+
try {
|
|
1901
|
+
const written = writePair(dir, makePair({ namespace: "work" }));
|
|
1902
|
+
const storage = makeResolutionStorage({ dir: "/tmp/work-namespace-storage" });
|
|
1903
|
+
const catalogTouches: Array<{ namespace: string | undefined; storageDir: string }> = [];
|
|
1904
|
+
|
|
1905
|
+
const result = await executeResolution(dir, storage, written.pairId, "keep-a", {
|
|
1906
|
+
storageForNamespace: () => storage,
|
|
1907
|
+
onMergedMemoryWritten: (namespace, storageDir) => {
|
|
1908
|
+
catalogTouches.push({ namespace, storageDir });
|
|
1909
|
+
},
|
|
1910
|
+
});
|
|
1911
|
+
|
|
1912
|
+
assert.deepEqual(result.affectedIds, ["mem-b-002"]);
|
|
1913
|
+
assert.equal(readPair(dir, written.pairId)?.resolution, "keep-a");
|
|
1914
|
+
assert.deepEqual(catalogTouches, [
|
|
1915
|
+
{ namespace: "work", storageDir: "/tmp/work-namespace-storage" },
|
|
1916
|
+
]);
|
|
1917
|
+
} finally {
|
|
1918
|
+
await cleanup();
|
|
1919
|
+
}
|
|
1920
|
+
});
|
|
1921
|
+
|
|
1922
|
+
test("executeResolution keep-b records exactly one catalog write touch on success", async () => {
|
|
1923
|
+
const { dir, cleanup } = await makeTempDir();
|
|
1924
|
+
try {
|
|
1925
|
+
const written = writePair(dir, makePair({ namespace: "work" }));
|
|
1926
|
+
const storage = makeResolutionStorage({ dir: "/tmp/work-namespace-storage" });
|
|
1927
|
+
const catalogTouches: Array<{ namespace: string | undefined; storageDir: string }> = [];
|
|
1928
|
+
|
|
1929
|
+
const result = await executeResolution(dir, storage, written.pairId, "keep-b", {
|
|
1930
|
+
storageForNamespace: () => storage,
|
|
1931
|
+
onMergedMemoryWritten: (namespace, storageDir) => {
|
|
1932
|
+
catalogTouches.push({ namespace, storageDir });
|
|
1933
|
+
},
|
|
1934
|
+
});
|
|
1935
|
+
|
|
1936
|
+
assert.deepEqual(result.affectedIds, ["mem-a-001"]);
|
|
1937
|
+
assert.equal(readPair(dir, written.pairId)?.resolution, "keep-b");
|
|
1938
|
+
assert.deepEqual(catalogTouches, [
|
|
1939
|
+
{ namespace: "work", storageDir: "/tmp/work-namespace-storage" },
|
|
1940
|
+
]);
|
|
1941
|
+
} finally {
|
|
1942
|
+
await cleanup();
|
|
1943
|
+
}
|
|
1944
|
+
});
|
|
1945
|
+
|
|
1946
|
+
test("executeResolution keep-a records NO catalog write touch when resolution persistence fails and rolls back", async () => {
|
|
1947
|
+
const { dir, cleanup } = await makeTempDir();
|
|
1948
|
+
try {
|
|
1949
|
+
const written = writePair(dir, makePair({ namespace: "work" }));
|
|
1950
|
+
const pairFile = path.join(dir, ".review", "contradictions", `${written.pairId}.json`);
|
|
1951
|
+
const storage = makeResolutionStorage({
|
|
1952
|
+
dir: "/tmp/work-namespace-storage",
|
|
1953
|
+
onSupersede: () => {
|
|
1954
|
+
fs.rmSync(pairFile, { force: true });
|
|
1955
|
+
},
|
|
1956
|
+
});
|
|
1957
|
+
const catalogTouches: Array<{ namespace: string | undefined; storageDir: string }> = [];
|
|
1958
|
+
|
|
1959
|
+
const result = await executeResolution(dir, storage, written.pairId, "keep-a", {
|
|
1960
|
+
storageForNamespace: () => storage,
|
|
1961
|
+
onMergedMemoryWritten: (namespace, storageDir) => {
|
|
1962
|
+
catalogTouches.push({ namespace, storageDir });
|
|
1963
|
+
},
|
|
1964
|
+
});
|
|
1965
|
+
|
|
1966
|
+
assert.match(result.message, /Resolution persistence failed; rolled back memory changes/);
|
|
1967
|
+
assert.deepEqual(result.affectedIds, []);
|
|
1968
|
+
assert.deepEqual(catalogTouches, [], "a rolled-back keep-a must not record a catalog write touch");
|
|
1969
|
+
assert.equal(readPair(dir, written.pairId), null);
|
|
1970
|
+
} finally {
|
|
1971
|
+
await cleanup();
|
|
1972
|
+
}
|
|
1973
|
+
});
|
|
1974
|
+
|
|
1975
|
+
test("executeResolution keep-a records a catalog write touch when resolution persistence fails and rollback is incomplete", async () => {
|
|
1976
|
+
const { dir, cleanup } = await makeTempDir();
|
|
1977
|
+
try {
|
|
1978
|
+
const written = writePair(dir, makePair({ namespace: "work" }));
|
|
1979
|
+
const pairFile = path.join(dir, ".review", "contradictions", `${written.pairId}.json`);
|
|
1980
|
+
const storage = makeResolutionStorage({
|
|
1981
|
+
dir: "/tmp/work-namespace-storage",
|
|
1982
|
+
failRollbackFor: "mem-b-002",
|
|
1983
|
+
onSupersede: () => {
|
|
1984
|
+
fs.rmSync(pairFile, { force: true });
|
|
1985
|
+
},
|
|
1986
|
+
});
|
|
1987
|
+
const catalogTouches: Array<{ namespace: string | undefined; storageDir: string }> = [];
|
|
1988
|
+
|
|
1989
|
+
const result = await executeResolution(dir, storage, written.pairId, "keep-a", {
|
|
1990
|
+
storageForNamespace: () => storage,
|
|
1991
|
+
onMergedMemoryWritten: (namespace, storageDir) => {
|
|
1992
|
+
catalogTouches.push({ namespace, storageDir });
|
|
1993
|
+
},
|
|
1994
|
+
});
|
|
1995
|
+
|
|
1996
|
+
assert.match(result.message, /Resolution persistence failed; rollback incomplete/);
|
|
1997
|
+
assert.equal(storage.memories.get("mem-b-002")?.frontmatter.status, "superseded");
|
|
1998
|
+
assert.equal(storage.memories.get("mem-b-002")?.frontmatter.supersededBy, "mem-a-001");
|
|
1999
|
+
assert.deepEqual(catalogTouches, [
|
|
2000
|
+
{ namespace: "work", storageDir: "/tmp/work-namespace-storage" },
|
|
2001
|
+
]);
|
|
2002
|
+
assert.equal(readPair(dir, written.pairId), null);
|
|
2003
|
+
} finally {
|
|
2004
|
+
await cleanup();
|
|
2005
|
+
}
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
// Non-mutating verbs never touch the catalog (no namespace memory changed).
|
|
2009
|
+
test("executeResolution both-valid records no catalog write touch", async () => {
|
|
2010
|
+
const { dir, cleanup } = await makeTempDir();
|
|
2011
|
+
try {
|
|
2012
|
+
const written = writePair(dir, makePair({ namespace: "work" }));
|
|
2013
|
+
const storage = makeResolutionStorage({ dir: "/tmp/work-namespace-storage" });
|
|
2014
|
+
const catalogTouches: Array<{ namespace: string | undefined; storageDir: string }> = [];
|
|
2015
|
+
|
|
2016
|
+
const result = await executeResolution(dir, storage, written.pairId, "both-valid", {
|
|
2017
|
+
storageForNamespace: () => storage,
|
|
2018
|
+
onMergedMemoryWritten: (namespace, storageDir) => {
|
|
2019
|
+
catalogTouches.push({ namespace, storageDir });
|
|
2020
|
+
},
|
|
2021
|
+
});
|
|
2022
|
+
|
|
2023
|
+
assert.deepEqual(result.affectedIds, []);
|
|
2024
|
+
assert.equal(readPair(dir, written.pairId)?.resolution, "both-valid");
|
|
2025
|
+
assert.deepEqual(catalogTouches, [], "both-valid mutates no namespace memory — no catalog touch");
|
|
2026
|
+
} finally {
|
|
2027
|
+
await cleanup();
|
|
2028
|
+
}
|
|
2029
|
+
});
|
|
2030
|
+
|
|
1822
2031
|
test("executeResolution merge rolls back the first supersession when the second fails", async () => {
|
|
1823
2032
|
const { dir, cleanup } = await makeTempDir();
|
|
1824
2033
|
try {
|
|
@@ -1948,9 +2157,13 @@ test("executeResolution merge keeps created replacement when rollback fails", as
|
|
|
1948
2157
|
failSupersedeFor: "mem-b-002",
|
|
1949
2158
|
failRollbackFor: "mem-a-001",
|
|
1950
2159
|
});
|
|
2160
|
+
const catalogTouches: Array<{ namespace: string | undefined; storageDir: string }> = [];
|
|
1951
2161
|
|
|
1952
2162
|
const result = await executeResolution(dir, storage, written.pairId, "merge", {
|
|
1953
2163
|
mergedContent: "merged canonical fact",
|
|
2164
|
+
onMergedMemoryWritten: (namespace, storageDir) => {
|
|
2165
|
+
catalogTouches.push({ namespace, storageDir });
|
|
2166
|
+
},
|
|
1954
2167
|
});
|
|
1955
2168
|
|
|
1956
2169
|
const mergedId = storage.supersedeCalls[0]?.newId;
|
|
@@ -1960,6 +2173,9 @@ test("executeResolution merge keeps created replacement when rollback fails", as
|
|
|
1960
2173
|
assert.equal(storage.memories.get("mem-a-001")?.frontmatter.supersededBy, mergedId);
|
|
1961
2174
|
assert.equal(storage.memories.get(mergedId)?.content, "merged canonical fact");
|
|
1962
2175
|
assert.deepEqual(storage.removedFactHashIds, []);
|
|
2176
|
+
assert.deepEqual(catalogTouches, [
|
|
2177
|
+
{ namespace: undefined, storageDir: "/tmp/contradiction-namespace-storage" },
|
|
2178
|
+
]);
|
|
1963
2179
|
assert.equal(readPair(dir, written.pairId)?.resolution, undefined);
|
|
1964
2180
|
} finally {
|
|
1965
2181
|
await cleanup();
|
|
@@ -2489,3 +2705,71 @@ test("readPair returns null for non-object JSON", async () => {
|
|
|
2489
2705
|
await cleanup();
|
|
2490
2706
|
}
|
|
2491
2707
|
});
|
|
2708
|
+
|
|
2709
|
+
// ── issue #1499 sweep: a contradiction merge that CREATES a new merged memory
|
|
2710
|
+
// writes durable data to the pair's (possibly dynamic) namespace storage,
|
|
2711
|
+
// bypassing the extraction write path. executeResolution must invoke
|
|
2712
|
+
// onMergedMemoryWritten(namespace, storageDir) so the caller records the catalog
|
|
2713
|
+
// write — otherwise a dynamic namespace whose only durable mutation is a merge
|
|
2714
|
+
// stays invisible to QMD maintenance / writtenSince.
|
|
2715
|
+
test("executeResolution merge fires onMergedMemoryWritten with the pair namespace when a new memory is created", async () => {
|
|
2716
|
+
const { dir, cleanup } = await makeTempDir();
|
|
2717
|
+
try {
|
|
2718
|
+
const written = writePair(dir, makePair({ namespace: "project-origin-dynamic" }));
|
|
2719
|
+
const storage = makeResolutionStorage();
|
|
2720
|
+
(storage as { dir?: string }).dir = "/memory/namespaces/project-origin-dynamic-token";
|
|
2721
|
+
|
|
2722
|
+
const touches: Array<{ namespace?: string; storageDir: string }> = [];
|
|
2723
|
+
|
|
2724
|
+
const result = await executeResolution(dir, storage, written.pairId, "merge", {
|
|
2725
|
+
mergedContent: "merged canonical fact for dynamic namespace",
|
|
2726
|
+
storageForNamespace: () => storage,
|
|
2727
|
+
onMergedMemoryWritten: (namespace, storageDir) => {
|
|
2728
|
+
touches.push({ namespace, storageDir });
|
|
2729
|
+
},
|
|
2730
|
+
});
|
|
2731
|
+
|
|
2732
|
+
assert.deepEqual(result.affectedIds, ["mem-a-001", "mem-b-002"]);
|
|
2733
|
+
assert.equal(touches.length, 1, "exactly one catalog write touch for a created merge memory");
|
|
2734
|
+
assert.equal(
|
|
2735
|
+
touches[0]!.namespace,
|
|
2736
|
+
"project-origin-dynamic",
|
|
2737
|
+
"the catalog touch carries the pair's (dynamic) namespace",
|
|
2738
|
+
);
|
|
2739
|
+
assert.equal(
|
|
2740
|
+
touches[0]!.storageDir,
|
|
2741
|
+
"/memory/namespaces/project-origin-dynamic-token",
|
|
2742
|
+
"the catalog touch carries the resolved namespace storage dir",
|
|
2743
|
+
);
|
|
2744
|
+
} finally {
|
|
2745
|
+
await cleanup();
|
|
2746
|
+
}
|
|
2747
|
+
});
|
|
2748
|
+
|
|
2749
|
+
// Reusing an EXISTING merged memory id still supersedes BOTH sources — a durable
|
|
2750
|
+
// namespace mutation — so the catalog touch MUST fire so `lastWriteAt` refreshes
|
|
2751
|
+
// (NH3X3). It fires exactly once, post-commit.
|
|
2752
|
+
test("executeResolution merge fires onMergedMemoryWritten exactly once when reusing an existing merged id", async () => {
|
|
2753
|
+
const { dir, cleanup } = await makeTempDir();
|
|
2754
|
+
try {
|
|
2755
|
+
const written = writePair(dir, makePair({ namespace: "project-origin-dynamic" }));
|
|
2756
|
+
const storage = makeResolutionStorage();
|
|
2757
|
+
(storage as { dir?: string }).dir = "/memory/namespaces/project-origin-dynamic-token";
|
|
2758
|
+
|
|
2759
|
+
let touchCount = 0;
|
|
2760
|
+
|
|
2761
|
+
const result = await executeResolution(dir, storage, written.pairId, "merge", {
|
|
2762
|
+
mergedMemoryId: "mem-merged-003", // pre-existing merged memory, sources still superseded
|
|
2763
|
+
storageForNamespace: () => storage,
|
|
2764
|
+
onMergedMemoryWritten: () => {
|
|
2765
|
+
touchCount += 1;
|
|
2766
|
+
},
|
|
2767
|
+
});
|
|
2768
|
+
|
|
2769
|
+
assert.deepEqual(result.affectedIds, ["mem-a-001", "mem-b-002"]);
|
|
2770
|
+
assert.equal(touchCount, 1, "reusing an existing merged memory still supersedes both sources — record one touch");
|
|
2771
|
+
assert.equal(readPair(dir, written.pairId)?.resolution, "merge");
|
|
2772
|
+
} finally {
|
|
2773
|
+
await cleanup();
|
|
2774
|
+
}
|
|
2775
|
+
});
|