@gmickel/gno 1.27.0 → 1.28.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 (83) hide show
  1. package/README.md +2 -2
  2. package/assets/skill/SKILL.md +26 -1
  3. package/browser-extension/artifacts/{gno-browser-clipper-v1.27.0.zip → gno-browser-clipper-v1.28.0.zip} +0 -0
  4. package/browser-extension/artifacts/gno-browser-clipper-v1.28.0.zip.sha256 +1 -0
  5. package/browser-extension/dist/manifest.json +1 -1
  6. package/package.json +2 -2
  7. package/spec/cli.md +41 -4
  8. package/spec/db/schema.sql +12 -0
  9. package/spec/mcp.md +5 -0
  10. package/spec/output-schemas/ask.schema.json +125 -0
  11. package/spec/output-schemas/context-capsule-v1.schema.json +94 -1
  12. package/spec/output-schemas/get.schema.json +94 -0
  13. package/spec/output-schemas/mcp-job-status.schema.json +28 -0
  14. package/spec/output-schemas/multi-get.schema.json +128 -0
  15. package/spec/output-schemas/record-import.schema.json +193 -0
  16. package/spec/output-schemas/search-result.schema.json +94 -0
  17. package/spec/output-schemas/search-results.schema.json +125 -0
  18. package/spec/project-profile.schema.json +66 -0
  19. package/src/app/context-format.ts +1 -1
  20. package/src/cli/commands/get.ts +12 -2
  21. package/src/cli/commands/index-cmd.ts +13 -0
  22. package/src/cli/commands/multi-get.ts +9 -2
  23. package/src/cli/commands/shared.ts +28 -0
  24. package/src/cli/commands/update.ts +5 -0
  25. package/src/cli/program.ts +4 -0
  26. package/src/config/project-profile.ts +2 -0
  27. package/src/config/types.ts +16 -0
  28. package/src/converters/adapters/browser-export/adapter.ts +199 -0
  29. package/src/converters/adapters/browser-export/formats.ts +358 -0
  30. package/src/converters/adapters/email/adapter.ts +429 -0
  31. package/src/converters/adapters/email/html.ts +162 -0
  32. package/src/converters/adapters/email/mime.ts +454 -0
  33. package/src/converters/adapters/email/parameters.ts +77 -0
  34. package/src/converters/adapters/ical/adapter.ts +475 -0
  35. package/src/converters/adapters/ical/recurrence.ts +186 -0
  36. package/src/converters/adapters/jsonl/adapter.ts +238 -0
  37. package/src/converters/adapters/jsonl/config.ts +105 -0
  38. package/src/converters/adapters/shared/html-text.ts +165 -0
  39. package/src/converters/adapters/shared/record-utils.ts +79 -0
  40. package/src/converters/adapters/shared/utf8-lines.ts +141 -0
  41. package/src/converters/adapters/transcript/adapter.ts +295 -0
  42. package/src/converters/adapters/transcript/json.ts +184 -0
  43. package/src/converters/adapters/transcript/model.ts +171 -0
  44. package/src/converters/adapters/transcript/text.ts +58 -0
  45. package/src/converters/adapters/transcript/timed.ts +152 -0
  46. package/src/converters/index.ts +11 -1
  47. package/src/converters/mime.ts +8 -0
  48. package/src/converters/pipeline.ts +15 -1
  49. package/src/converters/registry.ts +37 -1
  50. package/src/converters/types.ts +136 -0
  51. package/src/core/context-capsule-schema.ts +87 -0
  52. package/src/core/context-capsule.ts +20 -0
  53. package/src/core/context-evidence.ts +7 -1
  54. package/src/core/document-capabilities.ts +12 -0
  55. package/src/core/project-profile-apply-state.ts +5 -0
  56. package/src/core/project-profile.ts +4 -0
  57. package/src/core/record-metadata.ts +49 -0
  58. package/src/ingestion/record-adapter-canonical.ts +433 -0
  59. package/src/ingestion/record-adapter.ts +437 -0
  60. package/src/ingestion/record-container.ts +688 -0
  61. package/src/ingestion/record-path.ts +20 -0
  62. package/src/ingestion/record-sync.ts +70 -0
  63. package/src/ingestion/sync.ts +228 -36
  64. package/src/ingestion/types.ts +69 -1
  65. package/src/ingestion/walker.ts +31 -11
  66. package/src/mcp/tools/get.ts +10 -2
  67. package/src/mcp/tools/multi-get.ts +9 -2
  68. package/src/mcp/tools/workspace-write.ts +2 -0
  69. package/src/pipeline/filters.ts +1 -1
  70. package/src/pipeline/graph-retrieval.ts +7 -4
  71. package/src/pipeline/hybrid.ts +7 -4
  72. package/src/pipeline/result-context.ts +12 -4
  73. package/src/pipeline/search.ts +11 -4
  74. package/src/pipeline/types.ts +3 -0
  75. package/src/pipeline/vsearch.ts +26 -8
  76. package/src/sdk/documents.ts +13 -5
  77. package/src/serve/browse-tree.ts +4 -2
  78. package/src/serve/routes/api.ts +63 -65
  79. package/src/store/migrations/022-record-export-lineage.ts +42 -0
  80. package/src/store/migrations/index.ts +2 -0
  81. package/src/store/sqlite/adapter.ts +114 -7
  82. package/src/store/types.ts +40 -0
  83. package/browser-extension/artifacts/gno-browser-clipper-v1.27.0.zip.sha256 +0 -1
