pi-fabric 0.88.2 → 0.89.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/chunks/chunk-5SU3YXBS.js +15 -0
  2. package/dist/chunks/chunk-5SU3YXBS.js.map +7 -0
  3. package/dist/chunks/{chunk-73YWS2PJ.js → chunk-7X72JCGN.js} +4 -4
  4. package/dist/chunks/{chunk-ZV5AXJYG.js → chunk-BOCWQOTG.js} +2 -2
  5. package/dist/chunks/{chunk-WJW47JBN.js → chunk-G23OLG5P.js} +7 -7
  6. package/dist/chunks/{chunk-AIN5KTE6.js → chunk-GUQT67PQ.js} +600 -70
  7. package/dist/chunks/chunk-GUQT67PQ.js.map +7 -0
  8. package/dist/chunks/{chunk-CXUYJNP7.js → chunk-HDPGQRHV.js} +3 -3
  9. package/dist/chunks/{chunk-ZKJ62VD3.js → chunk-J7BQS5XH.js} +4 -1
  10. package/dist/chunks/{chunk-ZKJ62VD3.js.map → chunk-J7BQS5XH.js.map} +2 -2
  11. package/dist/chunks/{chunk-NFHAYIIQ.js → chunk-JQJBQA2C.js} +3 -5
  12. package/dist/chunks/{chunk-NFHAYIIQ.js.map → chunk-JQJBQA2C.js.map} +1 -1
  13. package/dist/chunks/{chunk-4RCPQIN2.js → chunk-M3R56VXZ.js} +4 -4
  14. package/dist/chunks/{chunk-KWBZBSMX.js → chunk-MYCDJTSM.js} +34 -20
  15. package/dist/chunks/{chunk-KWBZBSMX.js.map → chunk-MYCDJTSM.js.map} +2 -2
  16. package/dist/chunks/{chunk-XMLNV3QH.js → chunk-TDCMU5L4.js} +2 -2
  17. package/dist/chunks/{chunk-B7INQVFM.js → chunk-TQR7IE6H.js} +6 -6
  18. package/dist/chunks/{chunk-HOKY6UCZ.js → chunk-UTR3I23Z.js} +5 -3
  19. package/dist/chunks/chunk-UTR3I23Z.js.map +7 -0
  20. package/dist/chunks/chunk-XB3RJVT4.js +17 -0
  21. package/dist/chunks/chunk-XB3RJVT4.js.map +7 -0
  22. package/dist/chunks/{chunk-L6R3Y7XI.js → chunk-Y7V2ZTMF.js} +1470 -913
  23. package/dist/chunks/chunk-Y7V2ZTMF.js.map +7 -0
  24. package/dist/chunks/{chunk-LUADHXOA.js → chunk-YIDC2IVN.js} +28 -15
  25. package/dist/chunks/chunk-YIDC2IVN.js.map +7 -0
  26. package/dist/chunks/{chunk-LBSO3BIH.js → chunk-Z43ZKIZT.js} +76 -5
  27. package/dist/chunks/chunk-Z43ZKIZT.js.map +7 -0
  28. package/dist/core/action-registry.js +3 -3
  29. package/dist/entropy/index.js +5 -5
  30. package/dist/fabric-runtime-state.js +42 -44
  31. package/dist/fabric-runtime-state.js.map +1 -1
  32. package/dist/index.js +542 -50
  33. package/dist/index.js.map +4 -4
  34. package/dist/memory/client.d.ts +31 -0
  35. package/dist/memory/client.d.ts.map +1 -0
  36. package/dist/memory/context.d.ts +2 -0
  37. package/dist/memory/context.d.ts.map +1 -1
  38. package/dist/memory/discovery.js +2 -2
  39. package/dist/memory/expand-service.d.ts +1 -1
  40. package/dist/memory/expand-service.d.ts.map +1 -1
  41. package/dist/memory/host-source.d.ts +59 -0
  42. package/dist/memory/host-source.d.ts.map +1 -0
  43. package/dist/memory/index.d.ts +7 -1
  44. package/dist/memory/index.d.ts.map +1 -1
  45. package/dist/memory/lineage.d.ts +5 -0
  46. package/dist/memory/lineage.d.ts.map +1 -1
  47. package/dist/memory/normalize.d.ts +21 -3
  48. package/dist/memory/normalize.d.ts.map +1 -1
  49. package/dist/memory/normalize.js +3 -1
  50. package/dist/memory/portable.d.ts +64 -0
  51. package/dist/memory/portable.d.ts.map +1 -0
  52. package/dist/memory/recall-service.d.ts +1 -1
  53. package/dist/memory/recall-service.d.ts.map +1 -1
  54. package/dist/memory/request-cache.d.ts +2 -2
  55. package/dist/memory/request-cache.d.ts.map +1 -1
  56. package/dist/memory/request-context.d.ts +3 -0
  57. package/dist/memory/request-context.d.ts.map +1 -1
  58. package/dist/memory/search.js +2 -2
  59. package/dist/memory/sessions-service.d.ts +1 -1
  60. package/dist/memory/sessions-service.d.ts.map +1 -1
  61. package/dist/memory/source-observation.d.ts +6 -0
  62. package/dist/memory/source-observation.d.ts.map +1 -1
  63. package/dist/memory.d.ts +6 -0
  64. package/dist/memory.d.ts.map +1 -0
  65. package/dist/memory.js +79 -0
  66. package/dist/memory.js.map +7 -0
  67. package/dist/providers/memory-provider.d.ts +15 -0
  68. package/dist/providers/memory-provider.d.ts.map +1 -1
  69. package/dist/providers/memory-provider.js +6 -4
  70. package/dist/residency/host.js +8 -8
  71. package/dist/residency/pi-entry.js +8 -8
  72. package/dist/runtime/core-override-guest-types.js +2 -2
  73. package/dist/runtime/guest-types.d.ts +1 -1
  74. package/dist/runtime/guest-types.d.ts.map +1 -1
  75. package/dist/runtime/guest-types.js +1 -1
  76. package/dist/runtime/monty-runtime.js +2 -2
  77. package/dist/runtime/typescript-kernel.js +2 -2
  78. package/dist/ui/controller.d.ts.map +1 -1
  79. package/dist/ui/conversation-chrome.d.ts +1 -1
  80. package/dist/ui/conversation-chrome.d.ts.map +1 -1
  81. package/dist/ui/conversation-chrome.js +2 -1
  82. package/dist/ui/conversation-host.d.ts +7 -0
  83. package/dist/ui/conversation-host.d.ts.map +1 -0
  84. package/dist/ui/conversation-host.js +9 -0
  85. package/dist/ui/conversation-host.js.map +7 -0
  86. package/dist/ui/conversation-native-reader.d.ts +1 -1
  87. package/dist/ui/conversation-native-reader.d.ts.map +1 -1
  88. package/dist/ui/conversation-native-reader.js +6 -6
  89. package/dist/ui/conversation-native-reader.js.map +2 -2
  90. package/dist/ui/conversation-render.d.ts +1 -1
  91. package/dist/ui/conversation-render.d.ts.map +1 -1
  92. package/dist/ui/conversation.js +19 -28
  93. package/dist/ui/conversation.js.map +2 -2
  94. package/dist/ui/dashboard.js +6 -7
  95. package/dist/ui/dashboard.js.map +1 -1
  96. package/dist/ui/settings-component.d.ts +2 -2
  97. package/dist/ui/settings-component.d.ts.map +1 -1
  98. package/dist/ui/settings-rpc.d.ts +2 -2
  99. package/dist/ui/settings-rpc.d.ts.map +1 -1
  100. package/dist/ui/settings-sections.d.ts +2 -2
  101. package/dist/ui/settings-sections.d.ts.map +1 -1
  102. package/dist/ui/settings-submenus.d.ts +1 -1
  103. package/dist/ui/settings-submenus.d.ts.map +1 -1
  104. package/dist/ui/settings.d.ts +3 -3
  105. package/dist/ui/settings.d.ts.map +1 -1
  106. package/dist/ui/settings.js +3 -19
  107. package/dist/ui/settings.js.map +3 -3
  108. package/docs/memory-recall.md +52 -2
  109. package/package.json +5 -1
  110. package/dist/chunks/chunk-AIN5KTE6.js.map +0 -7
  111. package/dist/chunks/chunk-HOKY6UCZ.js.map +0 -7
  112. package/dist/chunks/chunk-K344VGRR.js +0 -506
  113. package/dist/chunks/chunk-K344VGRR.js.map +0 -7
  114. package/dist/chunks/chunk-L6R3Y7XI.js.map +0 -7
  115. package/dist/chunks/chunk-LBSO3BIH.js.map +0 -7
  116. package/dist/chunks/chunk-LUADHXOA.js.map +0 -7
  117. package/dist/chunks/chunk-VE5LH2Z7.js +0 -74
  118. package/dist/chunks/chunk-VE5LH2Z7.js.map +0 -7
  119. package/dist/chunks/chunk-Z3AEX7SQ.js +0 -574
  120. package/dist/chunks/chunk-Z3AEX7SQ.js.map +0 -7
  121. /package/dist/chunks/{chunk-73YWS2PJ.js.map → chunk-7X72JCGN.js.map} +0 -0
  122. /package/dist/chunks/{chunk-ZV5AXJYG.js.map → chunk-BOCWQOTG.js.map} +0 -0
  123. /package/dist/chunks/{chunk-WJW47JBN.js.map → chunk-G23OLG5P.js.map} +0 -0
  124. /package/dist/chunks/{chunk-CXUYJNP7.js.map → chunk-HDPGQRHV.js.map} +0 -0
  125. /package/dist/chunks/{chunk-4RCPQIN2.js.map → chunk-M3R56VXZ.js.map} +0 -0
  126. /package/dist/chunks/{chunk-XMLNV3QH.js.map → chunk-TDCMU5L4.js.map} +0 -0
  127. /package/dist/chunks/{chunk-B7INQVFM.js.map → chunk-TQR7IE6H.js.map} +0 -0
