@happyvertical/smrt-ingestion 0.0.0-stage → 0.55.6

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 (77) hide show
  1. package/AGENTS.md +35 -0
  2. package/CLAUDE.md +1 -0
  3. package/EXTRACTION.md +120 -0
  4. package/LICENSE +7 -0
  5. package/PROPOSALS.md +173 -0
  6. package/README.md +150 -2
  7. package/agents/execution.md +132 -0
  8. package/dist/__smrt-register__.d.ts +2 -0
  9. package/dist/__smrt-register__.d.ts.map +1 -0
  10. package/dist/chunks/execution-C6HJvl9e.js +7667 -0
  11. package/dist/chunks/execution-C6HJvl9e.js.map +1 -0
  12. package/dist/chunks/execution-internal-DWteDHM2.js +7 -0
  13. package/dist/chunks/execution-internal-DWteDHM2.js.map +1 -0
  14. package/dist/chunks/extraction-bseWZuWN.js +293 -0
  15. package/dist/chunks/extraction-bseWZuWN.js.map +1 -0
  16. package/dist/chunks/extraction-providers-C66ADDVe.js +433 -0
  17. package/dist/chunks/extraction-providers-C66ADDVe.js.map +1 -0
  18. package/dist/dto.d.ts +54 -0
  19. package/dist/dto.d.ts.map +1 -0
  20. package/dist/dto.js +0 -0
  21. package/dist/execution-contracts.d.ts +131 -0
  22. package/dist/execution-contracts.d.ts.map +1 -0
  23. package/dist/execution-dto.d.ts +70 -0
  24. package/dist/execution-dto.d.ts.map +1 -0
  25. package/dist/execution-internal.d.ts +7 -0
  26. package/dist/execution-internal.d.ts.map +1 -0
  27. package/dist/execution.d.ts +64 -0
  28. package/dist/execution.d.ts.map +1 -0
  29. package/dist/execution.js +2 -0
  30. package/dist/extraction-providers.d.ts +35 -0
  31. package/dist/extraction-providers.d.ts.map +1 -0
  32. package/dist/extraction-types.d.ts +94 -0
  33. package/dist/extraction-types.d.ts.map +1 -0
  34. package/dist/extraction-worker.d.ts +2 -0
  35. package/dist/extraction-worker.d.ts.map +1 -0
  36. package/dist/extraction-worker.js +81 -0
  37. package/dist/extraction-worker.js.map +1 -0
  38. package/dist/extraction.d.ts +49 -0
  39. package/dist/extraction.d.ts.map +1 -0
  40. package/dist/index.d.ts +4 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +3 -0
  43. package/dist/manifest.json +4091 -0
  44. package/dist/models.d.ts +186 -0
  45. package/dist/models.d.ts.map +1 -0
  46. package/dist/models.js +923 -0
  47. package/dist/models.js.map +1 -0
  48. package/dist/policy.d.ts +46 -0
  49. package/dist/policy.d.ts.map +1 -0
  50. package/dist/proposal-contracts.d.ts +85 -0
  51. package/dist/proposal-contracts.d.ts.map +1 -0
  52. package/dist/proposal-dto.d.ts +120 -0
  53. package/dist/proposal-dto.d.ts.map +1 -0
  54. package/dist/proposal-sdk.d.ts +12 -0
  55. package/dist/proposal-sdk.d.ts.map +1 -0
  56. package/dist/proposal-validation.d.ts +188 -0
  57. package/dist/proposal-validation.d.ts.map +1 -0
  58. package/dist/proposals.d.ts +41 -0
  59. package/dist/proposals.d.ts.map +1 -0
  60. package/dist/proposals.js +865 -0
  61. package/dist/proposals.js.map +1 -0
  62. package/dist/server.d.ts +200 -0
  63. package/dist/server.d.ts.map +1 -0
  64. package/dist/server.js +1588 -0
  65. package/dist/server.js.map +1 -0
  66. package/dist/smrt-knowledge.json +1621 -0
  67. package/dist/sources/common.d.ts +32 -0
  68. package/dist/sources/common.d.ts.map +1 -0
  69. package/dist/sources/email.d.ts +40 -0
  70. package/dist/sources/email.d.ts.map +1 -0
  71. package/dist/sources/http.d.ts +30 -0
  72. package/dist/sources/http.d.ts.map +1 -0
  73. package/dist/sources/index.d.ts +8 -0
  74. package/dist/sources/index.d.ts.map +1 -0
  75. package/dist/sources/watch-folder.d.ts +27 -0
  76. package/dist/sources/watch-folder.d.ts.map +1 -0
  77. package/package.json +86 -3
