@intx/hub-sessions 0.1.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +176 -0
- package/README.md +82 -1
- package/dist/agent-repo.d.ts +93 -0
- package/dist/agent-repo.js +109 -0
- package/dist/agent-state-kind.d.ts +12 -0
- package/dist/agent-state-kind.js +189 -0
- package/dist/asset-service.d.ts +104 -0
- package/dist/asset-service.js +267 -0
- package/dist/committed-source-tree.d.ts +10 -0
- package/dist/committed-source-tree.js +35 -0
- package/dist/credential-push.d.ts +33 -0
- package/dist/credential-push.js +109 -0
- package/dist/event-collector-registry.d.ts +20 -0
- package/dist/event-collector-registry.js +115 -0
- package/dist/event-collector.d.ts +39 -0
- package/dist/event-collector.js +365 -0
- package/dist/hub-session-lookups.d.ts +135 -0
- package/dist/hub-session-lookups.js +663 -0
- package/dist/hub-session-orchestrator.d.ts +25 -0
- package/dist/hub-session-orchestrator.js +87 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +24 -0
- package/dist/package-registry-kind.d.ts +70 -0
- package/dist/package-registry-kind.js +260 -0
- package/dist/repo-store/index.d.ts +4 -0
- package/dist/repo-store/index.js +3 -0
- package/dist/repo-store/store.d.ts +41 -0
- package/dist/repo-store/store.js +1829 -0
- package/dist/repo-store/subscribe-kind.d.ts +56 -0
- package/dist/repo-store/subscribe-kind.js +144 -0
- package/dist/repo-store/types.d.ts +571 -0
- package/dist/repo-store/types.js +42 -0
- package/dist/session-service.d.ts +416 -0
- package/dist/session-service.js +1191 -0
- package/dist/sidecar-allocation/contracts.d.ts +78 -0
- package/dist/sidecar-allocation/contracts.js +21 -0
- package/dist/sidecar-allocation/index.d.ts +4 -0
- package/dist/sidecar-allocation/index.js +3 -0
- package/dist/sidecar-allocation/placement-policy.d.ts +11 -0
- package/dist/sidecar-allocation/placement-policy.js +21 -0
- package/dist/sidecar-allocation/plugin-registry.d.ts +11 -0
- package/dist/sidecar-allocation/plugin-registry.js +37 -0
- package/dist/sidecar-allocation/reconciler.d.ts +42 -0
- package/dist/sidecar-allocation/reconciler.js +431 -0
- package/dist/skill-kind.d.ts +41 -0
- package/dist/skill-kind.js +292 -0
- package/dist/substrate.d.ts +8 -0
- package/dist/substrate.js +21 -0
- package/dist/workflow-allocation-service.d.ts +58 -0
- package/dist/workflow-allocation-service.js +239 -0
- package/dist/workflow-closure-resolution.d.ts +106 -0
- package/dist/workflow-closure-resolution.js +123 -0
- package/dist/workflow-definition-ensure.d.ts +24 -0
- package/dist/workflow-definition-ensure.js +75 -0
- package/dist/workflow-dispatch-service.d.ts +40 -0
- package/dist/workflow-dispatch-service.js +146 -0
- package/dist/workflow-dispatch-settlement.d.ts +29 -0
- package/dist/workflow-dispatch-settlement.js +140 -0
- package/dist/workflow-kind.d.ts +37 -0
- package/dist/workflow-kind.js +310 -0
- package/dist/workflow-probe-gate.d.ts +214 -0
- package/dist/workflow-probe-gate.js +207 -0
- package/dist/workflow-run-event-log.d.ts +21 -0
- package/dist/workflow-run-event-log.js +51 -0
- package/dist/workflow-run-kind.d.ts +440 -0
- package/dist/workflow-run-kind.js +2916 -0
- package/dist/workflow-run-reader.d.ts +47 -0
- package/dist/workflow-run-reader.js +153 -0
- package/dist/workflow-run-restore.d.ts +15 -0
- package/dist/workflow-run-restore.js +26 -0
- package/dist/workflow-source-closure.d.ts +35 -0
- package/dist/workflow-source-closure.js +342 -0
- package/dist/ws/index.d.ts +3 -0
- package/dist/ws/index.js +3 -0
- package/dist/ws/sidecar-events.d.ts +222 -0
- package/dist/ws/sidecar-events.js +72 -0
- package/dist/ws/sidecar-handler.d.ts +305 -0
- package/dist/ws/sidecar-handler.js +2537 -0
- package/dist/ws/sidecar-token-authenticator.d.ts +17 -0
- package/dist/ws/sidecar-token-authenticator.js +81 -0
- package/package.json +35 -12
- package/src/agent-repo.test.ts +0 -310
- package/src/agent-repo.ts +0 -165
- package/src/agent-state-kind.test.ts +0 -247
- package/src/agent-state-kind.ts +0 -204
- package/src/asset-service.test.ts +0 -540
- package/src/asset-service.ts +0 -378
- package/src/available-skills-stanza.test.ts +0 -87
- package/src/available-skills-stanza.ts +0 -47
- package/src/credential-push.ts +0 -65
- package/src/event-collector-registry.test.ts +0 -73
- package/src/event-collector-registry.ts +0 -171
- package/src/event-collector.test.ts +0 -1387
- package/src/event-collector.ts +0 -424
- package/src/hub-session-lookups.ts +0 -206
- package/src/hub-session-orchestrator.test.ts +0 -510
- package/src/hub-session-orchestrator.ts +0 -213
- package/src/index.ts +0 -78
- package/src/repo-store/index.ts +0 -15
- package/src/repo-store/store.test.ts +0 -1169
- package/src/repo-store/store.ts +0 -428
- package/src/repo-store/types.ts +0 -253
- package/src/session-service.test.ts +0 -895
- package/src/session-service.ts +0 -464
- package/src/skill-kind.test.ts +0 -599
- package/src/skill-kind.ts +0 -350
- package/src/ws/index.ts +0 -18
- package/src/ws/sidecar-events.test.ts +0 -96
- package/src/ws/sidecar-events.ts +0 -231
- package/src/ws/sidecar-handler.test.ts +0 -2217
- package/src/ws/sidecar-handler.ts +0 -1574
- package/tsconfig.json +0 -4
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { type AuthorizeFn, type CommittedReads, type KindHandler, type Principal, type RepoId, type RepoStore } from "./repo-store/index.js";
|
|
2
|
+
export type WorkflowRunHubPrincipal = {
|
|
3
|
+
readonly kind: "hub";
|
|
4
|
+
};
|
|
5
|
+
export type WorkflowRunSidecarPrincipal = {
|
|
6
|
+
readonly kind: "sidecar";
|
|
7
|
+
readonly agentId: string;
|
|
8
|
+
};
|
|
9
|
+
export type WorkflowRunWorkflowProcessPrincipal = {
|
|
10
|
+
readonly kind: "workflow-process";
|
|
11
|
+
readonly anchorRunId: string;
|
|
12
|
+
readonly runId?: string;
|
|
13
|
+
};
|
|
14
|
+
export type WorkflowRunSupervisorPrincipal = {
|
|
15
|
+
readonly kind: "supervisor";
|
|
16
|
+
readonly anchorRunId: string;
|
|
17
|
+
};
|
|
18
|
+
export type WorkflowRunPrincipal = WorkflowRunHubPrincipal | WorkflowRunSidecarPrincipal | WorkflowRunWorkflowProcessPrincipal | WorkflowRunSupervisorPrincipal;
|
|
19
|
+
export declare const WORKFLOW_RUN_GITIGNORE_PATH = ".gitignore";
|
|
20
|
+
export declare const WORKFLOW_RUN_RUNS_PREFIX = "runs";
|
|
21
|
+
export declare const WORKFLOW_RUN_EVENTS_DIR = "events";
|
|
22
|
+
export declare const WORKFLOW_RUN_BLOBS_DIR = "blobs";
|
|
23
|
+
export declare const WORKFLOW_RUN_GRANTS_FILE = "grants.json";
|
|
24
|
+
export declare const WORKFLOW_RUN_ADDRESSES_PREFIX = "addresses";
|
|
25
|
+
export declare const WORKFLOW_RUN_CONTROL_PREFIX = "control";
|
|
26
|
+
export declare const WORKFLOW_RUN_INBOX_DIR = "inbox";
|
|
27
|
+
export declare const WORKFLOW_RUN_PROCESSING_DIR = "processing";
|
|
28
|
+
export declare const WORKFLOW_RUN_CONSUMED_DIR = "consumed";
|
|
29
|
+
/**
|
|
30
|
+
* Filename of the per-address retention watermark blob, a direct child
|
|
31
|
+
* of `addresses/<urlEncoded>/` (a file, not a directory). Carries the
|
|
32
|
+
* monotonic `receivedAt`-horizon below which consumed entries may be
|
|
33
|
+
* pruned and at-or-below which inbound enqueues are refused as stale.
|
|
34
|
+
*/
|
|
35
|
+
export declare const WORKFLOW_RUN_WATERMARK_FILE = "watermark.json";
|
|
36
|
+
/**
|
|
37
|
+
* Default retention horizon for the consumed dedup index, in
|
|
38
|
+
* milliseconds. The boot edge resolves the operator's
|
|
39
|
+
* `CONSUMED_RETENTION_MS` config to a concrete value and threads it
|
|
40
|
+
* into `markConsumed`; this default applies only when no operator
|
|
41
|
+
* value is supplied. 24 hours is the conservative default: long enough
|
|
42
|
+
* that a duplicate from a retrying upstream within a day is still
|
|
43
|
+
* deduped by a retained consumed entry, short enough that `consumed/`
|
|
44
|
+
* reaches a bounded steady state of one day's message volume.
|
|
45
|
+
*
|
|
46
|
+
* INVARIANT (operator-owned): the horizon must be >= the longest window in
|
|
47
|
+
* which the same `messageId` could legitimately be re-submitted and still must
|
|
48
|
+
* be caught as a duplicate. The hub now redelivers un-acked inbound mail
|
|
49
|
+
* (connected-window retry and reconnect-redelivery), so an at-least-once
|
|
50
|
+
* internal source DOES exist -- but the dedup guarantee against it does not
|
|
51
|
+
* rest on window arithmetic. It rests on a STRUCTURAL fact: `enqueueInbox` is
|
|
52
|
+
* only ever called with a freshly stamped `receivedAt` (a redelivery
|
|
53
|
+
* re-enters `onMailMessage` and re-stamps `Date.now()`, never carrying the
|
|
54
|
+
* original), and the watermark only ever advances to at most
|
|
55
|
+
* `consumedAt - retentionHorizonMs`, which is <= now, so a fresh `receivedAt`
|
|
56
|
+
* always sits a full horizon above the watermark and can never be stale-
|
|
57
|
+
* refused. A redelivery instead hits the `consumed/`/`processing/`/`inbox/`
|
|
58
|
+
* dedup index and is deduped there. The sole path that carries an original
|
|
59
|
+
* (old) `receivedAt` back into the queue is `replayProcessingToInbox`, which
|
|
60
|
+
* writes straight to `inbox/` and bypasses the stale gate entirely. So
|
|
61
|
+
* `claim_check_stale_enqueue` is unreachable via redelivery today. If any
|
|
62
|
+
* redelivery source is ever changed to carry the ORIGINAL `receivedAt` into
|
|
63
|
+
* `enqueueInbox`, stale becomes reachable, the horizon must then be >= that
|
|
64
|
+
* source's maximum redelivery window, and `StaleInboxEnqueueError`'s
|
|
65
|
+
* withhold-not-ack handling becomes load-bearing; a breach surfaces LOUDLY (an
|
|
66
|
+
* old-`receivedAt` re-submission is refused at enqueue) rather than as silent
|
|
67
|
+
* double-processing.
|
|
68
|
+
*/
|
|
69
|
+
export declare const DEFAULT_CONSUMED_RETENTION_MS: number;
|
|
70
|
+
/**
|
|
71
|
+
* Per-agent durable conversation-state subtree (design §3c). A
|
|
72
|
+
* long-lived single-step agent's multi-turn conversation context is
|
|
73
|
+
* committed under `agent-state/<agentKey>/...` so it survives child
|
|
74
|
+
* respawn: on respawn the rebuilt warm agent reads its prior
|
|
75
|
+
* conversation back from here before the resumed run replays.
|
|
76
|
+
*
|
|
77
|
+
* Unlike `runs/` (append-only events, immutable blobs) this subtree is
|
|
78
|
+
* MUTABLE: each run boundary overwrites the agent's conversation
|
|
79
|
+
* snapshot with the latest turns. It is therefore exempt from the
|
|
80
|
+
* append-only / deletion-direction walks `runs/` is subject to; the
|
|
81
|
+
* only push-time constraint is segment shape (a single round-trip-safe
|
|
82
|
+
* `<agentKey>` directory layer below the prefix).
|
|
83
|
+
*/
|
|
84
|
+
export declare const WORKFLOW_RUN_AGENT_STATE_PREFIX = "agent-state";
|
|
85
|
+
/**
|
|
86
|
+
* Parse the seq from a per-event log filename `<seq>.json` under
|
|
87
|
+
* `runs/<runId>/events/`. Returns the non-negative integer seq, or
|
|
88
|
+
* `null` when the name is not a legal per-event filename. This is the
|
|
89
|
+
* one place the filename shape is defined; every reader of the event log
|
|
90
|
+
* narrows names through it rather than re-encoding the regex. Callers
|
|
91
|
+
* decide what an illegal name means -- a foreign entry to skip, or a
|
|
92
|
+
* substrate-invariant violation to surface -- since `validatePush` is
|
|
93
|
+
* the authority that keeps illegal names from landing in the first place.
|
|
94
|
+
*/
|
|
95
|
+
export declare function parseEventSeq(filename: string): number | null;
|
|
96
|
+
/**
|
|
97
|
+
* Narrow a per-event filename to its seq, throwing when it is illegal.
|
|
98
|
+
* A reader that enumerates the committed event log to act on its entries
|
|
99
|
+
* uses this rather than `parseEventSeq`: `validatePush` is the authority
|
|
100
|
+
* that keeps an illegal name from ever landing under
|
|
101
|
+
* `runs/<runId>/events/`, so a name that reaches a reader is corruption,
|
|
102
|
+
* and silently skipping it would drop an event from processing. `context`
|
|
103
|
+
* is the repo-root-relative blob path, surfaced in the error so the
|
|
104
|
+
* offending entry is identifiable.
|
|
105
|
+
*/
|
|
106
|
+
export declare function requireEventSeq(filename: string, context: string): number;
|
|
107
|
+
/**
|
|
108
|
+
* JSON envelope carried by inbox and processing entries. Keys:
|
|
109
|
+
* - `messageId`: dedup key for the inbound message.
|
|
110
|
+
* - `receivedAt`: epoch-ms timestamp the reactor accepted the
|
|
111
|
+
* message; sortable FIFO key prefix.
|
|
112
|
+
* - `address`: decoded canonical address (not URL-encoded).
|
|
113
|
+
* - `mailAuditRef`: pointer to the raw mail bytes in the mail-audit
|
|
114
|
+
* store. For the in-process single-agent path a separate
|
|
115
|
+
* `MailAuditStore` holds the authoritative bytes and this ref joins
|
|
116
|
+
* onto it.
|
|
117
|
+
* - `rawMessage`: base64 of the inbound mail's raw MIME bytes,
|
|
118
|
+
* inlined so the workflow-process child can read its step input by
|
|
119
|
+
* messageId at `trigger.fired` time. The supervisor is the sole
|
|
120
|
+
* mail owner under the unified-execution host (§3a); it has no
|
|
121
|
+
* separate durable byte store the child can read, so the bytes ride
|
|
122
|
+
* the claim-check envelope itself. Present whenever the supervisor
|
|
123
|
+
* enqueued the entry; omitted by callers that only stamp the audit
|
|
124
|
+
* ref. The bytes survive the inbox→processing transition verbatim
|
|
125
|
+
* (the dequeue copies the entry bytes), so a `trigger.fired` for a
|
|
126
|
+
* processing entry can always recover the input.
|
|
127
|
+
*/
|
|
128
|
+
declare const ClaimCheckEnvelope: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
129
|
+
messageId: string;
|
|
130
|
+
receivedAt: number;
|
|
131
|
+
address: string;
|
|
132
|
+
mailAuditRef: {
|
|
133
|
+
store: string;
|
|
134
|
+
path: string;
|
|
135
|
+
};
|
|
136
|
+
rawMessage?: string;
|
|
137
|
+
}, {}>;
|
|
138
|
+
/**
|
|
139
|
+
* JSON envelope carried by consumed entries. The consumed entry is the
|
|
140
|
+
* canonical dedup index keyed by messageId; the envelope preserves
|
|
141
|
+
* the originating receivedAt for audit and carries the runId that
|
|
142
|
+
* consumed the message.
|
|
143
|
+
*/
|
|
144
|
+
declare const ConsumedEnvelope: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
145
|
+
messageId: string;
|
|
146
|
+
receivedAt: number;
|
|
147
|
+
address: string;
|
|
148
|
+
runId: string;
|
|
149
|
+
consumedAt: number;
|
|
150
|
+
mailAuditRef: {
|
|
151
|
+
store: string;
|
|
152
|
+
path: string;
|
|
153
|
+
};
|
|
154
|
+
rejection?: {
|
|
155
|
+
code: string;
|
|
156
|
+
message: string;
|
|
157
|
+
};
|
|
158
|
+
}, {}>;
|
|
159
|
+
/**
|
|
160
|
+
* JSON envelope carried by the per-address `watermark.json` blob. The
|
|
161
|
+
* `watermark` is a `receivedAt` horizon (epoch ms): the oldest
|
|
162
|
+
* `receivedAt` a consumed entry may still retain. It only ever
|
|
163
|
+
* advances. A retention prune drops consumed entries strictly below
|
|
164
|
+
* it; `enqueueInbox` refuses any inbound strictly below it.
|
|
165
|
+
*/
|
|
166
|
+
declare const WatermarkEnvelope: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
167
|
+
watermark: number;
|
|
168
|
+
}, {}>;
|
|
169
|
+
export type ClaimCheckEnvelope = typeof ClaimCheckEnvelope.infer;
|
|
170
|
+
export type ConsumedEnvelope = typeof ConsumedEnvelope.infer;
|
|
171
|
+
export type WatermarkEnvelope = typeof WatermarkEnvelope.infer;
|
|
172
|
+
export declare const workflowRunKindHandler: KindHandler;
|
|
173
|
+
export declare const workflowRunAuthorize: AuthorizeFn;
|
|
174
|
+
export type EnqueueInboxArgs = {
|
|
175
|
+
address: string;
|
|
176
|
+
messageId: string;
|
|
177
|
+
receivedAt: number;
|
|
178
|
+
mailAuditRef: {
|
|
179
|
+
store: string;
|
|
180
|
+
path: string;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Base64 of the inbound mail's raw MIME bytes. Inlined on the
|
|
184
|
+
* claim-check envelope so the workflow-process child can recover its
|
|
185
|
+
* step input by messageId at `trigger.fired` time (§3a -- the
|
|
186
|
+
* supervisor is the sole mail owner and has no separate durable byte
|
|
187
|
+
* store the child reads). Omit to stamp only the audit ref.
|
|
188
|
+
*/
|
|
189
|
+
rawMessage?: string;
|
|
190
|
+
};
|
|
191
|
+
export type EnqueueInboxResult = {
|
|
192
|
+
commitSha: string;
|
|
193
|
+
inboxKey: string;
|
|
194
|
+
envelope: ClaimCheckEnvelope;
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Which already-present state an `enqueueInbox` call found the messageId
|
|
198
|
+
* in. Every value is POSITIVE evidence the message's bytes are durably on
|
|
199
|
+
* disk (inbox/processing) or were already consumed -- so a caller gating a
|
|
200
|
+
* receipt on the enqueue may safely acknowledge on any of them.
|
|
201
|
+
*/
|
|
202
|
+
export type EnqueueAlreadyPresentReason = "duplicate" | "already_inbox" | "processing" | "consumed";
|
|
203
|
+
/**
|
|
204
|
+
* Outcome of an `enqueueInbox` call. Modeled as a value (not an exception)
|
|
205
|
+
* precisely because the return/throw boundary is the ack/withhold boundary
|
|
206
|
+
* for a caller gating a durable-receipt ack: a returned outcome is safe to
|
|
207
|
+
* acknowledge (the bytes are on disk -- freshly written or already present),
|
|
208
|
+
* a throw is not (the write could not complete or its disposition cannot be
|
|
209
|
+
* decided). `enqueued` is the only outcome that added a new inbox entry, so
|
|
210
|
+
* it is the only one a dispatch-driving caller wakes its loop on.
|
|
211
|
+
*/
|
|
212
|
+
export type EnqueueInboxOutcome = ({
|
|
213
|
+
outcome: "enqueued";
|
|
214
|
+
} & EnqueueInboxResult) | {
|
|
215
|
+
outcome: "already-present";
|
|
216
|
+
reason: EnqueueAlreadyPresentReason;
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Thrown by `enqueueInbox` when the inbound's `receivedAt` is strictly below
|
|
220
|
+
* the address's retention watermark. This is refusal under UNCERTAINTY, not
|
|
221
|
+
* proof of prior receipt: the consumed dedup entry that would rule out a
|
|
222
|
+
* duplicate may have been pruned, so the substrate can no longer tell a
|
|
223
|
+
* duplicate from a never-processed message and refuses rather than risk
|
|
224
|
+
* reprocessing. A caller gating a durable-receipt ack MUST NOT acknowledge on
|
|
225
|
+
* this -- acking an "I cannot tell" would terminally drop a message that was
|
|
226
|
+
* never written. It is its own type (not a generic Error) so that a caller,
|
|
227
|
+
* and monitoring, can surface it as a distinct loud signal rather than
|
|
228
|
+
* blending it into ordinary I/O-failure noise.
|
|
229
|
+
*
|
|
230
|
+
* Structurally unreachable on the mail-inbound path today: `enqueueInbox` is
|
|
231
|
+
* only ever called with a freshly stamped `receivedAt` (a redelivery
|
|
232
|
+
* re-stamps `Date.now()` rather than carrying the original), and the watermark
|
|
233
|
+
* only ever advances to at most `consumedAt - retentionHorizonMs <= now`, so a
|
|
234
|
+
* fresh `receivedAt` sits a full horizon above it. The sole path that carries
|
|
235
|
+
* an original (old) `receivedAt` back into the queue is
|
|
236
|
+
* `replayProcessingToInbox`, which writes straight to `inbox/` and bypasses
|
|
237
|
+
* this gate entirely. If any redelivery source is ever changed to carry the
|
|
238
|
+
* original `receivedAt` into `enqueueInbox`, this becomes reachable and its
|
|
239
|
+
* withhold-not-ack handling becomes load-bearing.
|
|
240
|
+
*/
|
|
241
|
+
export declare class StaleInboxEnqueueError extends Error {
|
|
242
|
+
constructor(message: string);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Append a new inbox entry for `address`. The merge callback reads
|
|
246
|
+
* the address subtree under the per-repo lock, augments the inbox
|
|
247
|
+
* with the new entry, and returns the full set of address files. The
|
|
248
|
+
* substrate replaces the address subtree wholesale.
|
|
249
|
+
*
|
|
250
|
+
* Rejects if a same-messageId entry already exists in any queue
|
|
251
|
+
* state at the address — including a prior inbox entry at a
|
|
252
|
+
* different `receivedAt`. The caller is expected to consult the
|
|
253
|
+
* dedup index (consumed/) before calling, but enforcing the
|
|
254
|
+
* invariant here also catches the concurrent-enqueue race that the
|
|
255
|
+
* per-repo lock alone cannot surface.
|
|
256
|
+
*/
|
|
257
|
+
export declare function enqueueInbox(store: RepoStore, principal: Principal, repoId: RepoId, args: EnqueueInboxArgs): Promise<EnqueueInboxOutcome>;
|
|
258
|
+
export type DequeueToProcessingResult = {
|
|
259
|
+
commitSha: string;
|
|
260
|
+
key: string;
|
|
261
|
+
envelope: ClaimCheckEnvelope;
|
|
262
|
+
} | null;
|
|
263
|
+
/**
|
|
264
|
+
* Move the FIFO-first inbox entry for `address` to processing.
|
|
265
|
+
* Returns `null` when the inbox is empty so the caller can
|
|
266
|
+
* distinguish "nothing to do" from "operation failed".
|
|
267
|
+
*
|
|
268
|
+
* FIFO is keyed on the parsed numeric `receivedAt` prefix of the
|
|
269
|
+
* inbox filename, with a lexicographic messageId tiebreak. The
|
|
270
|
+
* substrate does NOT rely on uniform digit widths — sorting raw
|
|
271
|
+
* filenames would put `"100-…"` ahead of `"99-…"` since `'1' < '9'`,
|
|
272
|
+
* which violates the FIFO invariant.
|
|
273
|
+
*/
|
|
274
|
+
export declare function dequeueToProcessing(store: RepoStore, principal: Principal, repoId: RepoId, address: string): Promise<DequeueToProcessingResult>;
|
|
275
|
+
export type ReadProcessingEntryResult = {
|
|
276
|
+
envelope: ClaimCheckEnvelope;
|
|
277
|
+
} | null;
|
|
278
|
+
/**
|
|
279
|
+
* Read the processing-queue entry for `messageId` at `address` without
|
|
280
|
+
* mutating the tree. Returns the decoded claim-check envelope (carrying
|
|
281
|
+
* `mailAuditRef` and, when the enqueuer inlined them, the base64
|
|
282
|
+
* `rawMessage` bytes) or `null` when no processing entry exists for the
|
|
283
|
+
* messageId.
|
|
284
|
+
*
|
|
285
|
+
* This is the read half of mailbox ownership (§3a): the supervisor's
|
|
286
|
+
* dispatch loop moves an inbox entry to processing and forwards a
|
|
287
|
+
* `trigger.fired{messageId}` to the workflow-process child; the child
|
|
288
|
+
* calls this to recover the inbound message bytes that become its step
|
|
289
|
+
* input.
|
|
290
|
+
*
|
|
291
|
+
* The read is a flat working-tree read of
|
|
292
|
+
* `addresses/<seg>/processing/`. The substrate materializes each
|
|
293
|
+
* claim-check commit's touched paths into the repo's working tree (the
|
|
294
|
+
* delta write removes each deleted path and writes each put after
|
|
295
|
+
* validation passes), so a read issued after `dequeueToProcessing`
|
|
296
|
+
* committed -- which is exactly when the supervisor forwards
|
|
297
|
+
* `trigger.fired` -- observes the processing entry. Reading the working tree (rather than walking the
|
|
298
|
+
* committed git tree) matches the workflow-process child's sibling
|
|
299
|
+
* read of `runs/<runId>/events/`. Because the
|
|
300
|
+
* read issues no commit it cannot race the supervisor's `markConsumed`
|
|
301
|
+
* write; it returns a point-in-time snapshot of the directory.
|
|
302
|
+
*/
|
|
303
|
+
export declare function readProcessingEntry(store: RepoStore, _principal: Principal, repoId: RepoId, address: string, messageId: string): Promise<ReadProcessingEntryResult>;
|
|
304
|
+
export type MarkConsumedArgs = {
|
|
305
|
+
address: string;
|
|
306
|
+
messageId: string;
|
|
307
|
+
runId: string;
|
|
308
|
+
consumedAt: number;
|
|
309
|
+
/**
|
|
310
|
+
* Present when the supervisor deliberately refused the message instead of
|
|
311
|
+
* delivering it to the run. The consumed entry remains the durable dedup
|
|
312
|
+
* record, while Hub projection uses this detail to fail (rather than settle)
|
|
313
|
+
* an exclusive-dispatch row.
|
|
314
|
+
*/
|
|
315
|
+
rejection?: {
|
|
316
|
+
code: string;
|
|
317
|
+
message: string;
|
|
318
|
+
};
|
|
319
|
+
/**
|
|
320
|
+
* Retention horizon for the consumed dedup index, in milliseconds.
|
|
321
|
+
* The commit advances the per-address watermark to
|
|
322
|
+
* `consumedAt - retentionHorizonMs` (never backward, never past the
|
|
323
|
+
* entry being written) and prunes consumed entries below it. The
|
|
324
|
+
* boot edge resolves the operator's `CONSUMED_RETENTION_MS` config
|
|
325
|
+
* to a concrete value and threads it here. Omit to apply
|
|
326
|
+
* `DEFAULT_CONSUMED_RETENTION_MS` (24h).
|
|
327
|
+
*/
|
|
328
|
+
retentionHorizonMs?: number;
|
|
329
|
+
};
|
|
330
|
+
export type MarkConsumedResult = {
|
|
331
|
+
commitSha: string;
|
|
332
|
+
envelope: ConsumedEnvelope;
|
|
333
|
+
/** Watermark the commit advanced to (epoch-ms `receivedAt` horizon). */
|
|
334
|
+
watermark: number;
|
|
335
|
+
/** messageIds whose consumed entries this commit pruned. */
|
|
336
|
+
prunedMessageIds: string[];
|
|
337
|
+
};
|
|
338
|
+
/**
|
|
339
|
+
* Atomically remove the processing entry for `messageId` at `address`,
|
|
340
|
+
* write the canonical `consumed/<messageId>.json` dedup index entry,
|
|
341
|
+
* advance the per-address retention watermark, and prune consumed
|
|
342
|
+
* entries the watermark has passed. The caller is expected to have
|
|
343
|
+
* called `dequeueToProcessing` for this messageId; calling
|
|
344
|
+
* `markConsumed` without a matching processing entry throws.
|
|
345
|
+
*
|
|
346
|
+
* The consumed envelope preserves the original `receivedAt` and
|
|
347
|
+
* `mailAuditRef` from the processing entry so the dedup index doubles
|
|
348
|
+
* as an audit record.
|
|
349
|
+
*
|
|
350
|
+
* Retention (the bounded-`consumed/` contract): the watermark advances
|
|
351
|
+
* to `max(priorWatermark, min(consumedAt - retentionHorizonMs,
|
|
352
|
+
* thisEntry.receivedAt))` -- monotonic, and never past the entry being
|
|
353
|
+
* written so the new entry is always retained. Every consumed entry
|
|
354
|
+
* whose `receivedAt` is strictly below the new watermark is dropped
|
|
355
|
+
* (the oldest age-ordered tail). `consumed/` therefore reaches a
|
|
356
|
+
* bounded steady state of roughly one horizon's worth of entries
|
|
357
|
+
* instead of growing one entry per message forever.
|
|
358
|
+
*/
|
|
359
|
+
export declare function markConsumed(store: RepoStore, principal: Principal, repoId: RepoId, args: MarkConsumedArgs): Promise<MarkConsumedResult>;
|
|
360
|
+
export type ReplayProcessingToInboxResult = {
|
|
361
|
+
commitSha: string;
|
|
362
|
+
replayedKeys: string[];
|
|
363
|
+
};
|
|
364
|
+
/**
|
|
365
|
+
* Read the run event logs under `runs/` and return the set of
|
|
366
|
+
* `consumedMessageId`s belonging to NON-terminal runs -- the messages a
|
|
367
|
+
* live run still owns. The caller (the supervisor's spawn-time replay)
|
|
368
|
+
* feeds this into `replayProcessingToInbox`'s `ownedMessageIds` so a
|
|
369
|
+
* parked run's message is not re-admitted to inbox and dispatched a
|
|
370
|
+
* second time while the run is recovered by re-driving its durable log.
|
|
371
|
+
* Without this, the re-drive AND the re-triggered fresh run both re-park
|
|
372
|
+
* the same awaitSignal gate on the same runId, and the two concurrent
|
|
373
|
+
* runtime bodies race to a corrupt terminal.
|
|
374
|
+
*
|
|
375
|
+
* Reads the substrate's working tree via `getRepoDir`, mirroring the
|
|
376
|
+
* child's `discoverInFlightRuns`. The working tree tracks the run-event
|
|
377
|
+
* ref (`refs/heads/main`); the claim-check ref (`refs/heads/events`)
|
|
378
|
+
* cannot see it, which is why this lives at the caller rather than inside
|
|
379
|
+
* `replayProcessingToInbox`'s single-ref delta. A run whose log is sealed
|
|
380
|
+
* (combined `events.json`, only permitted for a terminated run) or
|
|
381
|
+
* carries a terminal event is excluded; an absent `runs/` directory
|
|
382
|
+
* yields an empty set.
|
|
383
|
+
*/
|
|
384
|
+
export declare function readOwnedMessageIds(store: RepoStore, repoId: RepoId): Promise<Set<string>>;
|
|
385
|
+
export type WorkflowRunLifecycle = "absent" | "live" | "terminal";
|
|
386
|
+
/** Read one run's lifecycle from a committed workflow-run tree. */
|
|
387
|
+
export declare function readCommittedWorkflowRunLifecycle(reads: CommittedReads | null, runId: string): Promise<WorkflowRunLifecycle>;
|
|
388
|
+
/**
|
|
389
|
+
* Read the durable lifecycle of one run from the workflow-run working tree.
|
|
390
|
+
* `grants.json` alone is still an absent run: grants are staged before the
|
|
391
|
+
* first trigger, while the first event is the durable proof that the run was
|
|
392
|
+
* fired. A sealed event log is terminal by the kind handler's compaction
|
|
393
|
+
* invariant.
|
|
394
|
+
*
|
|
395
|
+
* The supervisor uses this when in-memory cohort membership is empty. That
|
|
396
|
+
* happens both for a genuinely new deployment and briefly during recovery,
|
|
397
|
+
* so treating both states as "fire" would start a second driver for a live
|
|
398
|
+
* log or reuse a terminal run.
|
|
399
|
+
*/
|
|
400
|
+
export declare function readWorkflowRunLifecycle(store: RepoStore, repoId: RepoId, runId: string): Promise<WorkflowRunLifecycle>;
|
|
401
|
+
export type ReplayProcessingToInboxOpts = {
|
|
402
|
+
/**
|
|
403
|
+
* MessageIds whose run is still LIVE (non-terminal) and therefore owns
|
|
404
|
+
* its inbound message: recovery re-drives that run against the durable
|
|
405
|
+
* log, so re-admitting the message to `inbox/` would dispatch a SECOND
|
|
406
|
+
* run for it, colliding with the re-drive on the shared runId. Entries
|
|
407
|
+
* in this set are left in `processing/` untouched; the run's eventual
|
|
408
|
+
* `markConsumed` clears them. The caller computes this by reading the
|
|
409
|
+
* run event logs, which live on a DIFFERENT ref (`refs/heads/main`)
|
|
410
|
+
* than the claim-check subtree this operation commits to
|
|
411
|
+
* (`refs/heads/events`). Empty/absent means replay every processing
|
|
412
|
+
* entry (the pre-existing behaviour: recover all orphans).
|
|
413
|
+
*/
|
|
414
|
+
ownedMessageIds?: ReadonlySet<string>;
|
|
415
|
+
};
|
|
416
|
+
/**
|
|
417
|
+
* Recovery path: move every processing entry at `address` back to
|
|
418
|
+
* inbox preserving the original `<receivedAt>-<messageId>` filename
|
|
419
|
+
* key so FIFO ordering survives a workflow-process crash. Returns
|
|
420
|
+
* the set of keys that were moved; when nothing was in processing
|
|
421
|
+
* the returned `replayedKeys` is empty (and the commit is a no-op
|
|
422
|
+
* rewrite of the same tree).
|
|
423
|
+
*
|
|
424
|
+
* The replay is atomic across all processing entries — a partial
|
|
425
|
+
* replay that left some entries in processing would corrupt the
|
|
426
|
+
* FIFO discipline (the next dequeue would pull the wrong entry).
|
|
427
|
+
*
|
|
428
|
+
* Watermark carve-out (load-bearing — do NOT "tighten" this): the
|
|
429
|
+
* replay deliberately does NOT apply the `receivedAt < watermark`
|
|
430
|
+
* stale-reject that `enqueueInbox` applies. A `processing/` entry was
|
|
431
|
+
* already dequeued past the dedup index, so re-admitting it to
|
|
432
|
+
* `inbox/` even when its `receivedAt` has fallen below an advanced
|
|
433
|
+
* watermark is correct — the message is a legitimately in-flight one
|
|
434
|
+
* recovered after a crash, not a fresh inbound that could be a
|
|
435
|
+
* duplicate. Applying the stale-reject here would silently LOSE that
|
|
436
|
+
* message. The watermark only ever gates fresh inbound at the enqueue
|
|
437
|
+
* boundary; the recovery replay is exempt by design.
|
|
438
|
+
*/
|
|
439
|
+
export declare function replayProcessingToInbox(store: RepoStore, principal: Principal, repoId: RepoId, address: string, opts?: ReplayProcessingToInboxOpts): Promise<ReplayProcessingToInboxResult>;
|
|
440
|
+
export {};
|