@intx/hub-sessions 0.1.2 → 0.2.2
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 +84 -1
- package/dist/agent-repo.d.ts +89 -0
- package/dist/agent-repo.js +109 -0
- package/dist/agent-state-kind.d.ts +12 -0
- package/dist/agent-state-kind.js +185 -0
- package/dist/asset-service.d.ts +123 -0
- package/dist/asset-service.js +349 -0
- package/dist/available-skills-stanza.d.ts +21 -0
- package/dist/available-skills-stanza.js +32 -0
- package/dist/credential-push.d.ts +32 -0
- package/dist/credential-push.js +85 -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 +357 -0
- package/dist/hub-session-lookups.d.ts +17 -0
- package/dist/hub-session-lookups.js +204 -0
- package/dist/hub-session-orchestrator.d.ts +25 -0
- package/dist/hub-session-orchestrator.js +122 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +16 -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 +1692 -0
- package/dist/repo-store/subscribe-kind.d.ts +53 -0
- package/dist/repo-store/subscribe-kind.js +179 -0
- package/dist/repo-store/types.d.ts +483 -0
- package/dist/repo-store/types.js +42 -0
- package/dist/session-service.d.ts +235 -0
- package/dist/session-service.js +997 -0
- package/dist/skill-kind.d.ts +41 -0
- package/dist/skill-kind.js +288 -0
- package/dist/substrate.d.ts +8 -0
- package/dist/substrate.js +21 -0
- package/dist/workflow-kind.d.ts +21 -0
- package/dist/workflow-kind.js +263 -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 +326 -0
- package/dist/workflow-run-kind.js +2646 -0
- package/dist/workflow-run-reader.d.ts +47 -0
- package/dist/workflow-run-reader.js +157 -0
- package/dist/ws/index.d.ts +3 -0
- package/dist/ws/index.js +3 -0
- package/dist/ws/sidecar-events.d.ts +134 -0
- package/dist/ws/sidecar-events.js +70 -0
- package/dist/ws/sidecar-handler.d.ts +184 -0
- package/dist/ws/sidecar-handler.js +1603 -0
- package/dist/ws/sidecar-token-authenticator.d.ts +15 -0
- package/dist/ws/sidecar-token-authenticator.js +24 -0
- package/package.json +34 -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,51 @@
|
|
|
1
|
+
// The sealed-form event log for a terminated run. Compaction folds a run's
|
|
2
|
+
// per-event `runs/<runId>/events/<seq>.json` blobs into a single combined
|
|
3
|
+
// file, `runs/<runId>/events.jsonl`, once the run reaches a terminal event.
|
|
4
|
+
// Each line of the combined file is the verbatim text of the per-event blob
|
|
5
|
+
// it replaced, in seq order, so the fold is a byte-for-byte transition that
|
|
6
|
+
// the workflow-run kind handler can validate against the prior per-event
|
|
7
|
+
// tree. Readers handle both shapes: per-event files for in-flight runs, the
|
|
8
|
+
// combined file for sealed (terminal) runs.
|
|
9
|
+
/** Filename of a run's combined event log, a sibling of its `events/` dir. */
|
|
10
|
+
export const WORKFLOW_RUN_EVENTS_FILE = "events.jsonl";
|
|
11
|
+
/**
|
|
12
|
+
* Split a combined event-log file's content into the per-event JSON texts it
|
|
13
|
+
* holds, in file order. Each non-empty line is the verbatim text of what was
|
|
14
|
+
* an `events/<seq>.json` blob; the trailing newline yields no extra entry.
|
|
15
|
+
* Event JSON never contains a literal newline (JSON escapes them), so a line
|
|
16
|
+
* split is a faithful inverse of the encode side.
|
|
17
|
+
*/
|
|
18
|
+
export function splitCombinedEventLog(content) {
|
|
19
|
+
const out = [];
|
|
20
|
+
for (const line of content.split("\n")) {
|
|
21
|
+
if (line.length === 0)
|
|
22
|
+
continue;
|
|
23
|
+
out.push(line);
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Join per-event blobs (in seq order) into a combined event-log file: each
|
|
29
|
+
* blob's exact bytes followed by a newline. Operating on bytes -- not
|
|
30
|
+
* decoded strings -- keeps the sealed file a *verbatim* concatenation of
|
|
31
|
+
* the per-event blobs, which matters because each event is signed over its
|
|
32
|
+
* own bytes; a decode/re-encode round-trip could alter them. This is the
|
|
33
|
+
* single source of the combined-file byte layout shared by the compaction
|
|
34
|
+
* writer and the validator's byte-equality bridge, so the two cannot
|
|
35
|
+
* drift. An empty input yields an empty file.
|
|
36
|
+
*/
|
|
37
|
+
export function encodeCombinedEventLog(perEventBlobs) {
|
|
38
|
+
const NEWLINE = 0x0a;
|
|
39
|
+
let total = 0;
|
|
40
|
+
for (const blob of perEventBlobs)
|
|
41
|
+
total += blob.byteLength + 1;
|
|
42
|
+
const out = new Uint8Array(total);
|
|
43
|
+
let offset = 0;
|
|
44
|
+
for (const blob of perEventBlobs) {
|
|
45
|
+
out.set(blob, offset);
|
|
46
|
+
offset += blob.byteLength;
|
|
47
|
+
out[offset] = NEWLINE;
|
|
48
|
+
offset += 1;
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { type AuthorizeFn, 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 deploymentId: string;
|
|
12
|
+
readonly runId?: string;
|
|
13
|
+
};
|
|
14
|
+
export type WorkflowRunSupervisorPrincipal = {
|
|
15
|
+
readonly kind: "supervisor";
|
|
16
|
+
readonly deploymentId: 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_ADDRESSES_PREFIX = "addresses";
|
|
24
|
+
export declare const WORKFLOW_RUN_CONTROL_PREFIX = "control";
|
|
25
|
+
export declare const WORKFLOW_RUN_INBOX_DIR = "inbox";
|
|
26
|
+
export declare const WORKFLOW_RUN_PROCESSING_DIR = "processing";
|
|
27
|
+
export declare const WORKFLOW_RUN_CONSUMED_DIR = "consumed";
|
|
28
|
+
/**
|
|
29
|
+
* Filename of the per-address retention watermark blob, a direct child
|
|
30
|
+
* of `addresses/<urlEncoded>/` (a file, not a directory). Carries the
|
|
31
|
+
* monotonic `receivedAt`-horizon below which consumed entries may be
|
|
32
|
+
* pruned and at-or-below which inbound enqueues are refused as stale.
|
|
33
|
+
*/
|
|
34
|
+
export declare const WORKFLOW_RUN_WATERMARK_FILE = "watermark.json";
|
|
35
|
+
/**
|
|
36
|
+
* Default retention horizon for the consumed dedup index, in
|
|
37
|
+
* milliseconds. The boot edge resolves the operator's
|
|
38
|
+
* `CONSUMED_RETENTION_MS` config to a concrete value and threads it
|
|
39
|
+
* into `markConsumed`; this default applies only when no operator
|
|
40
|
+
* value is supplied. 24 hours is the conservative default: long enough
|
|
41
|
+
* that a duplicate from a retrying upstream within a day is still
|
|
42
|
+
* deduped by a retained consumed entry, short enough that `consumed/`
|
|
43
|
+
* reaches a bounded steady state of one day's message volume.
|
|
44
|
+
*
|
|
45
|
+
* INVARIANT (operator-owned): the horizon must be >= the longest
|
|
46
|
+
* window in which the same `messageId` could legitimately be
|
|
47
|
+
* re-submitted and still must be caught as a duplicate. There is no
|
|
48
|
+
* automatic internal mail redelivery in the system today, so this is
|
|
49
|
+
* the external re-submission window. If an at-least-once redelivery
|
|
50
|
+
* source is ever added, the horizon must be >= its maximum redelivery
|
|
51
|
+
* window or dedup breaks; a breach surfaces LOUDLY (a too-late
|
|
52
|
+
* re-submission carrying an old `receivedAt` is refused at enqueue,
|
|
53
|
+
* not silently reprocessed) rather than as silent double-processing.
|
|
54
|
+
*/
|
|
55
|
+
export declare const DEFAULT_CONSUMED_RETENTION_MS: number;
|
|
56
|
+
/**
|
|
57
|
+
* Per-agent durable conversation-state subtree (design §3c). A
|
|
58
|
+
* long-lived single-step agent's multi-turn conversation context is
|
|
59
|
+
* committed under `agent-state/<agentKey>/...` so it survives child
|
|
60
|
+
* respawn: on respawn the rebuilt warm agent reads its prior
|
|
61
|
+
* conversation back from here before the resumed run replays.
|
|
62
|
+
*
|
|
63
|
+
* Unlike `runs/` (append-only events, immutable blobs) this subtree is
|
|
64
|
+
* MUTABLE: each run boundary overwrites the agent's conversation
|
|
65
|
+
* snapshot with the latest turns. It is therefore exempt from the
|
|
66
|
+
* append-only / deletion-direction walks `runs/` is subject to; the
|
|
67
|
+
* only push-time constraint is segment shape (a single round-trip-safe
|
|
68
|
+
* `<agentKey>` directory layer below the prefix).
|
|
69
|
+
*/
|
|
70
|
+
export declare const WORKFLOW_RUN_AGENT_STATE_PREFIX = "agent-state";
|
|
71
|
+
/**
|
|
72
|
+
* JSON envelope carried by inbox and processing entries. Keys:
|
|
73
|
+
* - `messageId`: dedup key for the inbound message.
|
|
74
|
+
* - `receivedAt`: epoch-ms timestamp the reactor accepted the
|
|
75
|
+
* message; sortable FIFO key prefix.
|
|
76
|
+
* - `address`: decoded canonical address (not URL-encoded).
|
|
77
|
+
* - `mailAuditRef`: pointer to the raw mail bytes in the mail-audit
|
|
78
|
+
* store. For the in-process single-agent path a separate
|
|
79
|
+
* `MailAuditStore` holds the authoritative bytes and this ref joins
|
|
80
|
+
* onto it.
|
|
81
|
+
* - `rawMessage`: base64 of the inbound mail's raw MIME bytes,
|
|
82
|
+
* inlined so the workflow-process child can read its step input by
|
|
83
|
+
* messageId at `trigger.fired` time. The supervisor is the sole
|
|
84
|
+
* mail owner under the unified-execution host (§3a); it has no
|
|
85
|
+
* separate durable byte store the child can read, so the bytes ride
|
|
86
|
+
* the claim-check envelope itself. Present whenever the supervisor
|
|
87
|
+
* enqueued the entry; omitted by callers that only stamp the audit
|
|
88
|
+
* ref. The bytes survive the inbox→processing transition verbatim
|
|
89
|
+
* (the dequeue copies the entry bytes), so a `trigger.fired` for a
|
|
90
|
+
* processing entry can always recover the input.
|
|
91
|
+
*/
|
|
92
|
+
declare const ClaimCheckEnvelope: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
93
|
+
messageId: string;
|
|
94
|
+
receivedAt: number;
|
|
95
|
+
address: string;
|
|
96
|
+
mailAuditRef: {
|
|
97
|
+
store: string;
|
|
98
|
+
path: string;
|
|
99
|
+
};
|
|
100
|
+
rawMessage?: string;
|
|
101
|
+
}, {}>;
|
|
102
|
+
/**
|
|
103
|
+
* JSON envelope carried by consumed entries. The consumed entry is the
|
|
104
|
+
* canonical dedup index keyed by messageId; the envelope preserves
|
|
105
|
+
* the originating receivedAt for audit and carries the runId that
|
|
106
|
+
* consumed the message.
|
|
107
|
+
*/
|
|
108
|
+
declare const ConsumedEnvelope: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
109
|
+
messageId: string;
|
|
110
|
+
receivedAt: number;
|
|
111
|
+
address: string;
|
|
112
|
+
runId: string;
|
|
113
|
+
consumedAt: number;
|
|
114
|
+
mailAuditRef: {
|
|
115
|
+
store: string;
|
|
116
|
+
path: string;
|
|
117
|
+
};
|
|
118
|
+
}, {}>;
|
|
119
|
+
/**
|
|
120
|
+
* JSON envelope carried by the per-address `watermark.json` blob. The
|
|
121
|
+
* `watermark` is a `receivedAt` horizon (epoch ms): the oldest
|
|
122
|
+
* `receivedAt` a consumed entry may still retain. It only ever
|
|
123
|
+
* advances. A retention prune drops consumed entries strictly below
|
|
124
|
+
* it; `enqueueInbox` refuses any inbound strictly below it.
|
|
125
|
+
*/
|
|
126
|
+
declare const WatermarkEnvelope: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
127
|
+
watermark: number;
|
|
128
|
+
}, {}>;
|
|
129
|
+
export type ClaimCheckEnvelope = typeof ClaimCheckEnvelope.infer;
|
|
130
|
+
export type ConsumedEnvelope = typeof ConsumedEnvelope.infer;
|
|
131
|
+
export type WatermarkEnvelope = typeof WatermarkEnvelope.infer;
|
|
132
|
+
export declare const workflowRunKindHandler: KindHandler;
|
|
133
|
+
export declare const workflowRunAuthorize: AuthorizeFn;
|
|
134
|
+
export type EnqueueInboxArgs = {
|
|
135
|
+
address: string;
|
|
136
|
+
messageId: string;
|
|
137
|
+
receivedAt: number;
|
|
138
|
+
mailAuditRef: {
|
|
139
|
+
store: string;
|
|
140
|
+
path: string;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Base64 of the inbound mail's raw MIME bytes. Inlined on the
|
|
144
|
+
* claim-check envelope so the workflow-process child can recover its
|
|
145
|
+
* step input by messageId at `trigger.fired` time (§3a -- the
|
|
146
|
+
* supervisor is the sole mail owner and has no separate durable byte
|
|
147
|
+
* store the child reads). Omit to stamp only the audit ref.
|
|
148
|
+
*/
|
|
149
|
+
rawMessage?: string;
|
|
150
|
+
};
|
|
151
|
+
export type EnqueueInboxResult = {
|
|
152
|
+
commitSha: string;
|
|
153
|
+
inboxKey: string;
|
|
154
|
+
envelope: ClaimCheckEnvelope;
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* Append a new inbox entry for `address`. The merge callback reads
|
|
158
|
+
* the address subtree under the per-repo lock, augments the inbox
|
|
159
|
+
* with the new entry, and returns the full set of address files. The
|
|
160
|
+
* substrate replaces the address subtree wholesale.
|
|
161
|
+
*
|
|
162
|
+
* Rejects if a same-messageId entry already exists in any queue
|
|
163
|
+
* state at the address — including a prior inbox entry at a
|
|
164
|
+
* different `receivedAt`. The caller is expected to consult the
|
|
165
|
+
* dedup index (consumed/) before calling, but enforcing the
|
|
166
|
+
* invariant here also catches the concurrent-enqueue race that the
|
|
167
|
+
* per-repo lock alone cannot surface.
|
|
168
|
+
*/
|
|
169
|
+
export declare function enqueueInbox(store: RepoStore, principal: Principal, repoId: RepoId, args: EnqueueInboxArgs): Promise<EnqueueInboxResult>;
|
|
170
|
+
export type DequeueToProcessingResult = {
|
|
171
|
+
commitSha: string;
|
|
172
|
+
key: string;
|
|
173
|
+
envelope: ClaimCheckEnvelope;
|
|
174
|
+
} | null;
|
|
175
|
+
/**
|
|
176
|
+
* Move the FIFO-first inbox entry for `address` to processing.
|
|
177
|
+
* Returns `null` when the inbox is empty so the caller can
|
|
178
|
+
* distinguish "nothing to do" from "operation failed".
|
|
179
|
+
*
|
|
180
|
+
* FIFO is keyed on the parsed numeric `receivedAt` prefix of the
|
|
181
|
+
* inbox filename, with a lexicographic messageId tiebreak. The
|
|
182
|
+
* substrate does NOT rely on uniform digit widths — sorting raw
|
|
183
|
+
* filenames would put `"100-…"` ahead of `"99-…"` since `'1' < '9'`,
|
|
184
|
+
* which violates the FIFO invariant.
|
|
185
|
+
*/
|
|
186
|
+
export declare function dequeueToProcessing(store: RepoStore, principal: Principal, repoId: RepoId, address: string): Promise<DequeueToProcessingResult>;
|
|
187
|
+
export type ReadProcessingEntryResult = {
|
|
188
|
+
envelope: ClaimCheckEnvelope;
|
|
189
|
+
} | null;
|
|
190
|
+
/**
|
|
191
|
+
* Read the processing-queue entry for `messageId` at `address` without
|
|
192
|
+
* mutating the tree. Returns the decoded claim-check envelope (carrying
|
|
193
|
+
* `mailAuditRef` and, when the enqueuer inlined them, the base64
|
|
194
|
+
* `rawMessage` bytes) or `null` when no processing entry exists for the
|
|
195
|
+
* messageId.
|
|
196
|
+
*
|
|
197
|
+
* This is the read half of mailbox ownership (§3a): the supervisor's
|
|
198
|
+
* dispatch loop moves an inbox entry to processing and forwards a
|
|
199
|
+
* `trigger.fired{messageId}` to the workflow-process child; the child
|
|
200
|
+
* calls this to recover the inbound message bytes that become its step
|
|
201
|
+
* input.
|
|
202
|
+
*
|
|
203
|
+
* The read is a flat working-tree read of
|
|
204
|
+
* `addresses/<seg>/processing/`. The substrate materializes each
|
|
205
|
+
* claim-check commit's touched paths into the repo's working tree (the
|
|
206
|
+
* delta write removes each deleted path and writes each put after
|
|
207
|
+
* validation passes), so a read issued after `dequeueToProcessing`
|
|
208
|
+
* committed -- which is exactly when the supervisor forwards
|
|
209
|
+
* `trigger.fired` -- observes the processing entry. Reading the working tree (rather than walking the
|
|
210
|
+
* committed git tree) matches the workflow-process child's sibling
|
|
211
|
+
* reads of `workflow.json` and `runs/<runId>/events/`. Because the
|
|
212
|
+
* read issues no commit it cannot race the supervisor's `markConsumed`
|
|
213
|
+
* write; it returns a point-in-time snapshot of the directory.
|
|
214
|
+
*/
|
|
215
|
+
export declare function readProcessingEntry(store: RepoStore, _principal: Principal, repoId: RepoId, address: string, messageId: string): Promise<ReadProcessingEntryResult>;
|
|
216
|
+
export type MarkConsumedArgs = {
|
|
217
|
+
address: string;
|
|
218
|
+
messageId: string;
|
|
219
|
+
runId: string;
|
|
220
|
+
consumedAt: number;
|
|
221
|
+
/**
|
|
222
|
+
* Retention horizon for the consumed dedup index, in milliseconds.
|
|
223
|
+
* The commit advances the per-address watermark to
|
|
224
|
+
* `consumedAt - retentionHorizonMs` (never backward, never past the
|
|
225
|
+
* entry being written) and prunes consumed entries below it. The
|
|
226
|
+
* boot edge resolves the operator's `CONSUMED_RETENTION_MS` config
|
|
227
|
+
* to a concrete value and threads it here. Omit to apply
|
|
228
|
+
* `DEFAULT_CONSUMED_RETENTION_MS` (24h).
|
|
229
|
+
*/
|
|
230
|
+
retentionHorizonMs?: number;
|
|
231
|
+
};
|
|
232
|
+
export type MarkConsumedResult = {
|
|
233
|
+
commitSha: string;
|
|
234
|
+
envelope: ConsumedEnvelope;
|
|
235
|
+
/** Watermark the commit advanced to (epoch-ms `receivedAt` horizon). */
|
|
236
|
+
watermark: number;
|
|
237
|
+
/** messageIds whose consumed entries this commit pruned. */
|
|
238
|
+
prunedMessageIds: string[];
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* Atomically remove the processing entry for `messageId` at `address`,
|
|
242
|
+
* write the canonical `consumed/<messageId>.json` dedup index entry,
|
|
243
|
+
* advance the per-address retention watermark, and prune consumed
|
|
244
|
+
* entries the watermark has passed. The caller is expected to have
|
|
245
|
+
* called `dequeueToProcessing` for this messageId; calling
|
|
246
|
+
* `markConsumed` without a matching processing entry throws.
|
|
247
|
+
*
|
|
248
|
+
* The consumed envelope preserves the original `receivedAt` and
|
|
249
|
+
* `mailAuditRef` from the processing entry so the dedup index doubles
|
|
250
|
+
* as an audit record.
|
|
251
|
+
*
|
|
252
|
+
* Retention (the bounded-`consumed/` contract): the watermark advances
|
|
253
|
+
* to `max(priorWatermark, min(consumedAt - retentionHorizonMs,
|
|
254
|
+
* thisEntry.receivedAt))` -- monotonic, and never past the entry being
|
|
255
|
+
* written so the new entry is always retained. Every consumed entry
|
|
256
|
+
* whose `receivedAt` is strictly below the new watermark is dropped
|
|
257
|
+
* (the oldest age-ordered tail). `consumed/` therefore reaches a
|
|
258
|
+
* bounded steady state of roughly one horizon's worth of entries
|
|
259
|
+
* instead of growing one entry per message forever.
|
|
260
|
+
*/
|
|
261
|
+
export declare function markConsumed(store: RepoStore, principal: Principal, repoId: RepoId, args: MarkConsumedArgs): Promise<MarkConsumedResult>;
|
|
262
|
+
export type ReplayProcessingToInboxResult = {
|
|
263
|
+
commitSha: string;
|
|
264
|
+
replayedKeys: string[];
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* Read the run event logs under `runs/` and return the set of
|
|
268
|
+
* `consumedMessageId`s belonging to NON-terminal runs -- the messages a
|
|
269
|
+
* live run still owns. The caller (the supervisor's spawn-time replay)
|
|
270
|
+
* feeds this into `replayProcessingToInbox`'s `ownedMessageIds` so a
|
|
271
|
+
* parked run's message is not re-admitted to inbox and dispatched a
|
|
272
|
+
* second time while the run is recovered by re-driving its durable log.
|
|
273
|
+
* Without this, the re-drive AND the re-triggered fresh run both re-park
|
|
274
|
+
* the same awaitSignal gate on the same runId, and the two concurrent
|
|
275
|
+
* runtime bodies race to a corrupt terminal.
|
|
276
|
+
*
|
|
277
|
+
* Reads the substrate's working tree via `getRepoDir`, mirroring the
|
|
278
|
+
* child's `discoverInFlightRuns`. The working tree tracks the run-event
|
|
279
|
+
* ref (`refs/heads/main`); the claim-check ref (`refs/heads/events`)
|
|
280
|
+
* cannot see it, which is why this lives at the caller rather than inside
|
|
281
|
+
* `replayProcessingToInbox`'s single-ref delta. A run whose log is sealed
|
|
282
|
+
* (combined `events.json`, only permitted for a terminated run) or
|
|
283
|
+
* carries a terminal event is excluded; an absent `runs/` directory
|
|
284
|
+
* yields an empty set.
|
|
285
|
+
*/
|
|
286
|
+
export declare function readOwnedMessageIds(store: RepoStore, repoId: RepoId): Promise<Set<string>>;
|
|
287
|
+
export type ReplayProcessingToInboxOpts = {
|
|
288
|
+
/**
|
|
289
|
+
* MessageIds whose run is still LIVE (non-terminal) and therefore owns
|
|
290
|
+
* its inbound message: recovery re-drives that run against the durable
|
|
291
|
+
* log, so re-admitting the message to `inbox/` would dispatch a SECOND
|
|
292
|
+
* run for it, colliding with the re-drive on the shared runId. Entries
|
|
293
|
+
* in this set are left in `processing/` untouched; the run's eventual
|
|
294
|
+
* `markConsumed` clears them. The caller computes this by reading the
|
|
295
|
+
* run event logs, which live on a DIFFERENT ref (`refs/heads/main`)
|
|
296
|
+
* than the claim-check subtree this operation commits to
|
|
297
|
+
* (`refs/heads/events`). Empty/absent means replay every processing
|
|
298
|
+
* entry (the pre-existing behaviour: recover all orphans).
|
|
299
|
+
*/
|
|
300
|
+
ownedMessageIds?: ReadonlySet<string>;
|
|
301
|
+
};
|
|
302
|
+
/**
|
|
303
|
+
* Recovery path: move every processing entry at `address` back to
|
|
304
|
+
* inbox preserving the original `<receivedAt>-<messageId>` filename
|
|
305
|
+
* key so FIFO ordering survives a workflow-process crash. Returns
|
|
306
|
+
* the set of keys that were moved; when nothing was in processing
|
|
307
|
+
* the returned `replayedKeys` is empty (and the commit is a no-op
|
|
308
|
+
* rewrite of the same tree).
|
|
309
|
+
*
|
|
310
|
+
* The replay is atomic across all processing entries — a partial
|
|
311
|
+
* replay that left some entries in processing would corrupt the
|
|
312
|
+
* FIFO discipline (the next dequeue would pull the wrong entry).
|
|
313
|
+
*
|
|
314
|
+
* Watermark carve-out (load-bearing — do NOT "tighten" this): the
|
|
315
|
+
* replay deliberately does NOT apply the `receivedAt < watermark`
|
|
316
|
+
* stale-reject that `enqueueInbox` applies. A `processing/` entry was
|
|
317
|
+
* already dequeued past the dedup index, so re-admitting it to
|
|
318
|
+
* `inbox/` even when its `receivedAt` has fallen below an advanced
|
|
319
|
+
* watermark is correct — the message is a legitimately in-flight one
|
|
320
|
+
* recovered after a crash, not a fresh inbound that could be a
|
|
321
|
+
* duplicate. Applying the stale-reject here would silently LOSE that
|
|
322
|
+
* message. The watermark only ever gates fresh inbound at the enqueue
|
|
323
|
+
* boundary; the recovery replay is exempt by design.
|
|
324
|
+
*/
|
|
325
|
+
export declare function replayProcessingToInbox(store: RepoStore, principal: Principal, repoId: RepoId, address: string, opts?: ReplayProcessingToInboxOpts): Promise<ReplayProcessingToInboxResult>;
|
|
326
|
+
export {};
|