package/README.md CHANGED
@@ -112,7 +112,7 @@ gno daemon --detach # headless continuous indexing (background; --status / --st
112
112
 
113
113
  <!-- public-truth:current-version -->
114
114
 
115
- > Current release: **v1.26.0** — see [CHANGELOG.md](./CHANGELOG.md)
115
+ > Current release: **v1.27.1** — see [CHANGELOG.md](./CHANGELOG.md)
116
116
 
117
117
  <!-- /public-truth -->
118
118
 
@@ -904,7 +904,7 @@ graph TD
904
904
  | **Document Editor** | Create, edit, delete docs with live markdown preview |
905
905
  | **Web UI** | Visual dashboard for search, browse, edit, and AI Q&A |
906
906
  | **REST API** | HTTP API for custom tools and integrations |
907
- | **Multi-Format** | Markdown, PDF, DOCX, XLSX, PPTX, plain text |
907
+ | **Multi-Format** | Markdown, PDF, Office, JSONL, EML/MBOX, ICS, transcript, and browser exports |
908
908
  | **Local LLM** | AI answers via llama.cpp, no API keys |
909
909
  | **Remote Inference** | Optional HTTP endpoints for embedding, reranking, expansion, and generation |
910
910
  | **Privacy First** | Local by default; no telemetry; network use is explicit or model provisioning |
@@ -192,7 +192,32 @@ gno get gno://work/report.md --json
192
192
  gno multi-get gno://work/doc1.md gno://work/doc2.md