@@ -4,13 +4,20 @@ import {
4
4
  DEFAULT_REGEX_MAX_HAYSTACK_TERMS,
5
5
  DEFAULT_REGEX_MAX_PATTERN_BYTES,
6
6
  DEFAULT_REGEX_TIMEOUT_MS,
7
+ MEMORY_CACHE_VERSION,
8
+ classifySessionTiers,
9
+ digestPolicy,
7
10
  fingerprintSource,
11
+ fitDigestCache,
8
12
  loadTieredIndex,
13
+ reconstructRecordsLineage,
9
14
  reconstructSessionLineage,
10
- searchMemoryIndex
11
- } from "./chunk-LUADHXOA.js";
15
+ searchMemoryIndex,
16
+ shardPolicy
17
+ } from "./chunk-YIDC2IVN.js";
12
18
  import {
13
19
  compareLexical,
20
+ foldSessionDigest,
14
21
  normalizeMemoryPhrase,
15
22
  tokenizeLexical
16
23
  } from "./chunk-U5FYOMG5.js";
@@ -19,11 +26,12 @@ import {
19
26
  enumerateAllSessions,
20
27
  resolveScope,
21
28
  resolveSessionTarget
22
- } from "./chunk-XMLNV3QH.js";
29
+ } from "./chunk-TDCMU5L4.js";
23
30
  import {
24
31
  expandSessionEntriesChecked,
32
+ normalizeRecords,
25
33
  normalizeSession
26
- } from "./chunk-KWBZBSMX.js";
34
+ } from "./chunk-MYCDJTSM.js";
27
35
 
28
36
  // src/memory/context.ts
29
37
  var RECALL_DEFAULT_PAGE_SIZE = 10;
@@ -75,17 +83,19 @@ var makeSnippet = (text, query, queryMode, maxChars) => {
75
83
  truncated: true
76
84
  };
77
85
  };
