@happyvertical/smrt-ingestion 0.55.8 → 0.55.10

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.
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Declared ingestion workflows for applications that provide an authenticated
3
+ * intake-review host (#3725).
4
+ *
5
+ * These recipes describe the user-facing inbox and review work. They do not
6
+ * expose the ingestion records as generic CRUD models: all reads and writes
7
+ * continue through the host's scoped service callbacks.
8
+ */
9
+ import { SmrtRecipe } from '@happyvertical/smrt-core';
10
+ import { IntakeAction, IntakeAnalysis, IntakeEvidence, IntakeItem, IntakeReviewDecision } from './models.js';
11
+ /** Incoming material that needs authenticated human attention. */
12
+ export declare class IngestionInboxRecipe extends SmrtRecipe {
13
+ static id: string;
14
+ static help: string;
15
+ static label: string;
16
+ static summary: string;
17
+ static synonyms: string[];
18
+ static group: {
19
+ id: string;
20
+ label: string;
21
+ summary: string;
22
+ };
23
+ static section: {
24
+ id: string;
25
+ label: string;
26
+ icon: string;
27
+ description: string;
28
+ };
29
+ static models: (typeof IntakeItem | typeof IntakeEvidence)[];
30
+ static runtime: "both";
31
+ static surfaces: readonly [{
32
+ readonly kind: "route";
33
+ readonly path: "/ingestion/inbox";
34
+ readonly export: "@happyvertical/smrt-ingestion/svelte#IntakeInbox";
35
+ readonly label: "Ingestion inbox";
36
+ }];
37
+ static nav: {
38
+ label: string;
39
+ model: typeof IntakeItem;
40
+ icon: string;
41
+ description: string;
42
+ key: string;
43
+ noun: string;
44
+ }[];
45
+ }
46
+ /** Revision-bound human review of proposed actions. */
47
+ export declare class IngestionReviewRecipe extends SmrtRecipe {
48
+ static id: string;
49
+ static help: string;
50
+ static label: string;
51
+ static summary: string;
52
+ static synonyms: string[];
53
+ static group: {
54
+ id: string;
55
+ label: string;
56
+ summary: string;
57
+ };
58
+ static section: {
59
+ id: string;
60
+ label: string;
61
+ icon: string;
62
+ description: string;
63
+ };
64
+ static models: (typeof IntakeItem | typeof IntakeEvidence | typeof IntakeAnalysis | typeof IntakeAction | typeof IntakeReviewDecision)[];
65
+ static runtime: "both";
66
+ static surfaces: readonly [{
67
+ readonly kind: "route";
68
+ readonly path: "/ingestion/review";
69
+ readonly export: "@happyvertical/smrt-ingestion/svelte#IntakeReview";
70
+ readonly label: "Ingestion review";
71
+ }];
72
+ static nav: {
73
+ label: string;
74
+ model: typeof IntakeReviewDecision;
75
+ icon: string;
76
+ description: string;
77
+ key: string;
78
+ noun: string;
79
+ }[];
80
+ }
81
+ //# sourceMappingURL=recipes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipes.d.ts","sourceRoot":"","sources":["../src/recipes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EACL,YAAY,EACZ,cAAc,EACd,cAAc,EACd,UAAU,EACV,oBAAoB,EACrB,MAAM,aAAa,CAAC;AAErB,kEAAkE;AAClE,qBAAa,oBAAqB,SAAQ,UAAU;IAClD,MAAM,CAAC,EAAE,SAAqB;IAC9B,MAAM,CAAC,IAAI,SAAiC;IAC5C,MAAM,CAAC,KAAK,SAAqB;IACjC,MAAM,CAAC,OAAO,SACmD;IACjE,MAAM,CAAC,QAAQ,WAA0D;IACzE,MAAM,CAAC,KAAK;;;;MAIV;IACF,MAAM,CAAC,OAAO;;;;;MAKZ;IACF,MAAM,CAAC,MAAM,gDAAgC;IAG7C,MAAM,CAAC,OAAO,EAAG,MAAM,CAAU;IACjC,MAAM,CAAC,QAAQ;;;;;OAOJ;IACX,MAAM,CAAC,GAAG;;;;;;;QASR;CACH;AAED,uDAAuD;AACvD,qBAAa,qBAAsB,SAAQ,UAAU;IACnD,MAAM,CAAC,EAAE,SAAsB;IAC/B,MAAM,CAAC,IAAI,SAAkC;IAC7C,MAAM,CAAC,KAAK,SAAsB;IAClC,MAAM,CAAC,OAAO,SAC+D;IAC7E,MAAM,CAAC,QAAQ,WAA0D;IACzE,MAAM,CAAC,KAAK;;;;MAIV;IACF,MAAM,CAAC,OAAO;;;;;MAKZ;IACF,MAAM,CAAC,MAAM,4HAMX;IAGF,MAAM,CAAC,OAAO,EAAG,MAAM,CAAU;IACjC,MAAM,CAAC,QAAQ;;;;;OAOJ;IACX,MAAM,CAAC,GAAG;;;;;;;QASR;CACH"}
@@ -3,15 +3,15 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-ingestion",
6
- "packageVersion": "0.55.8",
6
+ "packageVersion": "0.55.10",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "c1c496cd1c89cd95614c46e10a7291611ee804745322f42c1daf85089d211a6f",
11
- "packageJson": "449a59551333b1e68201ebcb171f1e72bf2930bf6f838aa545c18c9fc7a89895",
10
+ "manifest": "6550d2dc16e37807c12e70984fdf582ad227bf53f2ac3400d9d7ef94e3b3228a",
11
+ "packageJson": "be11c4832929ade172a54ad0a3b522a2296083219417d252b05da153222e511a",
12
12
  "agents": "dea14442054ec1de798cba9be1fbdd955f44b0e4672639cbf3db8101d290c1fb",
13
13
  "moduleDoc:agents/execution.md": "6153206adeea46fee1be57ccc01d171621ffb3dc3a754b3634bfe2ce367e8416",
14
- "moduleDoc:README.md": "c95d019573db738fa250b41e9e80527dee376fb9ff17efd6714e4debd4038c68",
14
+ "moduleDoc:README.md": "810edf32fc35d12a6d1d4d8c506dca2c803f1db4bffd44201cb83993a1f06b25",
15
15
  "moduleDoc:EXTRACTION.md": "b735a25a7be75f3a13f3f9e92b0c25834c1394835c16ccd73a076d5720c14243",
16
16
  "moduleDoc:PROPOSALS.md": "3accad9430e917e65319b7b7708d5c1df277eea7eaa0490df913cf361e80b7bc",
17
17
  "moduleDoc:FEEDBACK.md": "795337ba256b486ecf0e836c4ad090d12f60c8c31dfb7c83155a8689da3d4dc5",
@@ -1604,7 +1604,7 @@
1604
1604
  {
1605
1605
  "path": "README.md",
1606
1606
  "module": "README",
1607
- "content": "# @happyvertical/smrt-ingestion\n\nDurable receipt, evidence, analysis attempts and action-journal foundations for\n[ADR 0004](../../docs/adr/0004-application-aware-ingestion.md).\n\nThe root, `/models` and `/dto` entry points are browser-safe. Import\n`IngestionService` from `/server` in trusted host code. Generated REST, CLI and\nMCP access to the persistence models is disabled; hosts expose authorized DTO\nmethods through their authenticated application transport.\n\nBefore starting a service, deploy the package manifest through the public s-m-r-t\nmigration APIs or `smrt db:migrate`. `assertReady()` only checks existing tables.\nSQLite and PostgreSQL are supported; other engines fail construction.\n\nA host supplies a database, AssetRuntime, authenticated tenant/actor/confidential\nscope, a live authorization callback, a registered jobs target, and an idempotent\n`purgeDerived` callback for host-owned previews, retrieval indexes and caches.\nThe authorization callback receives the captured ceiling and the current\ntransaction executor; it must intersect current grants with that ceiling. Never\nconstruct the scope, retention policy or budgets from incoming document labels.\n\n`receive` accepts bounded original byte buffers, with stable source part IDs,\nparent relationships and optional upstream identity/version. Source adapters own\nstreaming and upstream snapshot retrieval. Receipt identity is tenant + source +\ndelivery key; changing bytes or authenticated scope conflicts. Content equality\nin a different conversation does not deduplicate an arrival. The returned item\nID and version are safe acknowledgment data, never a storage URL.\n\nStorage intents contain a stable asset ID and a public AssetStore-planned URI\nbefore byte I/O. Preservation verifies existing bytes instead of replacing them.\nA receipt becomes ready only after every part is durable and its dispatch intent\ncommits. A source retries the same receive call after failure or lost\nacknowledgment. Hosts schedule `repairDispatches` after restart and periodically;\nit repairs missing/failed queue submissions through SmrtJobCollection. The host\njob restores the captured scope and calls the analysis lifecycle methods. Run\nTaskRunner with the `ingestion` queue enabled.\n\n`analyze` appends immutable input/configuration revisions. `claimAnalysis` uses\nleases, monotonic fences and attempt budgets; `completeAnalysis` rejects expired\nor superseded workers. Terminal attempt output/provenance is preserved. Queue\nsubmission budgets also stop repeated handler failures in visible\n`needs_attention`. Reprocessing does not mint actions. `createAction`,\n`publishProposal` and `appendPlan` persist explicit intentions and exact attempt\nbindings for the later review/execution layer. They do not approve or apply\nbusiness operations. Decision/execution/feedback models are persistence\nfoundations; policy, handler expansion, review and business apply belong to the\nlater epic children.\n\n`expire` revokes access and pending work before removing bytes and calling\n`purgeDerived`. Interrupted deletion is retried by `repairDeletions` and\n`sweepRetention`. Minimal receipt/action identities remain, including successful\naction result digests; all content-bearing journal payloads are redacted. Private\ncleanup locators remain in deletion tombstones so repeated sweeps can remove a\nlate stale writer's orphan bytes even after the asset owner row is gone. Source\nadapters must enforce their configured delivery-age cutoff; expired replay IDs\nnever become fresh work. Continue running the cleanup sweep for the retained\nstorage namespace. Upstream source mutation/deletion cannot change copied\noriginals; explicit privacy deletion revokes every ingestion-derived payload and\nrequires host cache/index purge.\n\nSee [EXTRACTION.md](EXTRACTION.md) for bounded extraction, lease-bound input\nsnapshots, provider isolation requirements and the extraction corpus.\n\nValidation commands are `pnpm test`, `pnpm test:postgres`, `pnpm test:providers`, `pnpm typecheck` and\n`pnpm build` (including built browser import inspection). The PostgreSQL lane\nrequires the repository's configured database service and never silently skips.\nThe provider lane exercises local SDK adapters and process isolation; it does not\nclaim live remote recognition quality. `pnpm test:components` and `pnpm test:e2e`\ncover the maintained review surface. `pnpm test:evaluation` checks repository\nevaluation tooling contracts; measured held-out quality requires a separately\nfrozen protocol and an approved aggregate provider budget. See the [behavior matrix](../../docs/test-matrix/3670-ingestion-foundation.md).\n\n## Authoritative review and execution\n\nConfigure `IngestionOptions.execution` for policy, immutable previews, authenticated\ndecisions and idempotent domain execution. See [execution integration](agents/execution.md)\nfor the public handler catalog, required host authorization/target locks, deployment\nmigrations and jobs continuation integration. The maintained application reference\nin `reference/handlers.ts` creates draft ContentDocuments and attaches retained\nevidence through public domain APIs; it is not a published runtime adapter.\n\n## Configured source adapters\n\nThe server entry exports `createSourceDeliveryHandler`, `EmailSourceAdapter`,\n`WatchFolderSourceAdapter`, and `createVendorWebhookHandler`. Construct a\n`SourceBinding` from authenticated host configuration: enabled flag, scoped\n`IngestionService`, stable source ID/version, captured authority ceiling,\nretention policy, allowed media types and receipt limits. Re-resolve this binding\nwhen grants/configuration change. Source payloads never supply authority.\n\nThe reference POST handler authenticates before reading bytes and requires\n`Idempotency-Key`. JSON accepts exactly `capturedAt` and one of `text`,\n`structured`, or `reference: { owner: 'asset'|'message', id, version }`.\nThe host's `snapshotReference` must authorize current ownership and return copied\nbytes; an absent snapshot fails rather than acknowledging a mutable locator.\nMultipart accepts one `file` plus `captureId` (equal to the idempotency key),\n`capturedAt`, `captureSource` (`camera`, `native_picker`, `audio`, `document`), and optional\nlowercase SHA-256 `sha256`. This reuses mobile's durable UUID Idempotency-Key,\nMIME-bearing file part and explicit capture metadata convention. Captures retain\noriginal bytes and a parent metadata evidence record. Configure both the wire\nlimit `maxRequestBytes` and receipt `maxBytes`/`maxParts`; include\n`application/json` in the media allowlist for capture/email metadata. Signature\nchecks cover PDF, TIFF, PNG/JPEG/WebP and WAV/Ogg/WebM/MP3/MP4 audio; signature\nacceptance promises preservation, not extraction support or codec validity.\n\nEmail is explicitly opt-in per configured account. `EmailSourceAdapter` takes a\n`readMessage` callback compatible with the messages owner's\n`EmailAccount.readIntakeMessage(providerLookup, maxBytes, imapIdentity?)` snapshot.\nThe provider lookup is Gmail message ID, POP3 UIDL, or IMAP RFC Message-ID; it\nis separate from the delivery dedupe key. IMAP requires `{ folder, uidValidity,\nuid }`: the method selects the mailbox and verifies UIDVALIDITY and returned UID.\nAmbiguous sender IDs resolving to another UID fail explicitly. That method connects,\nfetches the full message including actual attachment bytes, copies the bytes,\nand disconnects. Metadata-only attachments fail explicitly. It never reads an\nattachment path supplied by a provider. A host lists provider deliveries using a\nstable account-specific locator (for IMAP include folder, UIDVALIDITY and UID),\nan explicit revision, and an opaque resumable checkpoint. Same locator/revision\nwith changed bytes conflicts; an explicit new revision creates a new receipt.\nDo not use sender-controlled RFC Message-ID as the sole transport identity.\n`receiveBatch` processes in order and stops at the first failure. The host owns\ndurable checkpoint storage and commits its `acknowledge` callback only after the\nadapter has received accepted/duplicate. If checkpoint persistence fails, retry\nreturns the original receipt. Message/thread/attachment relationships are copied;\ncredentials and arbitrary headers are excluded. The ordinary `syncFrom` metadata\nsync remains separate; `hasAttachments` is never proof of byte preservation.\n\nWatch-folder intake polls only an explicitly configured existing private local\ndirectory. Producers should close their files, preferably publishing with atomic\nrename. Two unchanged size/inode/mtime/ctime observations separated by\n`stabilityMs` are required, and the interval runs again after claim/restart.\nA private `.ingestion-claims/<UUID>/claim.json` intent is synced before atomically\nrenaming the original into that directory. Keep the directory on one filesystem,\nunder the host's ownership; symlink inputs are refused. A file descriptor still\nheld by an uncooperative producer can write after any stability window, so the\nproducer close/publish protocol is required for immutable input. Accepted or\nduplicate receipts move the entire claim to `.ingestion-processed`; do not delete\nclaims to retry. Polling recovers persisted UUIDs after process death, so receipt\nreplay deduplicates ready-without-ack. `failure.json` and poll results expose safe\ncategories while originals remain available. Intent directories without an\noriginal mean death before claim or a competing poll won; the producer original\nhas not been removed. Operators may remove these empty intents after confirming\nno active importer. Keep processed originals according to the host's retention\npolicy. This adapter does not watch arbitrary paths or start background loops.\n\nVendor webhooks use a host `verify(request, originalBytes)` callback after bounded\nraw-body reading. The callback must verify signature, delivery time/replay window\nand account ownership, then return trusted binding and durable delivery identity.\nNo vendor implementation or hardware driver is selected. All adapters invoke\nonly receipt preservation, never domain writes, AI tools or external actions.\n\nSee [PROPOSALS.md](PROPOSALS.md) for bounded application catalog discovery, durable\ninterpretation revisions, optional typed decisions and explicit generated preview.\n\n## Human review\n\nMount the `/svelte` inbox and review components through an authenticated host using\nthe browser-safe `IntakeReviewHost` DTO contract. See [REVIEW.md](REVIEW.md) for\nexact revision-bound callbacks, live evidence access, logical split corrections,\nassignment ownership, and the maintained upload-to-domain-result browser fixture.\n\nOptional explicit feedback and scoped example retrieval are documented in\n[FEEDBACK.md](FEEDBACK.md). Approval and execution outcomes remain separate from\ninterpretation correctness; routing policy adoption is explicit and versioned.\n"
1607
+ "content": "# @happyvertical/smrt-ingestion\n\nDurable receipt, evidence, analysis attempts and action-journal foundations for\n[ADR 0004](../../docs/adr/0004-application-aware-ingestion.md).\n\nThe root, `/models` and `/dto` entry points are browser-safe. Import\n`IngestionService` from `/server` in trusted host code. Generated REST, CLI and\nMCP access to the persistence models is disabled; hosts expose authorized DTO\nmethods through their authenticated application transport.\n\nBefore starting a service, deploy the package manifest through the public s-m-r-t\nmigration APIs or `smrt db:migrate`. `assertReady()` only checks existing tables.\nSQLite and PostgreSQL are supported; other engines fail construction.\n\nA host supplies a database, AssetRuntime, authenticated tenant/actor/confidential\nscope, a live authorization callback, a registered jobs target, and an idempotent\n`purgeDerived` callback for host-owned previews, retrieval indexes and caches.\nThe authorization callback receives the captured ceiling and the current\ntransaction executor; it must intersect current grants with that ceiling. Never\nconstruct the scope, retention policy or budgets from incoming document labels.\n\n`receive` accepts bounded original byte buffers, with stable source part IDs,\nparent relationships and optional upstream identity/version. Source adapters own\nstreaming and upstream snapshot retrieval. Receipt identity is tenant + source +\ndelivery key; changing bytes or authenticated scope conflicts. Content equality\nin a different conversation does not deduplicate an arrival. The returned item\nID and version are safe acknowledgment data, never a storage URL.\n\nStorage intents contain a stable asset ID and a public AssetStore-planned URI\nbefore byte I/O. Preservation verifies existing bytes instead of replacing them.\nA receipt becomes ready only after every part is durable and its dispatch intent\ncommits. A source retries the same receive call after failure or lost\nacknowledgment. Hosts schedule `repairDispatches` after restart and periodically;\nit repairs missing/failed queue submissions through SmrtJobCollection. The host\njob restores the captured scope and calls the analysis lifecycle methods. Run\nTaskRunner with the `ingestion` queue enabled.\n\n`analyze` appends immutable input/configuration revisions. `claimAnalysis` uses\nleases, monotonic fences and attempt budgets; `completeAnalysis` rejects expired\nor superseded workers. Terminal attempt output/provenance is preserved. Queue\nsubmission budgets also stop repeated handler failures in visible\n`needs_attention`. Reprocessing does not mint actions. `createAction`,\n`publishProposal` and `appendPlan` persist explicit intentions and exact attempt\nbindings for the later review/execution layer. They do not approve or apply\nbusiness operations. Decision/execution/feedback models are persistence\nfoundations; policy, handler expansion, review and business apply belong to the\nlater epic children.\n\n`expire` revokes access and pending work before removing bytes and calling\n`purgeDerived`. Interrupted deletion is retried by `repairDeletions` and\n`sweepRetention`. Minimal receipt/action identities remain, including successful\naction result digests; all content-bearing journal payloads are redacted. Private\ncleanup locators remain in deletion tombstones so repeated sweeps can remove a\nlate stale writer's orphan bytes even after the asset owner row is gone. Source\nadapters must enforce their configured delivery-age cutoff; expired replay IDs\nnever become fresh work. Continue running the cleanup sweep for the retained\nstorage namespace. Upstream source mutation/deletion cannot change copied\noriginals; explicit privacy deletion revokes every ingestion-derived payload and\nrequires host cache/index purge.\n\nSee [EXTRACTION.md](EXTRACTION.md) for bounded extraction, lease-bound input\nsnapshots, provider isolation requirements and the extraction corpus.\n\nValidation commands are `pnpm test`, `pnpm test:postgres`, `pnpm test:providers`, `pnpm typecheck` and\n`pnpm build` (including built browser import inspection). The PostgreSQL lane\nrequires the repository's configured database service and never silently skips.\nThe provider lane exercises local SDK adapters and process isolation; it does not\nclaim live remote recognition quality. `pnpm test:components` and `pnpm test:e2e`\ncover the maintained review surface. `pnpm test:evaluation` checks repository\nevaluation tooling contracts; measured held-out quality requires a separately\nfrozen protocol and an approved aggregate provider budget. See the [behavior matrix](../../docs/test-matrix/3670-ingestion-foundation.md).\n\n## Authoritative review and execution\n\nConfigure `IngestionOptions.execution` for policy, immutable previews, authenticated\ndecisions and idempotent domain execution. See [execution integration](agents/execution.md)\nfor the public handler catalog, required host authorization/target locks, deployment\nmigrations and jobs continuation integration. The maintained application reference\nin `reference/handlers.ts` creates draft ContentDocuments and attaches retained\nevidence through public domain APIs; it is not a published runtime adapter.\n\n## Configured source adapters\n\nThe server entry exports `createSourceDeliveryHandler`, `EmailSourceAdapter`,\n`WatchFolderSourceAdapter`, and `createVendorWebhookHandler`. Construct a\n`SourceBinding` from authenticated host configuration: enabled flag, scoped\n`IngestionService`, stable source ID/version, captured authority ceiling,\nretention policy, allowed media types and receipt limits. Re-resolve this binding\nwhen grants/configuration change. Source payloads never supply authority.\n\nThe reference POST handler authenticates before reading bytes and requires\n`Idempotency-Key`. JSON accepts exactly `capturedAt` and one of `text`,\n`structured`, or `reference: { owner: 'asset'|'message', id, version }`.\nThe host's `snapshotReference` must authorize current ownership and return copied\nbytes; an absent snapshot fails rather than acknowledging a mutable locator.\nMultipart accepts one `file` plus `captureId` (equal to the idempotency key),\n`capturedAt`, `captureSource` (`camera`, `native_picker`, `audio`, `document`), and optional\nlowercase SHA-256 `sha256`. This reuses mobile's durable UUID Idempotency-Key,\nMIME-bearing file part and explicit capture metadata convention. Captures retain\noriginal bytes and a parent metadata evidence record. Configure both the wire\nlimit `maxRequestBytes` and receipt `maxBytes`/`maxParts`; include\n`application/json` in the media allowlist for capture/email metadata. Signature\nchecks cover PDF, TIFF, PNG/JPEG/WebP and WAV/Ogg/WebM/MP3/MP4 audio; signature\nacceptance promises preservation, not extraction support or codec validity.\n\nEmail is explicitly opt-in per configured account. `EmailSourceAdapter` takes a\n`readMessage` callback compatible with the messages owner's\n`EmailAccount.readIntakeMessage(providerLookup, maxBytes, imapIdentity?)` snapshot.\nThe provider lookup is Gmail message ID, POP3 UIDL, or IMAP RFC Message-ID; it\nis separate from the delivery dedupe key. IMAP requires `{ folder, uidValidity,\nuid }`: the method selects the mailbox and verifies UIDVALIDITY and returned UID.\nAmbiguous sender IDs resolving to another UID fail explicitly. That method connects,\nfetches the full message including actual attachment bytes, copies the bytes,\nand disconnects. Metadata-only attachments fail explicitly. It never reads an\nattachment path supplied by a provider. A host lists provider deliveries using a\nstable account-specific locator (for IMAP include folder, UIDVALIDITY and UID),\nan explicit revision, and an opaque resumable checkpoint. Same locator/revision\nwith changed bytes conflicts; an explicit new revision creates a new receipt.\nDo not use sender-controlled RFC Message-ID as the sole transport identity.\n`receiveBatch` processes in order and stops at the first failure. The host owns\ndurable checkpoint storage and commits its `acknowledge` callback only after the\nadapter has received accepted/duplicate. If checkpoint persistence fails, retry\nreturns the original receipt. Message/thread/attachment relationships are copied;\ncredentials and arbitrary headers are excluded. The ordinary `syncFrom` metadata\nsync remains separate; `hasAttachments` is never proof of byte preservation.\n\nWatch-folder intake polls only an explicitly configured existing private local\ndirectory. Producers should close their files, preferably publishing with atomic\nrename. Two unchanged size/inode/mtime/ctime observations separated by\n`stabilityMs` are required, and the interval runs again after claim/restart.\nA private `.ingestion-claims/<UUID>/claim.json` intent is synced before atomically\nrenaming the original into that directory. Keep the directory on one filesystem,\nunder the host's ownership; symlink inputs are refused. A file descriptor still\nheld by an uncooperative producer can write after any stability window, so the\nproducer close/publish protocol is required for immutable input. Accepted or\nduplicate receipts move the entire claim to `.ingestion-processed`; do not delete\nclaims to retry. Polling recovers persisted UUIDs after process death, so receipt\nreplay deduplicates ready-without-ack. `failure.json` and poll results expose safe\ncategories while originals remain available. Intent directories without an\noriginal mean death before claim or a competing poll won; the producer original\nhas not been removed. Operators may remove these empty intents after confirming\nno active importer. Keep processed originals according to the host's retention\npolicy. This adapter does not watch arbitrary paths or start background loops.\n\nVendor webhooks use a host `verify(request, originalBytes)` callback after bounded\nraw-body reading. The callback must verify signature, delivery time/replay window\nand account ownership, then return trusted binding and durable delivery identity.\nNo vendor implementation or hardware driver is selected. All adapters invoke\nonly receipt preservation, never domain writes, AI tools or external actions.\n\nSee [PROPOSALS.md](PROPOSALS.md) for bounded application catalog discovery, durable\ninterpretation revisions, optional typed decisions and explicit generated preview.\n\n## Human review\n\nMount the `/svelte` inbox and review components through an authenticated host using\nthe browser-safe `IntakeReviewHost` DTO contract. See [REVIEW.md](REVIEW.md) for\nexact revision-bound callbacks, live evidence access, logical split corrections,\nassignment ownership, and the maintained upload-to-domain-result browser fixture.\n\nOptional explicit feedback and scoped example retrieval are documented in\n[FEEDBACK.md](FEEDBACK.md). Approval and execution outcomes remain separate from\ninterpretation correctness; routing policy adoption is explicit and versioned.\n\n## Recipe preview\n\n`ingestion.inbox` and `ingestion.review` declare the user-facing inbox and\nreview workflows for app discovery. The reference workflow and measured\nevaluation are now published, but the evaluation did not meet supported-reference\nquality gates and did not establish safety. Automation remains disabled. The\nrecipes do not configure a source or provider, expose generic CRUD routes, or\nenable automatic execution. Mount `IntakeInbox` and `IntakeReview` with an\nauthenticated `IntakeReviewHost`; [REVIEW.md](REVIEW.md) remains the source of\ntruth for its scoped callbacks and freshness checks. See the [adoption\nchecklist](../../docs/evaluation/3677-adoption.md) and [measured\nreport](../../docs/evaluation/3677-measured-evaluation.md) for the release\nlimits.\n\nThe recipe manifest registers those existing browser components as\n`/ingestion/inbox` and `/ingestion/review` routes for a host to mount. The route\nmetadata does not supply a host, source, provider, or demo fixture, and it does\nnot change the preview-only status. Both recipes declare `both` runtime: their\ncomponents render in a browser, while the authenticated review host and package\ndependency closure require a server.\n"
1608
1608
  },
1609
1609
  {
1610
1610
  "path": "EXTRACTION.md",
@@ -1681,6 +1681,123 @@
1681
1681
  }
1682
1682
  ]