193
193
  ```
194
194
 
195
- **Editable vs read-only**: `gno get --json` returns a `capabilities` field showing whether a document is editable at its source. Markdown and plain text files are editable in place. Converted documents (PDF, DOCX, XLSX) are read-only -- to edit their content, create a new markdown note instead of overwriting the binary source.
195
+ **Editable vs read-only**: `gno get --json` returns a `capabilities` field showing whether a document is editable at its source. Markdown and plain text files are editable in place. Converted documents (PDF, DOCX, XLSX) and logical records from JSONL, mail, calendar, transcript, or browser exports are read-only -- edit/regenerate the source export or create a new markdown note instead of overwriting GNO's virtual record.
196
+
197
+ **Export records**: search/get JSON may include a `record` object containing an
198
+ exact bounded source locator, people/dates, thread/event/session identity,
199
+ attachment inventory, and cue/message/event anchors. `source.relPath` is the
200
+ real export file; `record.adapter` identifies the exact adapter version and
201
+ configuration fingerprint. Use the result's unique `uri` or `docid` with
202
+ `gno get`. If update/index reports a partial export snapshot, valid siblings
203
+ were indexed but unseen old records were intentionally preserved; regenerate
204
+ the export and rerun the command.
205
+
206
+ | Export source | Activation | Logical record | Important boundary |
207
+ | ---------------------------- | ------------------------------------------------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------- |
208
+ | JSONL/NDJSON | Automatic; optional `recordAdapters.jsonl.fieldMapping` | One object per line | Configure/map an ID for update-in-place identity; content-derived fallback edits become remove+add |
209
+ | EML/MBOX | Automatic | One message | MIME/body bounded; attachments inventoried, never opened or indexed |
210
+ | ICS | Automatic | One event/exception | Timezone normalized; recurrence anchors capped at 64 |
211
+ | VTT/SRT | Automatic | One cue/segment | Speaker and timestamp anchors retained |
212
+ | Generic JSON/text transcript | Explicit `recordAdapters.transcript.format` | One segment/record | Never guessed from generic JSON/text |
213
+ | `.browser-export` | Explicit export file | One bookmark/history/reading-list item | Live profiles/databases/cookies rejected; URLs never fetched |
214
+
215
+ Shared defaults: 100 MiB/container, 2,000,000 canonical characters/record,
216
+ 100,000 metadata characters/record, 50,000,000 characters or 100,000
217
+ records/snapshot, 1,000 retained failures, and a 60-second adapter deadline.
218
+ Only complete authoritative snapshots tombstone disappeared records. No export
219
+ adapter authenticates to a live account, fetches remote content, executes
220
+ embedded content, or unpacks attachments/archives.
196
221
 
197
222
  ## Search Then Get (common pipeline)
198
223
 
@@ -0,0 +1 @@
1
+ 5cdb85ddd0e73801a566527b7a908c8e0854a83c56cae281bab5f1285dae6734 gno-browser-clipper-v1.28.0.zip
@@ -21,5 +21,5 @@
21
21
  "content_security_policy": {
22
22
  "extension_pages": "script-src 'self'; object-src 'none'; connect-src http://127.0.0.1:*"
23
23
  },
24
- "version": "1.27.0"
24
+ "version": "1.28.0"
25
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmickel/gno",
3
- "version": "1.27.0",
3
+ "version": "1.28.0",
4
4
  "description": "Local semantic search for your documents. Index Markdown, PDF, and Office files with hybrid BM25 + vector search.",
5
5
  "keywords": [
6
6
  "embeddings",
@@ -220,7 +220,7 @@
220
220
  "playwright": "1.58.2",
221
221
  "pptxgenjs": "4.0.1",
222
222
  "ultracite": "7.1.5",
223
- "vitest": "4.0.18",
223
+ "vitest": "4.1.0",
224
224
  "web-tree-sitter": "0.26.8"
225
225
  },
226
226
  "peerDependencies": {
package/spec/cli.md CHANGED
@@ -73,8 +73,8 @@ equivalent files fail closed as ambiguous.
73
73
  | collection list | yes | no | no | yes | no | terminal |
74
74
  | collection remove | no | no | no | no | no | terminal |
75
75
  | collection rename | no | no | no | no | no | terminal |
76
- | update | no | no | no | no | no | terminal |
77
- | index | no | no | no | no | no | terminal |
76
+ | update | yes | no | no | no | no | terminal |
77
+ | index | yes | no | no | no | no | terminal |
78
78
  | embed | no | no | no | no | no | terminal |
79
79
  | search | yes | yes | yes | yes | yes | terminal |
80
80
  | vsearch | yes | yes | yes | yes | yes | terminal |
@@ -772,13 +772,14 @@ Sync files from disk into the index (ingestion without embedding).
772
772
  **Synopsis:**
773
773
 
774
774
  ```bash
