@gmickel/gno 1.27.1 → 1.29.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 (195) hide show
  1. package/README.md +46 -20
  2. package/assets/skill/SKILL.md +59 -1
  3. package/browser-extension/artifacts/gno-browser-clipper-v1.29.0.zip +0 -0
  4. package/browser-extension/artifacts/gno-browser-clipper-v1.29.0.zip.sha256 +1 -0
  5. package/browser-extension/dist/chunk-b2zm0jjd.js +50 -0
  6. package/browser-extension/dist/manifest.json +1 -1
  7. package/browser-extension/dist/preview.html +1 -1
  8. package/browser-extension/dist/service-worker.js +6 -6
  9. package/package.json +2 -2
  10. package/spec/cli.md +82 -5
  11. package/spec/db/schema.sql +31 -0
  12. package/spec/mcp.md +71 -0
  13. package/spec/output-schemas/ask.schema.json +156 -1
  14. package/spec/output-schemas/browser-clip-preview.schema.json +30 -0
  15. package/spec/output-schemas/collection-egress-check.schema.json +91 -0
  16. package/spec/output-schemas/collection-egress-policy-set.schema.json +56 -0
  17. package/spec/output-schemas/collection-egress-policy.schema.json +41 -0
  18. package/spec/output-schemas/context-capsule-v1.schema.json +183 -3
  19. package/spec/output-schemas/context-capsule-verification.schema.json +1 -1
  20. package/spec/output-schemas/egress-audit-management.schema.json +88 -0
  21. package/spec/output-schemas/get.schema.json +94 -0
  22. package/spec/output-schemas/mcp-http-error.schema.json +113 -2
  23. package/spec/output-schemas/mcp-job-status.schema.json +28 -0
  24. package/spec/output-schemas/multi-get.schema.json +128 -0
  25. package/spec/output-schemas/publish-artifact.schema.json +32 -0
  26. package/spec/output-schemas/record-import.schema.json +223 -0
  27. package/spec/output-schemas/retrieval-trace-common.schema.json +31 -0
  28. package/spec/output-schemas/retrieval-trace-export.schema.json +3 -0
  29. package/spec/output-schemas/retrieval-trace-qrels.schema.json +3 -0
  30. package/spec/output-schemas/retrieval-trace-summary.schema.json +4 -0
  31. package/spec/output-schemas/search-result.schema.json +94 -0
  32. package/spec/output-schemas/search-results.schema.json +155 -0
  33. package/spec/project-profile.schema.json +66 -0
  34. package/src/app/context-format.ts +1 -1
  35. package/src/app/context-runtime-contract.ts +16 -7
  36. package/src/cli/commands/ask.ts +7 -0
  37. package/src/cli/commands/collection/index.ts +5 -0
  38. package/src/cli/commands/collection/policy.ts +126 -0
  39. package/src/cli/commands/context-build.ts +5 -0
  40. package/src/cli/commands/daemon.ts +45 -3
  41. package/src/cli/commands/egress-audit.ts +68 -0
  42. package/src/cli/commands/embed.ts +2 -0
  43. package/src/cli/commands/get.ts +12 -2
  44. package/src/cli/commands/index-cmd.ts +13 -0
  45. package/src/cli/commands/multi-get.ts +9 -2
  46. package/src/cli/commands/query.ts +12 -0
  47. package/src/cli/commands/replay.ts +13 -3
  48. package/src/cli/commands/shared.ts +28 -0
  49. package/src/cli/commands/update.ts +5 -0
  50. package/src/cli/commands/vsearch.ts +3 -1
  51. package/src/cli/program.ts +127 -0
  52. package/src/config/index.ts +9 -0
  53. package/src/config/project-profile.ts +2 -0
  54. package/src/config/types.ts +66 -0
  55. package/src/converters/adapters/browser-export/adapter.ts +199 -0
  56. package/src/converters/adapters/browser-export/formats.ts +358 -0
  57. package/src/converters/adapters/email/adapter.ts +429 -0
  58. package/src/converters/adapters/email/html.ts +162 -0
  59. package/src/converters/adapters/email/mime.ts +454 -0
  60. package/src/converters/adapters/email/parameters.ts +77 -0
  61. package/src/converters/adapters/ical/adapter.ts +475 -0
  62. package/src/converters/adapters/ical/recurrence.ts +186 -0
  63. package/src/converters/adapters/jsonl/adapter.ts +238 -0
  64. package/src/converters/adapters/jsonl/config.ts +105 -0
  65. package/src/converters/adapters/shared/html-text.ts +165 -0
  66. package/src/converters/adapters/shared/record-utils.ts +79 -0
  67. package/src/converters/adapters/shared/utf8-lines.ts +141 -0
  68. package/src/converters/adapters/transcript/adapter.ts +295 -0
  69. package/src/converters/adapters/transcript/json.ts +184 -0
  70. package/src/converters/adapters/transcript/model.ts +171 -0
  71. package/src/converters/adapters/transcript/text.ts +58 -0
  72. package/src/converters/adapters/transcript/timed.ts +152 -0
  73. package/src/converters/index.ts +11 -1
  74. package/src/converters/mime.ts +8 -0
  75. package/src/converters/pipeline.ts +15 -1
  76. package/src/converters/registry.ts +37 -1
  77. package/src/converters/types.ts +136 -0
  78. package/src/core/browser-clip.ts +16 -1
  79. package/src/core/collection-egress-policy-projection.ts +28 -0
  80. package/src/core/collection-egress-policy-service.ts +443 -0
  81. package/src/core/collection-egress-policy-validation.ts +242 -0
  82. package/src/core/config-mutation.ts +31 -19
  83. package/src/core/context-capsule-schema.ts +198 -1
  84. package/src/core/context-capsule-validation.ts +4 -4
  85. package/src/core/context-capsule-verification.ts +5 -1
  86. package/src/core/context-capsule.ts +177 -112
  87. package/src/core/context-evidence.ts +93 -15
  88. package/src/core/destination-classifier.ts +402 -0
  89. package/src/core/document-capabilities.ts +12 -0
  90. package/src/core/egress-audit.ts +239 -0
  91. package/src/core/egress-authorization.ts +50 -0
  92. package/src/core/egress-enforcement.ts +264 -0
  93. package/src/core/egress-policy.ts +440 -0
  94. package/src/core/egress-provenance.ts +336 -0
  95. package/src/core/job-manager.ts +37 -0
  96. package/src/core/network-boundary-inventory.ts +261 -0
  97. package/src/core/project-profile-apply-state.ts +5 -0
  98. package/src/core/project-profile.ts +4 -0
  99. package/src/core/record-metadata.ts +49 -0
  100. package/src/core/retrieval-qrels.ts +6 -0
  101. package/src/core/retrieval-replay.ts +18 -4
  102. package/src/core/retrieval-trace-export.ts +23 -4
  103. package/src/core/retrieval-trace-management-helpers.ts +1 -0
  104. package/src/core/retrieval-trace-management-types.ts +3 -0
  105. package/src/core/retrieval-trace-management.ts +9 -1
  106. package/src/core/retrieval-trace-session.ts +94 -0
  107. package/src/core/retrieval-trace.ts +22 -0
  108. package/src/ingestion/record-adapter-canonical.ts +433 -0
  109. package/src/ingestion/record-adapter.ts +448 -0
  110. package/src/ingestion/record-container.ts +704 -0
  111. package/src/ingestion/record-path.ts +20 -0
  112. package/src/ingestion/record-sync.ts +70 -0
  113. package/src/ingestion/sync.ts +243 -36
  114. package/src/ingestion/types.ts +71 -1
  115. package/src/ingestion/walker.ts +31 -11
  116. package/src/llm/errors.ts +10 -0
  117. package/src/llm/http-inference.ts +175 -0
  118. package/src/llm/http-policy.ts +537 -0
  119. package/src/llm/httpEmbedding.ts +47 -28
  120. package/src/llm/httpGeneration.ts +31 -18
  121. package/src/llm/httpRerank.ts +30 -18
  122. package/src/llm/index.ts +1 -0
  123. package/src/llm/nodeLlamaCpp/adapter.ts +55 -20
  124. package/src/llm/pinned-http-connection.ts +177 -0
  125. package/src/mcp/context.ts +53 -1
  126. package/src/mcp/http-egress.ts +220 -0
  127. package/src/mcp/http-security.ts +32 -21
  128. package/src/mcp/http-session.ts +13 -2
  129. package/src/mcp/http-transport.ts +96 -6
  130. package/src/mcp/sync-egress.ts +24 -0
  131. package/src/mcp/tools/add-collection.ts +4 -0
  132. package/src/mcp/tools/ask.ts +1 -1
  133. package/src/mcp/tools/context.ts +9 -2
  134. package/src/mcp/tools/egress.ts +247 -0
  135. package/src/mcp/tools/embed.ts +1 -0
  136. package/src/mcp/tools/get.ts +10 -2
  137. package/src/mcp/tools/index-cmd.ts +7 -0
  138. package/src/mcp/tools/index.ts +98 -0
  139. package/src/mcp/tools/multi-get.ts +9 -2
  140. package/src/mcp/tools/query.ts +10 -0
  141. package/src/mcp/tools/remove-collection.ts +4 -0
  142. package/src/mcp/tools/sync.ts +6 -0
  143. package/src/mcp/tools/trace.ts +7 -1
  144. package/src/mcp/tools/vsearch.ts +1 -0
  145. package/src/mcp/tools/workspace-write.ts +2 -0
  146. package/src/pipeline/egress-lineage.ts +124 -0
  147. package/src/pipeline/filters.ts +1 -1
  148. package/src/pipeline/graph-retrieval.ts +7 -4
  149. package/src/pipeline/hybrid.ts +21 -4
  150. package/src/pipeline/result-context.ts +12 -4
  151. package/src/pipeline/search.ts +23 -4
  152. package/src/pipeline/types.ts +6 -0
  153. package/src/pipeline/vsearch.ts +67 -28
  154. package/src/publish/artifact.ts +31 -3
  155. package/src/publish/export-service.ts +25 -0
  156. package/src/sdk/client.ts +119 -2
  157. package/src/sdk/documents.ts +13 -5
  158. package/src/sdk/embed.ts +1 -0
  159. package/src/sdk/types.ts +33 -0
  160. package/src/serve/browse-tree.ts +4 -2
  161. package/src/serve/clipper-capture.ts +5 -1
  162. package/src/serve/closed-json.ts +61 -0
  163. package/src/serve/config-sync.ts +4 -1
  164. package/src/serve/context.ts +1 -0
  165. package/src/serve/doc-events.ts +110 -39
  166. package/src/serve/public/components/CollectionEgressCheckPanel.tsx +411 -0
  167. package/src/serve/public/components/CollectionEgressPolicyEditor.tsx +78 -0
  168. package/src/serve/public/components/CollectionModelDialog.tsx +93 -9
  169. package/src/serve/public/components/EgressAuditPanel.tsx +278 -0
  170. package/src/serve/public/globals.built.css +1 -1
  171. package/src/serve/public/pages/Collections.tsx +3 -0
  172. package/src/serve/resident-request.ts +80 -3
  173. package/src/serve/resident-runtime.ts +63 -1
  174. package/src/serve/routes/api.ts +237 -65
  175. package/src/serve/routes/clipper.ts +64 -1
  176. package/src/serve/routes/mcp.ts +6 -0
  177. package/src/serve/routes/traces.ts +27 -4
  178. package/src/serve/server.ts +118 -7
  179. package/src/store/migrations/022-record-export-lineage.ts +42 -0
  180. package/src/store/migrations/023-collection-egress-policy.ts +61 -0
  181. package/src/store/migrations/024-egress-derived-lineage.ts +121 -0
  182. package/src/store/migrations/025-collection-egress-policy-revision.ts +40 -0
  183. package/src/store/migrations/index.ts +8 -0
  184. package/src/store/retrieval-trace-codec.ts +15 -0
  185. package/src/store/sqlite/adapter.ts +284 -14
  186. package/src/store/sqlite/change-journal-store.ts +41 -2
  187. package/src/store/sqlite/egress-audit-store.ts +485 -0
  188. package/src/store/sqlite/retrieval-trace-management-store.ts +47 -6
  189. package/src/store/sqlite/retrieval-trace-retention.ts +2 -0
  190. package/src/store/sqlite/retrieval-trace-rows.ts +8 -1
  191. package/src/store/sqlite/retrieval-trace-store.ts +104 -5
  192. package/src/store/types.ts +166 -1
  193. package/browser-extension/artifacts/gno-browser-clipper-v1.27.1.zip +0 -0
  194. package/browser-extension/artifacts/gno-browser-clipper-v1.27.1.zip.sha256 +0 -1
  195. package/browser-extension/dist/chunk-vn5f663b.js +0 -50