package/AGENTS.md ADDED
@@ -0,0 +1,35 @@
1
+ # @happyvertical/smrt-ingestion
2
+
3
+ Durable foundation for ADR 0004. `src/models.ts` owns all ingestion tables;
4
+ `src/server.ts` owns authenticated service boundaries and lifecycle primitives.
5
+ `src/execution.ts` owns authoritative review/apply and imports server-only principals.
6
+ See [execution integration](agents/execution.md) for host transaction guarantees,
7
+ catalog contracts, deployment dependencies and continuation bindings.
8
+ Root, `/models`, and `/dto` remain browser-safe; provider/network/filesystem code
9
+ belongs under `/server`. Generated REST/CLI/MCP model mutation is disabled.
10
+
11
+ See [README.md](README.md) for host integration and recovery/deletion operation.
12
+ See [EXTRACTION.md](EXTRACTION.md) for extraction contracts, provider isolation
13
+ requirements and corpus validation.
14
+ See [PROPOSALS.md](PROPOSALS.md) for the optional server-only proposal stage,
15
+ current catalog/candidate authority and immutable source pinning.
16
+
17
+ All records require tenant and confidential scope. Hosts create service scopes
18
+ from authenticated sessions; source metadata is never authority. Every read and
19
+ write rechecks the host scope predicate. Analysis input revisions, attempts,
20
+ proposals, decisions, executions and feedback are append-only except documented
21
+ state projections and retention redaction. Terminal actions outlive job cleanup.
22
+
23
+ Only SQLite and PostgreSQL are supported. Deploy manifest migrations before
24
+ starting services; runtime performs no application DDL. Storage planning and
25
+ preservation use AssetRuntime/AssetStore public methods. Blob I/O and queue
26
+ submission never run inside ingestion database transactions.
27
+
28
+ Validation: `pnpm test`, `pnpm test:postgres`, `pnpm test:providers`, `pnpm typecheck`, `pnpm build`.
29
+ The PostgreSQL command provisions a real database and fails if unavailable.
30
+ Build includes browser import validation. The provider lane exercises local SDK
31
+ adapters and process isolation, without claiming live remote recognition quality.
32
+ The component/e2e/evaluation
33
+ commands are reserved by ADR 0004 and fail explicitly until their owning children
34
+ supply real suites.
35
+ See ../../docs/test-matrix/3670-ingestion-foundation.md for behavior coverage.
package/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ @AGENTS.md
package/EXTRACTION.md ADDED
@@ -0,0 +1,120 @@
1
+ # Extraction adapters
2
+
3
+ `@happyvertical/smrt-ingestion/server` exports `createSDKExtractionAdapter`,
4
+ `extractAnalysis`, extraction types, and `proposeDocumentSplits`. Root and DTO
5
+ exports remain browser-safe. No extraction result authorizes a business action.
6
+
7
+ Hosts construct the SDK adapter with explicit provider/model/version identities
8
+ (`unknown` for unavailable model/version), credential-bearing SDK options, and
9
+ `nativeMemoryIsolation: 'host-enforced'`. This last setting is an assertion by
10
+ trusted deployment configuration, never a source/body/client option. Deploy the
11
+ provider child under an OS/container native-memory ceiling. Node's heap limit
12
+ cannot bound native allocations, and PDF's renderer has no pre-render pixel
13
+ limit. Without that host guarantee, adapter construction refuses to run. A
14
+ configured provider must be pinned, not `auto`; OCR fallback is disabled so
15
+ capability provenance cannot silently change. Identity objects are projected onto
16
+ exactly `provider`, `model`, and `version`, each a required nonblank string. Extra
17
+ host fields never enter capability or segment identities, including through
18
+ trusted custom adapter results at the publication boundary.
19
+
20
+ The adapter forks one disposable Node process per retained part. Deadline or
21
+ cancellation kills it (and its process group on POSIX); provider stdout/stderr
22
+ are discarded. Calls are sequential and bounded by input bytes, page count,
23
+ image decode pixels, serialized output, vision tokens, elapsed time and Node
24
+ heap. Native memory remains the host boundary described above. Remote compute
25
+ already accepted by a provider may continue/bill after the local process dies.
26
+ Do not interpret a local cancellation as remote billing cancellation.
27
+
28
+ Persist `{extraction: {configurationRevision, limits}}` using `service.analyze`,
29
+ then claim its lease. Call `extractAnalysis(service, lease, adapter, options)`
30
+ with exactly that revision and limits. The service reloads the immutable input
31
+ snapshot and rechecks current processing authority before each provider and
32
+ before publication. The production child requests a parent-side authorization
33
+ handshake before each PDF/OCR/vision/speech operation, including calls on later
34
+ pages. Denial kills the child; already transmitted material cannot be recalled.
35
+ Custom trusted adapters must await `request.beforeProviderCall` before every
36
+ provider invocation. Bytes are read through the service's integrity-checked
37
+ reader; revoked, expired, foreign-scope, forged and stale leases fail closed.
38
+ A retry keeps the same analysis input revision; changed provider/model/options
39
+ must use a new configuration revision and `analyze` request key. Credentials are
40
+ never placed in persisted configuration or provider identity.
41
+
42
+ Results retain the original evidence ID/hash/part/parent references. Ordered
43
+ segments form whole-document context; extraction does not split originals into
44
+ business records. Valid UTF-8 JSON, text and HTML remain literal source text (render HTML
45
+ only through an application sanitizer). JSON syntax is validated without rewriting
46
+ its source text; malformed JSON or UTF-8 fails extraction. PDF text is extracted one page at a
47
+ time, falling back to actual rendered-page OCR for empty pages. Embedded text
48
+ is not evidence that every visual element was understood. TIFF is actually
49
+ decoded page-by-page with sharp to PNG; each result cites its original TIFF page.
50
+ Animated non-TIFF images are unsupported. Ordinary image OCR and scene vision
51
+ are separately configured. Speech uses source-level text unless actual segment
52
+ timestamps are returned; absent speech confidence calibration is unknown.
53
+
54
+ Confidence and boxes are explicitly null unless an OCR provider advertises and
55
+ returns them. Unlimited-OCR's synthetic common confidence is always discarded,
56
+ even if an injected capability claims otherwise. Measured OCR values retain an
57
+ unknown calibration label; they are not routing certainty or approval. Boxes
58
+ are in decoded image pixels, attached only to that actual page/source. No tables
59
+ are invented: the current PDF/OCR text APIs do not supply a table contract.
60
+
61
+ The persisted receipt's `maxOutputBytes`, exposed by `getAnalysisInput`, bounds
62
+ the complete serialized analysis envelope across all parts, including provenance,
63
+ capabilities, usage, and failure metadata. Extraction reserves room for a `limit`
64
+ outcome and retains fitting earlier parts/segments when the aggregate is too
65
+ large. Top-level `truncated` and `omittedEvidenceCount` identify publication
66
+ omissions; per-result omissions retain every discarded segment's exact page or
67
+ time location. Existing errors and omitted ranges survive trimming unchanged.
68
+ If those diagnostic records cannot fit, the entire result is omitted rather than
69
+ publishing an incomplete account of its known failures. If the ceiling
70
+ cannot fit the minimum result envelope, no provider runs: `failAnalysis` records
71
+ only a fixed safe category and terminal lifecycle state, with no provider output
72
+ or output digest. That service-owned failure accounting is outside the provider
73
+ payload budget; `completeAnalysis` never accepts an oversized result.
74
+
75
+ Partial page results survive later failures or process timeout. Warnings and
76
+ omitted page/source references remain inspectable. `unsupported_type` covers
77
+ video, unconfigured media and encrypted PDF; corrupt decoder failures without a
78
+ safe typed SDK category are `unavailable`, never raw exception strings. A host
79
+ can repair/reconfigure and create a fresh analysis without rewriting originals.
80
+ `proposeDocumentSplits` produces a hashed, versioned, non-executable grouping of
81
+ observed pages; overlap, unordered groups and nonexistent pages are rejected.
82
+
83
+ ## Evidence and limitations
84
+
85
+ `pnpm --filter @happyvertical/smrt-ingestion test:providers` runs actual local PDF
86
+ SDK extraction, actual sharp multipage TIFF decoding, and packaged process/HTTP
87
+ adapter lifecycle tests. The local speech server tests adapter transport and
88
+ termination, not transcription quality. The tiny TIFF fixture proves IFD/page
89
+ mapping, not fax OCR accuracy. `extraction.test.ts` injects deterministic SDK
90
+ boundaries for mixed/scanned PDF, Unlimited-OCR, camera observations, message
91
+ attachments, audio timestamps and malformed/partial output. These fixtures are
92
+ not real remote-provider integrations and are not the ADR evaluation corpus.
93
+
94
+ No live Unlimited-OCR, speech recognition or vision quality run is claimed.
95
+ No recorded-human-audio recognition accuracy, real camera-scene quality,
96
+ calibrated confidence, table extraction, or automatic-action acceptance is
97
+ established by these tests. Deployment owners must run the intended provider,
98
+ model and representative retained corpus before making those claims. The
99
+ [behavior matrix](../../docs/test-matrix/3672-ingestion-extraction.md) records the
100
+ release commands and authority obligations.
101
+
102
+ The versioned on-disk corpus manifest records exact hashes for valid embedded,
103
+ raster and mixed PDFs, two-page TIFF, a synthetic camera-format JPEG, a valid
104
+ PCM WAV recording of a generated tone, email text, corrupt PDF and unsupported
105
+ video. The tone is not human speech; the illustrated scene is not a photograph.
106
+ Real local HTTP fixture runs execute Unlimited-OCR's installed SDK and speech
107
+ transport against deterministic responses/timeouts. They verify adapters, not
108
+ remote models. Unlimited-OCR 0.61.6 rejects valid encoded images smaller than
109
+ 100 bytes; ingestion reports `unavailable` without altering them. This owning
110
+ SDK defect is tracked in [happyvertical/ocr#143](https://github.com/happyvertical/ocr/issues/143).
111
+
112
+ Each capability record explicitly reports `truncation` and `usage` as `unknown`
113
+ or `reported`. Unlimited-OCR's current SDK discards upstream `finish_reason`
114
+ and token usage, so both are `unknown` even for nonempty output. Downstream
115
+ rules must not interpret `status: 'complete'` or `truncated: false` as proof of
116
+ untruncated OCR: they describe completed input processing and *detected*
117
+ truncation only. The adapter never bypasses the SDK to recover hidden fields.
118
+ The missing SDK metadata is tracked in [happyvertical/ocr#144](https://github.com/happyvertical/ocr/issues/144).
119
+ AI vision retains reported truncation when supplied by its SDK. Missing provider
120
+ cost is unknown, not zero; no cost estimate is invented.
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright <2025> <Happy Vertical Corporation>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/PROPOSALS.md ADDED
@@ -0,0 +1,173 @@
1
+ # Application-aware proposal generation
2
+
3
+ `@happyvertical/smrt-ingestion/proposals` is server-only. Configure
4
+ `IngestionOptions.proposals` and the existing `execution.handlers` catalog.
5
+ Handlers opt into discovery with `discovery.mediaTypes`, `references`, and an
6
+ optional `candidates` callback. There is one catalog: the same operation or
7
+ ordered playbook adapter is discovered, validated, previewed, and later applied
8
+ by the execution service. Generation never approves or applies.
9
+
10
+ ## Discovery and authority
11
+
12
+ `listHandlers(itemId)` returns a bounded detached catalog. The owning execution
13
+ service checks the current principal, captured permissions/handler/operation
14
+ ceiling, policy, confidential scope, public registered operation facts, and
15
+ ordered playbook definition. Every configured provider must occur in the current
16
+ policy's `providers` allowlist. Missing grants remove handlers before model input.
17
+ An authorized catalog larger than the configured bound is rejected; the host must
18
+ narrow it explicitly instead of relying on silent truncation.
19
+
20
+ `findCandidates({itemId,handlerId,handlerVersion,query,limit})` returns
21
+ `{items,truncated}`. Its trusted handler callback receives `HandlerContext` with
22
+ the authorized transaction executor. Query by tenant and confidential scope
23
+ **before** selecting, ranking, or building labels. Use a bounded query (often
24
+ `limit + 1`) and report `hasMore`. The service projects candidate identity, model,
25
+ revision and label, then checks every target with the owning execution callback.
26
+ There are no hidden counts or pagination tokens. Truncated discovery forces
27
+ `needs_review` and cannot enter generated preview.
28
+
29
+ Completed generation reload checks process authority, the current attempt, and
30
+ owner/candidate visibility in the same final transaction. Database-backed grant
31
+ callbacks must use the supplied executor. The owning retention lifecycle encloses
32
+ that transaction so a denied read cannot roll back an authorized shorter privacy
33
+ ceiling; restoration starts only after the outer transaction has rolled back.
34
+
35
+ Candidate callbacks perform bounded database reads only: no provider I/O, network,
36
+ or domain mutations. Target locks and existing retention enforcement remain
37
+ owned by execution. Hosts configure their database's statement deadlines; an
38
+ arbitrary trusted callback is not a sandbox. Each existing-record argument must
39
+ be a declared top-level `references` field of kind `candidate` with its qualified
40
+ model, or `evidence`. Closed JSON argument schemas are required. The host owns
41
+ that declaration and its target authorization semantics.
42
+
43
+ ## Durable stages
44
+
45
+ 1. Complete extraction through the existing analysis ledger.
46
+ 2. Call `prepareGeneration(itemId, extractionAttemptId)`. It reads the current
47
+ completed/partial immutable extraction and returns configuration with
48
+ `stage: 'interpret'`, pinned source attempt/revision/input/output/evidence
49
+ digests, prompt/configuration/catalog/provider versions, limits and thresholds.
50
+ 3. Pass that configuration to `analyze(itemId, configuration, requestKey)` and
51
+ claim its new revision with `claimAnalysis`.
52
+ 4. The host's existing job callback routes on the frozen `configuration.stage`:
53
+ extraction invokes `extractAnalysis`; interpretation invokes
54
+ `generateProposals(lease)`.
55
+ 5. Reload the current completed generation using `getCompletedAnalysis(itemId)`,
56
+ or pass an explicit attempt ID to additionally reject stale client state.
57
+ Queued/running revisions never return an older completed attempt.
58
+
59
+ A generation revision does not rewrite terminal extraction. Reprocessing starts
60
+ with a new extraction revision, followed by interpretation, while the host keeps
61
+ the same business intention keys. An interrupted still-current generation lease
62
+ can be retried; terminal generation output remains immutable. `getGenerationInput`
63
+ reads historical extraction only through the current generation lease and its
64
+ frozen source pin. It verifies same-item scope, immutable hashes, durable evidence,
65
+ and current process permission before returning detached data. The source must be
66
+ the immediately preceding revision, so an intervening reprocess invalidates a
67
+ prepared generation. Completed generation reads additionally recheck current
68
+ catalog and target visibility before disclosing persisted candidate labels.
69
+ Expired, cancelled, revoked, retried or superseded workers cannot read or publish late output. The
70
+ current lease and each offered handler/candidate are rechecked immediately before
71
+ each outbound generative/decision boundary and before publication.
72
+
73
+ ## Providers and validation
74
+
75
+ `createSDKProposalGenerator(client, identity, {maxTokens,timeoutMs})` uses an
76
+ existing AI SDK `chat` client. It sends a fixed system instruction and separate
77
+ JSON evidence/catalog data, requests JSON, supplies cancellation and bounds, uses
78
+ no tools or continuation, and rejects known truncation or tool calls,
79
+ unexpected models, malformed JSON and oversized output. Configure SDK clients
80
+ without automatic retry that would bypass application boundary checks. The
81
+ adapter's public identity is exactly provider/model/version; credentials and raw
82
+ provider errors are never provenance.
83
+
84
+ A trusted custom `ProposalGenerator` must likewise honor cancellation, bound its
85
+ response, and explicitly declare validated `complete` or `unknown` completion.
86
+ The SDK currently normalizes missing/unknown finish reasons to `stop`
87
+ ([owning SDK follow-up](https://github.com/happyvertical/sdk/issues/1381)), so its
88
+ adapter conservatively declares `unknown`, persisted as
89
+ `generation_completion_unknown`. Otherwise valid suggestions remain available
90
+ for human preview; automatic eligibility always remains false. The service
91
+ deadline aborts requests and prevents late publication; it does not claim to terminate computation at a remote
92
+ provider or isolate native allocations. Extractor process limits remain separate.
93
+
94
+ Model output can contain only offered handler versions, schema-valid arguments,
95
+ retained evidence locations and offered alternative handler/candidate keys.
96
+ Candidate IDs must occur in the offered set, and evidence IDs in retained source
97
+ results. Unknown enums, invented IDs, extra authority/tool/approval fields and
98
+ malformed structures fail closed. Missing required top-level fields are retained
99
+ as `missingFields`, with `needs_review`; values are never invented to satisfy a
100
+ schema. Observed page/time/source granularity is preserved. Logical scan splits
101
+ use `proposeDocumentSplits`; originals and hashes remain intact.
102
+
103
+ `createSDKProposalDecisionClient(client)` adapts an existing SDK client’s optional
104
+ capability without changing its errors or protocol. Optional `decision` uses public core `executeDecision` and the existing SDK
105
+ predicate, choice, score and batch contracts. Supported-effect predicates,
106
+ handler classification, candidate entity matching, and action ambiguity/risk
107
+ are distinct questions. Their validated probabilities and provenance are stored
108
+ separately from generative provenance and unknown OCR certainty. The complete
109
+ serialized decision request, including suggestions and questions, must fit the
110
+ minimum configured/current policy byte ceiling before capability probing and
111
+ again before deciding. A policy change between these boundaries fails closed.
112
+ Ties, support probability or selected-handler confidence below
113
+ `minimumDecisionProbability`, and actually incomplete extraction require review.
114
+ Unknown provider
115
+ completion metadata is preserved as `source_completion_unknown`; it does not
116
+ block otherwise schema-valid **human** preview or imply completeness. Automatic
117
+ action eligibility remains false. An unconfigured decision path
118
+ is recorded explicitly. Configured errors produce a durable safe failure outcome;
119
+ they never silently switch to a generative decision fallback.
120
+
121
+ Output records zero, one or multiple suggestions, alternatives, missing fields,
122
+ source links, proposed splits, versions, allowlisted usage, warnings and explicit
123
+ `unknown`, `ambiguous`, `no_action`, `needs_review` or `provider_error` outcomes.
124
+ `automaticActionEligible` is always false. Provider input/output and the complete
125
+ serialized persisted envelope are bounded by configuration, live policy and the
126
+ receipt ceiling. Oversized output becomes a bounded review/limit result; when
127
+ no safe envelope fits, the owning service records fixed `failAnalysis` metadata
128
+ outside the provider payload budget without retaining provider output.
129
+
130
+ ## Explicit preview and stable intentions
131
+
132
+ The host chooses persisted suggestion indexes and calls:
133
+
134
+ ```ts
135
+ await service.previewGeneratedProposals({
136
+ itemId,
137
+ attemptId: generationAttemptId,
138
+ selections: [{
139
+ index: 0,
140
+ intentionKey: 'document:meeting-2026-10-09',
141
+ expectedRevision: 0,
142
+ requestId: 'review-request-1',
143
+ }],
144
+ });
145
+ ```
146
+
147
+ `intentionKey` is the host's stable business-intention identity across generation
148
+ revisions. Never derive a fresh key from model output, suggestion order or a retry.
149
+ Selections must reference current persisted validated `ready_for_review` output.
150
+ Each batch requires distinct suggestion indexes and intention keys; duplicate
151
+ indexes are rejected before any operation or plan preview is written.
152
+ The service rechecks current catalog/schema/target/source provenance, then calls
153
+ the existing operation or plan preview. Each result still requires the ordinary
154
+ human review and execution APIs. Reprocessing creates no new business identity;
155
+ successful-action replay protection stays with execution.
156
+
157
+ ## Evidence and limits
158
+
159
+ The test matrix is `docs/test-matrix/3673-ingestion-proposals.md`. SQLite and real
160
+ PostgreSQL suites exercise authority, revisions, abstention, hostile output,
161
+ compound proposals, split provenance and reviewed domain effects. The upload
162
+ reference composes authenticated multipart delivery of a generated embedded-text
163
+ PDF through the actual packaged PDF child, durable interpretation, and the existing
164
+ ordered ContentDocument draft/retained-asset plan. It checks duplicate delivery,
165
+ unauthenticated and foreign-tenant denial, explicit approval of both steps,
166
+ idempotent execution replay, page provenance, and unchanged original bytes.
167
+ The compound-email case uses a declared messages-owner snapshot fixture.
168
+
169
+ Local HTTP fixtures execute the actual AI SDK transport and cancellation contract.
170
+ Generative responses and the separate injected PDF split boundaries establish
171
+ deterministic orchestration, not model accuracy or OCR quality. No held-out routing
172
+ evaluation or automatic approval is claimed. Missing/unknown AI completion remains
173
+ explicit under [SDK #1381](https://github.com/happyvertical/sdk/issues/1381).
package/README.md CHANGED
@@ -1,3 +1,151 @@
1
- # Temporary Holding Version
1
+ # @happyvertical/smrt-ingestion
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Durable receipt, evidence, analysis attempts and action-journal foundations for
4
+ [ADR 0004](../../docs/adr/0004-application-aware-ingestion.md).
5
+
6
+ The root, `/models` and `/dto` entry points are browser-safe. Import
7
+ `IngestionService` from `/server` in trusted host code. Generated REST, CLI and
8
+ MCP access to the persistence models is disabled; hosts expose authorized DTO
9
+ methods through their authenticated application transport.
10
+
11
+ Before starting a service, deploy the package manifest through the public s-m-r-t
12
+ migration APIs or `smrt db:migrate`. `assertReady()` only checks existing tables.
13
+ SQLite and PostgreSQL are supported; other engines fail construction.
14
+
15
+ A host supplies a database, AssetRuntime, authenticated tenant/actor/confidential
16
+ scope, a live authorization callback, a registered jobs target, and an idempotent
17
+ `purgeDerived` callback for host-owned previews, retrieval indexes and caches.
18
+ The authorization callback receives the captured ceiling and the current
19
+ transaction executor; it must intersect current grants with that ceiling. Never
20
+ construct the scope, retention policy or budgets from incoming document labels.
21
+
22
+ `receive` accepts bounded original byte buffers, with stable source part IDs,
23
+ parent relationships and optional upstream identity/version. Source adapters own
24
+ streaming and upstream snapshot retrieval. Receipt identity is tenant + source +
25
+ delivery key; changing bytes or authenticated scope conflicts. Content equality
26
+ in a different conversation does not deduplicate an arrival. The returned item
27
+ ID and version are safe acknowledgment data, never a storage URL.
28
+
29
+ Storage intents contain a stable asset ID and a public AssetStore-planned URI
30
+ before byte I/O. Preservation verifies existing bytes instead of replacing them.
31
+ A receipt becomes ready only after every part is durable and its dispatch intent
32
+ commits. A source retries the same receive call after failure or lost
33
+ acknowledgment. Hosts schedule `repairDispatches` after restart and periodically;
34
+ it repairs missing/failed queue submissions through SmrtJobCollection. The host
35
+ job restores the captured scope and calls the analysis lifecycle methods. Run
36
+ TaskRunner with the `ingestion` queue enabled.
37
+
38
+ `analyze` appends immutable input/configuration revisions. `claimAnalysis` uses
39
+ leases, monotonic fences and attempt budgets; `completeAnalysis` rejects expired
40
+ or superseded workers. Terminal attempt output/provenance is preserved. Queue
41
+ submission budgets also stop repeated handler failures in visible
42
+ `needs_attention`. Reprocessing does not mint actions. `createAction`,
43
+ `publishProposal` and `appendPlan` persist explicit intentions and exact attempt
44
+ bindings for the later review/execution layer. They do not approve or apply
45
+ business operations. Decision/execution/feedback models are persistence
46
+ foundations; policy, handler expansion, review and business apply belong to the
47
+ later epic children.
48
+
49
+ `expire` revokes access and pending work before removing bytes and calling
50
+ `purgeDerived`. Interrupted deletion is retried by `repairDeletions` and
51
+ `sweepRetention`. Minimal receipt/action identities remain, including successful
52
+ action result digests; all content-bearing journal payloads are redacted. Private
53
+ cleanup locators remain in deletion tombstones so repeated sweeps can remove a
54
+ late stale writer's orphan bytes even after the asset owner row is gone. Source
55
+ adapters must enforce their configured delivery-age cutoff; expired replay IDs
56
+ never become fresh work. Continue running the cleanup sweep for the retained
57
+ storage namespace. Upstream source mutation/deletion cannot change copied
58
+ originals; explicit privacy deletion revokes every ingestion-derived payload and
59
+ requires host cache/index purge.
60
+
61
+ See [EXTRACTION.md](EXTRACTION.md) for bounded extraction, lease-bound input
62
+ snapshots, provider isolation requirements and the extraction corpus.
63
+
64
+ Validation commands are `pnpm test`, `pnpm test:postgres`, `pnpm test:providers`, `pnpm typecheck` and
65
+ `pnpm build` (including built browser import inspection). The PostgreSQL lane
66
+ requires the repository's configured database service and never silently skips.
67
+ The provider lane exercises local SDK adapters and process isolation; it does not
68
+ claim live remote recognition quality. Component, e2e and evaluation command names are reserved by the ADR and
69
+ explicitly unavailable until their owning children deliver the corresponding
70
+ capability. See the [behavior matrix](../../docs/test-matrix/3670-ingestion-foundation.md).
71
+
72
+ ## Authoritative review and execution
73
+
74
+ Configure `IngestionOptions.execution` for policy, immutable previews, authenticated
75
+ decisions and idempotent domain execution. See [execution integration](agents/execution.md)
76
+ for the public handler catalog, required host authorization/target locks, deployment
77
+ migrations and jobs continuation integration. The maintained application reference
78
+ in `reference/handlers.ts` creates draft ContentDocuments and attaches retained
79
+ evidence through public domain APIs; it is not a published runtime adapter.
80
+
81
+ ## Configured source adapters
82
+
83
+ The server entry exports `createSourceDeliveryHandler`, `EmailSourceAdapter`,
84
+ `WatchFolderSourceAdapter`, and `createVendorWebhookHandler`. Construct a
85
+ `SourceBinding` from authenticated host configuration: enabled flag, scoped
86
+ `IngestionService`, stable source ID/version, captured authority ceiling,
87
+ retention policy, allowed media types and receipt limits. Re-resolve this binding
88
+ when grants/configuration change. Source payloads never supply authority.
89
+
90
+ The reference POST handler authenticates before reading bytes and requires
91
+ `Idempotency-Key`. JSON accepts exactly `capturedAt` and one of `text`,
92
+ `structured`, or `reference: { owner: 'asset'|'message', id, version }`.
93
+ The host's `snapshotReference` must authorize current ownership and return copied
94
+ bytes; an absent snapshot fails rather than acknowledging a mutable locator.
95
+ Multipart accepts one `file` plus `captureId` (equal to the idempotency key),
96
+ `capturedAt`, `captureSource` (`camera`, `native_picker`, `audio`, `document`), and optional
97
+ lowercase SHA-256 `sha256`. This reuses mobile's durable UUID Idempotency-Key,
98
+ MIME-bearing file part and explicit capture metadata convention. Captures retain
99
+ original bytes and a parent metadata evidence record. Configure both the wire
100
+ limit `maxRequestBytes` and receipt `maxBytes`/`maxParts`; include
101
+ `application/json` in the media allowlist for capture/email metadata. Signature
102
+ checks cover PDF, TIFF, PNG/JPEG/WebP and WAV/Ogg/WebM/MP3/MP4 audio; signature
103
+ acceptance promises preservation, not extraction support or codec validity.
104
+
105
+ Email is explicitly opt-in per configured account. `EmailSourceAdapter` takes a
106
+ `readMessage` callback compatible with the messages owner's
107
+ `EmailAccount.readIntakeMessage(providerLookup, maxBytes, imapIdentity?)` snapshot.
108
+ The provider lookup is Gmail message ID, POP3 UIDL, or IMAP RFC Message-ID; it
109
+ is separate from the delivery dedupe key. IMAP requires `{ folder, uidValidity,
110
+ uid }`: the method selects the mailbox and verifies UIDVALIDITY and returned UID.
111
+ Ambiguous sender IDs resolving to another UID fail explicitly. That method connects,
112
+ fetches the full message including actual attachment bytes, copies the bytes,
113
+ and disconnects. Metadata-only attachments fail explicitly. It never reads an
114
+ attachment path supplied by a provider. A host lists provider deliveries using a
115
+ stable account-specific locator (for IMAP include folder, UIDVALIDITY and UID),
116
+ an explicit revision, and an opaque resumable checkpoint. Same locator/revision
117
+ with changed bytes conflicts; an explicit new revision creates a new receipt.
118
+ Do not use sender-controlled RFC Message-ID as the sole transport identity.
119
+ `receiveBatch` processes in order and stops at the first failure. The host owns
120
+ durable checkpoint storage and commits its `acknowledge` callback only after the
121
+ adapter has received accepted/duplicate. If checkpoint persistence fails, retry
122
+ returns the original receipt. Message/thread/attachment relationships are copied;
123
+ credentials and arbitrary headers are excluded. The ordinary `syncFrom` metadata
124
+ sync remains separate; `hasAttachments` is never proof of byte preservation.
125
+
126
+ Watch-folder intake polls only an explicitly configured existing private local
127
+ directory. Producers should close their files, preferably publishing with atomic
128
+ rename. Two unchanged size/inode/mtime/ctime observations separated by
129
+ `stabilityMs` are required, and the interval runs again after claim/restart.
130
+ A private `.ingestion-claims/<UUID>/claim.json` intent is synced before atomically
131
+ renaming the original into that directory. Keep the directory on one filesystem,
132
+ under the host's ownership; symlink inputs are refused. A file descriptor still
133
+ held by an uncooperative producer can write after any stability window, so the
134
+ producer close/publish protocol is required for immutable input. Accepted or
135
+ duplicate receipts move the entire claim to `.ingestion-processed`; do not delete
136
+ claims to retry. Polling recovers persisted UUIDs after process death, so receipt
137
+ replay deduplicates ready-without-ack. `failure.json` and poll results expose safe
138
+ categories while originals remain available. Intent directories without an
139
+ original mean death before claim or a competing poll won; the producer original
140
+ has not been removed. Operators may remove these empty intents after confirming
141
+ no active importer. Keep processed originals according to the host's retention
142
+ policy. This adapter does not watch arbitrary paths or start background loops.
143
+
144
+ Vendor webhooks use a host `verify(request, originalBytes)` callback after bounded
145
+ raw-body reading. The callback must verify signature, delivery time/replay window
146
+ and account ownership, then return trusted binding and durable delivery identity.
147
+ No vendor implementation or hardware driver is selected. All adapters invoke
148
+ only receipt preservation, never domain writes, AI tools or external actions.
149
+
150
+ See [PROPOSALS.md](PROPOSALS.md) for bounded application catalog discovery, durable
151
+ interpretation revisions, optional typed decisions and explicit generated preview.
@@ -0,0 +1,132 @@
1
+ # Review and execution integration
2
+
3
+ `/execution` exports `IntakeExecutionService`, handler/host types, policy
4
+ resolution, binding digests and `continueIntakeReview`. `IngestionService` exposes
5
+ these operations when its optional `execution` configuration is present. Its
6
+ loader is lazy: importing `/server` alone does not import the principal stack.
7
+ `/dto` exposes execution messages using type-only exports. No generated REST,
8
+ CLI or MCP mutation endpoint is enabled; applications authenticate their own
9
+ service surface and create `IngestionScope` from the session.
10
+
11
+ ## Catalog and authority
12
+
13
+ Applications supply a trusted `IntakeExecutionOptions.handlers` catalog.
14
+ `OperationHandler` has qualified ID, immutable version, description, object JSON
15
+ schemas for arguments/results, allowlisted `resultModels`, `validate`, `preview`,
16
+ a registered `{model, action, version}` operation and explicit effect/idempotency/
17
+ open-world capability declaration. Preview returns display, exact normalized
18
+ arguments and target revision preconditions. There is no arbitrary tool execution
19
+ or expression evaluation. Reviewer display wraps application fields under `preview`
20
+ and symbolic metadata under `dependencies`; plan steps additionally wrap that
21
+ object under `step` alongside the plan preview. Application display keys cannot
22
+ overwrite framework metadata or be silently overwritten by it. A version change
23
+ requires a new preview and approval.
24
+ `PlanHandler` references a real registered playbook key and resolved definition
25
+ hash; expansion contains only ordered operation handlers and typed earlier-step
26
+ result references. Discovery/proposal generation consumes this catalog rather
27
+ than creating another registry. Semantic model output is never host configuration.
28
+
29
+ Authenticated receipt context captures `capturedCeiling.execution` with principal
30
+ ID and permission/handler/operation ceilings. The execution host's `authorize`
31
+ resolves current grants and application/tenant/source/(optional request) policy
32
+ on the supplied executor, checking the active requester and confidential scope.
33
+ Lists intersect, budgets minimize, review requirements OR, and disabled policy
34
+ wins. Policy versions must change when policy semantics change. Normal operation
35
+ requires human review; machine authorization requires explicit evaluated opt-ins
36
+ at all three authority layers and remains separate provenance from human review.
37
+ The reference handlers always require review; no evaluation claim is made here.
38
+
39
+ `mutationBoundary: 'serialized'` is an explicit host guarantee, not an inferred
40
+ property of a permissions array: grant revocation must serialize with the supplied
41
+ transaction, or the owning mutation must revalidate under its own lock. The host
42
+ must implement that guarantee before enabling handlers. `assertTarget` similarly
43
+ locks current tenant/confidential ownership and revision against concurrent
44
+ writers using the supplied executor. The reference host demonstrates a scoped
45
+ row lock before public `Contents` reads. A current `PrincipalRun` intersects live
46
+ permissions with the captured ceiling and checks the exact registered collection
47
+ operation at every effect. No `runOnce` atomicity is assumed.
48
+
49
+ ## Durable review and effects
50
+
51
+ Preview appends a proposal bound to exact analysis attempt/evidence, handler,
52
+ arguments, policy and target preconditions. Decision requests use unique durable
53
+ request IDs; reusing an ID with changed content is rejected. First valid review
54
+ wins under the item lock. Corrections append a new revision; reject/defer remain
55
+ non-executable. Plan corrections use re-expansion. Human decisions expire and
56
+ live authority is checked again before each step. Raw foundation proposals cannot
57
+ bypass execution's schema, preview, evidence and approval verification.
58
+
59
+ Database handlers must use `context.db` for every domain effect and must perform
60
+ no external I/O. Domain result and successful execution commit together. A domain savepoint rolls failed effects
61
+ back while preserving the item lock and atomically consuming the retry attempt. Completed
62
+ actions survive job cleanup and evidence retention; expired originals leave a
63
+ minimal result digest tombstone, preventing a second effect. Active result reads
64
+ recheck host visibility and result target ownership.
65
+
66
+ External handlers reserve a stable action idempotency key before submission.
67
+ Timeout, process death or ambiguous outcome requires reconciliation; an expired
68
+ lease never means permission to resend. `reconcile` must return authoritative
69
+ `succeeded`, `not_applied` or `unknown`. `not_applied` is safe only when the provider
70
+ can exclude a late acceptance of the original send; uncertainty stays `unknown`.
71
+ The same key is reused after a conclusive non-application. Cancellation prevents
72
+ pending work but cannot retract a send already issued. Applications must arrange
73
+ provider reconciliation before privacy retention removes the binding; a redacted
74
+ unknown action fails closed and cannot automatically execute again.
75
+
76
+ Each plan step has a stable action identity. Completed steps are immutable and
77
+ return their original result. Symbolic record IDs bind to exact earlier proposal
78
+ revision, successful result digest/model and target revision; they cannot point
79
+ across items or form cycles. Failed dependent steps never repeat predecessors.
80
+ The plan preview and target preconditions are part of its digest and visible in
81
+ each step review; changed preview/preconditions require a fresh review.
82
+ Abort/continue follows the pinned public playbook definition, with fresh authority
83
+ at every step.
84
+
85
+ `continueIntakeReview` uses the existing jobs continuation context. Applications
86
+ create tasks with owner, tenant and exact `{recordId, revision, inputKey}` binding
87
+ and configure `authorizeMcpTask` to revalidate it. A continuation answer only wakes
88
+ the task; the service reloads the authoritative decision. Waiting persists without
89
+ an occupied worker and survives runner restart.
90
+
91
+ ## Deployment and evidence
92
+
93
+ Apply normal manifest migrations before runtime. Execution imports the public
94
+ agents/users principal stack, which registers profiles including `ProfileAsset`;
95
+ execution-enabled hosts must migrate their dependency graph (including profile
96
+ asset joins used by asset deletion). `/server` receipt-only imports register none
97
+ of these additional models. Content is not a runtime dependency of ingestion.
98
+ `reference/handlers.ts` is an application/test fixture, excluded from published
99
+ entries and declarations; importing it deliberately adds Content and its asset
100
+ joins, whose migrations the application then owns. No runtime DDL is performed.
101
+
102
+ Real SQLite and PostgreSQL execution suites exercise the public Content draft and
103
+ attachment APIs, transaction rollback, actor/context denials, simultaneous applies
104
+ and reviewers, retention replay, external process death and actual TaskRunner
105
+ restart. See the issue 3674 test matrix for the complete release evidence mapping.
106
+
107
+ ## Layered retention and request automation
108
+
109
+ Every automation-bearing policy layer, including an optional request layer, must
110
+ match the evaluated version. An absent request automation block adds no evaluator
111
+ restriction; it cannot replace the three required application/tenant/source opt-ins.
112
+
113
+ Authorized execution-policy access monotonically narrows the existing intake
114
+ expiry to `min(existing expires_at, item.created_at + resolved retentionMs)`.
115
+ The anchor is receipt creation, never review, retry or replay time. A longer later
116
+ policy never extends a recorded deadline. This hard maximum applies to the whole
117
+ intake item: the existing `sweepRetention` protocol removes retained originals and
118
+ derivatives, preserving replay identities. No second expiry column or hidden clock
119
+ is introduced, and no blob I/O occurs in an execution transaction.
120
+
121
+ At an observed expired deadline, execution reads/completions erase proposal,
122
+ decision, execution, plan, action and feedback payloads under the item lock. Review
123
+ reads return existing identity/hash fields with `state: 'expired'` and empty
124
+ `display`; completed action replay returns its digest-only tombstone. Failed writes, including ordinary validation/CAS/policy rejection and domain
125
+ savepoint rollback, cannot roll back an already-authorized retention ceiling or
126
+ redaction. Restoring a recorded authorized maximum needs no new content-read grant;
127
+ later revocation cannot undo it. Initial denied authorization records no intent.
128
+ Completed replay checks the deadline again after target authorization
129
+ callbacks before returning cached content. Proposal publication and automatic
130
+ authorization also recheck after preview/evaluator callbacks, before publishing
131
+ retained proposal or decision payloads. Unknown
132
+ external outcomes remain unknown/reconcile-only, never eligible for blind resend.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=__smrt-register__.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"__smrt-register__.d.ts","sourceRoot":"","sources":["../src/__smrt-register__.ts"],"names":[],"mappings":""}