775
- gno update [--git-pull]
775
+ gno update [--git-pull] [--json]
776
776
  ```
777
777
 
778
778
  **Options:**
779
779
  | Option | Type | Description |
780
780
  |--------|------|-------------|
781
781
  | `--git-pull` | boolean | Run `git pull` in git repositories before scanning |
782
+ | `--json` | boolean | Emit the complete deterministic sync result on stdout |
782
783
 
783
784
  **Behavior:**
784
785
 
@@ -789,6 +790,13 @@ gno update [--git-pull]
789
790
  5. Update FTS index
790
791
  6. Mark missing files as inactive
791
792
 
793
+ **JSON output:** The top-level value is the complete sync result. Each entry in
794
+ `collections[].files[]` may contain a `recordImport` receipt conforming to
795
+ [`record-import@1.0`](./output-schemas/record-import.schema.json). Receipt items
796
+ are sorted deterministically and capped at 1,000; `itemsTruncated` reports
797
+ omitted actions. Partial snapshots expose `warnings[]` even when no individual
798
+ record failure was reported. Human progress and diagnostics remain on stderr.
799
+
792
800
  **Exit Codes:**
793
801
 
794
802
  - 0: Success (conversion warnings do not affect exit code)
@@ -803,7 +811,7 @@ Build or update the index end-to-end (update + embed).
803
811
  **Synopsis:**
804
812
 
805
813
  ```bash
