@remnic/core 9.3.711 → 9.3.713
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-boundary.d.ts +5 -5
- package/dist/access-boundary.js +2 -2
- package/dist/access-cli.js +5 -5
- package/dist/access-http.d.ts +5 -5
- package/dist/access-http.js +7 -7
- package/dist/access-mcp.d.ts +11 -5
- package/dist/access-mcp.js +6 -6
- package/dist/access-operations-batch.js +3 -3
- package/dist/access-operations.d.ts +5 -5
- package/dist/access-operations.js +5 -5
- package/dist/access-schema.d.ts +10 -0
- package/dist/access-schema.js +1 -1
- package/dist/{access-service-CoIA0NrG.d.ts → access-service-DwVmvgBg.d.ts} +11 -4
- package/dist/access-service.d.ts +5 -5
- package/dist/access-service.js +1 -1
- package/dist/access-surface-catalog.d.ts +5 -5
- 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/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +4 -4
- package/dist/briefing.d.ts +1 -1
- 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/capabilities.d.ts +1 -1
- package/dist/{catalog-DQCZrBjw.d.ts → catalog-CadPvH29.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/{chunk-I3BT2IDW.js → chunk-DJNKN3XY.js} +31 -21
- package/dist/chunk-DJNKN3XY.js.map +1 -0
- package/dist/{chunk-NUWZFSKC.js → chunk-FVZ2ISIX.js} +9 -1
- package/dist/chunk-FVZ2ISIX.js.map +1 -0
- package/dist/{chunk-5KQCOIPW.js → chunk-JC3MIKR4.js} +4 -4
- package/dist/{chunk-ZVDRL6SK.js → chunk-RMT7PAVW.js} +2 -2
- package/dist/{chunk-YVWZW37D.js → chunk-UHN2TNSB.js} +25 -2
- package/dist/chunk-UHN2TNSB.js.map +1 -0
- package/dist/{chunk-Z3B2YW56.js → chunk-VUNI6Y3D.js} +4 -4
- package/dist/{chunk-A2R5NV6O.js → chunk-VW24K2Q4.js} +40 -25
- package/dist/chunk-VW24K2Q4.js.map +1 -0
- package/dist/{chunk-AMXOCZHE.js → chunk-ZDUF7K5V.js} +3 -3
- package/dist/{cli-qex-L3GT.d.ts → cli-DfDMJqPP.d.ts} +3 -3
- package/dist/cli.d.ts +6 -6
- package/dist/cli.js +8 -8
- package/dist/compounding/engine.d.ts +1 -1
- 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/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 +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +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-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +4 -4
- 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/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.js +8 -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/maintenance/memory-governance.d.ts +1 -1
- package/dist/mcp-memory-inspector-app.d.ts +5 -5
- 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 +2 -2
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +2 -2
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/{orchestrator-DsVKLEBk.d.ts → orchestrator-B1iVjEyc.d.ts} +3 -3
- package/dist/orchestrator.d.ts +4 -4
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/provenance.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-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-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- 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-_hVxkTuF.d.ts → semantic-consolidation-CY8uVRNJ.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 +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +1 -1
- 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-DUK4vVnN.d.ts → types-Dr0Cw3L9.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/package.json +2 -2
- package/src/access-http.ts +45 -19
- package/src/access-mcp.test.ts +1 -0
- package/src/access-mcp.ts +34 -17
- package/src/access-schema.ts +8 -0
- package/src/access-service-observe-idempotency.test.ts +308 -0
- package/src/access-service.ts +63 -3
- package/dist/chunk-A2R5NV6O.js.map +0 -1
- package/dist/chunk-I3BT2IDW.js.map +0 -1
- package/dist/chunk-NUWZFSKC.js.map +0 -1
- package/dist/chunk-YVWZW37D.js.map +0 -1
- /package/dist/{chunk-5KQCOIPW.js.map → chunk-JC3MIKR4.js.map} +0 -0
- /package/dist/{chunk-ZVDRL6SK.js.map → chunk-RMT7PAVW.js.map} +0 -0
- /package/dist/{chunk-Z3B2YW56.js.map → chunk-VUNI6Y3D.js.map} +0 -0
- /package/dist/{chunk-AMXOCZHE.js.map → chunk-ZDUF7K5V.js.map} +0 -0
|
@@ -3516,4 +3516,4 @@ interface HourlySummary {
|
|
|
3516
3516
|
generatedAt: string;
|
|
3517
3517
|
}
|
|
3518
3518
|
|
|
3519
|
-
export { type EntityStructuredSection as $, type ActiveRecallChatType as A, type BehaviorSignalEvent as B, type CalendarSource as C, type SecretRef as D, type EntityFile as E, type FaithfulnessFrontmatter as F, type GatewayConfig as G, type HourlySummary as H, type IdentityInjectionMode as I, type AgentAccessAuthToken as J, type CompressionGuidelineOptimizerEventCounts as K, type LiveConnectorsConfig as L, type MemoryCategory as M, type CompressionGuidelineOptimizerActionSummary as N, type CompressionGuidelineOptimizerRuleUpdate as O, type PluginConfig as P, type QmdSearchResult as Q, type RecallTierExplain as R, type SessionObserverBandConfig as S, type TranscriptEntry as T, type MemoryActionType as U, type VerificationState as V, type MemoryActionEvent as W, type CompressionGuidelineOptimizerState as X, type EntityMention as Y, type EntitySchemaDefinition as Z, type EntitySchemaSectionDefinition as _, type ActiveRecallQueryMode as a, type MemoryObservation as a$, type ExtractionResult as a0, type ConsolidationResult as a1, type DaySummaryResult as a2, type ContinuityIncidentRecord as a3, type ContinuityIncidentCloseInput as a4, type ContinuityIncidentOpenInput as a5, type ContinuityImprovementLoop as a6, type ContinuityLoopReviewInput as a7, type ContinuityLoopUpsertInput as a8, type ImportanceScore as a9, type WearableSourceConnector as aA, type WearablesConfig as aB, type WearableSyncSummary as aC, type WearableSourceStatus as aD, type WearableDayTranscript as aE, type WearableCorrectionRule as aF, type CodingContext as aG, type BootstrapOptions as aH, type BootstrapResult as aI, type ConsolidationObservation as aJ, type EntityTimelineEntry as aK, type ScopeProfileLayerId as aL, type ScopeProfilePromotionTarget as aM, type ScopeProfileConfig as aN, type DreamsStatusResult as aO, type DreamsPhase as aP, type DreamsRunResult as aQ, type CodingModeConfig as aR, type WearableCleanupSettings as aS, type WearableDayTranscriptMeta as aT, type BriefingConfig as aU, type BriefingOpenCommitment as aV, type BriefingWindow as aW, type CodexCompatConfig as aX, DEFAULT_RECALL_DISCLOSURE as aY, type ExtractedFact as aZ, type MemoryActionEligibilitySource as a_, type MemoryIntent as aa, type QmdSearchExplain as ab, type MemoryActionEligibilityContext as ac, type MemoryActionPolicyResult as ad, type MemoryLifecycleEventType as ae, type MemoryLifecycleStateSummary as af, type ModelProviderConfig as ag, type NativeKnowledgeConfig as ah, type ProvenanceSource as ai, type ProvenanceConfig as aj, type RetrievalTier as ak, type SignalScanResult as al, type ConversationThread as am, type TopicScore as an, type MemoryLink as ao, type BufferState as ap, type MetaState as aq, type FileHygieneConfig as ar, type EntityRelationship as as, type EntityActivityEntry as at, type AccessTrackingEntry as au, type MemorySummary as av, type WearableConversation as aw, type WearableSourceSettings as ax, type WearableNativeMemory as ay, type WearableMemoryMode as az, type ActiveRecallPromptStyle as b, type NativeKnowledgeObsidianVaultConfig as b$, type MemoryScope as b0, RECALL_DISCLOSURE_LEVELS as b1, type WearableAuthCheck as b2, type WearableFetchOptions as b3, type WearableFetchPage as b4, type WearableNativeMemoryPage as b5, type WearableTranscriptSegment as b6, isRecallDisclosure as b7, type AgentAccessHttpConfig as b8, type AgentDefaultsConfig as b9, type DreamsPhaseStatus as bA, type DreamsPhasesConfig as bB, type DreamsRemConfig as bC, type EngramTraceEvent as bD, type ExtractedProcedureStep as bE, type ExtractedQuestion as bF, type ExtractedReasoningTrace as bG, type ExtractedReasoningTraceStep as bH, type ExtractedRelationship as bI, type ExtractionPassSource as bJ, type GitHubLiveConnectorConfig as bK, type GmailLiveConnectorConfig as bL, type GoogleDriveLiveConnectorConfig as bM, type HeartbeatConfig as bN, type HeartbeatDetectionMode as bO, type LlmTraceCallback as bP, type LlmTraceEvent as bQ, type MemoryActionOutcome as bR, type MemoryActionPolicyDecision as bS, type MemoryActionStatus as bT, type MemoryLinkType as bU, type MemoryOsPresetName as bV, type ModelApi as bW, type ModelDefinitionConfig as bX, type ModelProviderAuthMode as bY, type NamespacePolicy as bZ, type NativeKnowledgeFolderRuleConfig as b_, type AgentPersona as ba, type BehaviorLoopAdjustment as bb, type BehaviorSignalDirection as bc, type BehaviorSignalType as bd, type BriefingCalendarSourceError as be, type BufferEntryState as bf, type CaptureMode as bg, type CodexCliReasoningEffort as bh, type CodexCompactionFlushMode as bi, type CodexConnectorConfig as bj, type CodingKnowledgeConfig as bk, type CompressionGuidelineActivationState as bl, type CompressionGuidelineOptimizerSourceWindow as bm, type ConfidenceTier as bn, type ConsolidationAction as bo, type ConsolidationItem as bp, type ContinuityIncidentState as bq, type ContinuityLoopCadence as br, type ContinuityLoopStatus as bs, type ContradictionScanConfig as bt, type CronConversationRecallMode as bu, type CronRecallMode as bv, type DreamingConfig as bw, type DreamingNarrativePromptStyle as bx, type DreamsDeepSleepConfig as by, type DreamsLightSleepConfig as bz, type ActiveRecallThinking as c, type NativeKnowledgeOpenClawWorkspaceConfig as c0, type NotionLiveConnectorConfig as c1, type PrincipalFromSessionKeyMode as c2, type PrincipalRule as c3, type ProceduralConfig as c4, type QuestionEntry as c5, type ReasoningEffort as c6, type RecallPipelineConfig as c7, type RecallSectionConfig as c8, type RecallTraceEvent as c9, type RelevanceFeedback as ca, SPECULATIVE_TTL_DAYS as cb, type ScopeProfileAutoPromoteConfig as cc, type ScopeProfileTeamProjectConfig as cd, type ScopeTeamConfig as ce, type ScoredEntity as cf, type SemanticChunkingConfigShape as cg, type SignalLevel as ch, type SlotBehaviorConfig as ci, type SlotMismatchMode as cj, type TriggerMode as ck, confidenceTier as cl, type ActiveRecallModelFallbackPolicy as d, type BehaviorLoopPolicyState as e, type
|
|
3519
|
+
export { type EntityStructuredSection as $, type ActiveRecallChatType as A, type BehaviorSignalEvent as B, type CalendarSource as C, type SecretRef as D, type EntityFile as E, type FaithfulnessFrontmatter as F, type GatewayConfig as G, type HourlySummary as H, type IdentityInjectionMode as I, type AgentAccessAuthToken as J, type CompressionGuidelineOptimizerEventCounts as K, type LiveConnectorsConfig as L, type MemoryCategory as M, type CompressionGuidelineOptimizerActionSummary as N, type CompressionGuidelineOptimizerRuleUpdate as O, type PluginConfig as P, type QmdSearchResult as Q, type RecallTierExplain as R, type SessionObserverBandConfig as S, type TranscriptEntry as T, type MemoryActionType as U, type VerificationState as V, type MemoryActionEvent as W, type CompressionGuidelineOptimizerState as X, type EntityMention as Y, type EntitySchemaDefinition as Z, type EntitySchemaSectionDefinition as _, type ActiveRecallQueryMode as a, type MemoryObservation as a$, type ExtractionResult as a0, type ConsolidationResult as a1, type DaySummaryResult as a2, type ContinuityIncidentRecord as a3, type ContinuityIncidentCloseInput as a4, type ContinuityIncidentOpenInput as a5, type ContinuityImprovementLoop as a6, type ContinuityLoopReviewInput as a7, type ContinuityLoopUpsertInput as a8, type ImportanceScore as a9, type WearableSourceConnector as aA, type WearablesConfig as aB, type WearableSyncSummary as aC, type WearableSourceStatus as aD, type WearableDayTranscript as aE, type WearableCorrectionRule as aF, type CodingContext as aG, type BootstrapOptions as aH, type BootstrapResult as aI, type ConsolidationObservation as aJ, type EntityTimelineEntry as aK, type ScopeProfileLayerId as aL, type ScopeProfilePromotionTarget as aM, type ScopeProfileConfig as aN, type DreamsStatusResult as aO, type DreamsPhase as aP, type DreamsRunResult as aQ, type CodingModeConfig as aR, type WearableCleanupSettings as aS, type WearableDayTranscriptMeta as aT, type BriefingConfig as aU, type BriefingOpenCommitment as aV, type BriefingWindow as aW, type CodexCompatConfig as aX, DEFAULT_RECALL_DISCLOSURE as aY, type ExtractedFact as aZ, type MemoryActionEligibilitySource as a_, type MemoryIntent as aa, type QmdSearchExplain as ab, type MemoryActionEligibilityContext as ac, type MemoryActionPolicyResult as ad, type MemoryLifecycleEventType as ae, type MemoryLifecycleStateSummary as af, type ModelProviderConfig as ag, type NativeKnowledgeConfig as ah, type ProvenanceSource as ai, type ProvenanceConfig as aj, type RetrievalTier as ak, type SignalScanResult as al, type ConversationThread as am, type TopicScore as an, type MemoryLink as ao, type BufferState as ap, type MetaState as aq, type FileHygieneConfig as ar, type EntityRelationship as as, type EntityActivityEntry as at, type AccessTrackingEntry as au, type MemorySummary as av, type WearableConversation as aw, type WearableSourceSettings as ax, type WearableNativeMemory as ay, type WearableMemoryMode as az, type ActiveRecallPromptStyle as b, type NativeKnowledgeObsidianVaultConfig as b$, type MemoryScope as b0, RECALL_DISCLOSURE_LEVELS as b1, type WearableAuthCheck as b2, type WearableFetchOptions as b3, type WearableFetchPage as b4, type WearableNativeMemoryPage as b5, type WearableTranscriptSegment as b6, isRecallDisclosure as b7, type AgentAccessHttpConfig as b8, type AgentDefaultsConfig as b9, type DreamsPhaseStatus as bA, type DreamsPhasesConfig as bB, type DreamsRemConfig as bC, type EngramTraceEvent as bD, type ExtractedProcedureStep as bE, type ExtractedQuestion as bF, type ExtractedReasoningTrace as bG, type ExtractedReasoningTraceStep as bH, type ExtractedRelationship as bI, type ExtractionPassSource as bJ, type GitHubLiveConnectorConfig as bK, type GmailLiveConnectorConfig as bL, type GoogleDriveLiveConnectorConfig as bM, type HeartbeatConfig as bN, type HeartbeatDetectionMode as bO, type LlmTraceCallback as bP, type LlmTraceEvent as bQ, type MemoryActionOutcome as bR, type MemoryActionPolicyDecision as bS, type MemoryActionStatus as bT, type MemoryLinkType as bU, type MemoryOsPresetName as bV, type ModelApi as bW, type ModelDefinitionConfig as bX, type ModelProviderAuthMode as bY, type NamespacePolicy as bZ, type NativeKnowledgeFolderRuleConfig as b_, type AgentPersona as ba, type BehaviorLoopAdjustment as bb, type BehaviorSignalDirection as bc, type BehaviorSignalType as bd, type BriefingCalendarSourceError as be, type BufferEntryState as bf, type CaptureMode as bg, type CodexCliReasoningEffort as bh, type CodexCompactionFlushMode as bi, type CodexConnectorConfig as bj, type CodingKnowledgeConfig as bk, type CompressionGuidelineActivationState as bl, type CompressionGuidelineOptimizerSourceWindow as bm, type ConfidenceTier as bn, type ConsolidationAction as bo, type ConsolidationItem as bp, type ContinuityIncidentState as bq, type ContinuityLoopCadence as br, type ContinuityLoopStatus as bs, type ContradictionScanConfig as bt, type CronConversationRecallMode as bu, type CronRecallMode as bv, type DreamingConfig as bw, type DreamingNarrativePromptStyle as bx, type DreamsDeepSleepConfig as by, type DreamsLightSleepConfig as bz, type ActiveRecallThinking as c, type NativeKnowledgeOpenClawWorkspaceConfig as c0, type NotionLiveConnectorConfig as c1, type PrincipalFromSessionKeyMode as c2, type PrincipalRule as c3, type ProceduralConfig as c4, type QuestionEntry as c5, type ReasoningEffort as c6, type RecallPipelineConfig as c7, type RecallSectionConfig as c8, type RecallTraceEvent as c9, type RelevanceFeedback as ca, SPECULATIVE_TTL_DAYS as cb, type ScopeProfileAutoPromoteConfig as cc, type ScopeProfileTeamProjectConfig as cd, type ScopeTeamConfig as ce, type ScoredEntity as cf, type SemanticChunkingConfigShape as cg, type SignalLevel as ch, type SlotBehaviorConfig as ci, type SlotMismatchMode as cj, type TriggerMode as ck, confidenceTier as cl, type ActiveRecallModelFallbackPolicy as d, type BehaviorLoopPolicyState as e, type MemoryStatus as f, type MemoryFile as g, type RecallPlanMode as h, type RecallDisclosure as i, type AgentPersonaModelConfig as j, type MemoryFrontmatter as k, type MemoryProjectionCurrentState as l, type MemoryLifecycleEvent as m, type BriefingSections as n, type BriefingFollowup as o, type BriefingFocus as p, type CalendarEvent as q, type BriefingActiveThread as r, type BriefingResult as s, type BriefingRecentEntity as t, type BufferTurn as u, type Checkpoint as v, type LifecycleState as w, type PolicyClass as x, type ImportanceLevel as y, type BufferSurpriseEvent as z };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import './types-gb_SNtjR.js';
|
|
2
2
|
import './message-parts/index.js';
|
|
3
|
-
export { au as AccessTrackingEntry, A as ActiveRecallChatType, d as ActiveRecallModelFallbackPolicy, b as ActiveRecallPromptStyle, a as ActiveRecallQueryMode, c as ActiveRecallThinking, J as AgentAccessAuthToken, b8 as AgentAccessHttpConfig, b9 as AgentDefaultsConfig, ba as AgentPersona,
|
|
3
|
+
export { au as AccessTrackingEntry, A as ActiveRecallChatType, d as ActiveRecallModelFallbackPolicy, b as ActiveRecallPromptStyle, a as ActiveRecallQueryMode, c as ActiveRecallThinking, J as AgentAccessAuthToken, b8 as AgentAccessHttpConfig, b9 as AgentDefaultsConfig, ba as AgentPersona, j as AgentPersonaModelConfig, bb as BehaviorLoopAdjustment, e as BehaviorLoopPolicyState, bc as BehaviorSignalDirection, B as BehaviorSignalEvent, bd as BehaviorSignalType, aH as BootstrapOptions, aI as BootstrapResult, r as BriefingActiveThread, be as BriefingCalendarSourceError, aU as BriefingConfig, p as BriefingFocus, o as BriefingFollowup, aV as BriefingOpenCommitment, t as BriefingRecentEntity, s as BriefingResult, n as BriefingSections, aW as BriefingWindow, bf as BufferEntryState, ap as BufferState, z as BufferSurpriseEvent, u as BufferTurn, q as CalendarEvent, C as CalendarSource, bg as CaptureMode, v as Checkpoint, bh as CodexCliReasoningEffort, bi as CodexCompactionFlushMode, aX as CodexCompatConfig, bj as CodexConnectorConfig, aG as CodingContext, bk as CodingKnowledgeConfig, aR as CodingModeConfig, bl as CompressionGuidelineActivationState, N as CompressionGuidelineOptimizerActionSummary, K as CompressionGuidelineOptimizerEventCounts, O as CompressionGuidelineOptimizerRuleUpdate, bm as CompressionGuidelineOptimizerSourceWindow, X as CompressionGuidelineOptimizerState, bn as ConfidenceTier, bo as ConsolidationAction, bp as ConsolidationItem, aJ as ConsolidationObservation, a1 as ConsolidationResult, a6 as ContinuityImprovementLoop, a4 as ContinuityIncidentCloseInput, a5 as ContinuityIncidentOpenInput, a3 as ContinuityIncidentRecord, bq as ContinuityIncidentState, br as ContinuityLoopCadence, a7 as ContinuityLoopReviewInput, bs as ContinuityLoopStatus, a8 as ContinuityLoopUpsertInput, bt as ContradictionScanConfig, am as ConversationThread, bu as CronConversationRecallMode, bv as CronRecallMode, aY as DEFAULT_RECALL_DISCLOSURE, a2 as DaySummaryResult, bw as DreamingConfig, bx as DreamingNarrativePromptStyle, by as DreamsDeepSleepConfig, bz as DreamsLightSleepConfig, aP as DreamsPhase, bA as DreamsPhaseStatus, bB as DreamsPhasesConfig, bC as DreamsRemConfig, aQ as DreamsRunResult, aO as DreamsStatusResult, bD as EngramTraceEvent, at as EntityActivityEntry, E as EntityFile, Y as EntityMention, as as EntityRelationship, Z as EntitySchemaDefinition, _ as EntitySchemaSectionDefinition, $ as EntityStructuredSection, aK as EntityTimelineEntry, aZ as ExtractedFact, bE as ExtractedProcedureStep, bF as ExtractedQuestion, bG as ExtractedReasoningTrace, bH as ExtractedReasoningTraceStep, bI as ExtractedRelationship, bJ as ExtractionPassSource, a0 as ExtractionResult, F as FaithfulnessFrontmatter, ar as FileHygieneConfig, G as GatewayConfig, bK as GitHubLiveConnectorConfig, bL as GmailLiveConnectorConfig, bM as GoogleDriveLiveConnectorConfig, bN as HeartbeatConfig, bO as HeartbeatDetectionMode, H as HourlySummary, I as IdentityInjectionMode, y as ImportanceLevel, a9 as ImportanceScore, w as LifecycleState, L as LiveConnectorsConfig, bP as LlmTraceCallback, bQ as LlmTraceEvent, ac as MemoryActionEligibilityContext, a_ as MemoryActionEligibilitySource, W as MemoryActionEvent, bR as MemoryActionOutcome, bS as MemoryActionPolicyDecision, ad as MemoryActionPolicyResult, bT as MemoryActionStatus, U as MemoryActionType, M as MemoryCategory, g as MemoryFile, k as MemoryFrontmatter, aa as MemoryIntent, m as MemoryLifecycleEvent, ae as MemoryLifecycleEventType, af as MemoryLifecycleStateSummary, ao as MemoryLink, bU as MemoryLinkType, a$ as MemoryObservation, bV as MemoryOsPresetName, l as MemoryProjectionCurrentState, b0 as MemoryScope, f as MemoryStatus, av as MemorySummary, aq as MetaState, bW as ModelApi, bX as ModelDefinitionConfig, bY as ModelProviderAuthMode, ag as ModelProviderConfig, bZ as NamespacePolicy, ah as NativeKnowledgeConfig, b_ as NativeKnowledgeFolderRuleConfig, b$ as NativeKnowledgeObsidianVaultConfig, c0 as NativeKnowledgeOpenClawWorkspaceConfig, c1 as NotionLiveConnectorConfig, P as PluginConfig, x as PolicyClass, c2 as PrincipalFromSessionKeyMode, c3 as PrincipalRule, c4 as ProceduralConfig, aj as ProvenanceConfig, ai as ProvenanceSource, ab as QmdSearchExplain, Q as QmdSearchResult, c5 as QuestionEntry, b1 as RECALL_DISCLOSURE_LEVELS, c6 as ReasoningEffort, i as RecallDisclosure, c7 as RecallPipelineConfig, h as RecallPlanMode, c8 as RecallSectionConfig, R as RecallTierExplain, c9 as RecallTraceEvent, ca as RelevanceFeedback, ak as RetrievalTier, cb as SPECULATIVE_TTL_DAYS, cc as ScopeProfileAutoPromoteConfig, aN as ScopeProfileConfig, aL as ScopeProfileLayerId, aM as ScopeProfilePromotionTarget, cd as ScopeProfileTeamProjectConfig, ce as ScopeTeamConfig, cf as ScoredEntity, D as SecretRef, cg as SemanticChunkingConfigShape, S as SessionObserverBandConfig, ch as SignalLevel, al as SignalScanResult, ci as SlotBehaviorConfig, cj as SlotMismatchMode, an as TopicScore, T as TranscriptEntry, ck as TriggerMode, V as VerificationState, cl as confidenceTier, b7 as isRecallDisclosure } from './types-Dr0Cw3L9.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/core",
|
|
3
|
-
"version": "9.3.
|
|
3
|
+
"version": "9.3.713",
|
|
4
4
|
"description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -2966,7 +2966,7 @@
|
|
|
2966
2966
|
"core"
|
|
2967
2967
|
],
|
|
2968
2968
|
"peerDependencies": {
|
|
2969
|
-
"@remnic/coding-graph": "^9.3.
|
|
2969
|
+
"@remnic/coding-graph": "^9.3.713"
|
|
2970
2970
|
},
|
|
2971
2971
|
"peerDependenciesMeta": {
|
|
2972
2972
|
"@remnic/coding-graph": {
|
package/src/access-http.ts
CHANGED
|
@@ -1257,24 +1257,39 @@ export class EngramAccessHttpServer {
|
|
|
1257
1257
|
if (req.method === "POST" && pathname === "/engram/v1/observe") {
|
|
1258
1258
|
void getOperation("observe"); // boundary dispatch (issue #1525)
|
|
1259
1259
|
const body = await this.readValidatedBody(req, "observe");
|
|
1260
|
-
this.
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1260
|
+
const response = await this.service.observe(
|
|
1261
|
+
{
|
|
1262
|
+
sessionKey: body.sessionKey,
|
|
1263
|
+
messages: body.messages.map((message) => ({
|
|
1264
|
+
role: message.role,
|
|
1265
|
+
content: message.content,
|
|
1266
|
+
sourceFormat: message.sourceFormat ?? undefined,
|
|
1267
|
+
rawContent: message.rawContent ?? undefined,
|
|
1268
|
+
parts: message.parts ?? undefined,
|
|
1269
|
+
})),
|
|
1270
|
+
namespace: this.resolveNamespace(req, body.namespace),
|
|
1271
|
+
authenticatedPrincipal: this.resolveRequestPrincipal(req),
|
|
1272
|
+
skipExtraction: body.skipExtraction === true,
|
|
1273
|
+
// Issue #1649: optional server-side dedup key for retried POSTs.
|
|
1274
|
+
idempotencyKey: body.idempotencyKey,
|
|
1275
|
+
// Forward cwd/projectTag for auto git-context resolution (issue #569).
|
|
1276
|
+
cwd: body.cwd,
|
|
1277
|
+
projectTag: body.projectTag,
|
|
1278
|
+
},
|
|
1279
|
+
// Enforce the write-quota INSIDE the service's idempotency lock
|
|
1280
|
+
// (beforeExecute, only on a real miss). A retried observe that hits the
|
|
1281
|
+
// replay path must NOT be rejected with 429 — that is exactly the
|
|
1282
|
+
// response-lost-after-process case the dedup exists for. The original
|
|
1283
|
+
// attempt may have consumed the last quota slot; the retry returns the
|
|
1284
|
+
// cached response without requiring another. Matches memory_store's
|
|
1285
|
+
// hook-in-the-lock pattern (#1434 invariant).
|
|
1286
|
+
{ enforceWriteQuota: () => this.ensureWriteRateLimitAvailable() },
|
|
1287
|
+
);
|
|
1288
|
+
// A replayed (deduplicated) observe must not consume a second write-quota
|
|
1289
|
+
// slot — same invariant as memory_store/suggestion_submit (#1434).
|
|
1290
|
+
if (this.shouldCountWriteRateLimit(response as { dryRun?: boolean; idempotencyReplay?: boolean })) {
|
|
1291
|
+
this.recordWriteRateLimitHit();
|
|
1292
|
+
}
|
|
1278
1293
|
this.respondJson(res, 202, response);
|
|
1279
1294
|
return;
|
|
1280
1295
|
}
|
|
@@ -2595,7 +2610,15 @@ export class EngramAccessHttpServer {
|
|
|
2595
2610
|
codingArchitectureWrite ||
|
|
2596
2611
|
codegraphWrite
|
|
2597
2612
|
);
|
|
2598
|
-
|
|
2613
|
+
// observe self-enforces quota INSIDE its idempotency lock via the
|
|
2614
|
+
// enforceWriteQuota hook (issue #1649) — mirroring the direct
|
|
2615
|
+
// /engram/v1/observe route — so it must NOT be pre-checked here: a
|
|
2616
|
+
// pre-check would 429 a response-lost replay. It still counts as a write
|
|
2617
|
+
// for post-recording (the replay guard in shouldCountWriteRateLimit skips
|
|
2618
|
+
// the record). Other write tools keep the coarse pre-check.
|
|
2619
|
+
const observeSelfEnforcesQuota =
|
|
2620
|
+
toolName === "engram.observe" || toolName === "remnic.observe";
|
|
2621
|
+
if (isMcpWrite && !observeSelfEnforcesQuota) {
|
|
2599
2622
|
this.ensureWriteRateLimitAvailable();
|
|
2600
2623
|
}
|
|
2601
2624
|
|
|
@@ -2611,6 +2634,9 @@ export class EngramAccessHttpServer {
|
|
|
2611
2634
|
sessionKeyOverride: requestIdentity.sessionKey,
|
|
2612
2635
|
sessionId,
|
|
2613
2636
|
correlationId: mcpCorrelationId,
|
|
2637
|
+
enforceWriteQuota: observeSelfEnforcesQuota
|
|
2638
|
+
? () => this.ensureWriteRateLimitAvailable()
|
|
2639
|
+
: undefined,
|
|
2614
2640
|
});
|
|
2615
2641
|
|
|
2616
2642
|
if (isMcpWrite && response !== null) {
|
package/src/access-mcp.test.ts
CHANGED
|
@@ -477,6 +477,7 @@ test("MCP session override is injected only into tools that accept sessionKey",
|
|
|
477
477
|
namespace: undefined,
|
|
478
478
|
authenticatedPrincipal: undefined,
|
|
479
479
|
skipExtraction: false,
|
|
480
|
+
idempotencyKey: undefined,
|
|
480
481
|
cwd: undefined,
|
|
481
482
|
projectTag: undefined,
|
|
482
483
|
});
|
package/src/access-mcp.ts
CHANGED
|
@@ -50,6 +50,12 @@ type McpRequestOptions = {
|
|
|
50
50
|
sessionKeyOverride?: string;
|
|
51
51
|
sessionId?: string;
|
|
52
52
|
correlationId?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Write-quota enforcer forwarded into observe's idempotency lock
|
|
55
|
+
* (issue #1649). Only the MCP-over-HTTP transport supplies this (it owns
|
|
56
|
+
* the write-rate-limit window); the standalone MCP server has no quota.
|
|
57
|
+
*/
|
|
58
|
+
enforceWriteQuota?: () => void | Promise<void>;
|
|
53
59
|
};
|
|
54
60
|
|
|
55
61
|
type McpTool = {
|
|
@@ -1156,6 +1162,11 @@ export class EngramMcpServer {
|
|
|
1156
1162
|
},
|
|
1157
1163
|
description: "Conversation messages to observe",
|
|
1158
1164
|
},
|
|
1165
|
+
idempotencyKey: {
|
|
1166
|
+
type: "string",
|
|
1167
|
+
description:
|
|
1168
|
+
"Optional idempotency key (issue #1649). Deduplicates a retried observe POST server-side so the batch is ingested once even when the HTTP response is lost. Reusing the key with a different payload is rejected.",
|
|
1169
|
+
},
|
|
1159
1170
|
namespace: { type: "string" },
|
|
1160
1171
|
skipExtraction: { type: "boolean" },
|
|
1161
1172
|
cwd: { type: "string", description: "Working directory for auto git-context resolution." },
|
|
@@ -2374,7 +2385,7 @@ export class EngramMcpServer {
|
|
|
2374
2385
|
argumentsObject = { ...argumentsObject, sessionKey: options.sessionKeyOverride };
|
|
2375
2386
|
}
|
|
2376
2387
|
const effectivePrincipal = options?.principalOverride ?? this.authenticatedPrincipal;
|
|
2377
|
-
const result = await this.callTool(name, argumentsObject, effectivePrincipal, options?.sessionId);
|
|
2388
|
+
const result = await this.callTool(name, argumentsObject, effectivePrincipal, options?.sessionId, options?.enforceWriteQuota);
|
|
2378
2389
|
return {
|
|
2379
2390
|
jsonrpc: "2.0",
|
|
2380
2391
|
id,
|
|
@@ -2562,7 +2573,7 @@ export class EngramMcpServer {
|
|
|
2562
2573
|
}));
|
|
2563
2574
|
}
|
|
2564
2575
|
|
|
2565
|
-
private async callTool(name: string, args: Record<string, unknown>, effectivePrincipal?: string, mcpSessionId?: string): Promise<unknown> {
|
|
2576
|
+
private async callTool(name: string, args: Record<string, unknown>, effectivePrincipal?: string, mcpSessionId?: string, enforceWriteQuota?: () => void | Promise<void>): Promise<unknown> {
|
|
2566
2577
|
// Migrated operations dispatch through the access boundary (issue #1525):
|
|
2567
2578
|
// one registry entry owns schema validation, normalization (rules
|
|
2568
2579
|
// 17/28/36/48/51), and error mapping for every surface. The switch
|
|
@@ -3104,21 +3115,27 @@ export class EngramMcpServer {
|
|
|
3104
3115
|
);
|
|
3105
3116
|
case "engram.observe": {
|
|
3106
3117
|
const body = parseMcpRequest("observe", args);
|
|
3107
|
-
return this.service.observe(
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3118
|
+
return this.service.observe(
|
|
3119
|
+
{
|
|
3120
|
+
sessionKey: body.sessionKey,
|
|
3121
|
+
messages: body.messages.map((message) => ({
|
|
3122
|
+
role: message.role,
|
|
3123
|
+
content: message.content,
|
|
3124
|
+
parts: message.parts ?? undefined,
|
|
3125
|
+
rawContent: message.rawContent ?? undefined,
|
|
3126
|
+
sourceFormat: message.sourceFormat ?? undefined,
|
|
3127
|
+
})),
|
|
3128
|
+
skipExtraction: body.skipExtraction === true,
|
|
3129
|
+
idempotencyKey: body.idempotencyKey,
|
|
3130
|
+
namespace: body.namespace,
|
|
3131
|
+
authenticatedPrincipal: effectivePrincipal,
|
|
3132
|
+
cwd: body.cwd,
|
|
3133
|
+
projectTag: body.projectTag,
|
|
3134
|
+
},
|
|
3135
|
+
// Forward the transport's write-quota enforcer into observe's
|
|
3136
|
+
// idempotency lock so a replay is never 429'd (issue #1649).
|
|
3137
|
+
enforceWriteQuota ? { enforceWriteQuota } : undefined,
|
|
3138
|
+
);
|
|
3122
3139
|
}
|
|
3123
3140
|
case "engram.lcm_search":
|
|
3124
3141
|
return this.service.lcmSearch({
|
package/src/access-schema.ts
CHANGED
|
@@ -216,6 +216,14 @@ export const observeRequestSchema = z.object({
|
|
|
216
216
|
messages: z.array(messageSchema).min(1, "messages must be a non-empty array"),
|
|
217
217
|
namespace: namespaceSchema,
|
|
218
218
|
skipExtraction: z.boolean().optional(),
|
|
219
|
+
/**
|
|
220
|
+
* Optional idempotency key for server-side dedup of retried observe POSTs
|
|
221
|
+
* (issue #1649). A retry that reaches the daemon after the first attempt
|
|
222
|
+
* already processed is replayed from cache instead of re-ingested. When the
|
|
223
|
+
* key is reused with a DIFFERENT payload the request is rejected as a
|
|
224
|
+
* conflict (same contract as memory_store/suggestion_submit).
|
|
225
|
+
*/
|
|
226
|
+
idempotencyKey: idempotencyKeySchema,
|
|
219
227
|
/** Working directory for auto git-context resolution (issue #569). */
|
|
220
228
|
cwd: z.string().trim().min(1, "cwd must be non-empty when provided").max(2048).optional(),
|
|
221
229
|
/**
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1649: a retried observe POST (same `idempotencyKey`) must be deduplicated
|
|
3
|
+
* server-side — the batch is ingested exactly once even when the HTTP response
|
|
4
|
+
* is lost and the client replays the request. Without the key the daemon
|
|
5
|
+
* re-runs every side effect under `skipDedupeCheck: true` and the turn is
|
|
6
|
+
* queued for extraction twice.
|
|
7
|
+
*
|
|
8
|
+
* Verified here against the `EngramAccessService.observe` path with a stub
|
|
9
|
+
* orchestrator that records every namespace-bearing side effect (LCM enqueue +
|
|
10
|
+
* extraction replay), the same probe shape used by the #1495 observe-scope
|
|
11
|
+
* tests. The idempotency store is filesystem-backed, so `memoryDir` is a real
|
|
12
|
+
* temp directory (not the synthetic path the scope tests use, which never touch
|
|
13
|
+
* the store because they omit the key).
|
|
14
|
+
*/
|
|
15
|
+
import assert from "node:assert/strict";
|
|
16
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
17
|
+
import { tmpdir } from "node:os";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import test from "node:test";
|
|
20
|
+
|
|
21
|
+
import { EngramAccessService } from "./access-service.js";
|
|
22
|
+
import { EngramAccessInputError } from "./access-service.js";
|
|
23
|
+
import { Orchestrator } from "./orchestrator.js";
|
|
24
|
+
import type { EngramAccessObserveRequest } from "./access-service.js";
|
|
25
|
+
import type { CodingContext, PluginConfig } from "./types.js";
|
|
26
|
+
|
|
27
|
+
interface ObserveProbe {
|
|
28
|
+
orch: Orchestrator;
|
|
29
|
+
contexts: Map<string, CodingContext>;
|
|
30
|
+
lcmCalls: Array<{ sessionKey: string }>;
|
|
31
|
+
extractionCalls: Array<{
|
|
32
|
+
sessionKeys: string[];
|
|
33
|
+
writeNamespaceOverride?: string;
|
|
34
|
+
principalOverride?: string;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function makeObserveProbe(memoryDir: string): ObserveProbe {
|
|
39
|
+
const contexts = new Map<string, CodingContext>();
|
|
40
|
+
const lcmCalls: ObserveProbe["lcmCalls"] = [];
|
|
41
|
+
const extractionCalls: ObserveProbe["extractionCalls"] = [];
|
|
42
|
+
|
|
43
|
+
const config = {
|
|
44
|
+
namespacesEnabled: true,
|
|
45
|
+
defaultNamespace: "default",
|
|
46
|
+
sharedNamespace: "shared",
|
|
47
|
+
namespacePolicies: [
|
|
48
|
+
{ name: "pi-geek", readPrincipals: ["pi-geek"], writePrincipals: ["pi-geek"] },
|
|
49
|
+
],
|
|
50
|
+
codingMode: { projectScope: true },
|
|
51
|
+
memoryDir,
|
|
52
|
+
// Disable objective-state snapshots so the probe doesn't need a writable
|
|
53
|
+
// storage backend — the idempotency contract under test is LCM + extraction.
|
|
54
|
+
objectiveStateMemoryEnabled: false,
|
|
55
|
+
objectiveStateSnapshotWritesEnabled: false,
|
|
56
|
+
principalFromSessionKeyMode: "prefix",
|
|
57
|
+
principalFromSessionKeyRules: [{ match: "pi-geek:", principal: "pi-geek" }],
|
|
58
|
+
recallCrossNamespaceBudgetEnabled: false,
|
|
59
|
+
recallCrossNamespaceBudgetWindowMs: 60_000,
|
|
60
|
+
recallCrossNamespaceBudgetSoftLimit: 10,
|
|
61
|
+
recallCrossNamespaceBudgetHardLimit: 30,
|
|
62
|
+
} as unknown as PluginConfig;
|
|
63
|
+
|
|
64
|
+
const orch = {
|
|
65
|
+
config,
|
|
66
|
+
getCodingContextForSession: (sk: string | undefined) =>
|
|
67
|
+
(sk ? contexts.get(sk) : null) ?? null,
|
|
68
|
+
setCodingContextForSession: (sk: string, ctx: CodingContext | null) => {
|
|
69
|
+
if (ctx === null) contexts.delete(sk);
|
|
70
|
+
else contexts.set(sk, ctx);
|
|
71
|
+
},
|
|
72
|
+
applyCodingNamespaceOverlay: (sk: string | undefined, base: string) =>
|
|
73
|
+
Orchestrator.prototype.applyCodingNamespaceOverlay.call(orch, sk, base),
|
|
74
|
+
getStorage: async (ns: string) => ({ dir: join(memoryDir, "storage", ns) }),
|
|
75
|
+
lcmEngine: {
|
|
76
|
+
enabled: true,
|
|
77
|
+
enqueueObserveMessages: (sessionKey: string) => {
|
|
78
|
+
lcmCalls.push({ sessionKey });
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
ingestReplayBatch: async (
|
|
82
|
+
turns: Array<{ sessionKey: string }>,
|
|
83
|
+
options: { writeNamespaceOverride?: string; principalOverride?: string } = {},
|
|
84
|
+
) => {
|
|
85
|
+
extractionCalls.push({
|
|
86
|
+
sessionKeys: turns.map((t) => t.sessionKey),
|
|
87
|
+
writeNamespaceOverride: options.writeNamespaceOverride,
|
|
88
|
+
principalOverride: options.principalOverride,
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
} as unknown as Orchestrator;
|
|
92
|
+
|
|
93
|
+
return { orch, contexts, lcmCalls, extractionCalls };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function observeRequest(
|
|
97
|
+
overrides: Partial<EngramAccessObserveRequest>,
|
|
98
|
+
): EngramAccessObserveRequest {
|
|
99
|
+
return {
|
|
100
|
+
sessionKey: "pi-geek:abc123",
|
|
101
|
+
messages: [
|
|
102
|
+
{ role: "user", content: "what database are we using?" },
|
|
103
|
+
{ role: "assistant", content: "we use postgres for the primary store" },
|
|
104
|
+
],
|
|
105
|
+
...overrides,
|
|
106
|
+
} as EngramAccessObserveRequest;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
test("#1649 a retried observe with the same idempotencyKey is deduplicated to a single ingest", async () => {
|
|
110
|
+
const memoryDir = mkdtempSync(join(tmpdir(), "remnic-observe-idem-"));
|
|
111
|
+
try {
|
|
112
|
+
const probe = makeObserveProbe(memoryDir);
|
|
113
|
+
const service = new EngramAccessService(probe.orch);
|
|
114
|
+
const key = "observe-batch-pi-geek-abc123-#1";
|
|
115
|
+
|
|
116
|
+
const first = await service.observe(observeRequest({ idempotencyKey: key }));
|
|
117
|
+
// First attempt is a real ingest.
|
|
118
|
+
assert.equal(first.idempotencyReplay, undefined, "first attempt is not a replay");
|
|
119
|
+
assert.equal(probe.extractionCalls.length, 1, "first attempt ingests once");
|
|
120
|
+
assert.equal(probe.lcmCalls.length, 1, "first attempt archives LCM once");
|
|
121
|
+
|
|
122
|
+
// Retry: same key, same payload — the daemon already processed the body, so
|
|
123
|
+
// this is the response-lost-after-process case the issue describes.
|
|
124
|
+
const second = await service.observe(observeRequest({ idempotencyKey: key }));
|
|
125
|
+
assert.equal(second.idempotencyReplay, true, "retry is served from the cache");
|
|
126
|
+
assert.equal(second.accepted, first.accepted, "retry reports the same accepted count");
|
|
127
|
+
assert.equal(
|
|
128
|
+
second.effectiveNamespace,
|
|
129
|
+
first.effectiveNamespace,
|
|
130
|
+
"retry reports the same effective namespace",
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// The defining contract: NO second ingest, NO second LCM archive.
|
|
134
|
+
assert.equal(probe.extractionCalls.length, 1, "retry must not queue extraction a second time");
|
|
135
|
+
assert.equal(probe.lcmCalls.length, 1, "retry must not archive LCM a second time");
|
|
136
|
+
} finally {
|
|
137
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("#1649 observe without an idempotencyKey is never deduplicated (backward compatible)", async () => {
|
|
142
|
+
const memoryDir = mkdtempSync(join(tmpdir(), "remnic-observe-nokey-"));
|
|
143
|
+
try {
|
|
144
|
+
const probe = makeObserveProbe(memoryDir);
|
|
145
|
+
const service = new EngramAccessService(probe.orch);
|
|
146
|
+
|
|
147
|
+
await service.observe(observeRequest({}));
|
|
148
|
+
await service.observe(observeRequest({}));
|
|
149
|
+
|
|
150
|
+
assert.equal(probe.extractionCalls.length, 2, "no key ⇒ both calls ingest");
|
|
151
|
+
assert.equal(probe.lcmCalls.length, 2, "no key ⇒ both calls archive LCM");
|
|
152
|
+
} finally {
|
|
153
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("#1649 reusing an idempotencyKey with a divergent payload is rejected as a conflict", async () => {
|
|
158
|
+
const memoryDir = mkdtempSync(join(tmpdir(), "remnic-observe-conflict-"));
|
|
159
|
+
try {
|
|
160
|
+
const probe = makeObserveProbe(memoryDir);
|
|
161
|
+
const service = new EngramAccessService(probe.orch);
|
|
162
|
+
const key = "observe-batch-pi-geek-abc123-#2";
|
|
163
|
+
|
|
164
|
+
await service.observe(observeRequest({ idempotencyKey: key }));
|
|
165
|
+
|
|
166
|
+
// Same key, DIFFERENT messages — a stale key must never silently mask a
|
|
167
|
+
// unrelated batch. Same contract as memory_store/suggestion_submit.
|
|
168
|
+
await assert.rejects(
|
|
169
|
+
service.observe(
|
|
170
|
+
observeRequest({
|
|
171
|
+
idempotencyKey: key,
|
|
172
|
+
messages: [
|
|
173
|
+
{ role: "user", content: "completely different question" },
|
|
174
|
+
{ role: "assistant", content: "completely different answer" },
|
|
175
|
+
],
|
|
176
|
+
}),
|
|
177
|
+
),
|
|
178
|
+
(err: unknown) =>
|
|
179
|
+
err instanceof EngramAccessInputError &&
|
|
180
|
+
/idempotencyKey reuse conflict/.test(err.message),
|
|
181
|
+
"divergent payload under a reused key must throw a conflict",
|
|
182
|
+
);
|
|
183
|
+
} finally {
|
|
184
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("#1649 review fix: enforceWriteQuota runs only on a cache miss, never on a replay", async () => {
|
|
189
|
+
const memoryDir = mkdtempSync(join(tmpdir(), "remnic-observe-quota-hook-"));
|
|
190
|
+
try {
|
|
191
|
+
const probe = makeObserveProbe(memoryDir);
|
|
192
|
+
const service = new EngramAccessService(probe.orch);
|
|
193
|
+
const key = "observe-batch-pi-geek-abc123-#3";
|
|
194
|
+
let quotaChecks = 0;
|
|
195
|
+
const enforceWriteQuota = () => {
|
|
196
|
+
quotaChecks += 1;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
await service.observe(observeRequest({ idempotencyKey: key }), { enforceWriteQuota });
|
|
200
|
+
assert.equal(quotaChecks, 1, "quota is enforced exactly once on the real ingest");
|
|
201
|
+
|
|
202
|
+
const replay = await service.observe(observeRequest({ idempotencyKey: key }), { enforceWriteQuota });
|
|
203
|
+
assert.equal(replay.idempotencyReplay, true, "second call is a replay");
|
|
204
|
+
assert.equal(quotaChecks, 1, "quota is NOT re-checked on a replay (response-lost retry must not 429)");
|
|
205
|
+
assert.equal(probe.extractionCalls.length, 1, "replay did not re-ingest");
|
|
206
|
+
} finally {
|
|
207
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("#1649 review fix: the same idempotencyKey under a different principal is a conflict, not a silent replay", async () => {
|
|
212
|
+
const memoryDir = mkdtempSync(join(tmpdir(), "remnic-observe-principal-conflict-"));
|
|
213
|
+
try {
|
|
214
|
+
const probe = makeObserveProbe(memoryDir);
|
|
215
|
+
const service = new EngramAccessService(probe.orch);
|
|
216
|
+
const key = "observe-batch-shared-key-#4";
|
|
217
|
+
|
|
218
|
+
// Principal alice ingests under the key.
|
|
219
|
+
await service.observe(
|
|
220
|
+
observeRequest({ idempotencyKey: key, authenticatedPrincipal: "alice" }),
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
// A different principal reusing the same key + same body must NOT replay
|
|
224
|
+
// alice's cached response — the fingerprint folds in authenticatedPrincipal,
|
|
225
|
+
// so this is a conflict (defends against cross-identity replay when the
|
|
226
|
+
// principal is supplied out-of-band via HTTP/MCP auth).
|
|
227
|
+
await assert.rejects(
|
|
228
|
+
service.observe(
|
|
229
|
+
observeRequest({ idempotencyKey: key, authenticatedPrincipal: "bob" }),
|
|
230
|
+
),
|
|
231
|
+
(err: unknown) =>
|
|
232
|
+
err instanceof EngramAccessInputError &&
|
|
233
|
+
/idempotencyKey reuse conflict/.test(err.message),
|
|
234
|
+
"cross-principal key reuse must throw a conflict",
|
|
235
|
+
);
|
|
236
|
+
} finally {
|
|
237
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("#1649 review fix: rebinding the session's ambient coding context makes the same key a conflict", async () => {
|
|
242
|
+
const memoryDir = mkdtempSync(join(tmpdir(), "remnic-observe-ambient-scope-"));
|
|
243
|
+
try {
|
|
244
|
+
const probe = makeObserveProbe(memoryDir);
|
|
245
|
+
const service = new EngramAccessService(probe.orch);
|
|
246
|
+
const key = "observe-batch-ambient-scope-#5";
|
|
247
|
+
const req = observeRequest({ idempotencyKey: key });
|
|
248
|
+
|
|
249
|
+
// No explicit namespace/cwd/projectTag and no ambient context yet — the
|
|
250
|
+
// scope resolves to the default store. This caches the response.
|
|
251
|
+
await service.observe(req);
|
|
252
|
+
assert.equal(probe.extractionCalls.length, 1, "first observe ingests once");
|
|
253
|
+
|
|
254
|
+
// The session is now rebound to a coding project by an external caller
|
|
255
|
+
// (e.g. a memory_store with cwd, or a direct setCodingContextForSession).
|
|
256
|
+
// The resolved writeNamespace changed, so reusing the same key + same body
|
|
257
|
+
// must NOT silently replay — it is a conflict.
|
|
258
|
+
probe.contexts.set(req.sessionKey, {
|
|
259
|
+
projectId: "tag:remnic",
|
|
260
|
+
branch: null,
|
|
261
|
+
rootPath: "/projects/remnic",
|
|
262
|
+
defaultBranch: null,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
await assert.rejects(
|
|
266
|
+
service.observe(req),
|
|
267
|
+
(err: unknown) =>
|
|
268
|
+
err instanceof EngramAccessInputError &&
|
|
269
|
+
/idempotencyKey reuse conflict/.test(err.message),
|
|
270
|
+
"key reuse after ambient scope rebind must throw a conflict, not silently replay",
|
|
271
|
+
);
|
|
272
|
+
} finally {
|
|
273
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("#1649 review fix: projectTag observe replays correctly despite scope resolution side effects", async () => {
|
|
278
|
+
// The fingerprint folds in the RESOLVED writeNamespace (not the raw ambient
|
|
279
|
+
// context). resolveMemoryScopePlan seeds the session context from projectTag
|
|
280
|
+
// on the first call; the replay reads the SAME seeded context and resolves the
|
|
281
|
+
// SAME writeNamespace. This proves the fingerprint is stable across replays
|
|
282
|
+
// even though the scope resolution mutates session state on the first call.
|
|
283
|
+
const memoryDir = mkdtempSync(join(tmpdir(), "remnic-observe-tag-replay-"));
|
|
284
|
+
try {
|
|
285
|
+
const probe = makeObserveProbe(memoryDir);
|
|
286
|
+
const service = new EngramAccessService(probe.orch);
|
|
287
|
+
const key = "observe-batch-tag-replay-#6";
|
|
288
|
+
const req = observeRequest({ idempotencyKey: key, projectTag: "remnic" });
|
|
289
|
+
|
|
290
|
+
const first = await service.observe(req);
|
|
291
|
+
assert.equal(first.idempotencyReplay, undefined, "first attempt is a real ingest");
|
|
292
|
+
|
|
293
|
+
// The first observe's resolveMemoryScopePlan seeded a coding context for
|
|
294
|
+
// this session. Verify it was actually set (the side effect we depend on).
|
|
295
|
+
assert.ok(
|
|
296
|
+
probe.contexts.get(req.sessionKey),
|
|
297
|
+
"first observe seeded a coding context via resolveMemoryScopePlan",
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
// Retry with the same key + same body + same projectTag — the pre-resolved
|
|
301
|
+
// writeNamespace matches because the seeded context produces the same scope.
|
|
302
|
+
const replay = await service.observe(req);
|
|
303
|
+
assert.equal(replay.idempotencyReplay, true, "projectTag observe replays despite scope-resolution side effects");
|
|
304
|
+
assert.equal(probe.extractionCalls.length, 1, "replay did not re-ingest");
|
|
305
|
+
} finally {
|
|
306
|
+
rmSync(memoryDir, { recursive: true, force: true });
|
|
307
|
+
}
|
|
308
|
+
});
|