@dsh-enhanced/assistant-work-items 0.0.0-stage → 0.1.66

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 (152) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +685 -2
  3. package/cordis.patch.yml +11 -0
  4. package/lib/acceptance-binding.d.ts +86 -0
  5. package/lib/acceptance-binding.d.ts.map +1 -0
  6. package/lib/acceptance-binding.js +250 -0
  7. package/lib/acceptance-binding.js.map +1 -0
  8. package/lib/admission.d.ts +95 -0
  9. package/lib/admission.d.ts.map +1 -0
  10. package/lib/admission.js +642 -0
  11. package/lib/admission.js.map +1 -0
  12. package/lib/authority.d.ts +92 -0
  13. package/lib/authority.d.ts.map +1 -0
  14. package/lib/authority.js +205 -0
  15. package/lib/authority.js.map +1 -0
  16. package/lib/config.d.ts +365 -0
  17. package/lib/config.d.ts.map +1 -0
  18. package/lib/config.js +672 -0
  19. package/lib/config.js.map +1 -0
  20. package/lib/continuation-contract.d.ts +126 -0
  21. package/lib/continuation-contract.d.ts.map +1 -0
  22. package/lib/continuation-contract.js +109 -0
  23. package/lib/continuation-contract.js.map +1 -0
  24. package/lib/continuation-record.d.ts +32 -0
  25. package/lib/continuation-record.d.ts.map +1 -0
  26. package/lib/continuation-record.js +57 -0
  27. package/lib/continuation-record.js.map +1 -0
  28. package/lib/continuation.d.ts +89 -0
  29. package/lib/continuation.d.ts.map +1 -0
  30. package/lib/continuation.js +607 -0
  31. package/lib/continuation.js.map +1 -0
  32. package/lib/diagnosis-contract.d.ts +95 -0
  33. package/lib/diagnosis-contract.d.ts.map +1 -0
  34. package/lib/diagnosis-contract.js +167 -0
  35. package/lib/diagnosis-contract.js.map +1 -0
  36. package/lib/diagnosis.d.ts +70 -0
  37. package/lib/diagnosis.d.ts.map +1 -0
  38. package/lib/diagnosis.js +466 -0
  39. package/lib/diagnosis.js.map +1 -0
  40. package/lib/dispatch.d.ts +100 -0
  41. package/lib/dispatch.d.ts.map +1 -0
  42. package/lib/dispatch.js +317 -0
  43. package/lib/dispatch.js.map +1 -0
  44. package/lib/engineering-context.d.ts +86 -0
  45. package/lib/engineering-context.d.ts.map +1 -0
  46. package/lib/engineering-context.js +263 -0
  47. package/lib/engineering-context.js.map +1 -0
  48. package/lib/evaluation.d.ts +125 -0
  49. package/lib/evaluation.d.ts.map +1 -0
  50. package/lib/evaluation.js +295 -0
  51. package/lib/evaluation.js.map +1 -0
  52. package/lib/event-follow.d.ts +93 -0
  53. package/lib/event-follow.d.ts.map +1 -0
  54. package/lib/event-follow.js +150 -0
  55. package/lib/event-follow.js.map +1 -0
  56. package/lib/feedback-targets.d.ts +91 -0
  57. package/lib/feedback-targets.d.ts.map +1 -0
  58. package/lib/feedback-targets.js +362 -0
  59. package/lib/feedback-targets.js.map +1 -0
  60. package/lib/index.d.ts +27 -0
  61. package/lib/index.d.ts.map +1 -0
  62. package/lib/index.js +25 -0
  63. package/lib/index.js.map +1 -0
  64. package/lib/intake-admission.d.ts +49 -0
  65. package/lib/intake-admission.d.ts.map +1 -0
  66. package/lib/intake-admission.js +100 -0
  67. package/lib/intake-admission.js.map +1 -0
  68. package/lib/intake-evaluation-runtime.d.ts +74 -0
  69. package/lib/intake-evaluation-runtime.d.ts.map +1 -0
  70. package/lib/intake-evaluation-runtime.js +343 -0
  71. package/lib/intake-evaluation-runtime.js.map +1 -0
  72. package/lib/intake-evaluation.d.ts +180 -0
  73. package/lib/intake-evaluation.d.ts.map +1 -0
  74. package/lib/intake-evaluation.js +440 -0
  75. package/lib/intake-evaluation.js.map +1 -0
  76. package/lib/intake.d.ts +118 -0
  77. package/lib/intake.d.ts.map +1 -0
  78. package/lib/intake.js +137 -0
  79. package/lib/intake.js.map +1 -0
  80. package/lib/ledger.d.ts +86 -0
  81. package/lib/ledger.d.ts.map +1 -0
  82. package/lib/ledger.js +145 -0
  83. package/lib/ledger.js.map +1 -0
  84. package/lib/mandate.d.ts +45 -0
  85. package/lib/mandate.d.ts.map +1 -0
  86. package/lib/mandate.js +168 -0
  87. package/lib/mandate.js.map +1 -0
  88. package/lib/outcome.d.ts +137 -0
  89. package/lib/outcome.d.ts.map +1 -0
  90. package/lib/outcome.js +303 -0
  91. package/lib/outcome.js.map +1 -0
  92. package/lib/owner-access.d.ts +35 -0
  93. package/lib/owner-access.d.ts.map +1 -0
  94. package/lib/owner-access.js +152 -0
  95. package/lib/owner-access.js.map +1 -0
  96. package/lib/owner-continuation.d.ts +27 -0
  97. package/lib/owner-continuation.d.ts.map +1 -0
  98. package/lib/owner-continuation.js +131 -0
  99. package/lib/owner-continuation.js.map +1 -0
  100. package/lib/owner-recall.d.ts +85 -0
  101. package/lib/owner-recall.d.ts.map +1 -0
  102. package/lib/owner-recall.js +134 -0
  103. package/lib/owner-recall.js.map +1 -0
  104. package/lib/owner-replies.d.ts +44 -0
  105. package/lib/owner-replies.d.ts.map +1 -0
  106. package/lib/owner-replies.js +232 -0
  107. package/lib/owner-replies.js.map +1 -0
  108. package/lib/peers.d.ts +622 -0
  109. package/lib/peers.d.ts.map +1 -0
  110. package/lib/peers.js +10 -0
  111. package/lib/peers.js.map +1 -0
  112. package/lib/policy-notice.d.ts +31 -0
  113. package/lib/policy-notice.d.ts.map +1 -0
  114. package/lib/policy-notice.js +137 -0
  115. package/lib/policy-notice.js.map +1 -0
  116. package/lib/reply-contract.d.ts +67 -0
  117. package/lib/reply-contract.d.ts.map +1 -0
  118. package/lib/reply-contract.js +58 -0
  119. package/lib/reply-contract.js.map +1 -0
  120. package/lib/service.d.ts +625 -0
  121. package/lib/service.d.ts.map +1 -0
  122. package/lib/service.js +2388 -0
  123. package/lib/service.js.map +1 -0
  124. package/lib/settlement.d.ts +74 -0
  125. package/lib/settlement.d.ts.map +1 -0
  126. package/lib/settlement.js +233 -0
  127. package/lib/settlement.js.map +1 -0
  128. package/lib/signals.d.ts +176 -0
  129. package/lib/signals.d.ts.map +1 -0
  130. package/lib/signals.js +497 -0
  131. package/lib/signals.js.map +1 -0
  132. package/lib/store.d.ts +789 -0
  133. package/lib/store.d.ts.map +1 -0
  134. package/lib/store.js +3266 -0
  135. package/lib/store.js.map +1 -0
  136. package/lib/tools.d.ts +7 -0
  137. package/lib/tools.d.ts.map +1 -0
  138. package/lib/tools.js +443 -0
  139. package/lib/tools.js.map +1 -0
  140. package/lib/types.d.ts +831 -0
  141. package/lib/types.d.ts.map +1 -0
  142. package/lib/types.js +29 -0
  143. package/lib/types.js.map +1 -0
  144. package/lib/version.d.ts +2 -0
  145. package/lib/version.d.ts.map +1 -0
  146. package/lib/version.js +2 -0
  147. package/lib/version.js.map +1 -0
  148. package/lib/workspace.d.ts +43 -0
  149. package/lib/workspace.d.ts.map +1 -0
  150. package/lib/workspace.js +137 -0
  151. package/lib/workspace.js.map +1 -0
  152. package/package.json +101 -3