78
- var sourceFromSegment = (segment) => ({
86
+ var sourceFromSegment = (segment, sourceId) => ({
79
87
  sessionId: boundedMetadata(segment.sessionId) ?? "",
80
88
  sessionFile: segment.sessionFile,
89
+ ...sourceId ? { source: sourceId } : {},
81
90
  sourceHash: segment.sourceHash,
82
91
  branches: segment.branches,
83
92
  lineageFingerprint: segment.lineageFingerprint,
84
93
  tier: segment.tier
85
94
  });
86
- var sourceFromDigest = (digest) => ({
95
+ var sourceFromDigest = (digest, sourceId) => ({
87
96
  sessionId: boundedMetadata(digest.sessionId) ?? "",
88
97
  sessionFile: digest.sessionFile,
98
+ ...sourceId ? { source: sourceId } : {},
89
99
  sourceHash: digest.sourceHash,
90
100
  branches: digest.branches,
91
101
  lineageFingerprint: digest.lineageFingerprint,
@@ -93,14 +103,15 @@ var sourceFromDigest = (digest) => ({
93
103
  });
94
104
  var expandArgs = (source, entry) => ({
95
105
  session: source.sessionFile,
106
+ ...source.source ? { source: source.source } : {},
96
107
  expectedSourceHash: source.sourceHash,
97
108
  expectedLineageFingerprint: source.lineageFingerprint,
98
109
  branches: source.branches,
99
110
  indices: [entry.index]
100
111
  });
101
- var entryHit = (segment, item, query, queryMode, snippetChars) => {
112
+ var entryHit = (segment, item, query, queryMode, snippetChars, sourceId) => {
102
113
  const entry = item.entry;
103
- const source = sourceFromSegment(segment);
114
+ const source = sourceFromSegment(segment, sourceId);
104
115
  const snippet = makeSnippet(entry.text, query, queryMode, snippetChars);
105
116
  return {
106
117
  kind: "entry",
@@ -135,8 +146,8 @@ var hydrationArgs = (digest, request) => {
135
146
  expectedLineageFingerprint: digest.lineageFingerprint
136
147
  };
137
148
  };
138
- var sessionHit = (digest, request) => {
139
- const source = sourceFromDigest(digest);
149
+ var sessionHit = (digest, request, sourceId) => {
150
+ const source = sourceFromDigest(digest, sourceId);
140
151
  return {
141
152
  kind: "session",
142
153
  sessionId: source.sessionId,
@@ -185,7 +196,7 @@ var flattenCandidates = (result) => {
185
196
  recallCandidateCache.set(result, candidates);
186
197
  return candidates;
187
198
  };
188
- var materializeCandidate = (candidate, query, queryMode, snippetChars, request) => candidate.kind === "session" ? sessionHit(candidate.digest, request) : entryHit(candidate.segment, candidate.item, query, queryMode, snippetChars);
199
+ var materializeCandidate = (candidate, query, queryMode, snippetChars, request, sourceId) => candidate.kind === "session" ? sessionHit(candidate.digest, request, sourceId) : entryHit(candidate.segment, candidate.item, query, queryMode, snippetChars, sourceId);
189
200
  var canonicalNextArgs = (request, offset) => {
190
201
  const { offset: _offset, ...base } = request;
191
202
  return { ...base, offset };
@@ -201,7 +212,8 @@ var presentRecall = (input) => {
201
212
  input.query,
202
213
  input.queryMode,
203
214
  input.snippetChars,
204
- input.requestArgs
215
+ input.requestArgs,
216
+ input.requestArgs.source
205
217
  )
206
218
  );
207
219
  return {
@@ -308,6 +320,7 @@ var recallFailure = (error) => ({
308
320
  import path3 from "node:path";
309
321
 
310
322
  // src/memory/source-observation.ts
323
+ import crypto from "node:crypto";
311
324
  import fs from "node:fs";
312
325
  import path2 from "node:path";
313
326
  var sourceIdentity = (file) => {
@@ -336,6 +349,21 @@ var observeSource = (file, branches, liveBranch) => {
336
349
  liveBranchSignature: branches === "active" ? liveBranchSignature(liveBranch) : null
337
350
  };
338
351
  };
352
+ var hostIdentity = (revision, sourceHash) => {
353
+ const digest = crypto.createHash("sha256").update(`${revision}\0${sourceHash}`).digest("hex");
354
+ return {
355
+ device: BigInt(parseInt(digest.slice(0, 8), 16)),
356
+ inode: BigInt(parseInt(digest.slice(8, 16), 16)),
357
+ size: BigInt(parseInt(digest.slice(16, 24), 16)),
358
+ modifiedAt: BigInt(parseInt(digest.slice(24, 32), 16)),
359
+ changedAt: BigInt(parseInt(digest.slice(32, 40), 16))
360
+ };
361
+ };
362
+ var observeHostSource = (displayKey, revision, sourceHash) => ({
363
+ file: displayKey,
364
+ identity: hostIdentity(revision, sourceHash),
365
+ liveBranchSignature: null
366
+ });
339
367
  var observeSources = (refs, branches, liveResolver) => {
340
368
  const observations = [];
341
369
  for (const ref of refs) {
@@ -443,6 +471,419 @@ var MemoryRequestCache = class {
443
471
  }
444
472
  };
445
473
 
474
+ // src/memory/host-source.ts
475
+ import crypto2 from "node:crypto";
476
+
477
+ // src/memory/portable.ts
478
+ var MEMORY_SOURCE_INTERFACE_VERSION = 1;
479
+ var MemorySourceError = class extends Error {
480
+ constructor(code, message) {
481
+ super(message);
482
+ this.code = code;
483
+ this.name = "MemorySourceError";
484
+ }
485
+ };
486
+ var SOURCE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
487
+ var isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
488
+ var assertValidSource = (source) => {
489
+ if (!isRecord(source) || typeof source !== "object") {
490
+ throw new TypeError("A portable memory source must be an object");
491
+ }
492
+ if (source.interfaceVersion !== MEMORY_SOURCE_INTERFACE_VERSION) {
493
+ throw new TypeError(
494
+ `Portable memory source interface version must be ${MEMORY_SOURCE_INTERFACE_VERSION}`
495
+ );
496
+ }
497
+ if (typeof source.id !== "string" || !SOURCE_ID_PATTERN.test(source.id)) {
498
+ throw new TypeError(
499
+ "Portable memory source id must be 1-128 chars of letters, digits, dot, underscore, or dash"
500
+ );
501
+ }
502
+ if (typeof source.listSessions !== "function" || typeof source.loadSession !== "function") {
503
+ throw new TypeError("A portable memory source requires listSessions and loadSession");
504
+ }
505
+ };
506
+ var defineMemorySource = (source) => {
507
+ assertValidSource(source);
508
+ return source;
509
+ };
510
+ var createMemorySourceRegistry = () => {
511
+ const sources = /* @__PURE__ */ new Map();
512
+ return {
513
+ register(source) {
514
+ assertValidSource(source);
515
+ sources.set(source.id, source);
516
+ },
517
+ get(id) {
518
+ return sources.get(id);
519
+ },
520
+ ids() {
521
+ return [...sources.keys()].sort();
522
+ }
523
+ };
524
+ };
525
+
526
+ // src/memory/host-source.ts
527
+ var HOST_KEY_PREFIX = "memory-source:";
528
+ var REASON_PATTERN = /^[a-z0-9_.:-]{1,64}$/;
529
+ var hostDisplayKey = (sourceId, sessionKey) => `${HOST_KEY_PREFIX}${sourceId}/${sessionKey}`;
530
+ var hostSessionKey = (sourceId, session) => {
531
+ const prefix = `${HOST_KEY_PREFIX}${sourceId}/`;
532
+ return session.startsWith(prefix) ? session.slice(prefix.length) : session;
533
+ };
534
+ var resolveRegisteredSource = (registry, sourceId) => {
535
+ const source = registry?.get(sourceId);
536
+ if (!source) throw new MemorySourceError("source_not_found", `Memory source not registered: ${sourceId}`);
537
+ return source;
538
+ };
539
+ var memorySourceFailure = (error) => error instanceof MemorySourceError ? { code: error.code, message: error.message } : null;
540
+ var checkAbort = (signal) => {
541
+ if (signal?.aborted) throw new MemorySourceError("aborted", "Memory source call was aborted.");
542
+ };
543
+ var assertAuthorized = async (source, action, sessionKey) => {
544
+ if (!source.authorize) return;
545
+ let allowed;
546
+ try {
547
+ allowed = await source.authorize(action, sessionKey);
548
+ } catch {
549
+ throw new MemorySourceError("source_unauthorized", "Memory source authorization failed.");
550
+ }
551
+ if (allowed !== true) {
552
+ throw new MemorySourceError("source_unauthorized", "Memory source denied access.");
553
+ }
554
+ };
555
+ var coverageReasons = (coverage) => {
556
+ if (!coverage || coverage.complete !== false) return [];
557
+ const reason = typeof coverage.reason === "string" && REASON_PATTERN.test(coverage.reason) ? `source_coverage:${coverage.reason}` : "source_coverage_incomplete";
558
+ return [reason];
559
+ };
560
+ var asSessionKey = (value) => typeof value === "string" && value.length > 0 ? value : "";
561
+ var normalizeListResponse = (response) => Array.isArray(response) ? { sessions: response, coverageReasons: [] } : { sessions: response.sessions, coverageReasons: coverageReasons(response.coverage) };
562
+ var loadHostSnapshot = async (source, sessionKey, action, signal) => {
563
+ checkAbort(signal);
564
+ await assertAuthorized(source, action, sessionKey);
565
+ const response = await source.loadSession(sessionKey, signal === void 0 ? {} : { signal });
566
+ checkAbort(signal);
567
+ await assertAuthorized(source, action, sessionKey);
568
+ if (response === null || response === void 0) return null;
569
+ const key = asSessionKey(response.sessionKey);
570
+ const revision = asSessionKey(response.revision);
571
+ if (!revision || !Array.isArray(response.records) || key !== sessionKey) {
572
+ throw new MemorySourceError("invalid_source_response", "Memory source returned an invalid snapshot.");
573
+ }
574
+ if (!response.records.every((record) => record !== null && typeof record === "object" && !Array.isArray(record))) {
575
+ throw new MemorySourceError("invalid_source_response", "Memory source snapshot has non-object records.");
576
+ }
577
+ let records;
578
+ try {
579
+ records = JSON.parse(JSON.stringify(response.records));
580
+ } catch {
581
+ throw new MemorySourceError(
582
+ "invalid_source_response",
583
+ "Memory source snapshot records are not JSON-serializable."
584
+ );
585
+ }
586
+ return {
587
+ sourceId: source.id,
588
+ sessionKey,
589
+ displayKey: hostDisplayKey(source.id, sessionKey),
590
+ sessionId: response.sessionId ?? sessionKey,
591
+ revision,
592
+ metadata: { ...response.metadata ?? {} },
593
+ records,
594
+ coverageReasons: coverageReasons(response.coverage)
595
+ };
596
+ };
597
+ var listHostSnapshots = async (source, limit, action, signal) => {
598
+ checkAbort(signal);
599
+ await assertAuthorized(source, action, null);
600
+ const boundedLimit = Math.max(0, Math.floor(limit));
601
+ const listed = normalizeListResponse(
602
+ await source.listSessions(signal === void 0 ? { limit: boundedLimit } : { limit: boundedLimit, signal })
603
+ );
604
+ checkAbort(signal);
605
+ const snapshots = [];
606
+ const coverageReasons2 = new Set(listed.coverageReasons);
607
+ if (listed.sessions.length > boundedLimit) {
608
+ coverageReasons2.add("source_limit_exceeded");
609
+ }
610
+ for (const descriptor of listed.sessions.slice(0, boundedLimit)) {
611
+ checkAbort(signal);
612
+ const key = asSessionKey(descriptor.sessionKey);
613
+ if (!key) {
614
+ coverageReasons2.add("invalid_source_response");
615
+ continue;
616
+ }
617
+ const snapshot = await loadHostSnapshot(source, key, action, signal);
618
+ if (!snapshot) {
619
+ coverageReasons2.add("source_unavailable");
620
+ continue;
621
+ }
622
+ for (const reason of snapshot.coverageReasons) coverageReasons2.add(reason);
623
+ snapshots.push(snapshot);
624
+ }
625
+ if (snapshots.length === 0) {
626
+ await assertAuthorized(source, action, null);
627
+ }
628
+ return { snapshots, coverageReasons: [...coverageReasons2] };
629
+ };
630
+ var hostSourceState = (snapshot) => {
631
+ const canonical = JSON.stringify(snapshot.records);
632
+ const sourceHash = crypto2.createHash("sha256").update(`${snapshot.revision}\0${canonical}`).digest("hex");
633
+ return {
634
+ mtime: snapshot.metadata.updatedAt ?? 0,
635
+ size: Buffer.byteLength(canonical, "utf8"),
636
+ sourceHash
637
+ };
638
+ };
639
+ var hostRef = (snapshot) => ({
640
+ id: snapshot.sessionId,
641
+ file: snapshot.displayKey,
642
+ cwd: snapshot.metadata.cwd ?? "",
643
+ mtime: snapshot.metadata.updatedAt ?? 0
644
+ });
645
+ var hostLineage = (snapshot, branches) => reconstructRecordsLineage(snapshot.records, branches);
646
+ var hostIdentity2 = (sessionFile, sessionId, cwd) => ({
647
+ sessionFile,
648
+ ...sessionId !== void 0 ? { sessionId } : {},
649
+ ...cwd !== void 0 ? { cwd } : {}
650
+ });
651
+ var mergedCoverage = (base, extra) => {
652
+ if (extra.length === 0) return base;
653
+ const reasons = [.../* @__PURE__ */ new Set([...base.reasons, ...extra])].sort();
654
+ return { complete: false, reasons };
655
+ };
656
+ var buildHostShard = (snapshot, options, tier, entryRange) => {
657
+ const state = hostSourceState(snapshot);
658
+ const lineage = hostLineage(snapshot, options.branches ?? "active");
659
+ const { entries, header, indexCoverage } = normalizeRecords(
660
+ snapshot.records,
661
+ hostIdentity2(snapshot.displayKey, snapshot.sessionId, snapshot.metadata.cwd),
662
+ options.maxEntryChars,
663
+ {
664
+ lineage,
665
+ indexThinking: options.indexThinking ?? false,
666
+ indexToolOutput: options.indexToolOutput ?? true
667
+ }
668
+ );
669
+ const selected = entryRange ? entries.filter((entry) => entry.index >= entryRange.first && entry.index <= entryRange.last) : entries;
670
+ return {
671
+ cacheVersion: MEMORY_CACHE_VERSION,
672
+ kind: "shard",
673
+ sessionFile: snapshot.displayKey,
674
+ sessionId: header?.sessionId ?? snapshot.sessionId,
675
+ mtime: state.mtime,
676
+ size: state.size,
677
+ sourceHash: state.sourceHash,
678
+ branches: lineage.branches,
679
+ lineageFingerprint: lineage.fingerprint,
680
+ policy: shardPolicy(options, lineage),
681
+ cacheBytes: 0,
682
+ cacheSourceRatio: 0,
683
+ entries: selected,
684
+ totalEntryCount: entries.length,
685
+ indexCoverage: mergedCoverage(indexCoverage, snapshot.coverageReasons),
686
+ tier
687
+ };
688
+ };
689
+ var buildHostDigest = (snapshot, options) => {
690
+ const state = hostSourceState(snapshot);
691
+ const lineage = hostLineage(snapshot, options.branches ?? "active");
692
+ const { entries, header, indexCoverage } = normalizeRecords(
693
+ snapshot.records,
694
+ hostIdentity2(snapshot.displayKey, snapshot.sessionId, snapshot.metadata.cwd),
695
+ Number.MAX_SAFE_INTEGER,
696
+ {
697
+ lineage,
698
+ indexThinking: options.indexThinking ?? false,
699
+ indexToolOutput: options.indexToolOutput ?? true
700
+ }
701
+ );
702
+ const digest = foldSessionDigest({
703
+ sessionId: header?.sessionId ?? snapshot.sessionId,
704
+ file: snapshot.displayKey,
705
+ cwd: header?.cwd ?? snapshot.metadata.cwd ?? "",
706
+ entries,
707
+ maxVocabularyBytes: options.maxColdVocabularyBytes ?? 512 * 1024,
708
+ normalizationCoverage: mergedCoverage(indexCoverage, snapshot.coverageReasons)
709
+ });
710
+ return fitDigestCache({
711
+ cacheVersion: MEMORY_CACHE_VERSION,
712
+ kind: "digest",
713
+ ...digest,
714
+ mtime: state.mtime,
715
+ size: state.size,
716
+ sourceHash: state.sourceHash,
717
+ branches: lineage.branches,
718
+ lineageFingerprint: lineage.fingerprint,
719
+ policy: digestPolicy(options, lineage),
720
+ cacheBytes: 0,
721
+ cacheSourceRatio: 0
722
+ }, options.maxColdCacheBytes ?? 1024 * 1024);
723
+ };
724
+ var loadHostTieredIndex = (snapshots, options, hydrate = false, entryRange, extraReasons = []) => {
725
+ const refs = snapshots.map(hostRef);
726
+ const tiers = classifySessionTiers(refs, options.hotSessions ?? 50);
727
+ const maxSessions = options.maxSyncSessions ?? 1e4;
728
+ const maxSourceBytes = options.maxSyncSourceBytes ?? 512 * 1024 * 1024;
729
+ const shards = [];
730
+ const digests = [];
731
+ const reasons = new Set(extraReasons);
732
+ let indexedSessions = 0;
733
+ let incompleteSessions = 0;
734
+ let processedSessions = 0;
735
+ let processedSourceBytes = 0;
736
+ const byKey = new Map(snapshots.map((snapshot) => [snapshot.displayKey, snapshot]));
737
+ for (const ref of refs) {
738
+ const snapshot = byKey.get(ref.file);
739
+ if (!snapshot) {
740
+ reasons.add("source_unavailable");
741
+ continue;
742
+ }
743
+ const size = hostSourceState(snapshot).size;
744
+ if (processedSessions >= maxSessions) {
745
+ reasons.add("max_sync_sessions");
746
+ continue;
747
+ }
748
+ const tier = tiers.get(ref.file) ?? "cold";
749
+ const sourceWorkBytes = size * (hydrate && tier === "cold" ? 6 : 3);
750
+ if (processedSourceBytes + sourceWorkBytes > maxSourceBytes) {
751
+ reasons.add("max_sync_source_bytes");
752
+ continue;
753
+ }
754
+ processedSessions += 1;
755
+ processedSourceBytes += sourceWorkBytes;
756
+ if (hydrate) {
757
+ if (tier === "cold") digests.push(buildHostDigest(snapshot, options));
758
+ const shard = buildHostShard(snapshot, options, "cold", entryRange);
759
+ shards.push(shard);
760
+ if (shard.sourceHash) indexedSessions += 1;
761
+ if (shard.sourceHash && !shard.indexCoverage.complete) {
762
+ incompleteSessions += 1;
763
+ for (const reason of shard.indexCoverage.reasons) reasons.add(reason);
764
+ }
765
+ } else if (tier === "hot") {
766
+ const shard = buildHostShard(snapshot, options, "hot");
767
+ shards.push(shard);
768
+ if (shard.sourceHash) indexedSessions += 1;
769
+ if (shard.sourceHash && !shard.indexCoverage.complete) {
770
+ incompleteSessions += 1;
771
+ for (const reason of shard.indexCoverage.reasons) reasons.add(reason);
772
+ }
773
+ } else {
774
+ const digest = buildHostDigest(snapshot, options);
775
+ digests.push(digest);
776
+ if (digest.sourceHash) indexedSessions += 1;
777
+ if (!digest.indexCoverage.complete) {
778
+ incompleteSessions += 1;
779
+ for (const reason of digest.indexCoverage.reasons) reasons.add(reason);
780
+ }
781
+ }
782
+ }
783
+ const eligibleSessions = refs.length;
784
+ const staleSessions = eligibleSessions - indexedSessions;
785
+ const coverage = {
786
+ complete: reasons.size === 0 && staleSessions === 0 && incompleteSessions === 0,
787
+ indexedSessions,
788
+ eligibleSessions,
789
+ staleSessions,
790
+ incompleteSessions,
791
+ reasons: [...reasons].sort()
792
+ };
793
+ return { shards, digests, refs, tiers, coverage };
794
+ };
795
+ var fileRecallPlan = (refs, context) => {
796
+ const liveResolver = liveBranchResolver(context);
797
+ return {
798
+ refs,
799
+ sessionKey: null,
800
+ observeAll: (branches) => observeSources(refs, branches, liveResolver),
801
+ stateFor: (ref) => fingerprintSource(ref.file),
802
+ lineageFor: (ref, branches) => reconstructSessionLineage(ref.file, branches, liveResolver?.(ref.file)),
803
+ loadIndex: (options, hydrate, entryRange) => loadTieredIndex(refs, resolveTierRefs(refs, context), options, hydrate, entryRange)
804
+ };
805
+ };
806
+ var hostRecallPlan = async (source, sourceId, sessionKey, context, signal) => {
807
+ const loaded = sessionKey !== null ? await (async () => {
808
+ const snapshot = await loadHostSnapshot(source, sessionKey, "recall", signal);
809
+ return snapshot ? { snapshots: [snapshot], coverageReasons: [] } : null;
810
+ })() : await listHostSnapshots(source, context.config.maxSessions, "recall", signal);
811
+ if (!loaded) throw new MemorySourceError("session_not_found", `Session not found in source ${sourceId}: ${sessionKey}`);
812
+ const snapshots = loaded.snapshots;
813
+ const refs = snapshots.map(hostRef);
814
+ const byKey = new Map(snapshots.map((snapshot) => [snapshot.displayKey, snapshot]));
815
+ const stateByKey = /* @__PURE__ */ new Map();
816
+ const stateOf = (snapshot) => {
817
+ let state = stateByKey.get(snapshot.displayKey);
818
+ if (!state) {
819
+ state = hostSourceState(snapshot);
820
+ stateByKey.set(snapshot.displayKey, state);
821
+ }
822
+ return state;
823
+ };
824
+ return {
825
+ refs,
826
+ sessionKey,
827
+ observeAll: (branches) => snapshots.map((snapshot) => observeHostSource(snapshot.displayKey, snapshot.revision, stateOf(snapshot).sourceHash)),
828
+ stateFor: (ref) => {
829
+ const snapshot = byKey.get(ref.file);
830
+ return snapshot ? stateOf(snapshot) : null;
831
+ },
832
+ lineageFor: (ref, branches) => {
833
+ const snapshot = byKey.get(ref.file);
834
+ return snapshot ? hostLineage(snapshot, branches) : reconstructSessionLineage(ref.file, branches);
835
+ },
836
+ loadIndex: (options, hydrate, entryRange) => loadHostTieredIndex(snapshots, options, hydrate, entryRange, loaded.coverageReasons)
837
+ };
838
+ };
839
+ var fileExpansionAccess = (session, context) => {
840
+ const ref = resolveSessionTarget(context.agentDir, session);
841
+ if (!ref) return null;
842
+ const liveResolver = liveBranchResolver(context);
843
+ return {
844
+ ref,
845
+ state: () => fingerprintSource(ref.file),
846
+ lineage: (branches) => reconstructSessionLineage(ref.file, branches, liveResolver?.(ref.file)),
847
+ observe: (branches) => observeSource(ref.file, branches, branches === "active" ? liveResolver?.(ref.file) : void 0),
848
+ normalizeFull: (branches, policy, lineage) => {
849
+ const resolvedLineage = lineage ?? reconstructSessionLineage(ref.file, branches, liveResolver?.(ref.file));
850
+ const normalized = normalizeSession(ref.file, Number.MAX_SAFE_INTEGER, {
851
+ lineage: resolvedLineage,
852
+ ...policy
853
+ });
854
+ return normalized.header === null && normalized.entries.length === 0 && normalized.indexCoverage.reasons.includes("source_unavailable") ? null : { entries: normalized.entries, indexCoverage: normalized.indexCoverage };
855
+ }
856
+ };
857
+ };
858
+ var hostExpansionAccess = async (sourceId, session, context, signal) => {
859
+ const source = resolveRegisteredSource(context.sources, sourceId);
860
+ const sessionKey = hostSessionKey(sourceId, session);
861
+ const snapshot = await loadHostSnapshot(source, sessionKey, "expand", signal);
862
+ if (!snapshot) {
863
+ throw new MemorySourceError("session_not_found", `Session not found in source ${sourceId}: ${sessionKey}`);
864
+ }
865
+ const state = hostSourceState(snapshot);
866
+ const ref = hostRef(snapshot);
867
+ return {
868
+ ref,
869
+ state: () => state,
870
+ lineage: (branches) => hostLineage(snapshot, branches),
871
+ observe: () => observeHostSource(snapshot.displayKey, snapshot.revision, state.sourceHash),
872
+ normalizeFull: (branches, policy) => {
873
+ const normalized = normalizeRecords(
874
+ snapshot.records,
875
+ hostIdentity2(snapshot.displayKey, snapshot.sessionId, snapshot.metadata.cwd),
876
+ Number.MAX_SAFE_INTEGER,
877
+ { lineage: hostLineage(snapshot, branches), ...policy }
878
+ );
879
+ return {
880
+ entries: normalized.entries,
881
+ indexCoverage: mergedCoverage(normalized.indexCoverage, snapshot.coverageReasons)
882
+ };
883
+ }
884
+ };
885
+ };
886
+
446
887
  // src/memory/recall-service.ts
447
888
  async function processMemoryRecall(args, invocationContext, context, cache) {
448
889
  const query = typeof args.query === "string" ? args.query : void 0;
@@ -478,15 +919,38 @@ async function processMemoryRecall(args, invocationContext, context, cache) {
478
919
  const offset = typeof args.offset === "number" && args.offset >= 0 ? Math.floor(args.offset) : void 0;
479
920
  const pageSize = typeof args.pageSize === "number" && args.pageSize >= 1 ? Math.min(Math.floor(args.pageSize), RECALL_MAX_PAGE_SIZE) : RECALL_DEFAULT_PAGE_SIZE;
480
921
  const snippetChars = typeof args.snippetChars === "number" && args.snippetChars >= 80 ? Math.min(Math.floor(args.snippetChars), RECALL_MAX_SNIPPET_CHARS) : RECALL_DEFAULT_SNIPPET_CHARS;
481
- const refs = resolveRefs(scope, context, false);
482
- const liveResolver = liveBranchResolver(context);
922
+ const sourceId = typeof args.source === "string" && args.source.length > 0 ? args.source : void 0;
923
+ let plan;
924
+ if (sourceId !== void 0) {
925
+ if (scope !== void 0 && scope !== `source:${sourceId}` && !scope.startsWith("session:")) {
926
+ throw new Error(
927
+ "memory.recall scope must name the selected source, one of its sessions, or be omitted"
928
+ );
929
+ }
930
+ try {
931
+ plan = await hostRecallPlan(
932
+ resolveRegisteredSource(context.sources, sourceId),
933
+ sourceId,
934
+ scope?.startsWith("session:") ? scope.slice("session:".length).trim() : null,
935
+ context,
936
+ invocationContext.signal
937
+ );
938
+ } catch (error) {
939
+ const failure = memorySourceFailure(error);
940
+ if (!failure) throw error;
941
+ return recallFailure(failure);
942
+ }
943
+ } else {
944
+ plan = fileRecallPlan(resolveRefs(scope, context, false), context);
945
+ }
946
+ const refs = plan.refs;
483
947
  const options = resolveIndexOptions(
484
948
  context.config,
485
949
  context.agentDir,
486
950
  branches,
487
- liveResolver
951
+ sourceId === void 0 ? liveBranchResolver(context) : void 0
488
952
  );
489
- const hydrate = scope?.trim().startsWith("session:") ?? false;
953
+ const hydrate = sourceId !== void 0 ? plan.sessionKey !== null : scope?.trim().startsWith("session:") ?? false;
490
954
  if ((expectedSourceHash !== void 0 || expectedLineageFingerprint !== void 0) && !hydrate) {
491
955
  throw new Error("memory.recall integrity expectations require scope session:<id-or-path>");
492
956
  }
@@ -514,13 +978,14 @@ async function processMemoryRecall(args, invocationContext, context, cache) {
514
978
  if (since !== void 0) filters.since = since;
515
979
  if (until !== void 0) filters.until = until;
516
980
  const baseRequestArgs = {
981
+ ...sourceId ? { source: sourceId } : {},
517
982
  ...query === void 0 ? {} : { query },
518
983
  queryMode,
519
984
  ...queryMode === "literal" ? { queryMatch } : {},
520
985
  ...expectedSourceHash ? { expectedSourceHash } : {},
521
986
  ...expectedLineageFingerprint ? { expectedLineageFingerprint } : {},
522
987
  branches,
523
- scope: scope ?? "session",
988
+ scope: sourceId ? plan.sessionKey !== null ? `session:${plan.refs[0]?.file ?? plan.sessionKey}` : `source:${sourceId}` : scope ?? "session",
524
989
  ...offset === void 0 ? {} : { offset },
525
990
  pageSize,
526
991
  snippetChars,
@@ -539,7 +1004,7 @@ async function processMemoryRecall(args, invocationContext, context, cache) {
539
1004
  searchResult.matchMode === "structural" ? `memory.recall: ${searchResult.matchedCount} structural matches` : searchResult.matchMode === "combined" ? `memory.recall: ${searchResult.matchedCount} filtered matches` : query ? `memory.recall: ${searchResult.matchedCount} matches` : `memory.recall: ${searchResult.matchedCount} recent entries`
540
1005
  );
541
1006
  };
542
- const observationsBefore = observeSources(refs, branches, liveResolver);
1007
+ const observationsBefore = await plan.observeAll(branches);
543
1008
  const cached = offset === void 0 ? void 0 : cache.cachedRecallContinuation(
544
1009
  recallContinuationKey(baseRequestArgs),
545
1010
  observationsBefore
@@ -555,7 +1020,7 @@ async function processMemoryRecall(args, invocationContext, context, cache) {
555
1020
  snippetChars,
556
1021
  requestArgs: cached.requestArgs
557
1022
  });
558
- const observationsAfterCachedPage = observeSources(refs, branches, liveResolver);
1023
+ const observationsAfterCachedPage = await plan.observeAll(branches);
559
1024
  if (observationsBefore && observationsAfterCachedPage && sameSourceObservations(observationsBefore, observationsAfterCachedPage)) {
560
1025
  if (response2.next === null) cache.forgetRecallContinuation(cached);
561
1026
  updateProgress(cached.result);
@@ -564,12 +1029,8 @@ async function processMemoryRecall(args, invocationContext, context, cache) {
564
1029
  cache.forgetRecallContinuation(cached);
565
1030
  }
566
1031
  if (hydrate && refs[0]) {
567
- const state = fingerprintSource(refs[0].file);
568
- const lineage = reconstructSessionLineage(
569
- refs[0].file,
570
- branches,
571
- liveResolver?.(refs[0].file)
572
- );
1032
+ const state = plan.stateFor(refs[0]);
1033
+ const lineage = plan.lineageFor(refs[0], branches);
573
1034
  const sourceChanged = expectedSourceHash !== void 0 && state?.sourceHash !== expectedSourceHash;
574
1035
  const lineageChanged = expectedLineageFingerprint !== void 0 && lineage.fingerprint !== expectedLineageFingerprint;
575
1036
  if (state && (sourceChanged || lineageChanged)) {
@@ -582,13 +1043,7 @@ async function processMemoryRecall(args, invocationContext, context, cache) {
582
1043
  ));
583
1044
  }
584
1045
  }
585
- const index = loadTieredIndex(
586
- refs,
587
- resolveTierRefs(refs, context),
588
- options,
589
- hydrate,
590
- selectedRange
591
- );
1046
+ const index = plan.loadIndex(options, hydrate, selectedRange);
592
1047
  const hydratedShard = index.shards[0];
593
1048
  const hydratedSourceChanged = expectedSourceHash !== void 0 && hydratedShard?.sourceHash !== expectedSourceHash;
594
1049
  const hydratedLineageChanged = expectedLineageFingerprint !== void 0 && hydratedShard?.lineageFingerprint !== expectedLineageFingerprint;
@@ -652,7 +1107,7 @@ async function processMemoryRecall(args, invocationContext, context, cache) {
652
1107
  snippetChars,
653
1108
  requestArgs
654
1109
  });
655
- const observationsAfter = observeSources(refs, branches, liveResolver);
1110
+ const observationsAfter = await plan.observeAll(branches);
656
1111
  if (response.next !== null && observationsBefore && observationsAfter && sameSourceObservations(observationsBefore, observationsAfter)) {
657
1112
  cache.rememberRecallContinuation({
658
1113
  key: recallContinuationKey(requestArgs),
@@ -668,8 +1123,9 @@ async function processMemoryRecall(args, invocationContext, context, cache) {
668
1123
  }
669
1124
 
670
1125
  // src/memory/expand-service.ts
671
- async function processMemoryExpand(args, context, cache) {
1126
+ async function processMemoryExpand(args, context, cache, signal) {
672
1127
  const session = typeof args.session === "string" ? args.session : "";
1128
+ const sourceId = typeof args.source === "string" && args.source.length > 0 ? args.source : void 0;
673
1129
  const expectedSourceHash = typeof args.expectedSourceHash === "string" ? args.expectedSourceHash : void 0;
674
1130
  const expectedLineageFingerprint = typeof args.expectedLineageFingerprint === "string" ? args.expectedLineageFingerprint : void 0;
675
1131
  const branches = parseBranches(args.branches, "memory.expand");
@@ -708,17 +1164,27 @@ async function processMemoryExpand(args, context, cache) {
708
1164
  const textOffset = numeric(args.textOffset, 0, Number.MAX_SAFE_INTEGER);
709
1165
  const maxChars = Math.max(256, numeric(args.maxChars, EXPAND_DEFAULT_MAX_CHARS, EXPAND_MAX_CHARS));
710
1166
  const maxEntries = Math.max(1, numeric(args.maxEntries, EXPAND_DEFAULT_MAX_ENTRIES, EXPAND_MAX_ENTRIES));
711
- const ref = resolveSessionTarget(context.agentDir, session);
712
- if (!ref) {
1167
+ let access;
1168
+ if (sourceId !== void 0) {
1169
+ try {
1170
+ access = await hostExpansionAccess(sourceId, session, context, signal);
1171
+ } catch (error) {
1172
+ const failure = memorySourceFailure(error);
1173
+ if (!failure) throw error;
1174
+ return { session, error: failure, entries: [] };
1175
+ }
1176
+ } else {
1177
+ access = fileExpansionAccess(session, context);
1178
+ }
1179
+ if (!access) {
713
1180
  return {
714
1181
  session,
715
1182
  error: { code: "session_not_found", message: `Session not found: ${session}` },
716
1183
  entries: []
717
1184
  };
718
1185
  }
719
- const liveResolver = liveBranchResolver(context);
720
- const observedLiveBranch = branches === "active" ? liveResolver?.(ref.file) : void 0;
721
- const observedSource = observeSource(ref.file, branches, observedLiveBranch);
1186
+ const ref = access.ref;
1187
+ const observedSource = access.observe(branches);
722
1188
  let snapshot = cache.cachedExpansionSnapshot(ref.file, branches, observedSource);
723
1189
  if (snapshot) {
724
1190
  const sourceChanged = expectedSourceHash !== void 0 && snapshot.sourceHash !== expectedSourceHash;
@@ -739,7 +1205,7 @@ async function processMemoryExpand(args, context, cache) {
739
1205
  }
740
1206
  }
741
1207
  if (!snapshot) {
742
- const initialState = fingerprintSource(ref.file);
1208
+ const initialState = access.state();
743
1209
  if (!initialState) {
744
1210
  return {
745
1211
  session: ref.file,
@@ -747,12 +1213,7 @@ async function processMemoryExpand(args, context, cache) {
747
1213
  entries: []
748
1214
  };
749
1215
  }
750
- const initialLiveBranch = branches === "active" ? liveResolver?.(ref.file) : void 0;
751
- const initialLineage = reconstructSessionLineage(
752
- ref.file,
753
- branches,
754
- initialLiveBranch
755
- );
1216
+ const initialLineage = access.lineage(branches);
756
1217
  const sourceChanged = expectedSourceHash !== void 0 && initialState.sourceHash !== expectedSourceHash;
757
1218
  const lineageChanged = expectedLineageFingerprint !== void 0 && initialLineage.fingerprint !== expectedLineageFingerprint;
758
1219
  if (sourceChanged || lineageChanged) {
@@ -769,23 +1230,28 @@ async function processMemoryExpand(args, context, cache) {
769
1230
  entries: []
770
1231
  };
771
1232
  }
772
- const normalized = normalizeSession(
773
- ref.file,
774
- Number.MAX_SAFE_INTEGER,
775
- {
776
- lineage: initialLineage,
777
- indexThinking: true,
778
- indexToolOutput: true
779
- }
780
- ).entries;
781
- const finalState2 = fingerprintSource(ref.file);
782
- const finalLiveBranch = branches === "active" ? liveResolver?.(ref.file) : void 0;
783
- const finalLineage2 = reconstructSessionLineage(
784
- ref.file,
1233
+ const normalizedFull = access.normalizeFull(
785
1234
  branches,
786
- finalLiveBranch
1235
+ { indexThinking: true, indexToolOutput: true },
1236
+ initialLineage
787
1237
  );
788
- const finalObservation = observeSource(ref.file, branches, finalLiveBranch);
1238
+ if (!normalizedFull) {
1239
+ return {
1240
+ session: ref.file,
1241
+ error: stalePointerError(
1242
+ ref.file,
1243
+ expectedSourceHash ?? initialState.sourceHash,
1244
+ initialState.sourceHash,
1245
+ expectedLineageFingerprint ?? initialLineage.fingerprint,
1246
+ initialLineage.fingerprint
1247
+ ),
1248
+ entries: []
1249
+ };
1250
+ }
1251
+ const normalized = normalizedFull.entries;
1252
+ const finalState2 = access.state();
1253
+ const finalLineage2 = access.lineage(branches);
1254
+ const finalObservation = access.observe(branches);
789
1255
  if (!finalState2 || !finalObservation || finalState2.sourceHash !== initialState.sourceHash || finalLineage2.fingerprint !== initialLineage.fingerprint) {
790
1256
  return {
791
1257
  session: ref.file,
@@ -994,6 +1460,7 @@ async function processMemoryExpand(args, context, cache) {
994
1460
  const responseAt = (records, cursor2) => {
995
1461
  const hasNext = cursor2.position < selected.length;
996
1462
  const nextArgs = hasNext ? {
1463
+ ...sourceId ? { source: sourceId } : {},
997
1464
  session: ref.file,
998
1465
  expectedSourceHash: finalState.sourceHash,
999
1466
  expectedLineageFingerprint: finalLineage.fingerprint,
@@ -1071,12 +1538,11 @@ async function processMemoryExpand(args, context, cache) {
1071
1538
  response = responseAt(output, cursor);
1072
1539
  }
1073
1540
  }
1074
- const endingLiveBranch = branches === "active" ? liveResolver?.(ref.file) : void 0;
1075
- const endingObservation = observeSource(ref.file, branches, endingLiveBranch);
1541
+ const endingObservation = access.observe(branches);
1076
1542
  if (!endingObservation || !sameSourceObservation(snapshot.observation, endingObservation)) {
1077
1543
  cache.forgetExpansionSnapshot(snapshot);
1078
- const actualState = fingerprintSource(ref.file);
1079
- const actualLineage = reconstructSessionLineage(ref.file, branches, endingLiveBranch);
1544
+ const actualState = access.state();
1545
+ const actualLineage = access.lineage(branches);
1080
1546
  if (!actualState || !endingObservation || actualState.sourceHash !== snapshot.sourceHash || actualLineage.fingerprint !== snapshot.lineageFingerprint) {
1081
1547
  return {
1082
1548
  session: ref.file,
@@ -1102,10 +1568,51 @@ async function processMemoryExpand(args, context, cache) {
1102
1568
  }
1103
1569
 
1104
1570
  // src/memory/sessions-service.ts
1105
- async function processMemorySessions(args, context) {
1571
+ async function processMemorySessions(args, context, signal) {
1572
+ const sourceId = typeof args.source === "string" && args.source.length > 0 ? args.source : void 0;
1106
1573
  const scope = typeof args.scope === "string" ? args.scope : void 0;
1107
1574
  const branches = parseBranches(args.branches, "memory.sessions");
1108
1575
  const limit = typeof args.limit === "number" && Number.isSafeInteger(args.limit) && args.limit >= 1 ? Math.min(args.limit, SESSIONS_MAX) : SESSIONS_MAX;
1576
+ if (sourceId !== void 0) {
1577
+ try {
1578
+ const source = resolveRegisteredSource(context.sources, sourceId);
1579
+ const { snapshots, coverageReasons: coverageReasons2 } = await listHostSnapshots(source, limit, "list", signal);
1580
+ const options2 = resolveIndexOptions(context.config, context.agentDir, branches);
1581
+ const index2 = loadHostTieredIndex(snapshots, options2, false, void 0, coverageReasons2);
1582
+ const shards2 = new Map(index2.shards.map((shard) => [shard.sessionFile, shard]));
1583
+ const digests2 = new Map(index2.digests.map((digest) => [digest.file, digest]));
1584
+ const sessions2 = snapshots.map((snapshot) => {
1585
+ const tier = index2.tiers.get(snapshot.displayKey) ?? "cold";
1586
+ const shard = shards2.get(snapshot.displayKey);
1587
+ const digest = digests2.get(snapshot.displayKey);
1588
+ return {
1589
+ id: shard?.sessionId ?? digest?.sessionId ?? snapshot.sessionId,
1590
+ file: snapshot.displayKey,
1591
+ cwd: digest?.cwd ?? snapshot.metadata.cwd ?? "",
1592
+ mtime: snapshot.metadata.updatedAt ?? 0,
1593
+ entryCount: shard?.entries.length ?? digest?.entryCount ?? 0,
1594
+ tier,
1595
+ branches,
1596
+ lineageFingerprint: shard?.lineageFingerprint ?? digest?.lineageFingerprint ?? null
1597
+ };
1598
+ });
1599
+ return {
1600
+ scope: `source:${sourceId}`,
1601
+ branches,
1602
+ sessions: sessions2,
1603
+ ...index2.coverage.complete ? {} : { coverage: index2.coverage }
1604
+ };
1605
+ } catch (error) {
1606
+ const failure = memorySourceFailure(error);
1607
+ if (!failure) throw error;
1608
+ return {
1609
+ scope: `source:${sourceId}`,
1610
+ branches,
1611
+ sessions: [],
1612
+ error: failure
1613
+ };
1614
+ }
1615
+ }
1109
1616
  const refs = resolveRefs(scope, context, true).slice(0, limit);
1110
1617
  const options = resolveIndexOptions(
1111
1618
  context.config,
@@ -1569,6 +2076,11 @@ var sessionsOutputSchema = {
1569
2076
  error: errorOutputSchema
1570
2077
  }
1571
2078
  };
2079
+ var SOURCE_ARG_SCHEMA = {
2080
+ type: "string",
2081
+ minLength: 1,
2082
+ description: "Registered portable memory source id. When set, the call resolves against that source only \u2014 filesystem scopes are never consulted and there is no filesystem fallback."
2083
+ };
1572
2084
  var descriptors = [
1573
2085
  {
1574
2086
  name: "recall",
@@ -1576,6 +2088,7 @@ var descriptors = [
1576
2088
  inputSchema: {
1577
2089
  type: "object",
1578
2090
  properties: {
2091
+ source: SOURCE_ARG_SCHEMA,
1579
2092
  query: { type: "string", maxLength: 4096 },
1580
2093
  queryMode: {
1581
2094
  type: "string",
@@ -1684,7 +2197,8 @@ var descriptors = [
1684
2197
  inputSchema: {
1685
2198
  type: "object",
1686
2199
  properties: {
1687
- session: { type: "string", description: "Exact session file path or unambiguous id." },
2200
+ source: SOURCE_ARG_SCHEMA,
2201
+ session: { type: "string", description: "Exact session file path, unambiguous id, or source session key." },
1688
2202
  expectedSourceHash: {
1689
2203
  type: "string",
1690
2204
  description: "SHA-256 from a prior pointer; stale sources are refused."
@@ -1762,6 +2276,7 @@ var descriptors = [
1762
2276
  inputSchema: {
1763
2277
  type: "object",
1764
2278
  properties: {
2279
+ source: SOURCE_ARG_SCHEMA,
1765
2280
  scope: { type: "string" },
1766
2281
  branches: {
1767
2282
  type: "string",
@@ -1797,6 +2312,12 @@ var normalizeMemoryArgs = actionArgNormalizer(
1797
2312
  () => descriptors,
1798
2313
  MEMORY_ARG_NORMALIZATION
1799
2314
  );
2315
+ var memoryActionSchemas = Object.fromEntries(
2316
+ descriptors.map((descriptor) => [
2317
+ descriptor.name,
2318
+ { inputSchema: descriptor.inputSchema, outputSchema: descriptor.outputSchema }
2319
+ ])
2320
+ );
1800
2321
  var MemoryProvider = class {
1801
2322
  constructor(context) {
1802
2323
  this.context = context;
@@ -1822,9 +2343,9 @@ var MemoryProvider = class {
1822
2343
  case "recall":
1823
2344
  return await processMemoryRecall(args, invocationContext, this.context, this.cache);
1824
2345
  case "expand":
1825
- return await processMemoryExpand(args, this.context, this.cache);
2346
+ return await processMemoryExpand(args, this.context, this.cache, invocationContext.signal);
1826
2347
  case "sessions":
1827
- return await processMemorySessions(args, this.context);
2348
+ return await processMemorySessions(args, this.context, invocationContext.signal);
1828
2349
  default:
1829
2350
  throw new Error(`Unknown memory action: ${actionName}`);
1830
2351
  }
@@ -1848,7 +2369,16 @@ var MemoryProvider = class {
1848
2369
  export {
1849
2370
  uniqueDeclaredKeyForSpelling,
1850
2371
  actionArgNormalizer,
2372
+ MemoryRequestCache,
2373
+ MEMORY_SOURCE_INTERFACE_VERSION,
2374
+ MemorySourceError,
2375
+ defineMemorySource,
2376
+ createMemorySourceRegistry,
2377
+ processMemoryRecall,
2378
+ processMemoryExpand,
2379
+ processMemorySessions,
1851
2380
  normalizeMemoryArgs,
2381
+ memoryActionSchemas,
1852
2382
  MemoryProvider
1853
2383
  };
1854
- //# sourceMappingURL=chunk-AIN5KTE6.js.map
2384
+ //# sourceMappingURL=chunk-GUQT67PQ.js.map