1683
1683
  },
1684
+ "recipes": [
1685
+ {
1686
+ "id": "ingestion.inbox",
1687
+ "className": "IngestionInboxRecipe",
1688
+ "label": "Ingestion inbox",
1689
+ "summary": "Review incoming material and see which items need attention.",
1690
+ "synonyms": [
1691
+ "intake inbox",
1692
+ "incoming documents",
1693
+ "review queue"
1694
+ ],
1695
+ "models": [
1696
+ "@happyvertical/smrt-ingestion:IntakeItem",
1697
+ "@happyvertical/smrt-ingestion:IntakeEvidence"
1698
+ ],
1699
+ "nav": [
1700
+ {
1701
+ "label": "Ingestion inbox",
1702
+ "model": "@happyvertical/smrt-ingestion:IntakeItem",
1703
+ "icon": "archive",
1704
+ "description": "Incoming material that needs authenticated attention.",
1705
+ "key": "inbox",
1706
+ "noun": "inbox item"
1707
+ }
1708
+ ],
1709
+ "requires": [],
1710
+ "group": {
1711
+ "id": "ingestion",
1712
+ "label": "Ingestion",
1713
+ "summary": "Review incoming material safely before it can change records."
1714
+ },
1715
+ "section": {
1716
+ "id": "ingestion",
1717
+ "label": "Ingestion",
1718
+ "icon": "archive",
1719
+ "description": "Incoming material and its authenticated review workflow."
1720
+ },
1721
+ "help": {
1722
+ "markdown": "## Overview\n\nThe ingestion inbox is where an authenticated reviewer finds incoming material\nthat needs attention. It keeps the original material and its related evidence\ntogether, so a reviewer can understand what arrived before choosing a next\nstep.\n\n## Tasks\n\n### Check items that need attention\n\n1. Open the ingestion inbox in an application that has configured an\n authenticated review host.\n2. Choose an item to inspect its evidence and current review state.\n3. Open the item in Ingestion review when it has a proposed action.\n\n### Keep uncertain material for review\n\n1. Leave material in the inbox when there is not enough information to decide.\n2. Return after the application has new information or a reviewer can make a\n decision.\n3. Do not treat an inbox item as permission to change another record.\n\n## Preview status\n\nThis recipe describes a preview workflow. The published evaluation did not meet\nquality gates and did not establish safety, so automation remains disabled. It\ndoes not configure a source, provider, generic record route, or automatic\naction. Applications continue to use the scoped callbacks described in the\ningestion review documentation.\n",
1723
+ "fieldRefs": []
1724
+ },
1725
+ "surfaces": [
1726
+ {
1727
+ "kind": "route",
1728
+ "path": "/ingestion/inbox",
1729
+ "export": "@happyvertical/smrt-ingestion/svelte#IntakeInbox",
1730
+ "label": "Ingestion inbox"
1731
+ }
1732
+ ],
1733
+ "runtime": "both",
1734
+ "demo": {
1735
+ "mode": "server",
1736
+ "reasons": [
1737
+ "Needs a server because its package depends on @happyvertical/smrt-agents, @happyvertical/smrt-jobs, @happyvertical/smrt-profiles, @happyvertical/smrt-reports, @happyvertical/smrt-secrets, @happyvertical/smrt-tenancy, @happyvertical/smrt-users, which do not build for a browser (#3615, #3617, #3624, #3625, #3626, #3627)."
1738
+ ]
1739
+ }
1740
+ },
1741
+ {
1742
+ "id": "ingestion.review",
1743
+ "className": "IngestionReviewRecipe",
1744
+ "label": "Ingestion review",
1745
+ "summary": "Inspect evidence and approve, change, reject, or defer proposed actions.",
1746
+ "synonyms": [
1747
+ "intake review",
1748
+ "proposal review",
1749
+ "human approval"
1750
+ ],
1751
+ "models": [
1752
+ "@happyvertical/smrt-ingestion:IntakeItem",
1753
+ "@happyvertical/smrt-ingestion:IntakeEvidence",
1754
+ "@happyvertical/smrt-ingestion:IntakeAnalysis",
1755
+ "@happyvertical/smrt-ingestion:IntakeAction",
1756
+ "@happyvertical/smrt-ingestion:IntakeReviewDecision"
1757
+ ],
1758
+ "nav": [
1759
+ {
1760
+ "label": "Ingestion review",
1761
+ "model": "@happyvertical/smrt-ingestion:IntakeReviewDecision",
1762
+ "icon": "fileText",
1763
+ "description": "Revision-bound decisions on proposed actions.",
1764
+ "key": "review",
1765
+ "noun": "review decision"
1766
+ }
1767
+ ],
1768
+ "requires": [],
1769
+ "group": {
1770
+ "id": "ingestion",
1771
+ "label": "Ingestion",
1772
+ "summary": "Review incoming material safely before it can change records."
1773
+ },
1774
+ "section": {
1775
+ "id": "ingestion",
1776
+ "label": "Ingestion",
1777
+ "icon": "archive",
1778
+ "description": "Incoming material and its authenticated review workflow."
1779
+ },
1780
+ "help": {
1781
+ "markdown": "## Overview\n\nIngestion review lets an authorized person inspect the evidence behind a\nproposed action before it can affect another record. A decision is tied to the\nspecific material and proposal that the reviewer saw.\n\n## Tasks\n\n### Review a proposed action\n\n1. Open an inbox item that has a proposed action.\n2. Read the evidence and the proposed result in the authenticated review host.\n3. Approve, change, reject, or defer the proposal according to the current\n application policy.\n\n### Return to an item after it changes\n\n1. Refresh the review when the host indicates that the material or proposal has\n changed.\n2. Review the current proposal before making a decision.\n3. Leave a proposal deferred when its evidence or destination is still\n uncertain.\n\n## Preview status\n\nThis recipe describes a preview workflow. The published evaluation did not meet\nquality gates and did not establish safety, so automation remains disabled. It\ndoes not create a generic editing surface or enable automatic execution. A host\nrechecks the reviewer, scope, and current proposal before it accepts a decision.\n",
1782
+ "fieldRefs": []
1783
+ },
1784
+ "surfaces": [
1785
+ {
1786
+ "kind": "route",
1787
+ "path": "/ingestion/review",
1788
+ "export": "@happyvertical/smrt-ingestion/svelte#IntakeReview",
1789
+ "label": "Ingestion review"
1790
+ }
1791
+ ],
1792
+ "runtime": "both",
1793
+ "demo": {
1794
+ "mode": "server",
1795
+ "reasons": [
1796
+ "Needs a server because its package depends on @happyvertical/smrt-agents, @happyvertical/smrt-jobs, @happyvertical/smrt-profiles, @happyvertical/smrt-reports, @happyvertical/smrt-secrets, @happyvertical/smrt-tenancy, @happyvertical/smrt-users, which do not build for a browser (#3615, #3617, #3624, #3625, #3626, #3627)."
1797
+ ]
1798
+ }
1799
+ }
1800
+ ],
1684
1801
  "browser": {
1685
1802
  "status": "server-only",
1686
1803
  "issues": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-ingestion",
3
- "version": "0.55.8",
3
+ "version": "0.55.10",
4
4
  "description": "Durable tenant-scoped intake and evidence foundations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -50,32 +50,32 @@
50
50
  }