package/lib/store.d.ts ADDED
@@ -0,0 +1,789 @@
1
+ import { type WorkItemContinuationInput, type WorkItemContinuationRecord } from './continuation-record.js';
2
+ import { type WorkItemDiagnosisPlan } from './diagnosis-contract.js';
3
+ import type { WorkItemClaimRecord, WorkItemClaimState, WorkItemDeliveryState, WorkItemDispatchRecord, WorkItemEventRecord, WorkItemFollowRecord, WorkItemFollowUpOwnResult, WorkItemFollowUpRecord, WorkItemPolicyNotice, WorkItemAcceptanceBinding, WorkItemAcceptanceVerdict, WorkItemAcceptanceBlockReason, WorkItemAcceptanceGateExpectation, WorkItemLateAcceptanceBinding, WorkItemOutcomeEvidence, WorkItemOutcomeEvidenceReason, WorkItemOutcomeEvidenceState, WorkItemOutcomeRecord, WorkItemOutcomeView, WorkItemRecord, WorkItemReportRecord, WorkItemReportStatus, WorkItemResumeContext, WorkItemState, WorkItemThreadClosure } from './types.ts';
4
+ import type { WorkItemIntakeCandidate, WorkItemIntakeRecord, WorkItemIntakeResult } from './intake.ts';
5
+ import type { WorkItemIntakeEvaluationRecord } from './intake-evaluation.ts';
6
+ import type { WorkItemIntakeAdmissionRecord } from './intake-admission.ts';
7
+ /** Waiting reasons that mean an admission mandate failed revalidation. */
8
+ declare const AUTHORITY_REVOKED = "authority-revoked";
9
+ declare const AUTHORITY_UNAVAILABLE = "authority-unavailable";
10
+ /** One validated follow, already resolved to its registered source at admission. */
11
+ /** Append-only event kinds for WP21 notices and follow-up ownership (no schema change). */
12
+ export declare const POLICY_NOTICE_EVENT = "policy-change-noticed";
13
+ export declare const FOLLOW_UP_OWNED_EVENT = "follow-up-owned";
14
+ export declare const FOLLOW_UP_PROCESSED_EVENT = "follow-up-processed";
15
+ export declare const FOLLOW_UP_RELEASED_EVENT = "follow-up-released";
16
+ /** WP21 slice 4: the independent verifier's verdict, read back once per bound item. */
17
+ export declare const ACCEPTANCE_VERIFIED_EVENT = "acceptance-verified";
18
+ /** WP21 slice 6: a worker run's contract bound after wake; the latest row is current. */
19
+ export declare const ACCEPTANCE_BOUND_EVENT = "acceptance-bound";
20
+ /** Message of the fail-closed storage error for a malformed latest `acceptance-bound` row. */
21
+ export declare const MALFORMED_ACCEPTANCE_BINDING = "recorded acceptance binding is malformed";
22
+ /** One dispatched, unbound item whose current worker run may be late-bound. */
23
+ export interface WorkItemLateAcceptanceCandidate {
24
+ readonly item: WorkItemRecord;
25
+ readonly automationId: string;
26
+ readonly occurrenceId: string;
27
+ /** Earliest time this generation's run can have started: max(item createdAt, dispatch occurredAt). */
28
+ readonly notBefore: number;
29
+ }
30
+ /** Position of one released follow-up in release order. */
31
+ export interface WorkItemFollowUpCursor {
32
+ readonly recordedAt: number;
33
+ readonly itemId: string;
34
+ readonly sequence: number;
35
+ }
36
+ export interface WorkItemFollowInput {
37
+ readonly kind: string;
38
+ readonly ref: string;
39
+ /** Registered source key; equals `kind` and is persisted for audit. */
40
+ readonly source: string;
41
+ /** Registration generation at admission; a newer one cannot close this row. */
42
+ readonly sourceGeneration: string;
43
+ }
44
+ /**
45
+ * Deterministic external event identity for one dispatch generation.
46
+ *
47
+ * Generation 0 is the admission identity and equals the item id, so existing
48
+ * rows keep their exact `(event_id, occurred_at)` pair. Later generations append
49
+ * a suffix, because the automation intake dedupes occurrences by
50
+ * `(automationId, eventId, occurredAt)` and would replay a completed occurrence
51
+ * instead of starting a new run.
52
+ */
53
+ export declare function dispatchEventId(itemId: string, generation: number): string;
54
+ /** Create a mode-0700 directory and prove it is not a link or group-writable. */
55
+ export declare function preparePrivateDirectory(path: string): void;
56
+ /** Create the SQLite file with mode 0600 before SQLite opens it. */
57
+ export declare function prepareDatabaseFile(path: string): void;
58
+ export interface ClaimResult {
59
+ readonly item: WorkItemRecord;
60
+ readonly claim: WorkItemClaimRecord;
61
+ readonly replayed: boolean;
62
+ }
63
+ export interface ReportResult {
64
+ readonly item: WorkItemRecord;
65
+ readonly report: WorkItemReportRecord;
66
+ readonly replayed: boolean;
67
+ }
68
+ export interface ReclaimResult {
69
+ readonly requeued: readonly string[];
70
+ readonly blocked: readonly string[];
71
+ }
72
+ /** Which signal drove a withdrawal; recorded on every retired item's event. */
73
+ export interface WithdrawAttribution {
74
+ readonly sourceId: string;
75
+ readonly externalId: string;
76
+ readonly observedAt: number;
77
+ }
78
+ /** Result of retiring every open item of one canonical dedup key. */
79
+ export interface WithdrawDedupResult {
80
+ readonly outcome: 'withdrawn' | 'settled' | 'absent';
81
+ /** Items actually retired (`withdrawn`), the newest item (`settled`), or none. */
82
+ readonly items: readonly WorkItemRecord[];
83
+ }
84
+ export interface WorkItemStoreStats {
85
+ readonly schemaVersion: number;
86
+ readonly items: number;
87
+ readonly openItems: number;
88
+ readonly reports: number;
89
+ readonly dispatches: number;
90
+ readonly outcomes: number;
91
+ readonly pendingOutcomes: number;
92
+ readonly unverifiedOutcomes: number;
93
+ /** Latest settled revisions still waiting for one trusted Evaluation append. */
94
+ readonly evaluationPending: number;
95
+ readonly evaluationExported: number;
96
+ readonly evaluationSkipped: number;
97
+ /** Persisted followed-thread rows (open and closed). */
98
+ readonly follows: number;
99
+ /** Open items admitted before the revalidation basis existed (schema < v7). */
100
+ readonly authorityLegacy: number;
101
+ /** Open items parked because their admission mandate failed revalidation. */
102
+ readonly authorityParked: number;
103
+ }
104
+ /**
105
+ * Durable execution facts one work item can contribute to an Evaluation claim.
106
+ *
107
+ * Only counts the ledger already records are exposed. No provider token/cost
108
+ * usage is read here: this ledger owns no run-usage contract, while the
109
+ * automation side records those figures for the same run scope, so copying them
110
+ * would double-count cost. Zero counts are omitted so a quiet item stays compact.
111
+ */
112
+ export interface WorkItemExecutionMetrics {
113
+ readonly latencyMs: number;
114
+ readonly retries?: number;
115
+ readonly claims?: number;
116
+ readonly reports?: number;
117
+ readonly waitingReports?: number;
118
+ readonly unknownDispatches?: number;
119
+ readonly claimExpiries?: number;
120
+ }
121
+ /**
122
+ * Single-writer SQLite ledger.
123
+ *
124
+ * Every mutation runs inside `BEGIN IMMEDIATE`; the database file is created
125
+ * 0600 next to a 0700 directory, WAL keeps readers unblocked, and schema
126
+ * upgrades are bounded to the single-writer upgrade boundary at open time.
127
+ */
128
+ export declare class WorkItemStore {
129
+ #private;
130
+ constructor(path: string, options?: {
131
+ now?: () => number;
132
+ settleWindowMs?: number;
133
+ });
134
+ /** Keep report/plan sources writer-fenced through a synchronous Host commit.
135
+ * Delivery and Evaluation own their outer locks; this method never acquires
136
+ * either peer or starts any execution. */
137
+ withFeedbackSourceFence<T>(work: () => T): T;
138
+ close(): void;
139
+ get closed(): boolean;
140
+ stats(): WorkItemStoreStats;
141
+ /**
142
+ * Record one classified intake candidate exactly once.
143
+ *
144
+ * A replay of the same `(source, sourceEventId)` returns the original row
145
+ * unchanged, with no second write. A new event whose content digest matches
146
+ * an earlier `stored` row of the same source and at least the same strength
147
+ * is recorded as `duplicate`; a stronger repeat (for example the same text
148
+ * re-posted with a mention) is never suppressed by a weaker original.
149
+ * This method writes only the intake table; it admits and dispatches nothing.
150
+ */
151
+ recordIntake(candidate: WorkItemIntakeCandidate): WorkItemIntakeResult;
152
+ getIntake(source: string, sourceEventId: string): WorkItemIntakeRecord | undefined;
153
+ /**
154
+ * Append one evaluation result exactly once per `(row, rule, Policy version)`.
155
+ * The intake row must be a `stored` `inferred` row whose content digest is the
156
+ * evaluated source digest. A replay returns the original row unchanged.
157
+ */
158
+ recordIntakeEvaluation(record: Omit<WorkItemIntakeEvaluationRecord, 'recordedAt'>): {
159
+ readonly record: WorkItemIntakeEvaluationRecord;
160
+ readonly replayed: boolean;
161
+ };
162
+ getIntakeEvaluation(source: string, sourceEventId: string, ruleId: string, policyVersion: string): WorkItemIntakeEvaluationRecord | undefined;
163
+ /** Every persisted evaluation of one intake row, oldest first; read-only. */
164
+ listIntakeEvaluations(source: string, sourceEventId: string): readonly WorkItemIntakeEvaluationRecord[];
165
+ /** Status counts over the evaluation table; read-only. */
166
+ countIntakeEvaluations(): Readonly<Record<WorkItemIntakeEvaluationRecord['status'], number>>;
167
+ /**
168
+ * Append one admission outcome exactly once per evaluation row. The
169
+ * evaluation must exist and would accept. A replay returns the first row
170
+ * unchanged, so a later outcome never rewrites an earlier one.
171
+ */
172
+ recordIntakeAdmission(record: Omit<WorkItemIntakeAdmissionRecord, 'recordedAt'>): WorkItemIntakeAdmissionRecord;
173
+ getIntakeAdmission(source: string, sourceEventId: string, ruleId: string, policyVersion: string): WorkItemIntakeAdmissionRecord | undefined;
174
+ /** Every admission outcome of one intake row, oldest first; read-only. */
175
+ listIntakeAdmissions(source: string, sourceEventId: string): readonly WorkItemIntakeAdmissionRecord[];
176
+ /** Oldest accepting evaluations without an admission row, at most `limit`; read-only. */
177
+ pendingIntakeAdmissions(limit: number): readonly WorkItemIntakeEvaluationRecord[];
178
+ /**
179
+ * The intake decision that admitted one item, if any: its first
180
+ * `admitted` (or crash-recovered `replayed`) admission row joined with the
181
+ * evaluation it acted on.
182
+ * Read-only. Served by the partial `item_id` index (schema 13).
183
+ */
184
+ intakeProvenance(itemId: string): Readonly<{
185
+ admission: WorkItemIntakeAdmissionRecord;
186
+ evaluation: WorkItemIntakeEvaluationRecord;
187
+ }> | undefined;
188
+ /** Items admitted from intake at or after `since`; the hourly intake budget. */
189
+ countIntakeAdmitted(since: number): number;
190
+ /** Outcome counts over the admission table; read-only. */
191
+ countIntakeAdmissions(): Readonly<Record<WorkItemIntakeAdmissionRecord['outcome'], number>>;
192
+ /** Decision counts over the intake table; read-only. */
193
+ countIntake(): Readonly<Record<WorkItemIntakeRecord['decision'], number>>;
194
+ /** SQL owner filtering precedes keyset pagination; no recovery or writes. */
195
+ listOwnerItems(owner: Readonly<{
196
+ principalId: string;
197
+ principalRecordId: string;
198
+ principalVersion: number;
199
+ workspace: string;
200
+ preset: string;
201
+ ownerRouteId: string;
202
+ workerAutomationId: string;
203
+ continuationAutomationId?: string;
204
+ generation: number;
205
+ bindingVersion: number;
206
+ }>, after: Readonly<{
207
+ createdAt: number;
208
+ id: string;
209
+ }> | undefined, limit: number, itemId?: string): readonly WorkItemRecord[];
210
+ /** Exact UTF-8 body bytes after the caller has selected an owner-filtered item. */
211
+ ownerRecallText(itemId: string, revision?: number): string;
212
+ findItemById(itemId: string): WorkItemRecord | undefined;
213
+ findItemByDedup(dedupKey: string, dedupBucket: number): WorkItemRecord | undefined;
214
+ /**
215
+ * Latest item admitted for one canonical dedup key, across all windows.
216
+ *
217
+ * The dedup window forgets history when a bucket rolls over; this lookup is
218
+ * what lets a correction or withdrawal of an already-settled signal stay
219
+ * settled instead of reviving a completed item.
220
+ */
221
+ findLatestItemByDedupKey(dedupKey: string): WorkItemRecord | undefined;
222
+ /**
223
+ * The item whose frozen mandate already binds `contractId`, if any. One
224
+ * acceptance contract settles exactly one item. A full scan over the stored
225
+ * basis JSON (no index, no schema change); unreadable bases never match.
226
+ */
227
+ findItemByAcceptanceContract(contractId: string): WorkItemRecord | undefined;
228
+ /**
229
+ * WP21 slice 6 candidates: items whose worker dispatch is `dispatched` to
230
+ * `workerAutomationId` under their current occurrence, whose mandate binds
231
+ * no contract, that have no verdict yet and no `acceptance-bound` row for
232
+ * that occurrence. Never-run, withdrawn, failed and dropped items are out.
233
+ * (created_at, id) order strictly after `after`; bounded scan, no schema change.
234
+ */
235
+ listLateAcceptanceCandidates(workerAutomationId: string, after: Readonly<{
236
+ createdAt: number;
237
+ id: string;
238
+ }> | null, limit: number): readonly WorkItemLateAcceptanceCandidate[];
239
+ /** The current (latest) late acceptance binding of one item, if any; malformed rows fail closed. */
240
+ lateAcceptanceBinding(itemId: string): WorkItemLateAcceptanceBinding | undefined;
241
+ /**
242
+ * Append one late acceptance binding for the item's current occurrence.
243
+ * Refused for a mandate-bound or already-verified item, for a stale
244
+ * occurrence, and for a contract another item already binds. The same
245
+ * occurrence and contract again is idempotent; a later occurrence's row
246
+ * supersedes earlier ones. State, lease and mandate are untouched.
247
+ */
248
+ recordLateAcceptanceBinding(input: Readonly<{
249
+ itemId: string;
250
+ occurrenceId: string;
251
+ binding: WorkItemAcceptanceBinding;
252
+ }>): Readonly<{
253
+ binding: WorkItemLateAcceptanceBinding;
254
+ created: boolean;
255
+ }>;
256
+ /**
257
+ * Executed (`ready`, attempt ≥ 1) items whose frozen mandate binds an
258
+ * acceptance contract, or whose latest late binding is for the item's
259
+ * current occurrence, and that have no recorded verdict yet, in
260
+ * (created_at, id) order strictly after `after`. A bounded full scan over
261
+ * the stored basis JSON, like `findItemByAcceptanceContract` (no schema change).
262
+ */
263
+ listUnsettledAcceptanceItems(after: Readonly<{
264
+ createdAt: number;
265
+ id: string;
266
+ }> | null, limit: number): readonly WorkItemRecord[];
267
+ /**
268
+ * WP21 slice 8 (`acceptance.holdReport`): executed items whose latest report
269
+ * is a terminal `ready` self-report that has not been handed to Delivery and
270
+ * that is either still waiting for the verifier (`ready`) or was moved to
271
+ * `blocked` by the verdict gate (`acceptance-*`). One row per item, oldest
272
+ * report first. `after` is the `(recordedAt, itemId)` keyset position of the
273
+ * previous page, so a full page advances the caller's in-memory round-robin
274
+ * position instead of rescanning rows that are still waiting. Read-only and
275
+ * bounded; no schema change.
276
+ */
277
+ listHeldAcceptanceReports(limit: number, after?: Readonly<{
278
+ recordedAt: number;
279
+ itemId: string;
280
+ }>): readonly Readonly<{
281
+ item: WorkItemRecord;
282
+ report: WorkItemReportRecord;
283
+ }>[];
284
+ /** Read one durable key/value row from the schema-meta table. */
285
+ meta(key: string): string | null;
286
+ /** Persist one durable key/value row; no schema change is required for it. */
287
+ setMeta(key: string, value: string): void;
288
+ /** Full SQLite bytes; TEXT projection is insufficient for authority-bearing JSON. */
289
+ readCanonicalMeta(key: string): unknown;
290
+ /** Only the exact first admitted event may confer a diagnostic plan. Never scan later events for one. */
291
+ readDiagnosisPlan(itemId: string): Readonly<WorkItemDiagnosisPlan> | undefined;
292
+ diagnosisInterventionPending(itemId: string): boolean;
293
+ diagnosisInterventionItems(limit?: number): readonly WorkItemRecord[];
294
+ diagnosisExplicitlyDisposed(itemId: string): boolean;
295
+ clearDiagnosisIntervention(itemId: string): void;
296
+ /** Locate a real report, then prove its released/completed claim using complete NUL-delimited bytes. */
297
+ diagnosisReportOccurrence(itemId: string, revision: number): Readonly<{
298
+ itemId: string;
299
+ revision: number;
300
+ status: string;
301
+ occurrenceId: string;
302
+ }>;
303
+ /** Row order is database-owned; wall-clock jumps cannot choose a different last report. */
304
+ latestWorkerDiagnosisReport(workerId?: string): Readonly<{
305
+ itemId: string;
306
+ revision: number;
307
+ status: string;
308
+ occurrenceId: string;
309
+ }> | undefined;
310
+ /** Drop one durable key/value row when its cursor no longer applies. */
311
+ deleteMeta(key: string): void;
312
+ /** Count latest outcome revisions in one evidence state. */
313
+ countLatestOutcomes(evidenceState: WorkItemOutcomeEvidenceState): number;
314
+ findItemByOccurrence(occurrenceId: string): WorkItemRecord | undefined;
315
+ listItems(options?: {
316
+ states?: readonly WorkItemState[];
317
+ includeObserved?: boolean;
318
+ limit?: number;
319
+ }): readonly WorkItemRecord[];
320
+ lastAdmitAt(kind?: string): number | null;
321
+ /** Latest successful dispatch timestamp, for bounded health reporting. */
322
+ lastDispatchedAt(): number | null;
323
+ countItemsSince(createdAt: number): number;
324
+ countOpenItems(): number;
325
+ /** Open items admitted before schema v7, i.e. with no persisted mandate. */
326
+ countAuthorityLegacy(): number;
327
+ /** Open items parked because their admission mandate failed revalidation. */
328
+ countAuthorityParked(): number;
329
+ /** Follows of one item, in admission order (their stable position). */
330
+ follows(itemId: string): readonly WorkItemFollowRecord[];
331
+ countOpenFollows(itemId: string): number;
332
+ /** Global follow counts for health reporting (open rows of live items only). */
333
+ countFollows(): {
334
+ open: number;
335
+ closed: number;
336
+ };
337
+ /**
338
+ * Identity of every open follow on a live item, for health projections.
339
+ *
340
+ * A follow row outlives the item it parked when that item is withdrawn or
341
+ * otherwise settled by another path (no resolver ever observed those threads).
342
+ * Reporting those rows as gaps would make health permanently degraded over
343
+ * dead items, so only follows of still-open items count.
344
+ */
345
+ openFollowIdentities(): readonly {
346
+ readonly source: string;
347
+ readonly sourceGeneration: string;
348
+ }[];
349
+ /** Open follows across live items, oldest first, bounded for diagnostics. */
350
+ listOpenFollows(limit: number): readonly WorkItemFollowRecord[];
351
+ /**
352
+ * Close one open follow exactly once.
353
+ *
354
+ * The CAS on `state = 'open'` is what makes duplicate and out-of-order
355
+ * closures harmless: a second observation of the same closure changes no row
356
+ * and reports `undefined`, so no settlement signal can be produced twice.
357
+ */
358
+ closeFollow(itemId: string, position: number, closure: WorkItemThreadClosure, now?: number): WorkItemFollowRecord | undefined;
359
+ /**
360
+ * Waiting items that owe a thread-closure settlement.
361
+ *
362
+ * `waiting` with a terminal `last_report_status` is produced only by the
363
+ * deferred report branch, so this is exactly the deferred set. Selecting on
364
+ * the report status rather than on open follow rows keeps the sweep
365
+ * crash-safe: a crash after closing the last follow but before appending the
366
+ * outcome still finds the item on the next pass. A live lease is excluded
367
+ * (additionally to the state check) so a re-claimed item is never settled
368
+ * underneath its new worker.
369
+ */
370
+ itemsAwaitingThreadClosure(limit: number): readonly WorkItemRecord[];
371
+ /**
372
+ * Settle one deferred item because every followed thread is closed.
373
+ *
374
+ * This appends at most one outcome revision, and a second call is a no-op.
375
+ * The transition reuses the recorded terminal report status, so the item
376
+ * still ends in the state the worker reported; the closure itself is never
377
+ * treated as evidence of objective achievement.
378
+ */
379
+ settleThreadClosure(itemId: string): WorkItemOutcomeRecord | undefined;
380
+ /**
381
+ * Ambiguous sends that still describe an actionable gap.
382
+ *
383
+ * A failed row is dead weight once its item is terminal or observed (for
384
+ * example a withdrawal after an ambiguous send): no retry is owed and the
385
+ * owner already settled the item, so counting it would report a permanent
386
+ * false gap. The gate mirrors `wakePending()`'s eligibility check.
387
+ */
388
+ countDispatchUnknown(): number;
389
+ /**
390
+ * Park one item whose admission mandate failed revalidation.
391
+ *
392
+ * Parking is never terminal: the item stays `waiting` with a typed reason, its
393
+ * pending dispatch row is left intact, and it becomes claimable/wakeable again
394
+ * as soon as the identical mandate allows. Repeating the same park only
395
+ * refreshes the audit timestamp, so a sweep that observes the same denial on
396
+ * every tick does not flood the event log.
397
+ */
398
+ parkAuthority(itemId: string, reason: typeof AUTHORITY_REVOKED | typeof AUTHORITY_UNAVAILABLE, detail: Readonly<Record<string, unknown>>, now: number): WorkItemRecord;
399
+ /**
400
+ * Record a successful revalidation of one item's mandate.
401
+ *
402
+ * Only a wait this gate itself created is lifted; a slice-9
403
+ * `following-thread` wait is a different concern and stays untouched.
404
+ */
405
+ restoreAuthority(itemId: string, now: number): WorkItemRecord;
406
+ /** Latest authority denial for bounded health reporting. */
407
+ lastAuthorityDenial(): {
408
+ readonly at: number;
409
+ readonly reason: string;
410
+ } | null;
411
+ getContinuation(itemId: string): Readonly<WorkItemContinuationRecord> | undefined;
412
+ findContinuationBySourceKey(key: string): Readonly<WorkItemContinuationRecord> | undefined;
413
+ /** Most recently admitted dispatched child, by immutable dispatch rowid; no wall-clock ordering. */
414
+ latestContinuationDispatch(automationId: string): Readonly<{
415
+ item: WorkItemRecord;
416
+ dispatch: WorkItemDispatchRecord;
417
+ continuation: WorkItemContinuationRecord;
418
+ }> | undefined;
419
+ continuationDispatchUncertain(automationId: string): boolean;
420
+ listPendingContinuations(automationId: string, limit?: number, after?: Readonly<{
421
+ createdAt: number;
422
+ id: string;
423
+ }>): readonly Readonly<{
424
+ item: WorkItemRecord;
425
+ dispatch: WorkItemDispatchRecord;
426
+ continuation: WorkItemContinuationRecord;
427
+ }>[];
428
+ continuationParentBrief(itemId: string): string;
429
+ continuationParentReports(itemId: string, afterRevision?: number, limit?: number): readonly WorkItemReportRecord[];
430
+ continuationParentOutcomes(itemId: string, afterRevision?: number, limit?: number): readonly WorkItemOutcomeRecord[];
431
+ /**
432
+ * Insert one admitted item, its lifecycle event and its single dispatch row
433
+ * in one transaction. A crash between these writes is impossible.
434
+ */
435
+ admitItem(record: WorkItemRecord, event: {
436
+ kind: string;
437
+ actor: string;
438
+ detail?: unknown;
439
+ }, dispatch: WorkItemDispatchRecord, follows?: readonly WorkItemFollowInput[], diagnosisPlan?: WorkItemDiagnosisPlan, continuation?: WorkItemContinuationInput): WorkItemRecord;
440
+ /**
441
+ * Retire every open item a canonical signal produced, because it was withdrawn.
442
+ *
443
+ * This is the only lifecycle transition driven by the read side of autonomy
444
+ * rather than by a worker report. It is deliberately narrow: the items stop
445
+ * being open, their active claim (if any) is released so the current run
446
+ * cannot keep reporting, and a still-pending wake-up is failed rather than
447
+ * re-armed. A wake-up already handed to the automation is left alone —
448
+ * delivery to a running occurrence cannot be recalled from here.
449
+ *
450
+ * Withdrawal targets the whole dedup key, not the newest row, because the
451
+ * dedup window is a freshness bound: once it rolls, a still-open item can be
452
+ * re-admitted under the same key with a different bucket. Retiring only the
453
+ * newest row would leave the older one winnable by `wakePending()` on the next
454
+ * host start, i.e. cancelled work could still be woken. All rows of the key
455
+ * therefore settle together in one transaction.
456
+ *
457
+ * Idempotent: an already-terminal key is returned unchanged (`settled`) with
458
+ * no new event, so a repeated withdrawn revision never rewrites history.
459
+ */
460
+ withdrawByDedupKey(dedupKey: string, attribution?: WithdrawAttribution): WithdrawDedupResult;
461
+ latestOutcome(itemId: string): WorkItemOutcomeRecord | undefined;
462
+ /** Every outcome revision for one item, oldest first. */
463
+ outcomes(itemId: string): readonly WorkItemOutcomeRecord[];
464
+ /**
465
+ * Latest outcome plus the report delivery state it depends on.
466
+ *
467
+ * `reportRevision` is the terminal report that caused a report-derived
468
+ * transition; observation and expiry revisions keep pointing at it, while
469
+ * withdrawal and retry exhaustion never had a report.
470
+ */
471
+ outcomeView(itemId: string): WorkItemOutcomeView | undefined;
472
+ /** Latest outcomes, newest first, optionally filtered by evidence state. */
473
+ listOutcomes(options?: {
474
+ limit?: number;
475
+ evidenceState?: WorkItemOutcomeEvidenceState;
476
+ }): readonly WorkItemOutcomeView[];
477
+ /**
478
+ * Latest outcome revisions still waiting for evidence, soonest deadline first.
479
+ *
480
+ * Only the newest revision per item is considered: an older revision that was
481
+ * superseded still says `pending` in the immutable row, but it is history, not
482
+ * an open window.
483
+ */
484
+ pendingOutcomes(limit: number): readonly WorkItemOutcomeRecord[];
485
+ /**
486
+ * Append the settling revision for one pending outcome.
487
+ *
488
+ * Compare-and-swap on the revision number: a concurrent pass that already
489
+ * settled the item fails here instead of overwriting its history.
490
+ *
491
+ * This intentionally appends no `work_item_event`: the outcome ledger is its
492
+ * own append-only audit trail, and duplicating every revision into the event
493
+ * log would mix worker lifecycle with independently sourced evidence.
494
+ */
495
+ settleOutcome(input: {
496
+ itemId: string;
497
+ revision: number;
498
+ evidence: WorkItemOutcomeEvidence | null;
499
+ reason: WorkItemOutcomeEvidenceReason | null;
500
+ degraded: boolean;
501
+ expired: boolean;
502
+ }): WorkItemOutcomeRecord;
503
+ /**
504
+ * Latest outcome revisions that still owe exactly one trusted Evaluation
505
+ * append.
506
+ *
507
+ * Only the newest revision per item is exportable, and only once it carries a
508
+ * settled evidence state: an unresolved `pending` window is still changing, so
509
+ * exporting it early would publish a claim that later revisions contradict.
510
+ */
511
+ outcomesAwaitingEvaluation(limit: number, attemptBound?: number): readonly WorkItemOutcomeRecord[];
512
+ /** Count settled revisions that used up their bounded export attempts. */
513
+ countExhaustedEvaluations(attemptBound: number): number;
514
+ /** Count latest export-eligible revisions per Evaluation export state. */
515
+ countEvaluations(): {
516
+ pending: number;
517
+ exported: number;
518
+ skipped: number;
519
+ };
520
+ /**
521
+ * Bounded, read-only execution facts for one item at a settlement instant.
522
+ *
523
+ * Every counter is a COUNT over durable rows this ledger already keeps for
524
+ * the item; nothing is re-derived from live process state. `settledAt` bounds
525
+ * each read because rows recorded after the settled revision belong to a
526
+ * later observation and must not inflate an earlier claim. An absent item row
527
+ * returns `undefined` so the caller can keep the live-record fallback.
528
+ *
529
+ * Claims are counted from `claimed` ledger events rather than claim rows:
530
+ * each committed claim appends exactly one such event in the same transaction
531
+ * as the claim row and the `item.attempt` increment (the replay branch appends
532
+ * neither, retry-exhausted appends `blocked` only, and nothing deletes either
533
+ * record), while `work_item_claim` has no `item_id` index. `work_item_event`
534
+ * is `WITHOUT ROWID` keyed by `(item_id, sequence)`, so its count is an
535
+ * indexed prefix search instead of a scan of every claim ever recorded.
536
+ */
537
+ workItemExecutionMetrics(itemId: string, settledAt: number): WorkItemExecutionMetrics | undefined;
538
+ /**
539
+ * Bounded, read-only resume brief for one item at a claim instant.
540
+ *
541
+ * Everything is derived from the item's own append-only ledger rows, so a
542
+ * fresh occurrence can continue without replaying a prior model's memory.
543
+ * Absent items return `undefined` (mirroring `workItemExecutionMetrics`) so
544
+ * the caller never invents an empty context for a row that vanished.
545
+ *
546
+ * The event read is a bounded newest-first scan of one item's primary-key
547
+ * prefix; it reconstructs prior attempts from the `claimed` event that
548
+ * records `{runId, attempt}` and the recovery/report events that end one.
549
+ * Malformed or absent detail JSON is skipped, never thrown. Reports are read
550
+ * newest-first through the same primary-key prefix and capped at five, with
551
+ * each summary cut to a safe UTF-8 byte boundary.
552
+ */
553
+ workItemResumeContext(itemId: string, now?: number): WorkItemResumeContext | undefined;
554
+ /** Mark one settled revision exported; CAS on `pending` so a replay is refused. */
555
+ markOutcomeExported(itemId: string, revision: number, digest: string, recordedAt: number): WorkItemOutcomeRecord;
556
+ /** Mark one settled revision skipped; CAS on `pending`. */
557
+ markOutcomeSkipped(itemId: string, revision: number, recordedAt: number): WorkItemOutcomeRecord;
558
+ /** Count one failed export attempt, saturated at the caller's bound. */
559
+ recordOutcomeEvaluationAttempt(itemId: string, revision: number, maxAttempts: number): WorkItemOutcomeRecord;
560
+ appendEvent(itemId: string, kind: string, actor: string, detail?: unknown): void;
561
+ events(itemId: string): readonly WorkItemEventRecord[];
562
+ /**
563
+ * Record Policy-change notices for live items in one transaction, at most
564
+ * once per (item, policyVersion). A notice is an append-only event: it
565
+ * changes no state, lease or dispatch, so a Policy change never cancels
566
+ * work. All or nothing: a failure writes none of the batch, and a retry with
567
+ * the same version re-derives it. Each entry yields the written notice, or
568
+ * `undefined` when the item already had one for this version or is no
569
+ * longer live work.
570
+ */
571
+ recordPolicyNotices(policyVersion: string, entries: ReadonlyArray<Readonly<{
572
+ itemId: string;
573
+ notice: Omit<WorkItemPolicyNotice, 'itemId' | 'recordedAt' | 'policyVersion'>;
574
+ }>>): ReadonlyArray<WorkItemPolicyNotice | undefined>;
575
+ /**
576
+ * Live (open, non-observed) items in admission order after `after`, for a
577
+ * bounded notice pass; also how many live items lie beyond the page.
578
+ */
579
+ listLiveItemsPage(options: {
580
+ after?: Readonly<{
581
+ createdAt: number;
582
+ id: string;
583
+ }>;
584
+ limit: number;
585
+ }): Readonly<{
586
+ items: readonly WorkItemRecord[];
587
+ remaining: number;
588
+ }>;
589
+ /**
590
+ * The independent verifier's verdict recorded for one item, if any.
591
+ *
592
+ * Append-only event, at most one per item: a receipt is final for its
593
+ * contract, so the first validated read-back is the item's verdict.
594
+ */
595
+ acceptanceVerdict(itemId: string): WorkItemAcceptanceVerdict | undefined;
596
+ /**
597
+ * Record the verifier's verdict for one item, at most once. The same
598
+ * receipt again is idempotent; a different receipt for an item that already
599
+ * has a verdict is an identity mismatch and writes nothing. The item's state,
600
+ * lease and outcome revisions are untouched.
601
+ */
602
+ recordAcceptanceVerdict(verdict: Omit<WorkItemAcceptanceVerdict, 'recordedAt'>, gate?: WorkItemAcceptanceGateExpectation): Readonly<{
603
+ verdict: WorkItemAcceptanceVerdict;
604
+ created: boolean;
605
+ blockedReason?: WorkItemAcceptanceBlockReason;
606
+ }>;
607
+ /**
608
+ * WP21 slice 7 (`acceptance.verdictGate`): an independent verifier said the
609
+ * executed item's objective was not achieved, could not tell, or needs
610
+ * attention. Move it from `ready` to the existing `blocked` state with an
611
+ * `acceptance-*` reason and one `blocked` event. Compare-and-swap on the
612
+ * expected state, attempt and occurrence: an item that moved on (re-armed,
613
+ * withdrawn, already blocked) is left untouched and nothing is written. The
614
+ * outcome ledger is not rewritten; the self-reported `ready` revision stays
615
+ * history.
616
+ */
617
+ blockForAcceptance(itemId: string, expect: WorkItemAcceptanceGateExpectation, reason: WorkItemAcceptanceBlockReason, detail: Readonly<{
618
+ contractId: string;
619
+ receiptDigest?: string;
620
+ verifierReason?: string | null;
621
+ }>): boolean;
622
+ /** Every Policy-change notice recorded for one item, oldest first. */
623
+ policyNotices(itemId: string): readonly WorkItemPolicyNotice[];
624
+ /**
625
+ * Hand one human follow-up on the item's work thread to that item.
626
+ *
627
+ * The message id is owned by at most one item for its whole life; the same
628
+ * (item, message, digest) is idempotent, anything else under the same id is
629
+ * an identity mismatch. An item that already ended takes nothing: the
630
+ * message stays with judgment and no row is written.
631
+ */
632
+ ownFollowUp(input: {
633
+ itemId: string;
634
+ bindingId: string;
635
+ messageId: string;
636
+ sourceDigest: string;
637
+ }): WorkItemFollowUpOwnResult;
638
+ /**
639
+ * Mark one owned follow-up processed by its live item. Idempotent; once the
640
+ * item ended an unprocessed follow-up belongs to judgment and cannot be
641
+ * claimed as processed any more, whether or not the release sweep ran.
642
+ */
643
+ markFollowUpProcessed(itemId: string, messageId: string): WorkItemFollowUpRecord;
644
+ /** Follow-ups owned by one item, in the order they were handed over. */
645
+ followUps(itemId: string): readonly WorkItemFollowUpRecord[];
646
+ /**
647
+ * Materialize the release of every unprocessed follow-up whose item ended.
648
+ *
649
+ * Derived from durable state only (terminal item, owned, neither processed
650
+ * nor released), so it is restart-safe and covers every terminal path
651
+ * without hooking each one; a second sweep releases nothing new.
652
+ */
653
+ releaseEndedFollowUps(limit?: number): readonly WorkItemFollowUpRecord[];
654
+ /**
655
+ * Released follow-ups in release order, for judgment to (re)read after a
656
+ * restart. `after` is the cursor of the last record already seen.
657
+ */
658
+ listReleasedFollowUps(options?: {
659
+ limit?: number;
660
+ after?: WorkItemFollowUpCursor;
661
+ }): Readonly<{
662
+ items: readonly WorkItemFollowUpRecord[];
663
+ next: WorkItemFollowUpCursor | null;
664
+ }>;
665
+ getDispatch(itemId: string): WorkItemDispatchRecord | undefined;
666
+ /** Final synchronous wake fence. This method calls no peer or user callback;
667
+ * full SQLite text equality also closes Node 24's hidden-NUL projection gap. */
668
+ isWakeSnapshotCurrent(item: WorkItemRecord, dispatch: WorkItemDispatchRecord): boolean;
669
+ /**
670
+ * Pending dispatches whose due time has arrived.
671
+ *
672
+ * `occurred_at` doubles as the due time: admission sets it to the admit
673
+ * instant, a lease reclaim re-arms it at the reclaim instant, and a `waiting`
674
+ * report re-arms it at `now + limits.waitingResumeMs`. Filtering here keeps
675
+ * recovery lazy and prevents an early wake of a deliberately delayed resume.
676
+ */
677
+ listOpenDispatches(limit: number): readonly WorkItemDispatchRecord[];
678
+ /** At most ten due waiting resumes, with durable keyset round-robin fairness. */
679
+ listDueWaitingResumes(limit?: number): readonly WorkItemDispatchRecord[];
680
+ /** Advance even past denied/expired-owner rows so later items are not starved. */
681
+ advanceWaitingResumeCursor(dispatch: WorkItemDispatchRecord): void;
682
+ /**
683
+ * Failed dispatches whose wake outcome is unknown and that still own one
684
+ * bounded retry inside the same generation.
685
+ */
686
+ listRetryableUnknownDispatches(limit: number): readonly WorkItemDispatchRecord[];
687
+ markDispatchDispatched(itemId: string, occurrenceId: string): void;
688
+ markDispatchFailed(itemId: string, reason: string): void;
689
+ /**
690
+ * Record an ambiguous wake failure: the peer may or may not have committed
691
+ * the occurrence, so the row is failed with `unknown_after_send` and the
692
+ * generation's retry budget is spent once.
693
+ *
694
+ * The identity is preserved, so a retry resolves the same occurrence instead
695
+ * of minting a second run.
696
+ */
697
+ markDispatchUnknown(itemId: string): WorkItemDispatchRecord;
698
+ /**
699
+ * Re-arm one failed-with-unknown row inside the *same* generation.
700
+ *
701
+ * The persisted event id and occurred-at are reused verbatim, so the retry is
702
+ * idempotent against the automation intake: if the first attempt did commit,
703
+ * the retry resolves that occurrence; if it did not, the retry creates it.
704
+ */
705
+ retryUnknownDispatch(itemId: string): WorkItemDispatchRecord;
706
+ /**
707
+ * Re-arm a fresh wake after a claim-time revalidation denial.
708
+ *
709
+ * At claim time the occurrence already exists, so parking alone would leave
710
+ * the item without any future wake. A new generation is minted because the
711
+ * denied run never claimed the item; once the mandate allows again, the
712
+ * ordinary wake path hands the same item to a fresh occurrence.
713
+ */
714
+ rearmAuthority(itemId: string): void;
715
+ /**
716
+ * Reclaim every claim whose lease elapsed. Lazy by design: no timer owns
717
+ * recovery, so a host that was down simply reclaims on the next touch.
718
+ *
719
+ * The expired claim is retired explicitly, the item returns to `queued` and
720
+ * its dispatch row is re-armed so the normal wake path can hand the item to a
721
+ * fresh occurrence. A re-queued item that nobody wakes would otherwise stay
722
+ * queued forever, counted against `maxOpenItems`.
723
+ */
724
+ reclaimExpired(): ReclaimResult;
725
+ /** Latest claim for one run; a re-claim after expiry appends a newer row. */
726
+ findClaimByRun(runId: string): WorkItemClaimRecord | undefined;
727
+ activeClaim(itemId: string): WorkItemClaimRecord | undefined;
728
+ /**
729
+ * Compare-and-swap claim.
730
+ *
731
+ * A repeated claim from the same run replays the existing claim only while
732
+ * that claim is still active, so a tool retry after transport loss is safe.
733
+ * Once the lease elapsed the claim is retired and the same run obtains a new
734
+ * claim row (and a new attempt), instead of replaying a dead lease forever.
735
+ * A competing run loses with `claim-conflict`.
736
+ */
737
+ claim(input: {
738
+ itemId: string;
739
+ runId: string;
740
+ occurrenceId: string;
741
+ leaseMs: number;
742
+ reclaimExpired?: boolean;
743
+ }): ClaimResult;
744
+ reportCount(itemId: string): number;
745
+ lastReport(itemId: string): WorkItemReportRecord | undefined;
746
+ reports(itemId: string): readonly WorkItemReportRecord[];
747
+ /**
748
+ * Compare the existing scoped key and normalized persisted payload in SQLite.
749
+ * Node 24.7.0 TEXT projections truncate at NUL; bound SQL comparisons retain
750
+ * every byte. The existing key format alone also has delimiter collisions.
751
+ */
752
+ reportReplayState(input: Pick<WorkItemReportRecord, 'itemId' | 'idempotencyKey' | 'status' | 'summary' | 'truncated' | 'artifactPaths'>): 'absent' | 'match' | 'conflict';
753
+ /** Exact report and adjacent references, using the existing item/revision key. */
754
+ reportByRevision(itemId: string, revision: number): Readonly<{
755
+ report: WorkItemReportRecord;
756
+ previousRevision: number | null;
757
+ nextRevision: number | null;
758
+ }> | undefined;
759
+ /** Feedback evidence keeps immutable report TEXT bytes intact on Node 24.
760
+ * This reads exactly the requested revision and performs no state changes. */
761
+ feedbackReportByRevision(itemId: string, revision: number): WorkItemReportRecord | undefined;
762
+ /**
763
+ * Append one report revision under the active claim, then apply the state
764
+ * transition for that status.
765
+ *
766
+ * The caller's idempotency key is scoped to the claim row, so identical text
767
+ * reported by different claims (or different items) is not mistaken for a
768
+ * replay, while a retried call on the same claim returns the stored revision.
769
+ */
770
+ report(input: {
771
+ itemId: string;
772
+ runId: string;
773
+ status: WorkItemReportStatus;
774
+ summary: string;
775
+ truncated: boolean;
776
+ artifactPaths: readonly string[];
777
+ idempotencyKey: string;
778
+ maxReports: number;
779
+ leaseMs: number;
780
+ waitingResumeMs: number;
781
+ }): ReportResult;
782
+ setReportDelivery(itemId: string, revision: number, state: WorkItemDeliveryState, deliveryId: string | null): void;
783
+ /** Terminal item states map to terminal claim states. */
784
+ static claimStateFor(report: WorkItemReportStatus): WorkItemClaimState;
785
+ /** Open states are exposed for admission accounting and tests. */
786
+ static openStates(): readonly WorkItemState[];
787
+ }
788
+ export {};
789
+ //# sourceMappingURL=store.d.ts.map