806
- gno index [--collection <name>] [--no-embed] [--models-pull] [--git-pull] [--yes]
814
+ gno index [collection] [--no-embed] [--models-pull] [--git-pull] [--json] [--yes]
807
815
  ```
808
816
 
809
817
  **Options:**
@@ -813,6 +821,7 @@ gno index [--collection <name>] [--no-embed] [--models-pull] [--git-pull] [--yes
813
821
  | `--no-embed` | boolean | Run ingestion only, skip embedding |
814
822
  | `--models-pull` | boolean | Download models if missing (prompts unless `--yes`) |
815
823
  | `--git-pull` | boolean | Run `git pull` in git repositories |
824
+ | `--json` | boolean | Emit the complete deterministic sync and embedding result on stdout |
816
825
  | `--yes` | boolean | Accept defaults, no prompts |
817
826
 
818
827
  **Behavior:**
@@ -820,6 +829,13 @@ gno index [--collection <name>] [--no-embed] [--models-pull] [--git-pull] [--yes
820
829
  - Runs `update` then `embed` by default
821
830
  - With `--no-embed`, runs `update` only
822
831
 
832
+ **JSON output:** Emits `{ syncResult, embedSkipped, embedResult? }`.
833
+ `syncResult.collections[].files[].recordImport`, when present, conforms to
834
+ [`record-import@1.0`](./output-schemas/record-import.schema.json) with the same
835
+ deterministic ordering, bounds, truncation disclosure, and partial-snapshot
836
+ warnings as `gno update --json`. Human progress and diagnostics remain on
837
+ stderr.
838
+
823
839
  **Exit Codes:**
824
840
 
825
841
  - 0: Success
@@ -1034,6 +1050,27 @@ broad-to-specific path-prefix order. The field is absent when no scope matches;
1034
1050
  `uri` and `docid` remain the exact source identity. The same contract applies to
1035
1051
  `vsearch`, `query`, and the `results` array returned by `ask`.
1036
1052
 
1053
+ Logical documents created from file/export adapters additionally include a
1054
+ bounded `record` object with their opaque record key, exact export locator,
1055
+ people/dates, thread/event/session identity, attachment inventory, and anchors.
1056
+ `source.relPath` names the real export container; `uri` and `docid` address the
1057
+ individual virtual document. The closed `record.adapter` identity contains the
1058
+ adapter ID, version, and configuration fingerprint. `get` and `multi-get`
1059
+ preserve the same fields.
1060
+
1061
+ File/export adapter iteration has a 60-second deadline in addition to the byte,
1062
+ record, metadata, and total-character caps. A partial import that retains valid
1063
+ siblings exits successfully but is never silent: terminal output includes the
1064
+ file, bounded warning count, and snapshot state; `--verbose` adds stable failure
1065
+ codes, bounded source locators, retryability, and redacted messages.
1066
+ `gno update --json`, `gno index --json`, and internal and SDK-facing sync
1067
+ results include a `recordImport` receipt conforming to
1068
+ [`record-import@1.0`](./output-schemas/record-import.schema.json), with adapter
1069
+ identity, configuration fingerprint, snapshot authority, cap state, bytes read,
1070
+ per-action identity/provenance/attachment receipts, truncation disclosure,
1071
+ warnings, and bounded failures. `.gno/records/` is a reserved virtual
1072
+ namespace and is never walked as physical collection content.
1073
+
1037
1074
  **Exit Codes:**
1038
1075
 
1039
1076
  - 0: Success (including zero results)
@@ -92,6 +92,12 @@ CREATE TABLE IF NOT EXISTS documents (
92
92
  converter_version TEXT,
93
93
  language_hint TEXT, -- BCP-47 or NULL
94
94
  content_type_source TEXT, -- frontmatter | rules | default | extension | null
95
+ record_key TEXT, -- opaque stable logical-record identity
96
+ record_source_path TEXT, -- collection-relative export/container path
97
+ record_source_locator TEXT, -- bounded exact record/cue/message/event locator
98
+ record_metadata TEXT, -- bounded canonical JSON record metadata
99
+ record_anchors TEXT, -- bounded canonical JSON exact anchors
100
+ record_adapter_fingerprint TEXT, -- config-bound adapter identity for re-import invalidation
95
101
 
96
102
  -- Status
97
103
  active INTEGER NOT NULL DEFAULT 1,
@@ -116,6 +122,12 @@ CREATE INDEX IF NOT EXISTS idx_documents_active ON documents(active);
116
122
  CREATE INDEX IF NOT EXISTS idx_documents_mirror_hash ON documents(mirror_hash);
117
123
  CREATE INDEX IF NOT EXISTS idx_documents_docid ON documents(docid);
118
124
  CREATE INDEX IF NOT EXISTS idx_documents_uri ON documents(uri);
125
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_documents_record_key
126
+ ON documents(collection, record_source_path, record_key)
127
+ WHERE record_source_path IS NOT NULL AND record_key IS NOT NULL;
128
+ CREATE INDEX IF NOT EXISTS idx_documents_record_source_path
129
+ ON documents(collection, record_source_path)
130
+ WHERE record_source_path IS NOT NULL;
119
131
 
120
132
  -- ─────────────────────────────────────────────────────────────────────────────
121
133
  -- Content (content-addressed markdown mirrors)
package/spec/mcp.md CHANGED
@@ -697,6 +697,11 @@ Search result items include `contentType` when available and always include
697
697
  human-oriented; structured clients should read `structuredContent.results`.
698
698
  Structured result items also preserve optional `context` guidance in
699
699
  global-to-specific order without changing the result `uri` or `docid`.
700
+ File/export records also include bounded `record` provenance in structured
701
+ search/get/multi-get/Ask output. Their `source.relPath` is the real local export
702
+ container while the unique `uri` remains the required exact retrieval ref. The
703
+ closed `record.adapter` object identifies the adapter ID, version, and
704
+ configuration fingerprint used to produce the logical record.
700
705
  When `explain: true`, `structuredContent.meta.explain` includes deterministic
701
706
  stage lines and per-result score receipts. Non-neutral configured content-type
702
707
  rules add `contentTypeBoost` with base/raw scores, factor, capped contribution,
@@ -131,6 +131,131 @@
131
131
  "type": "string",
132
132
  "description": "Matching text snippet from document mirror"
133
133
  },
134
+ "record": {
135
+ "type": "object",
136
+ "additionalProperties": false,
137
+ "description": "Bounded logical-record provenance for a file/export source",
138
+ "required": ["recordKey", "sourceLocator", "anchors", "adapter"],
139
+ "properties": {
140
+ "recordKey": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
141
+ "sourceLocator": {
142
+ "type": "string",
143
+ "minLength": 1,
144
+ "maxLength": 512
145
+ },
146
+ "anchors": {
147
+ "type": "array",
148
+ "maxItems": 256,
149
+ "items": {
150
+ "type": "object",
151
+ "additionalProperties": false,
152
+ "required": ["kind", "value"],
153
+ "properties": {
154
+ "kind": {
155
+ "enum": [
156
+ "line",
157
+ "cue",
158
+ "timestamp",
159
+ "message",
160
+ "event",
161
+ "record"
162
+ ]
163
+ },
164
+ "value": {
165
+ "type": "string",
166
+ "minLength": 1,
167
+ "maxLength": 512
168
+ },
169
+ "endValue": {
170
+ "type": "string",
171
+ "minLength": 1,
172
+ "maxLength": 512
173
+ }
174
+ }
175
+ }
176
+ },
177
+ "adapter": {
178
+ "type": "object",
179
+ "additionalProperties": false,
180
+ "required": ["id", "version", "fingerprint"],
181
+ "properties": {
182
+ "id": { "type": "string", "minLength": 1, "maxLength": 128 },
183
+ "version": {
184
+ "type": "string",
185
+ "minLength": 1,
186
+ "maxLength": 64
187
+ },
188
+ "fingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
189
+ }
190
+ },
191
+ "author": { "type": "string", "maxLength": 2048 },
192
+ "participants": {
193
+ "type": "array",
194
+ "maxItems": 256,
195
+ "items": { "type": "string", "minLength": 1, "maxLength": 2048 }
196
+ },
197
+ "categories": {
198
+ "type": "array",
199
+ "maxItems": 256,
200
+ "items": { "type": "string", "minLength": 1, "maxLength": 512 }
201
+ },
202
+ "dateFields": {
203
+ "type": "object",
204
+ "maxProperties": 256,
205
+ "propertyNames": { "type": "string", "maxLength": 128 },
206
+ "additionalProperties": {
207
+ "type": "string",
208
+ "minLength": 1,
209
+ "maxLength": 256
210
+ }
211
+ },
212
+ "messageId": { "type": "string", "maxLength": 2048 },
213
+ "inReplyTo": { "type": "string", "maxLength": 2048 },
214
+ "references": {
215
+ "type": "array",
216
+ "maxItems": 256,
217
+ "items": {
218
+ "type": "string",
219
+ "minLength": 1,
220
+ "maxLength": 2048
221
+ }
222
+ },
223
+ "threadId": { "type": "string", "maxLength": 2048 },
224
+ "eventId": { "type": "string", "maxLength": 2048 },
225
+ "sessionId": { "type": "string", "maxLength": 2048 },
226
+ "attachments": {
227
+ "type": "array",
228
+ "maxItems": 256,
229
+ "items": {
230
+ "type": "object",
231
+ "additionalProperties": false,
232
+ "required": ["name"],
233
+ "properties": {
234
+ "name": {
235
+ "type": "string",
236
+ "minLength": 1,
237
+ "maxLength": 512
238
+ },
239
+ "mime": {
240
+ "type": "string",
241
+ "minLength": 1,
242
+ "maxLength": 256
243
+ },
244
+ "bytes": {
245
+ "type": "integer",
246
+ "minimum": 0,
247
+ "maximum": 9007199254740991
248
+ },
249
+ "disposition": { "enum": ["inline", "attachment"] },
250
+ "sha256": {
251
+ "type": "string",
252
+ "pattern": "^[a-f0-9]{64}$"
253
+ }
254
+ }
255
+ }
256
+ }
257
+ }
258
+ },
134
259
  "snippetRange": {
135
260
  "type": "object",
136
261
  "description": "Line range of snippet in mirror content",
@@ -330,6 +330,98 @@
330
330
  }
331
331
  },
332
332
  "definitions": {
333
+ "recordAnchor": {
334
+ "type": "object",
335
+ "additionalProperties": false,
336
+ "required": ["kind", "value"],
337
+ "properties": {
338
+ "kind": {
339
+ "enum": ["line", "cue", "timestamp", "message", "event", "record"]
340
+ },
341
+ "value": { "type": "string", "minLength": 1, "maxLength": 512 },
342
+ "endValue": { "type": "string", "minLength": 1, "maxLength": 512 }
343
+ }
344
+ },
345
+ "recordAttachment": {
346
+ "type": "object",
347
+ "additionalProperties": false,
348
+ "required": ["name"],
349
+ "properties": {
350
+ "name": { "type": "string", "minLength": 1, "maxLength": 512 },
351
+ "mime": { "type": "string", "minLength": 1, "maxLength": 256 },
352
+ "bytes": {
353
+ "type": "integer",
354
+ "minimum": 0,
355
+ "maximum": 9007199254740991
356
+ },
357
+ "disposition": { "enum": ["inline", "attachment"] },
358
+ "sha256": { "$ref": "#/definitions/sha256" }
359
+ }
360
+ },
361
+ "recordEvidenceMetadata": {
362
+ "type": "object",
363
+ "additionalProperties": false,
364
+ "required": ["recordKey", "sourceLocator", "anchors", "adapter"],
365
+ "properties": {
366
+ "recordKey": { "$ref": "#/definitions/sha256" },
367
+ "sourceLocator": { "type": "string", "minLength": 1, "maxLength": 512 },
368
+ "anchors": {
369
+ "type": "array",
370
+ "maxItems": 256,
371
+ "items": { "$ref": "#/definitions/recordAnchor" }
372
+ },
373
+ "adapter": {
374
+ "type": "object",
375
+ "additionalProperties": false,
376
+ "required": ["id", "version", "fingerprint"],
377
+ "properties": {
378
+ "id": { "type": "string", "minLength": 1, "maxLength": 128 },
379
+ "version": {
380
+ "type": "string",
381
+ "minLength": 1,
382
+ "maxLength": 64
383
+ },
384
+ "fingerprint": { "$ref": "#/definitions/sha256" }
385
+ }
386
+ },
387
+ "author": { "type": "string", "maxLength": 2048 },
388
+ "participants": {
389
+ "type": "array",
390
+ "maxItems": 256,
391
+ "items": { "type": "string", "minLength": 1, "maxLength": 2048 }
392
+ },
393
+ "categories": {
394
+ "type": "array",
395
+ "maxItems": 256,
396
+ "items": { "type": "string", "minLength": 1, "maxLength": 512 }
397
+ },
398
+ "dateFields": {
399
+ "type": "object",
400
+ "maxProperties": 256,
401
+ "propertyNames": { "type": "string", "maxLength": 128 },
402
+ "additionalProperties": {
403
+ "type": "string",
404
+ "minLength": 1,
405
+ "maxLength": 256
406
+ }
407
+ },
408
+ "messageId": { "type": "string", "maxLength": 2048 },
409
+ "inReplyTo": { "type": "string", "maxLength": 2048 },
410
+ "references": {
411
+ "type": "array",
412
+ "maxItems": 256,
413
+ "items": { "type": "string", "minLength": 1, "maxLength": 2048 }
414
+ },
415
+ "threadId": { "type": "string", "maxLength": 2048 },
416
+ "eventId": { "type": "string", "maxLength": 2048 },
417
+ "sessionId": { "type": "string", "maxLength": 2048 },
418
+ "attachments": {
419
+ "type": "array",
420
+ "maxItems": 256,
421
+ "items": { "$ref": "#/definitions/recordAttachment" }
422
+ }
423
+ }
424
+ },
333
425
  "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
334
426
  "nullableSha256": {
335
427
  "oneOf": [{ "$ref": "#/definitions/sha256" }, { "type": "null" }]
@@ -577,7 +669,8 @@
577
669
  "trust": { "const": "untrusted" },
578
670
  "egress": {
579
671
  "enum": ["local_only", "lan", "remote", "unclassified", "unavailable"]
580
- }
672
+ },
673
+ "record": { "$ref": "#/definitions/recordEvidenceMetadata" }
581
674
  }
582
675
  },
583
676
  "coverage": {
@@ -129,6 +129,100 @@
129
129
  "reason": { "type": "string" }
130
130
  }
131
131
  },
132
+ "record": {
133
+ "type": "object",
134
+ "additionalProperties": false,
135
+ "description": "Bounded logical-record provenance for a file/export source",
136
+ "required": ["recordKey", "sourceLocator", "anchors", "adapter"],
137
+ "properties": {
138
+ "recordKey": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
139
+ "sourceLocator": { "type": "string", "minLength": 1, "maxLength": 512 },
140
+ "anchors": {
141
+ "type": "array",
142
+ "maxItems": 256,
143
+ "items": {
144
+ "type": "object",
145
+ "additionalProperties": false,
146
+ "required": ["kind", "value"],
147
+ "properties": {
148
+ "kind": {
149
+ "enum": [
150
+ "line",
151
+ "cue",
152
+ "timestamp",
153
+ "message",
154
+ "event",
155
+ "record"
156
+ ]
157
+ },
158
+ "value": { "type": "string", "minLength": 1, "maxLength": 512 },
159
+ "endValue": { "type": "string", "minLength": 1, "maxLength": 512 }
160
+ }
161
+ }
162
+ },
163
+ "adapter": {
164
+ "type": "object",
165
+ "additionalProperties": false,
166
+ "required": ["id", "version", "fingerprint"],
167
+ "properties": {
168
+ "id": { "type": "string", "minLength": 1, "maxLength": 128 },
169
+ "version": { "type": "string", "minLength": 1, "maxLength": 64 },
170
+ "fingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
171
+ }
172
+ },
173
+ "author": { "type": "string", "maxLength": 2048 },
174
+ "participants": {
175
+ "type": "array",
176
+ "maxItems": 256,
177
+ "items": { "type": "string", "minLength": 1, "maxLength": 2048 }
178
+ },
179
+ "categories": {
180
+ "type": "array",
181
+ "maxItems": 256,
182
+ "items": { "type": "string", "minLength": 1, "maxLength": 512 }
183
+ },
184
+ "dateFields": {
185
+ "type": "object",
186
+ "maxProperties": 256,
187
+ "propertyNames": { "type": "string", "maxLength": 128 },
188
+ "additionalProperties": {
189
+ "type": "string",
190
+ "minLength": 1,
191
+ "maxLength": 256
192
+ }
193
+ },
194
+ "messageId": { "type": "string", "maxLength": 2048 },
195
+ "inReplyTo": { "type": "string", "maxLength": 2048 },
196
+ "references": {
197
+ "type": "array",
198
+ "maxItems": 256,
199
+ "items": { "type": "string", "minLength": 1, "maxLength": 2048 }
200
+ },
201
+ "threadId": { "type": "string", "maxLength": 2048 },
202
+ "eventId": { "type": "string", "maxLength": 2048 },
203
+ "sessionId": { "type": "string", "maxLength": 2048 },
204
+ "attachments": {
205
+ "type": "array",
206
+ "maxItems": 256,
207
+ "items": {
208
+ "type": "object",
209
+ "additionalProperties": false,
210
+ "required": ["name"],
211
+ "properties": {
212
+ "name": { "type": "string", "minLength": 1, "maxLength": 512 },
213
+ "mime": { "type": "string", "minLength": 1, "maxLength": 256 },
214
+ "bytes": {
215
+ "type": "integer",
216
+ "minimum": 0,
217
+ "maximum": 9007199254740991
218
+ },
219
+ "disposition": { "enum": ["inline", "attachment"] },
220
+ "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
221
+ }
222
+ }
223
+ }
224
+ }
225
+ },
132
226
  "tags": {
133
227
  "type": "array",
134
228
  "description": "Tags associated with the document",
@@ -74,6 +74,34 @@
74
74
  }
75
75
  }
76
76
  }
77
+ },
78
+ "files": {
79
+ "type": "array",
80
+ "items": {
81
+ "$ref": "#/$defs/fileSyncResult"
82
+ }
83
+ }
84
+ }
85
+ },
86
+ "fileSyncResult": {
87
+ "type": "object",
88
+ "additionalProperties": false,
89
+ "required": ["relPath", "status"],
90
+ "properties": {
91
+ "relPath": { "type": "string" },
92
+ "status": {
93
+ "enum": ["added", "updated", "unchanged", "error", "skipped"]
94
+ },
95
+ "docid": { "type": "string" },
96
+ "mirrorHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
97
+ "contentType": { "type": "string" },
98
+ "contentTypeSource": {
99
+ "enum": ["frontmatter-type", "prefix", "path-ext", "fallback"]
100
+ },
101
+ "errorCode": { "type": "string" },
102
+ "errorMessage": { "type": "string" },
103
+ "recordImport": {
104
+ "$ref": "gno://schemas/record-import@1.0"
77
105
  }
78
106
  }
79
107
  },