51
51
  },
52
52
  "dependencies": {
53
- "@happyvertical/smrt-core": "0.55.8",
54
- "@happyvertical/smrt-types": "0.55.8",
55
- "@happyvertical/smrt-tenancy": "0.55.8",
56
- "@happyvertical/smrt-assets": "0.55.8",
57
- "@happyvertical/smrt-jobs": "0.55.8",
53
+ "@happyvertical/smrt-core": "0.55.10",
54
+ "@happyvertical/smrt-types": "0.55.10",
55
+ "@happyvertical/smrt-tenancy": "0.55.10",
56
+ "@happyvertical/smrt-assets": "0.55.10",
57
+ "@happyvertical/smrt-jobs": "0.55.10",
58
58
  "@happyvertical/sql": "^0.103.1",
59
59
  "@happyvertical/pdf": "^0.65.9",
60
60
  "@happyvertical/ocr": "^0.61.6",
61
61
  "@happyvertical/speech": "^0.103.1",
62
62
  "@happyvertical/ai": "^0.103.1",
63
63
  "sharp": "^0.35.5",
64
- "@happyvertical/smrt-agents": "0.55.8",
65
- "@happyvertical/smrt-playbooks": "0.55.8",
64
+ "@happyvertical/smrt-agents": "0.55.10",
65
+ "@happyvertical/smrt-playbooks": "0.55.10",
66
66
  "ajv": "8.20.0",
67
- "@happyvertical/smrt-ui": "0.55.8"
67
+ "@happyvertical/smrt-ui": "0.55.10"
68
68
  },
69
69
  "devDependencies": {
70
- "@happyvertical/smrt-vitest": "0.55.8",
70
+ "@happyvertical/smrt-vitest": "0.55.10",
71
71
  "@types/node": "24.13.6",
72
72
  "typescript": "6.0.3",
73
73
  "vite": "8.1.4",
74
74
  "vitest": "4.1.10",
75
- "@happyvertical/smrt-content": "0.55.8",
76
- "@happyvertical/smrt-messages": "0.55.8",
75
+ "@happyvertical/smrt-content": "0.55.10",
76
+ "@happyvertical/smrt-messages": "0.55.10",
77
77
  "@happyvertical/email": "^0.103.1",
78
- "@happyvertical/smrt-users": "0.55.8",
78
+ "@happyvertical/smrt-users": "0.55.10",
79
79
  "@sveltejs/package": "^2.5.8",
80
80
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
81
81
  "svelte": "^5.57.1",