@@ -3,8 +3,10 @@ import { z } from "zod";
3
3
  import { canonicalizeIndexName } from "../app/index-name";
4
4
  import {
5
5
  contextCapsulePayloadV1Schema,
6
- type ContextCapsulePayloadV1,
6
+ contextCapsulePayloadV1_1Schema,
7
+ type ContextCapsulePayload,
7
8
  } from "./context-capsule-schema";
9
+ import { createEgressLineage, type EgressLineage } from "./egress-provenance";
8
10
 
9
11
  const sha256Schema = z.string().regex(/^[a-f0-9]{64}$/);
10
12
 
@@ -51,116 +53,155 @@ const normalizeSet = (values: readonly string[]): string[] =>
51
53
  [...new Set(values.map(normalizeText))].sort(compareCodeUnits);
52
54
 
53
55
  const normalizePayload = (
54
- value: ContextCapsulePayloadV1
55
- ): ContextCapsulePayloadV1 => ({
56
- ...value,
57
- goal: normalizeText(value.goal),
58
- query: normalizeText(value.query),
59
- scope: {
60
- ...value.scope,
61
- indexName: canonicalizeIndexName(value.scope.indexName),
62
- collections: normalizeSet(value.scope.collections),
63
- uriPrefix:
64
- value.scope.uriPrefix === null
65
- ? null
66
- : normalizeText(value.scope.uriPrefix),
67
- tagsAll: normalizeSet(value.scope.tagsAll),
68
- tagsAny: normalizeSet(value.scope.tagsAny),
69
- categories: normalizeSet(value.scope.categories),
70
- since: normalizeDate(value.scope.since),
71
- until: normalizeDate(value.scope.until),
72
- },
73
- retrieval: {
74
- ...value.retrieval,
75
- facets: normalizeSet(value.retrieval.facets),
76
- queryVariants: value.retrieval.queryVariants.map(normalizeText),
77
- request: {
78
- ...value.retrieval.request,
79
- author:
80
- value.retrieval.request.author === null
56
+ value: ContextCapsulePayload
57
+ ): ContextCapsulePayload =>
58
+ ({
59
+ ...value,
60
+ goal: normalizeText(value.goal),
61
+ query: normalizeText(value.query),
62
+ scope: {
63
+ ...value.scope,
64
+ indexName: canonicalizeIndexName(value.scope.indexName),
65
+ collections: normalizeSet(value.scope.collections),
66
+ uriPrefix:
67
+ value.scope.uriPrefix === null
81
68
  ? null
82
- : normalizeText(value.retrieval.request.author),
83
- lang:
84
- value.retrieval.request.lang === null
85
- ? null
86
- : normalizeText(value.retrieval.request.lang),
87
- ...(value.retrieval.request.intent === undefined
69
+ : normalizeText(value.scope.uriPrefix),
70
+ tagsAll: normalizeSet(value.scope.tagsAll),
71
+ tagsAny: normalizeSet(value.scope.tagsAny),
72
+ categories: normalizeSet(value.scope.categories),
73
+ since: normalizeDate(value.scope.since),
74
+ until: normalizeDate(value.scope.until),
75
+ },
76
+ retrieval: {
77
+ ...value.retrieval,
78
+ facets: normalizeSet(value.retrieval.facets),
79
+ queryVariants: value.retrieval.queryVariants.map(normalizeText),
80
+ request: {
81
+ ...value.retrieval.request,
82
+ author:
83
+ value.retrieval.request.author === null
84
+ ? null
85
+ : normalizeText(value.retrieval.request.author),
86
+ lang:
87
+ value.retrieval.request.lang === null
88
+ ? null
89
+ : normalizeText(value.retrieval.request.lang),
90
+ ...(value.retrieval.request.intent === undefined
91
+ ? {}
92
+ : {
93
+ intent:
94
+ value.retrieval.request.intent === null
95
+ ? null
96
+ : normalizeText(value.retrieval.request.intent),
97
+ }),
98
+ ...(value.retrieval.request.exclude === undefined
99
+ ? {}
100
+ : { exclude: normalizeSet(value.retrieval.request.exclude) }),
101
+ queryModes: value.retrieval.request.queryModes.map((mode) => ({
102
+ ...mode,
103
+ text: normalizeText(mode.text),
104
+ })),
105
+ },
106
+ capabilityStates: Object.fromEntries(
107
+ Object.entries(value.retrieval.capabilityStates).map(([key, state]) => [
108
+ key,
109
+ { ...state, fallbackReasons: normalizeSet(state.fallbackReasons) },
110
+ ])
111
+ ) as typeof value.retrieval.capabilityStates,
112
+ },
113
+ fallbacks: [...value.fallbacks].sort((left, right) =>
114
+ compareCodeUnits(
115
+ `${left.code}\0${left.capability}`,
116
+ `${right.code}\0${right.capability}`
117
+ )
118
+ ),
119
+ ...(value.schemaVersion === "1.1"
120
+ ? {
121
+ egressLineage: {
122
+ ...value.egressLineage,
123
+ sources: [...value.egressLineage.sources],
124
+ },
125
+ }
126
+ : {}),
127
+ evidence: value.evidence.map((item) => ({
128
+ ...item,
129
+ title: item.title === null ? null : normalizeText(item.title),
130
+ heading: item.heading === null ? null : normalizeText(item.heading),
131
+ modifiedAt: normalizeDate(item.modifiedAt),
132
+ documentDate: normalizeDocumentDate(item.documentDate),
133
+ observedAt: normalizeDate(item.observedAt),
134
+ contextIds: normalizeSet(item.contextIds),
135
+ ...(item.egressLineage === undefined
136
+ ? {}
137
+ : {
138
+ egressLineage: {
139
+ ...item.egressLineage,
140
+ sources: [...item.egressLineage.sources],
141
+ },
142
+ }),
143
+ ...(item.retrievalSources === undefined
88
144
  ? {}
89
145
  : {
90
- intent:
91
- value.retrieval.request.intent === null
92
- ? null
93
- : normalizeText(value.retrieval.request.intent),
146
+ retrievalSources: [...new Set(item.retrievalSources)].sort(
147
+ compareCodeUnits
148
+ ),
94
149
  }),
95
- ...(value.retrieval.request.exclude === undefined
150
+ facets: normalizeSet(item.facets),
151
+ ...(item.record === undefined
96
152
  ? {}
97
- : { exclude: normalizeSet(value.retrieval.request.exclude) }),
98
- queryModes: value.retrieval.request.queryModes.map((mode) => ({
99
- ...mode,
100
- text: normalizeText(mode.text),
101
- })),
153
+ : {
154
+ record: {
155
+ ...item.record,
156
+ anchors: item.record.anchors.map((anchor) => ({
157
+ ...anchor,
158
+ value: normalizeText(anchor.value),
159
+ ...(anchor.endValue === undefined
160
+ ? {}
161
+ : { endValue: normalizeText(anchor.endValue) }),
162
+ })),
163
+ ...(item.record.participants === undefined
164
+ ? {}
165
+ : { participants: normalizeSet(item.record.participants) }),
166
+ ...(item.record.categories === undefined
167
+ ? {}
168
+ : { categories: normalizeSet(item.record.categories) }),
169
+ },
170
+ }),
171
+ })),
172
+ guidance: {
173
+ ...value.guidance,
174
+ configuredContexts: [...value.guidance.configuredContexts]
175
+ .map((item) => ({
176
+ ...item,
177
+ scopeKey: normalizeText(item.scopeKey),
178
+ text: normalizeText(item.text),
179
+ }))
180
+ .sort((left, right) =>
181
+ compareCodeUnits(left.contextId, right.contextId)
182
+ ),
102
183
  },
103
- capabilityStates: Object.fromEntries(
104
- Object.entries(value.retrieval.capabilityStates).map(([key, state]) => [
105
- key,
106
- { ...state, fallbackReasons: normalizeSet(state.fallbackReasons) },
107
- ])
108
- ) as typeof value.retrieval.capabilityStates,
109
- },
110
- fallbacks: [...value.fallbacks].sort((left, right) =>
111
- compareCodeUnits(
112
- `${left.code}\0${left.capability}`,
113
- `${right.code}\0${right.capability}`
114
- )
115
- ),
116
- evidence: value.evidence.map((item) => ({
117
- ...item,
118
- title: item.title === null ? null : normalizeText(item.title),
119
- heading: item.heading === null ? null : normalizeText(item.heading),
120
- modifiedAt: normalizeDate(item.modifiedAt),
121
- documentDate: normalizeDocumentDate(item.documentDate),
122
- observedAt: normalizeDate(item.observedAt),
123
- contextIds: normalizeSet(item.contextIds),
124
- ...(item.retrievalSources === undefined
125
- ? {}
126
- : {
127
- retrievalSources: [...new Set(item.retrievalSources)].sort(
128
- compareCodeUnits
129
- ),
130
- }),
131
- facets: normalizeSet(item.facets),
132
- })),
133
- guidance: {
134
- ...value.guidance,
135
- configuredContexts: [...value.guidance.configuredContexts]
136
- .map((item) => ({
137
- ...item,
138
- scopeKey: normalizeText(item.scopeKey),
139
- text: normalizeText(item.text),
140
- }))
141
- .sort((left, right) => compareCodeUnits(left.contextId, right.contextId)),
142
- },
143
- coverage: {
144
- ...value.coverage,
145
- requestedFacets: normalizeSet(value.coverage.requestedFacets),
146
- coveredFacets: [...value.coverage.coveredFacets]
147
- .map((item) => ({
148
- facet: normalizeText(item.facet),
149
- evidenceIds: normalizeSet(item.evidenceIds),
150
- }))
151
- .sort((left, right) => compareCodeUnits(left.facet, right.facet)),
152
- unresolvedFacets: normalizeSet(value.coverage.unresolvedFacets),
153
- gaps: [...value.coverage.gaps].sort((left, right) =>
154
- compareCodeUnits(
155
- `${left.facet}\0${left.code}`,
156
- `${right.facet}\0${right.code}`
157
- )
184
+ coverage: {
185
+ ...value.coverage,
186
+ requestedFacets: normalizeSet(value.coverage.requestedFacets),
187
+ coveredFacets: [...value.coverage.coveredFacets]
188
+ .map((item) => ({
189
+ facet: normalizeText(item.facet),
190
+ evidenceIds: normalizeSet(item.evidenceIds),
191
+ }))
192
+ .sort((left, right) => compareCodeUnits(left.facet, right.facet)),
193
+ unresolvedFacets: normalizeSet(value.coverage.unresolvedFacets),
194
+ gaps: [...value.coverage.gaps].sort((left, right) =>
195
+ compareCodeUnits(
196
+ `${left.facet}\0${left.code}`,
197
+ `${right.facet}\0${right.code}`
198
+ )
199
+ ),
200
+ },
201
+ warnings: [...value.warnings].sort((left, right) =>
202
+ compareCodeUnits(left.code, right.code)
158
203
  ),
159
- },
160
- warnings: [...value.warnings].sort((left, right) =>
161
- compareCodeUnits(left.code, right.code)
162
- ),
163
- });
204
+ }) as ContextCapsulePayload;
164
205
 
165
206
  export type ContextCapsuleErrorCode =
166
207
  | "identity_mismatch"
@@ -247,9 +288,14 @@ const contractError = (
247
288
  );
248
289
  };
249
290
 
250
- const parsePayload = (input: unknown): ContextCapsulePayloadV1 => {
291
+ const contextCapsulePayloadSchema = z.union([
292
+ contextCapsulePayloadV1Schema,
293
+ contextCapsulePayloadV1_1Schema,
294
+ ]);
295
+
296
+ const parsePayload = (input: unknown): ContextCapsulePayload => {
251
297
  try {
252
- return contextCapsulePayloadV1Schema.parse(input);
298
+ return contextCapsulePayloadSchema.parse(input);
253
299
  } catch (error) {
254
300
  throw contractError(input, error);
255
301
  }
@@ -259,7 +305,7 @@ export interface ContextCapsuleCreateOptions {
259
305
  countTokens?: (accountingJson: string) => number;
260
306
  }
261
307
 
262
- const identityPayload = (payload: ContextCapsulePayloadV1) => {
308
+ const identityPayload = (payload: ContextCapsulePayload) => {
263
309
  const {
264
310
  usedBytes: _usedBytes,
265
311
  usedTokens: _usedTokens,
@@ -268,7 +314,7 @@ const identityPayload = (payload: ContextCapsulePayloadV1) => {
268
314
  return { ...payload, budget: stableBudget };
269
315
  };
270
316
 
271
- const accountingPayload = <T extends ContextCapsulePayloadV1>(value: T) => {
317
+ const accountingPayload = <T extends ContextCapsulePayload>(value: T) => {
272
318
  const {
273
319
  usedBytes: _usedBytes,
274
320
  usedTokens: _usedTokens,
@@ -292,7 +338,7 @@ const activeTokenCount = (
292
338
  };
293
339
 
294
340
  const fixedPointCapsule = (
295
- payloadInput: ContextCapsulePayloadV1,
341
+ payloadInput: ContextCapsulePayload,
296
342
  options: ContextCapsuleCreateOptions
297
343
  ) => {
298
344
  const capsuleId = hashCanonical(identityPayload(payloadInput));
@@ -342,9 +388,10 @@ const fixedPointCapsule = (
342
388
  );
343
389
  };
344
390
 
345
- const contextCapsuleBaseV1Schema = contextCapsulePayloadV1Schema.extend({
346
- capsuleId: sha256Schema,
347
- });
391
+ const contextCapsuleBaseV1Schema = z.union([
392
+ contextCapsulePayloadV1Schema.extend({ capsuleId: sha256Schema }),
393
+ contextCapsulePayloadV1_1Schema.extend({ capsuleId: sha256Schema }),
394
+ ]);
348
395
 
349
396
  export const contextCapsuleV1Schema = contextCapsuleBaseV1Schema.superRefine(
350
397
  (value, context) => {
@@ -450,6 +497,24 @@ export const canonicalContextCapsuleJson = (input: unknown): string =>
450
497
  export const canonicalContextCapsuleAccountingJson = (input: unknown): string =>
451
498
  canonicalJson(accountingPayload(parseContextCapsuleV1(input)));
452
499
 
500
+ /**
501
+ * Runtime-only conservative projection for legacy 1.0 Capsules. Canonical
502
+ * bytes and capsuleId remain governed by the original schema.
503
+ */
504
+ export const contextCapsuleEgressLineage = (input: unknown): EgressLineage => {
505
+ const capsule = parseContextCapsuleV1(input);
506
+ if (capsule.schemaVersion === "1.1") return capsule.egressLineage;
507
+ return createEgressLineage(
508
+ [...new Set(capsule.evidence.map(({ collection }) => collection))].map(
509
+ (collection) => ({
510
+ collection,
511
+ policy: "local_only" as const,
512
+ source: "legacy_default" as const,
513
+ })
514
+ )
515
+ );
516
+ };
517
+
453
518
  export {
454
519
  contextCapsuleVerificationEvidenceSchema,
455
520
  contextCapsuleVerificationSchema,
@@ -1,5 +1,6 @@
1
1
  /** Strict indexed-evidence loading for Context Capsule compilation. */
2
2
 
3
+ import type { EgressPolicy } from "../config/types";
3
4
  import type { SearchResults } from "../pipeline/types";
4
5
  import type {
5
6
  ActivationIndexSnapshot,
@@ -12,7 +13,7 @@ import type {
12
13
  ContextCanonicalProjection,
13
14
  MaterializedContextCandidate,
14
15
  } from "./context-budget";
15
- import type { ContextCapsulePayloadV1 } from "./context-capsule-schema";
16
+ import type { ContextCapsulePayloadV1_1 } from "./context-capsule-schema";
16
17
  import type {
17
18
  ContextCanonicalPlanDraft,
18
19
  ContextCompilerInput,
@@ -21,6 +22,8 @@ import type {
21
22
  ContextRetrievalCandidate,
22
23
  ContextRetrievalRequest,
23
24
  } from "./context-compiler";
25
+ import type { EgressLineage } from "./egress-provenance";
26
+ import type { RecordEvidenceMetadata } from "./record-metadata";
24
27
 
25
28
  import { decorateUriForIndex, deriveDocid, parseUri } from "../app/constants";
26
29
  import { canonicalizeIndexName } from "../app/index-name";
@@ -35,6 +38,8 @@ import {
35
38
  } from "./context-capsule-validation";
36
39
  import { planContextEvidence } from "./context-compiler";
37
40
  import { projectContextEvidenceMetadata } from "./context-evidence-metadata";
41
+ import { createEgressLineage, resolveEgressLineage } from "./egress-provenance";
42
+ import { projectRecordEvidenceMetadata } from "./record-metadata";
38
43
  import {
39
44
  extractInclusiveLines,
40
45
  extractSections,
@@ -80,6 +85,7 @@ export class ContextEvidenceError extends Error {
80
85
 
81
86
  export interface ContextEvidenceSnapshot {
82
87
  collections: string[];
88
+ egressLineage: EgressLineage;
83
89
  contexts: ContextRow[];
84
90
  documents: ContextEvidenceSnapshotDocument[];
85
91
  contextFingerprint: string;
@@ -103,7 +109,9 @@ export interface ContextEvidenceValue {
103
109
  observedAt: null;
104
110
  contextIds: string[];
105
111
  trust: "untrusted";
106
- egress: "unavailable";
112
+ egress: EgressPolicy | "unavailable";
113
+ egressLineage?: EgressLineage;
114
+ record?: RecordEvidenceMetadata;
107
115
  }
108
116
 
109
117
  export type ContextEvidenceCompilerInput = Omit<
@@ -114,6 +122,7 @@ export type ContextEvidenceCompilerInput = Omit<
114
122
  export interface ContextEvidenceProjectionContext {
115
123
  contextFingerprint: string;
116
124
  indexFingerprint: string;
125
+ egressLineage: EgressLineage;
117
126
  }
118
127
 
119
128
  export interface ContextEvidenceCompilerDeps<P> {
@@ -205,18 +214,34 @@ export const captureContextEvidenceSnapshot = async (
205
214
  "context_load_failed",
206
215
  "Failed to load configured contexts"
207
216
  ).map((row) => ({ ...row }));
217
+ const collectionRows = unwrapStore(
218
+ await store.getCollections(),
219
+ "collection_load_failed",
220
+ "Failed to load indexed collections"
221
+ );
208
222
  const collections =
209
223
  requestedCollections.length > 0
210
224
  ? [...new Set(requestedCollections)].sort(compareCodeUnits)
211
- : [
212
- ...new Set(
213
- unwrapStore(
214
- await store.getCollections(),
215
- "collection_load_failed",
216
- "Failed to load indexed collections"
217
- ).map((row) => row.name)
218
- ),
219
- ].sort(compareCodeUnits);
225
+ : [...new Set(collectionRows.map((row) => row.name))].sort(
226
+ compareCodeUnits
227
+ );
228
+ let egressLineage: EgressLineage;
229
+ try {
230
+ egressLineage = resolveEgressLineage(
231
+ collectionRows.map((row) => ({
232
+ collection: row.name,
233
+ policy: row.egressPolicy,
234
+ source: row.egressPolicySource,
235
+ })),
236
+ collections
237
+ );
238
+ } catch (error) {
239
+ throw new ContextEvidenceError(
240
+ "collection_load_failed",
241
+ "Failed to resolve complete collection policy lineage",
242
+ error
243
+ );
244
+ }
220
245
  const indexName = canonicalizeIndexName(indexNameInput);
221
246
  const indexSnapshots: ReturnType<typeof canonicalIndexSnapshot>[] = [];
222
247
  const documents: ContextEvidenceSnapshotDocument[] = [];
@@ -245,6 +270,7 @@ export const captureContextEvidenceSnapshot = async (
245
270
  }
246
271
  return {
247
272
  collections,
273
+ egressLineage,
248
274
  contexts,
249
275
  documents: documents.sort(
250
276
  (left, right) =>
@@ -253,7 +279,10 @@ export const captureContextEvidenceSnapshot = async (
253
279
  compareCodeUnits(left.mirrorHash ?? "", right.mirrorHash ?? "")
254
280
  ),
255
281
  contextFingerprint: fingerprintContextRows(contexts),
256
- indexFingerprint: hashJson(indexSnapshots),
282
+ indexFingerprint: hashJson({
283
+ snapshots: indexSnapshots,
284
+ egressLineage,
285
+ }),
257
286
  };
258
287
  };
259
288
 
@@ -283,7 +312,7 @@ const referenceDocument = (
283
312
  !document.active ||
284
313
  !document.mirrorHash ||
285
314
  parsedUri?.collection !== document.collection ||
286
- result.source.relPath !== document.relPath ||
315
+ result.source.relPath !== (document.recordSourcePath ?? document.relPath) ||
287
316
  sourceHash !== document.sourceHash ||
288
317
  mirrorHash !== document.mirrorHash ||
289
318
  deriveDocid(document.sourceHash) !== document.docid
@@ -310,6 +339,21 @@ export const materializeContextEvidenceCandidates = async (
310
339
  "document_load_failed",
311
340
  "Failed to batch-load Context evidence documents"
312
341
  );
342
+ const collectionRows = unwrapStore(
343
+ await store.getCollections(),
344
+ "collection_load_failed",
345
+ "Failed to load Context evidence policies"
346
+ );
347
+ const policyByCollection = new Map(
348
+ collectionRows.map((row) => [
349
+ row.name,
350
+ {
351
+ collection: row.name,
352
+ policy: row.egressPolicy,
353
+ source: row.egressPolicySource,
354
+ },
355
+ ])
356
+ );
313
357
  const alignedDocuments = candidates.map((candidate) =>
314
358
  referenceDocument(documents, candidate, indexName)
315
359
  );
@@ -340,6 +384,24 @@ export const materializeContextEvidenceCandidates = async (
340
384
  return candidates.map((candidate, index) => {
341
385
  const result = candidate.result;
342
386
  const document = alignedDocuments[index];
387
+ if (!document) {
388
+ throw new ContextEvidenceError(
389
+ "document_load_failed",
390
+ `Document identity is unavailable for ${result.uri}`
391
+ );
392
+ }
393
+ const fallbackPolicy = policyByCollection.get(document.collection);
394
+ if (!result.egressLineage && !fallbackPolicy) {
395
+ throw new ContextEvidenceError(
396
+ "collection_load_failed",
397
+ `Policy lineage is unavailable for ${result.uri}`
398
+ );
399
+ }
400
+ const egressLineage =
401
+ result.egressLineage ??
402
+ createEgressLineage([
403
+ fallbackPolicy as NonNullable<typeof fallbackPolicy>,
404
+ ]);
343
405
  const metadata = result[SEARCH_RESULT_PLANNER_METADATA];
344
406
  const snippetRange = result.snippetRange;
345
407
  if (!document?.mirrorHash || !metadata || !snippetRange) {
@@ -415,7 +477,11 @@ export const materializeContextEvidenceCandidates = async (
415
477
  observedAt: null,
416
478
  contextIds: [...candidate.contextIds],
417
479
  trust: "untrusted",
418
- egress: "unavailable",
480
+ egress: egressLineage.effectivePolicy,
481
+ egressLineage,
482
+ ...(projectRecordEvidenceMetadata(document)
483
+ ? { record: projectRecordEvidenceMetadata(document) }
484
+ : {}),
419
485
  },
420
486
  },
421
487
  };
@@ -435,6 +501,7 @@ export const compileContextEvidence = async <P>(
435
501
  const fingerprints = {
436
502
  contextFingerprint: before.contextFingerprint,
437
503
  indexFingerprint: before.indexFingerprint,
504
+ egressLineage: before.egressLineage,
438
505
  };
439
506
  const plan = await planContextEvidence(
440
507
  { ...input, contextSnapshot: before.contexts, observedAt: null },
@@ -473,7 +540,16 @@ export const compileContextEvidence = async <P>(
473
540
  export const toContextCapsuleEvidence = (
474
541
  candidate: MaterializedContextCandidate<ContextEvidenceValue>,
475
542
  selectionRank: number
476
- ): ContextCapsulePayloadV1["evidence"][number] => {
543
+ ): ContextCapsulePayloadV1_1["evidence"][number] => {
544
+ if (
545
+ candidate.value.egress === "unavailable" ||
546
+ candidate.value.egressLineage === undefined
547
+ ) {
548
+ throw new ContextEvidenceError(
549
+ "collection_load_failed",
550
+ `Policy lineage is unavailable for ${candidate.uri}`
551
+ );
552
+ }
477
553
  const identity = {
478
554
  uri: candidate.uri,
479
555
  docid: candidate.docid,
@@ -487,6 +563,8 @@ export const toContextCapsuleEvidence = (
487
563
  evidenceId: contextCapsuleEvidenceIdentity(identity),
488
564
  ...identity,
489
565
  ...candidate.value,
566
+ egress: candidate.value.egress,
567
+ egressLineage: candidate.value.egressLineage,
490
568
  text: candidate.text,
491
569
  retrievalRank: candidate.retrievalRank,
492
570
  selectionRank,