@happyvertical/smrt-ingestion 0.57.21 → 0.57.23
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.
- package/FEEDBACK.md +104 -0
- package/PROPOSALS.md +77 -0
- package/README.md +126 -0
- package/REVIEW.md +272 -4
- package/agents/execution.md +72 -0
- package/dist/chunks/{execution-9CfjxX8I.js → execution-BzVQax0n.js} +421 -23
- package/dist/chunks/execution-BzVQax0n.js.map +1 -0
- package/dist/chunks/{feedback-CuUzWKzP.js → feedback-D3UTF8fm.js} +119 -21
- package/dist/chunks/feedback-D3UTF8fm.js.map +1 -0
- package/dist/chunks/{extraction-CJ-zrbg0.js → proposal-contracts-77Uo1B1j.js} +39 -39
- package/dist/chunks/proposal-contracts-77Uo1B1j.js.map +1 -0
- package/dist/chunks/{reviewer-instruction-Db3jDN9a.js → reviewer-instruction-CEc3AOuU.js} +2 -2
- package/dist/chunks/{reviewer-instruction-Db3jDN9a.js.map → reviewer-instruction-CEc3AOuU.js.map} +1 -1
- package/dist/dto.d.ts +69 -0
- package/dist/dto.d.ts.map +1 -1
- package/dist/execution-contracts.d.ts +4 -1
- package/dist/execution-contracts.d.ts.map +1 -1
- package/dist/execution-dto.d.ts +16 -0
- package/dist/execution-dto.d.ts.map +1 -1
- package/dist/execution.d.ts +58 -3
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +2 -2
- package/dist/feedback-dto.d.ts +12 -4
- package/dist/feedback-dto.d.ts.map +1 -1
- package/dist/feedback-internal.d.ts +9 -0
- package/dist/feedback-internal.d.ts.map +1 -1
- package/dist/feedback.d.ts +3 -0
- package/dist/feedback.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/manifest.json +321 -3
- package/dist/models.d.ts +15 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +73 -2
- package/dist/models.js.map +1 -1
- package/dist/policy.d.ts +3 -0
- package/dist/policy.d.ts.map +1 -1
- package/dist/proposal-errors.d.ts +4 -0
- package/dist/proposal-errors.d.ts.map +1 -1
- package/dist/proposal-sdk.d.ts +54 -1
- package/dist/proposal-sdk.d.ts.map +1 -1
- package/dist/proposals.d.ts +2 -1
- package/dist/proposals.d.ts.map +1 -1
- package/dist/proposals.js +215 -10
- package/dist/proposals.js.map +1 -1
- package/dist/provider-failure.d.ts +3 -0
- package/dist/provider-failure.d.ts.map +1 -0
- package/dist/redaction.d.ts +9 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/review-dto.d.ts +14 -4
- package/dist/review-dto.d.ts.map +1 -1
- package/dist/review-host.d.ts.map +1 -1
- package/dist/server.d.ts +82 -10
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +366 -47
- package/dist/server.js.map +1 -1
- package/dist/smrt-knowledge.json +128 -22
- package/dist/svelte/components/ActionReview.svelte +39 -9
- package/dist/svelte/components/ActionReview.svelte.d.ts +8 -1
- package/dist/svelte/components/ActionReview.svelte.d.ts.map +1 -1
- package/dist/svelte/components/EvidenceViewer.svelte +60 -4
- package/dist/svelte/components/EvidenceViewer.svelte.d.ts +12 -1
- package/dist/svelte/components/EvidenceViewer.svelte.d.ts.map +1 -1
- package/dist/svelte/components/InboxView.svelte +160 -18
- package/dist/svelte/components/InboxView.svelte.d.ts +28 -1
- package/dist/svelte/components/InboxView.svelte.d.ts.map +1 -1
- package/dist/svelte/components/IntakeWorkspace.svelte +27 -0
- package/dist/svelte/components/IntakeWorkspace.svelte.d.ts +19 -0
- package/dist/svelte/components/IntakeWorkspace.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ReviewView.svelte +264 -23
- package/dist/svelte/components/ReviewView.svelte.d.ts +18 -1
- package/dist/svelte/components/ReviewView.svelte.d.ts.map +1 -1
- package/dist/svelte/components/TriageView.svelte +669 -0
- package/dist/svelte/components/TriageView.svelte.d.ts +16 -0
- package/dist/svelte/components/TriageView.svelte.d.ts.map +1 -0
- package/dist/svelte/i18n.d.ts +39 -0
- package/dist/svelte/i18n.d.ts.map +1 -1
- package/dist/svelte/i18n.js +39 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -0
- package/dist/svelte/view-utils.d.ts +65 -0
- package/dist/svelte/view-utils.d.ts.map +1 -1
- package/dist/svelte/view-utils.js +145 -0
- package/package.json +13 -13
- package/dist/chunks/execution-9CfjxX8I.js.map +0 -1
- package/dist/chunks/extraction-CJ-zrbg0.js.map +0 -1
- package/dist/chunks/feedback-CuUzWKzP.js.map +0 -1
package/FEEDBACK.md
CHANGED
|
@@ -30,6 +30,110 @@ an authorized existing event; it cannot supply a correctness label or fabricate
|
|
|
30
30
|
an execution result. An initial denial does not mutate retention. Once authorized,
|
|
31
31
|
a narrowed privacy ceiling survives business rollback and later revocation.
|
|
32
32
|
|
|
33
|
+
Observed `action_decision` events carry the decision's `decisionMode`
|
|
34
|
+
(`review` or `review_own`) and `selfDecision` flag from the durable decision
|
|
35
|
+
record; `selfDecision` is true whenever the decider submitted the item, in
|
|
36
|
+
either mode. A submitter confirming their own proposal (#4085) is observed as a
|
|
37
|
+
decision by that actor, never as reviewer correctness evidence; consumers that
|
|
38
|
+
derive reviewer agreement must exclude `selfDecision: true` events, and nothing
|
|
39
|
+
in this package turns them into automation eligibility.
|
|
40
|
+
|
|
41
|
+
## Reviewer routing choices
|
|
42
|
+
|
|
43
|
+
A `reviewer_routing` record is appended inside the owning transaction when
|
|
44
|
+
feedback is configured and `capture` is authorized, at the moment the choice is
|
|
45
|
+
final: `resolveItem` (REVIEW.md) records `choice: 'none'` keyed
|
|
46
|
+
`routing:<requestId>`, and `submitDecision` records `choice: 'handler'` keyed
|
|
47
|
+
`routing:<decision requestId>` when a reviewer **approves** a reviewer-selected
|
|
48
|
+
action. Handler-choice capture requires `mode: 'review'`: a `review_own`
|
|
49
|
+
submitter approval authorizes normally but appends no reviewer-routing example or
|
|
50
|
+
rule support. A reviewer using the reviewer path may still record an explicit
|
|
51
|
+
routing choice for their own receipt; it does not judge any model suggestion.
|
|
52
|
+
Starting an action records nothing; rejecting or deferring it never produces an
|
|
53
|
+
example. The record needs no model suggestion: it binds the current
|
|
54
|
+
completed/partial attempt (`attemptId`, output digest, analysis revision, frozen
|
|
55
|
+
evidence hashes and the extracted text as `query`), the acting reviewer
|
|
56
|
+
(`reviewer`, also `approvedBy` for a handler choice), the actor who started the
|
|
57
|
+
action (`startedBy`), the start reason, and for a handler choice the
|
|
58
|
+
`handlerId@version`, exact approved arguments and the approved action binding
|
|
59
|
+
(revision and binding hash). Denied or oversized capture, an unavailable source
|
|
60
|
+
pin or malformed evidence skip the projection and leave the authoritative
|
|
61
|
+
decision or disposition intact; database write failures still roll everything
|
|
62
|
+
back. There is no
|
|
63
|
+
supersession: a correction after approval (`previewProposal` on the current
|
|
64
|
+
revision) changes the revision, which retires the old record from retrieval and
|
|
65
|
+
support until the new revision is approved and captured again; a correction
|
|
66
|
+
before approval records nothing until that revision is approved.
|
|
67
|
+
|
|
68
|
+
Settledness is a selection-time condition. Once a generation used an example,
|
|
69
|
+
or a rule suggestion stored its support, the lineage re-checks (reading the
|
|
70
|
+
generation, reviewing or deciding its actions, reading or adopting the rule,
|
|
71
|
+
and replaying `suggestRoutingRule`, which returns the stored suggestion) verify:
|
|
72
|
+
|
|
73
|
+
- every record: its digest, that it is not superseded, retention (no
|
|
74
|
+
redaction or deletion), the unchanged source attempt digest and frozen
|
|
75
|
+
evidence hashes, and live `retrieve` authority in the same tenant and
|
|
76
|
+
confidential scope with every generation recipient allowed by the source's
|
|
77
|
+
provider policy;
|
|
78
|
+
- an `interpretation` record (unchanged from #3676): its action's current
|
|
79
|
+
revision, binding hash, attempt, handler@version and arguments; the action's
|
|
80
|
+
target preconditions at their recorded revisions unless the action
|
|
81
|
+
`succeeded`; the prompt, configuration and model versions; and that the action
|
|
82
|
+
is not reviewer-selected;
|
|
83
|
+
- a `reviewer_routing` record with `choice: 'handler'`: its action's current
|
|
84
|
+
revision, binding hash, attempt, handler@version and arguments, that the
|
|
85
|
+
action is still reviewer-selected, and that its targets are still accessible
|
|
86
|
+
(not their revisions: the record means the handler choice, not target state);
|
|
87
|
+
- a `reviewer_routing` record with `choice: 'none'`: it has no action, so the
|
|
88
|
+
source item is re-checked under the first currently offered receiving handler
|
|
89
|
+
that still authorizes it.
|
|
90
|
+
|
|
91
|
+
Rule support (`suggestRoutingRule`, and on read, adoption and replay of a stored
|
|
92
|
+
suggestion) additionally requires: at least two records from distinct source
|
|
93
|
+
items; `judgment: 'correct'` with matching prompt/configuration/model versions
|
|
94
|
+
for an interpretation record; `choice: 'handler'` for a routing record; each
|
|
95
|
+
record's handler@version equal to the receiving binding's; and each source's
|
|
96
|
+
live routing projection (or full arguments when no projection is configured)
|
|
97
|
+
equal to the receiving action's. Settledness of a routing source is checked only
|
|
98
|
+
when the support is first selected.
|
|
99
|
+
|
|
100
|
+
A later disposition, approval lapse, outcome change or target write of a routing
|
|
101
|
+
source never revokes it.
|
|
102
|
+
|
|
103
|
+
Retrieval scans `interpretation` and `reviewer_routing` records together under
|
|
104
|
+
the same bounds and lexical matcher. A routing example is returned only when its
|
|
105
|
+
digest verifies, its attempt and evidence hashes are unchanged, the source item
|
|
106
|
+
is still readable in the same tenant and confidential scope, every generation
|
|
107
|
+
recipient is allowed by the source's live provider policy, and (for a chosen
|
|
108
|
+
handler) the action is still reviewer-selected and settled — applied
|
|
109
|
+
(`executing`, `succeeded`) or `authorized` with an unexpired approval, on an item
|
|
110
|
+
without a disposition — its binding is still the current revision, and the handler with its candidate
|
|
111
|
+
references is currently offered to the receiver. A `none` choice is
|
|
112
|
+
revalidated under the first currently offered receiving handler the source item
|
|
113
|
+
authorizes. Routing examples carry `kind: 'reviewer_routing'`,
|
|
114
|
+
`judgment: 'correct'`, `routing: { choice, reason }`, `handlerId: 'none'` with an
|
|
115
|
+
empty action for closed items, and no model/prompt/configuration versions, since
|
|
116
|
+
no suggestion was judged. They are included in provider input and selection
|
|
117
|
+
provenance exactly like interpretation examples and are rechecked by the same
|
|
118
|
+
lineage gates.
|
|
119
|
+
|
|
120
|
+
`suggestRoutingRule` accepts `reviewer_routing` support only for `choice: 'handler'`
|
|
121
|
+
records whose action is settled in the same sense when the support is chosen
|
|
122
|
+
(a lapsed, unapplied approval or a disposition excludes it) and whose handler, version and
|
|
123
|
+
full arguments match the receiving binding and each other, from distinct source
|
|
124
|
+
items, under the live routing projection; `none`, rejected, deferred and
|
|
125
|
+
re-corrected records never support a rule. Suggestions and adoptions keep every existing #3676
|
|
126
|
+
rule: no catalog, candidate, permission or automation expansion, and
|
|
127
|
+
`automaticActionEligible: false`. Reviewer-selected actions themselves are refused
|
|
128
|
+
by `authorizeAutomatic`. They are routing choices, never labels for a model
|
|
129
|
+
suggestion: `recordFeedback` refuses an interpretation judgment on one (even
|
|
130
|
+
when the item also carries a generation, and whatever its decision), retrieval
|
|
131
|
+
and rule support ignore interpretation records bound to one, and `ActionReview`
|
|
132
|
+
hides the correctness buttons for them. A disposition says nothing about
|
|
133
|
+
whether a suggestion was right, so interpretation judgments on a resolved item
|
|
134
|
+
still count. Only
|
|
135
|
+
`startReviewerAction` mints that origin; `createAction` refuses it.
|
|
136
|
+
|
|
33
137
|
## Retrieval and provider boundaries
|
|
34
138
|
|
|
35
139
|
`retrieveFeedback({ itemId, query })` derives the current offered catalog,
|
package/PROPOSALS.md
CHANGED
|
@@ -85,6 +85,25 @@ Expired, cancelled, revoked, retried or superseded workers cannot read or publis
|
|
|
85
85
|
current lease and each offered handler/candidate are rechecked immediately before
|
|
86
86
|
each outbound generative/decision boundary and before publication.
|
|
87
87
|
|
|
88
|
+
### Inline interpretation
|
|
89
|
+
|
|
90
|
+
Receipts created with `dispatch: 'inline'` (text/structured parts only) run the
|
|
91
|
+
same stages in-process through `interpretInline(itemId, { requestId, extraction
|
|
92
|
+
})`: an immutable extraction revision claimed under a fenced lease and extracted
|
|
93
|
+
with the built-in UTF-8 extractor, then `prepareGeneration` and a fenced
|
|
94
|
+
interpretation revision generated by `generateProposals`. The analysis lease,
|
|
95
|
+
catalog digest and live catalog/candidate checks, source pin, provenance and
|
|
96
|
+
`StageFailure` categories are identical to the job path; no dispatch intent is
|
|
97
|
+
ever deliverable and nothing is enqueued. A retry with the same `requestId`
|
|
98
|
+
replays the recorded revisions rather than re-preparing against a superseded
|
|
99
|
+
source, also under concurrency. The interpretation append rechecks, under the
|
|
100
|
+
item lock, that its pinned extraction is still the latest revision and refuses
|
|
101
|
+
a superseded pin: first through the general latest-extraction check that
|
|
102
|
+
every interpretation append runs (`GenerationSourceStaleError`), then by the
|
|
103
|
+
inline check that the pinned attempt is still current (`Generation source changed`). Generated suggestions still require explicit preview and a human
|
|
104
|
+
decision, either through the reviewer surface or the submitter-scoped surface
|
|
105
|
+
described in [REVIEW.md](REVIEW.md).
|
|
106
|
+
|
|
88
107
|
## Providers and validation
|
|
89
108
|
|
|
90
109
|
`createSDKProposalGenerator(client, identity, {maxTokens,timeoutMs})` uses an
|
|
@@ -161,6 +180,64 @@ The SDK adapter constrains replies with a `json_schema` response format built
|
|
|
161
180
|
from the validator's schema and the fixed instructions describe the shape; a
|
|
162
181
|
custom `ProposalGenerator` receives no schema, so its raw reply meets the
|
|
163
182
|
validator directly and the same reason is recorded.
|
|
183
|
+
The provider-facing schema is made portable (`portableResponseSchema`, #4260):
|
|
184
|
+
keywords that common OpenAI-compatible structured-output backends reject are
|
|
185
|
+
removed at schema positions, including inside handler argument schemas
|
|
186
|
+
(`uniqueItems`, `contains`, `multipleOf`, `format`, `patternProperties`,
|
|
187
|
+
`propertyNames`, `min`/`maxProperties`, `not`, `if`/`then`/`else`,
|
|
188
|
+
`dependent*`, `unevaluated*`, and the `default`/`examples` annotations; the
|
|
189
|
+
full list and reasons are in `src/proposal-sdk.ts`). With `uniqueItems`, for
|
|
190
|
+
example, vLLM's xgrammar feature check flags the schema, its auto mode routes
|
|
191
|
+
it to llguidance, and llguidance answers
|
|
192
|
+
`400 Grammar error: Unimplemented keys: ["uniqueItems"]`. This only loosens the
|
|
193
|
+
grammar: the validator still enforces the constraints, including unique
|
|
194
|
+
`alternatives` and `missingFields`, and the fixed instructions ask for distinct
|
|
195
|
+
entries. The exception is `format`, which is advisory: the validator runs Ajv
|
|
196
|
+
with `validateFormats: false`, so `format` is enforced nowhere. When
|
|
197
|
+
`patternProperties` is removed, the same object's `additionalProperties` is
|
|
198
|
+
removed too, so the grammar does not shrink to `{}`. The `contentSchema` annotation is omitted as well, so nested unsupported
|
|
199
|
+
constraints cannot leak through an unvisited schema position. A backend may
|
|
200
|
+
recursively inspect typed schema-like objects even inside `enum` or `const`
|
|
201
|
+
data. When such data contains an unsupported keyword, the whole containing
|
|
202
|
+
`enum`/`const` constraint is omitted conservatively; literal data is never
|
|
203
|
+
rewritten. Harmless property names and ordinary enum/const data stay intact.
|
|
204
|
+
The original validator remains authoritative for omitted literal constraints.
|
|
205
|
+
There is no automatic `json_object` fallback.
|
|
206
|
+
|
|
207
|
+
Invariant: the provider copy only ever accepts a superset of what the original
|
|
208
|
+
accepts. Each removed keyword is a conjunct, so removing it only widens. The two
|
|
209
|
+
places where removal would narrow are rewritten:
|
|
210
|
+
- every `oneOf` becomes `anyOf` (or joins an existing `anyOf` through `allOf`),
|
|
211
|
+
because loosened `oneOf` branches could both match and reject a value the
|
|
212
|
+
original accepted;
|
|
213
|
+
- an object that loses `patternProperties` also loses `additionalProperties`.
|
|
214
|
+
|
|
215
|
+
Provider-only `$ref`, `$dynamicRef`, and `$recursiveRef` constraints are also
|
|
216
|
+
removed. Rewriting or deleting a subschema can invalidate pointer or anchor
|
|
217
|
+
targets, including targets inside nested `$id` resources. Dropping references
|
|
218
|
+
conservatively widens the grammar without resolving untrusted schema resources;
|
|
219
|
+
property names and enum/const data named `$ref` remain unchanged. Referenced
|
|
220
|
+
constraints, recursion, and dynamic scope remain enforced by the original
|
|
221
|
+
validator, so provider guidance for reference-based arguments is less precise.
|
|
222
|
+
|
|
223
|
+
The validator still checks the original, including `oneOf` exclusivity. A
|
|
224
|
+
property-style regression checks original-valid ⇒ portable-valid across the
|
|
225
|
+
removed keywords. Handler argument schemas may still carry `allOf` through
|
|
226
|
+
unchanged; its support across backends is unverified, so check such a handler
|
|
227
|
+
against the deployed backend.
|
|
228
|
+
|
|
229
|
+
A generator call that throws ends the attempt as `provider_error` with the
|
|
230
|
+
`unavailable` warning and `provenance.failure` `{category: 'unavailable',
|
|
231
|
+
reason}`. The reason is value-free by construction: a provider's own text can
|
|
232
|
+
quote the prompt or document, so it is never copied. The reason is built only
|
|
233
|
+
from a fixed phrase and, for an SDK `AIError` only, the HTTP status (from a
|
|
234
|
+
structured field, or from the exact `<status> ...` / `TypeSafe request failed
|
|
235
|
+
(<status>): ...` shapes the SDK produces), the SDK error code (from the closed
|
|
236
|
+
list of codes the SDK raises) and, for a schema rejection, quoted tokens that
|
|
237
|
+
are JSON Schema keywords. Any other error contributes at most a structured
|
|
238
|
+
numeric status. For example: `provider rejected the response schema (HTTP 400,
|
|
239
|
+
code API_ERROR); unsupported response-schema keywords: uniqueItems`, or just
|
|
240
|
+
`provider call failed`. It is cut at 500 characters.
|
|
164
241
|
|
|
165
242
|
`createSDKProposalDecisionClient(client)` adapts an existing SDK client’s optional
|
|
166
243
|
capability without changing its errors or protocol. Optional `decision` uses public core `executeDecision` and the existing SDK
|
package/README.md
CHANGED
|
@@ -197,11 +197,22 @@ Mount the `/svelte` inbox and review components through an authenticated host us
|
|
|
197
197
|
the browser-safe `IntakeReviewHost` DTO contract. See [REVIEW.md](REVIEW.md) for
|
|
198
198
|
exact revision-bound callbacks, live evidence access, logical split corrections,
|
|
199
199
|
assignment ownership, and the maintained upload-to-domain-result browser fixture.
|
|
200
|
+
`IntakeWorkspace` puts the inbox, the selected item with its original inline,
|
|
201
|
+
the message box and approve-and-advance on one page with keyboard next/previous;
|
|
202
|
+
it uses the same callbacks and adds no authority (REVIEW.md, "Single-page triage
|
|
203
|
+
workspace").
|
|
200
204
|
|
|
201
205
|
Optional explicit feedback and scoped example retrieval are documented in
|
|
202
206
|
[FEEDBACK.md](FEEDBACK.md). Approval and execution outcomes remain separate from
|
|
203
207
|
interpretation correctness; routing policy adoption is explicit and versioned.
|
|
204
208
|
|
|
209
|
+
Reviewers can start any offered handler for an item (`startReviewerAction`) or
|
|
210
|
+
close it as handled without action (`resolveItem`); the published host adapter
|
|
211
|
+
wires both as `startAction`/`resolve` and exposes the offered `catalog`. Hosts
|
|
212
|
+
with their own transport add the two POST operations behind the same session,
|
|
213
|
+
CSRF and reviewer checks as `decide`. The `intake_dispositions` table requires
|
|
214
|
+
a manifest migration before upgrading.
|
|
215
|
+
|
|
205
216
|
## Recipe preview
|
|
206
217
|
|
|
207
218
|
`ingestion.inbox` and `ingestion.review` declare the user-facing inbox and
|
|
@@ -248,6 +259,121 @@ and `maxSuggestions` are each capped at 20; `timeoutMs` is capped at 2147483647.
|
|
|
248
259
|
Invalid limits are rejected when constructing `IngestionService`, before receipt
|
|
249
260
|
or interpretation. See [REVIEW.md](REVIEW.md) for the published server review host.
|
|
250
261
|
|
|
262
|
+
## Trusted internal sources
|
|
263
|
+
|
|
264
|
+
A host with a trusted internal source, such as chat messages recorded in
|
|
265
|
+
smrt-messages, can receive **by reference**, interpret **inline**, and let the
|
|
266
|
+
**submitter** confirm their own proposal (#4085). None of these modes changes
|
|
267
|
+
copied receipts: their identity, validation, queueing and retention are
|
|
268
|
+
untouched, and a replay that switches mode conflicts.
|
|
269
|
+
|
|
270
|
+
**Receive by reference.** A part may be `{ partId, reference: { owner, id,
|
|
271
|
+
version? }, snapshot: { text } | { structured } }` instead of bytes. The
|
|
272
|
+
service derives UTF-8 bytes once (`text/plain` or canonical `application/json`;
|
|
273
|
+
text, reference fields and owner identity containing NUL or unpaired
|
|
274
|
+
surrogates are rejected `invalid` on every engine, and `expireOwned` refuses
|
|
275
|
+
them, so distinct owners can never collapse onto one stored key),
|
|
276
|
+
hashes them into the receipt identity, and stores only that snapshot in the
|
|
277
|
+
durable evidence row; no AssetStore object or asset record is created, and
|
|
278
|
+
`readEvidence` serves the snapshot after the same hash/length check. The
|
|
279
|
+
reference is identity and provenance, never authority. Such parts must be
|
|
280
|
+
text or structured; files still arrive as copied bytes.
|
|
281
|
+
|
|
282
|
+
**Owner-bound retention.** `retention: { kind: 'owner', owner: { kind, id },
|
|
283
|
+
version, expiresAt, replayUntil, acceptAfter }` binds the receipt to its
|
|
284
|
+
owner's lifecycle. The time deadlines remain required and validated exactly as
|
|
285
|
+
before: they are the hard privacy ceiling, and `sweepRetention` and execution's
|
|
286
|
+
policy ceiling still apply. When the owner is deleted the host calls
|
|
287
|
+
`expireOwned({ kind, id })` from its deletion path; every active receipt bound
|
|
288
|
+
to that owner is revoked and removed through the existing `expire` /
|
|
289
|
+
`repairDeletions` protocol, which redacts the snapshot together with every
|
|
290
|
+
other payload, and the deleted delivery is never re-admitted. The owner key is
|
|
291
|
+
recorded in the indexed `owner_key` column (`<kind>:<id>`, so `kind` cannot
|
|
292
|
+
contain `:`). Deletion redaction blanks `owner_key` and `submitter_id` together
|
|
293
|
+
with every payload: only the receipt identity (`delivery_key`) and the captured
|
|
294
|
+
ceiling survive, and the ceiling loses `execution.principalId` unless an
|
|
295
|
+
external action is still `executing` or `outcome_unknown`. So once nothing is
|
|
296
|
+
left to reconcile, a redacted row no longer links an owner to an actor, and a
|
|
297
|
+
repeated `expireOwned` finds nothing. Redaction also overwrites every
|
|
298
|
+
identity- or content-derived digest (`payload_digest`, `scope_digest`, analysis
|
|
299
|
+
`input_digest`, attempt `output_digest`, plan `digest` and proposal, decision
|
|
300
|
+
and execution `binding_hash`) with the fixed marker `redacted`; successful
|
|
301
|
+
result digests remain as the replay tombstone. The principal is retained only
|
|
302
|
+
for an external action that was still in flight when the item was redacted,
|
|
303
|
+
because reconciling it authorizes under that principal; it is dropped when that
|
|
304
|
+
reconciliation reaches a final state, or by the next `repairDeletions` /
|
|
305
|
+
`sweepRetention` pass. For a self-approvable receipt the principal is the
|
|
306
|
+
submitter, so until then it identifies the sender. Later deletion sweeps pass
|
|
307
|
+
this redacted ceiling to the host's `authorize({ operation: 'delete' })`, so
|
|
308
|
+
delete authorization must not depend on `execution.principalId`: a host that
|
|
309
|
+
requires it would refuse, and the sweep would stop with later deletion intents
|
|
310
|
+
still pending. A successful result digest
|
|
311
|
+
is a plain SHA-256 of the canonical result, so it can confirm a guessed result:
|
|
312
|
+
keep handler results to record ids or opaque references, never content.
|
|
313
|
+
|
|
314
|
+
A replay of a deleted delivery is rejected and never becomes fresh work. While
|
|
315
|
+
deletion is still pending it returns `rejected/expired`; once redaction has
|
|
316
|
+
completed the identity digests are gone, so it returns `rejected/conflict`.
|
|
317
|
+
Sources treat both as terminal and must not retry. `delivery_key` stays in plaintext because it is
|
|
318
|
+
the unique receipt identity and the package holds no secret to key it: hosts
|
|
319
|
+
must use an opaque value (for example a random UUID recorded with the message)
|
|
320
|
+
and must not encode owner ids, sender ids or content in `deliveryKey`.
|
|
321
|
+
|
|
322
|
+
Host ordering for owner deletion: first stop issuing receipts for the owner,
|
|
323
|
+
then call `expireOwned`, and call it again once in-flight receipts settle (or
|
|
324
|
+
from a periodic sweep). A receipt still in flight when its owner is expired
|
|
325
|
+
returns `{ kind: 'rejected', category: 'expired' }` rather than `accepted`, but
|
|
326
|
+
one that reserved its row after the first `expireOwned` is only found by the
|
|
327
|
+
later call; until then the time ceiling bounds it. Services are confidential-scope bound, so
|
|
328
|
+
a host whose owner has receipts in several confidential scopes calls
|
|
329
|
+
`expireOwned` from each scope's service; a foreign tenant or scope sees zero.
|
|
330
|
+
|
|
331
|
+
**Inline mode.** `receive({ ..., dispatch: 'inline' })` requires text/structured
|
|
332
|
+
parts only, records the route provenance but **no deliverable dispatch intent**
|
|
333
|
+
for any revision (every intent is retained as `completed`, so no worker or
|
|
334
|
+
`repairDispatches` ever picks it up), and does not enqueue. The host then calls
|
|
335
|
+
`interpretInline(itemId, { requestId, extraction: { configurationRevision,
|
|
336
|
+
limits } })`, which runs in-process exactly what the job path runs: an
|
|
337
|
+
immutable `stage: 'extract'` revision claimed under a fenced lease and
|
|
338
|
+
extracted with the built-in UTF-8 extractor, then `prepareGeneration` and the
|
|
339
|
+
`stage: 'interpret'` revision with the same analysis lease, catalog-live
|
|
340
|
+
checks, provenance and failure categories as `generateProposals`. A retry with
|
|
341
|
+
the same `requestId` replays the immutable revisions, including when two
|
|
342
|
+
identical calls race. The interpretation is appended under the item lock only
|
|
343
|
+
while its pinned extraction is still the latest revision; if another request
|
|
344
|
+
reprocessed the item meanwhile it fails with `GenerationSourceStaleError` (the
|
|
345
|
+
general latest-extraction check every interpretation append runs, #4228) or,
|
|
346
|
+
for a pin whose extraction attempt was replaced, `Generation source changed`. The result reports each
|
|
347
|
+
stage's attempt and safe error and, when generation completed, the authorized
|
|
348
|
+
`getCompletedAnalysis` snapshot. Inline is for send-triggered suggestions that
|
|
349
|
+
must not depend on the scheduler; it is not a bypass of review or execution.
|
|
350
|
+
|
|
351
|
+
**Submitter-scoped approval.** The service records `submitter_id` as the
|
|
352
|
+
authenticated `scope.actorId` of the receiving service, server-side; payload
|
|
353
|
+
and source metadata never supply it. `listOwnProposals({ cursor?, limit? })`
|
|
354
|
+
lists only this actor's own active submissions with their saved review pages,
|
|
355
|
+
reloaded under current item/handler/target authority, without the reviewer
|
|
356
|
+
inbox. A submission the actor can no longer read is skipped before projection
|
|
357
|
+
and never named by the cursor; a readable one whose reviews are denied by
|
|
358
|
+
ingestion authority is returned as `{ item, reviews: null, unavailable: true }`
|
|
359
|
+
with no reason, so one revoked grant never hides the others. `submitOwnDecision(input)` accepts `approve`, `reject` or `defer` (never
|
|
360
|
+
`correct`) under the `review_own` authority: the host's execution `authorize`
|
|
361
|
+
must return `submitter: true` for that actor, the application policy must opt
|
|
362
|
+
in with `submitterReview: true` (narrower layers may only disable it), and the
|
|
363
|
+
item's recorded submitter must equal the current actor. Every other check is
|
|
364
|
+
the ordinary one, and `applyAction` remains the single once-only effect path
|
|
365
|
+
under the approver's live grants. Because the effect runs as the receipt's
|
|
366
|
+
captured `capturedCeiling.execution.principalId`, `review_own` additionally
|
|
367
|
+
requires that principal to be the submitter itself (`submitter_id ===
|
|
368
|
+
scope.actorId === principalId`), checked under the item lock at decision time
|
|
369
|
+
and again at apply; a receipt captured for a bot or service principal can only
|
|
370
|
+
be approved by a reviewer. The decision is recorded with `mode` (`review` or
|
|
371
|
+
`review_own`) and `selfDecision`, which is true whenever the deciding actor is
|
|
372
|
+
the item's submitter, in either mode (mirrored as `decisionMode` /
|
|
373
|
+
`selfDecision` on feedback events), so a self-confirmation is never mistaken
|
|
374
|
+
for independent review and never contributes to automation eligibility. See
|
|
375
|
+
[REVIEW.md](REVIEW.md) and [execution integration](agents/execution.md).
|
|
376
|
+
|
|
251
377
|
## Dispatch route ownership and legacy recovery
|
|
252
378
|
|
|
253
379
|
A receipt retains a versioned, server-owned `jobTarget` snapshot, and every
|