effect-agent 0.1.0-beta.142 → 0.1.0-beta.143
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/dist/{Admin-DGZXE5BU.d.mts → Admin-CQMppCcy.d.mts} +986 -3
- package/dist/{DurableAgentRuntime-DQXDcPKQ.mjs → DurableAgentRuntime-D8GI66mP.mjs} +20 -2
- package/dist/DurableAgentRuntime-D8GI66mP.mjs.map +1 -0
- package/dist/{EventSource-Cg3IeJa6.d.mts → EventSource-w9PonN9X.d.mts} +2 -2
- package/dist/{PreparedInputAdmission-CLvaruRe.d.mts → PreparedInputAdmission-BfcLy_k4.d.mts} +3 -3
- package/dist/{Subscription-_VTIVN8o.d.mts → Subscription-ClkC4_T0.d.mts} +2 -2
- package/dist/{SubscriptionInput-BIH1f3Th.d.mts → SubscriptionInput-CMjEKL1z.d.mts} +2 -2
- package/dist/durable/Admin.d.mts +1 -1
- package/dist/durable/AgentRegistration.d.mts +1 -1
- package/dist/durable/Certification.d.mts +1 -1
- package/dist/durable/CommittedActivity.d.mts +1 -1
- package/dist/durable/DurableAgentRuntime.d.mts +1 -1
- package/dist/durable/DurableAgentRuntime.mjs +1 -1
- package/dist/durable/EventSource.d.mts +1 -1
- package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
- package/dist/durable/LifecyclePublication.d.mts +2 -0
- package/dist/durable/LifecyclePublication.mjs +169 -0
- package/dist/durable/LifecyclePublication.mjs.map +1 -0
- package/dist/durable/MessageDelivery.d.mts +4 -3
- package/dist/durable/MessageDelivery.mjs.map +1 -1
- package/dist/durable/MessageDeliveryStoreConformance.d.mts +1 -1
- package/dist/durable/PersistentHistory.d.mts +1 -1
- package/dist/durable/PreparedInputAdmission.d.mts +1 -1
- package/dist/durable/Recovery.d.mts +1 -1
- package/dist/durable/RunJournal.d.mts +1 -1
- package/dist/durable/Scheduling.d.mts +1 -1
- package/dist/durable/SubmissionLedger.d.mts +1 -1
- package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
- package/dist/durable/SubmissionStatus.d.mts +1 -1
- package/dist/durable/Subscription.d.mts +1 -1
- package/dist/durable/SubscriptionInput.d.mts +1 -1
- package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
- package/dist/durable/SubscriptionTools.d.mts +2 -2
- package/dist/durable/SubscriptionTransition.d.mts +1 -1
- package/dist/durable/Subscriptions.d.mts +5 -5
- package/dist/durable/ThreadContextHistory.d.mts +1 -1
- package/dist/durable/ThreadInvariants.d.mts +1 -1
- package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
- package/dist/durable/ThreadStore.d.mts +1 -1
- package/dist/durable/ThreadStore.mjs.map +1 -1
- package/dist/durable/ThreadStoreConformance.d.mts +1 -1
- package/dist/durable/WorkerHost.d.mts +1 -1
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +3 -2
- package/package.json +1 -1
- package/src/durable/DurableAgentRuntime.ts +41 -1
- package/src/durable/LifecyclePublication.ts +244 -0
- package/src/durable/MessageDelivery.ts +2 -0
- package/src/durable/ThreadStore.ts +2 -0
- package/src/index.ts +1 -0
- package/dist/DurableAgentRuntime-DQXDcPKQ.mjs.map +0 -1
|
@@ -1265,6 +1265,21 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
1265
1265
|
const ledger = yield* SubmissionLedger;
|
|
1266
1266
|
const submissionScheduling = yield* SubmissionScheduling;
|
|
1267
1267
|
const store = yield* ThreadStore;
|
|
1268
|
+
|
|
1269
|
+
const publicationPending = (threadId: ThreadId) =>
|
|
1270
|
+
store.lifecyclePublications === undefined
|
|
1271
|
+
? Effect.succeed(false)
|
|
1272
|
+
: store.lifecyclePublications.pendingDeadlineFor(threadId).pipe(
|
|
1273
|
+
Effect.map(Option.isSome),
|
|
1274
|
+
Effect.mapError((cause) =>
|
|
1275
|
+
ThreadStoreError.make({
|
|
1276
|
+
operation: "read lifecycle publication gate",
|
|
1277
|
+
message: "Native lifecycle publication state is unavailable",
|
|
1278
|
+
cause,
|
|
1279
|
+
}),
|
|
1280
|
+
),
|
|
1281
|
+
);
|
|
1282
|
+
|
|
1268
1283
|
const wake = yield* WakeScheduler;
|
|
1269
1284
|
const failpoint = yield* DurableRuntimeFailpoint;
|
|
1270
1285
|
const config = yield* DurableRuntimeConfig;
|
|
@@ -4609,6 +4624,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
4609
4624
|
) =>
|
|
4610
4625
|
Effect.gen(function* () {
|
|
4611
4626
|
const submissionId = submission.submissionId;
|
|
4627
|
+
|
|
4628
|
+
if (yield* publicationPending(submission.threadId)) return { _tag: "yielded" as const };
|
|
4612
4629
|
const runId = runIdForSubmission(submissionId);
|
|
4613
4630
|
const boundaries: Array<JournalBoundary> = [];
|
|
4614
4631
|
|
|
@@ -6234,6 +6251,19 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
6234
6251
|
return yield* renderInputPrompt(inputPrompt, decodedInput, encodedInput);
|
|
6235
6252
|
});
|
|
6236
6253
|
|
|
6254
|
+
const yieldForPublication = Effect.gen(function* () {
|
|
6255
|
+
const pending = yield* Effect.exit(publicationPending(submission.threadId));
|
|
6256
|
+
|
|
6257
|
+
if (Exit.isSuccess(pending) && !pending.value) return;
|
|
6258
|
+
// A completed plain-text response may not yet have reached beforeTurn. Retain it
|
|
6259
|
+
// before either a publication wait or its failed state read closes the Attempt.
|
|
6260
|
+
yield* recordHalt(commitPendingTurn);
|
|
6261
|
+
if (Exit.isFailure(pending)) return yield* recordHalt(Effect.failCause(pending.cause));
|
|
6262
|
+
yield* Deferred.succeed(yieldSignal, undefined);
|
|
6263
|
+
|
|
6264
|
+
return yield* Effect.never;
|
|
6265
|
+
});
|
|
6266
|
+
|
|
6237
6267
|
const input: RunInputHook<
|
|
6238
6268
|
CoordinatorHalt | Agent.Failure<typeof agent>,
|
|
6239
6269
|
Agent.DefinitionRequirements<(typeof agent)["definition"]>
|
|
@@ -6252,6 +6282,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
6252
6282
|
)
|
|
6253
6283
|
yield* recordHalt(commitPendingTurn);
|
|
6254
6284
|
|
|
6285
|
+
yield* yieldForPublication;
|
|
6286
|
+
|
|
6255
6287
|
const joinedInputs = yield* recordHalt(
|
|
6256
6288
|
Effect.gen(function* () {
|
|
6257
6289
|
const limit = policy === "one" ? 1 : MAX_JOIN_DRAIN;
|
|
@@ -6390,6 +6422,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
6390
6422
|
}),
|
|
6391
6423
|
);
|
|
6392
6424
|
|
|
6425
|
+
// Appending a canonical joined input may retain a new publication. Its native
|
|
6426
|
+
// receipt remains successful; recovery reattaches it after acknowledgement.
|
|
6427
|
+
yield* yieldForPublication;
|
|
6428
|
+
|
|
6393
6429
|
return yield* Effect.forEach(joinedInputs, (joinedInput) =>
|
|
6394
6430
|
renderJoinedInput(joinedInput).pipe(
|
|
6395
6431
|
Effect.map((input): RunInputCommand => ({
|
|
@@ -7072,6 +7108,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
7072
7108
|
// Preparation may resolve the next Model from canonical Tool results or invoke
|
|
7073
7109
|
// a compaction Model. Publish the completed Turn before either can observe it.
|
|
7074
7110
|
yield* recordHalt(commitPendingTurn);
|
|
7111
|
+
yield* yieldForPublication;
|
|
7075
7112
|
|
|
7076
7113
|
if (
|
|
7077
7114
|
yieldAfter !== undefined &&
|
|
@@ -7655,7 +7692,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
7655
7692
|
const execution = Effect.raceFirst(consume, Effect.raceFirst(abortWatcher, renewal));
|
|
7656
7693
|
|
|
7657
7694
|
const raced = (
|
|
7658
|
-
yieldAfter === undefined &&
|
|
7695
|
+
yieldAfter === undefined &&
|
|
7696
|
+
submissionScheduling.yieldTo === undefined &&
|
|
7697
|
+
store.lifecyclePublications === undefined
|
|
7659
7698
|
? execution
|
|
7660
7699
|
: Effect.raceFirst(
|
|
7661
7700
|
execution,
|
|
@@ -8533,6 +8572,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
8533
8572
|
|
|
8534
8573
|
const attempt = Effect.scoped(
|
|
8535
8574
|
Effect.gen(function* () {
|
|
8575
|
+
if (yield* publicationPending(threadId)) return Option.none();
|
|
8536
8576
|
const claimed = yield* acquireClaim(threadId, handoff);
|
|
8537
8577
|
|
|
8538
8578
|
if (Option.isNone(claimed)) return Option.none();
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { Clock, Context, Crypto, Effect, Layer, Option, Schema, type Scope } from "effect";
|
|
2
|
+
|
|
3
|
+
import { SubmissionId, ThreadId } from "../core/Identifiers.ts";
|
|
4
|
+
import { Receipt } from "../core/Receipt.ts";
|
|
5
|
+
import { AssignmentTerminal } from "../core/Worker.ts";
|
|
6
|
+
import { MessageDeliveryKey } from "./MessageDelivery.ts";
|
|
7
|
+
import {
|
|
8
|
+
AbortRequested,
|
|
9
|
+
AgentUpdateEmitted,
|
|
10
|
+
CanonicalSequence,
|
|
11
|
+
RunStartedRecord,
|
|
12
|
+
SubagentJoined,
|
|
13
|
+
SubagentRequested,
|
|
14
|
+
SubagentStarted,
|
|
15
|
+
SubmissionSettledRecord,
|
|
16
|
+
ToolApprovalDecided,
|
|
17
|
+
ToolApprovalRequested,
|
|
18
|
+
UserInputRecorded,
|
|
19
|
+
WorkerInputCompleted,
|
|
20
|
+
WorkerInputRequested,
|
|
21
|
+
WorkerStopRequested,
|
|
22
|
+
} from "./Records.ts";
|
|
23
|
+
import {
|
|
24
|
+
AbortIntent,
|
|
25
|
+
Settlement,
|
|
26
|
+
SubmissionLedger,
|
|
27
|
+
SubmissionLookupById,
|
|
28
|
+
SubmissionSnapshot,
|
|
29
|
+
SuspensionReason,
|
|
30
|
+
} from "./SubmissionLedger.ts";
|
|
31
|
+
import { PreparedInput } from "./Subscription.ts";
|
|
32
|
+
import { getRunInput } from "./ThreadStore.ts";
|
|
33
|
+
|
|
34
|
+
/** Immutable native admission evidence. It is private and grants no application authority. */
|
|
35
|
+
export const LifecyclePublicationSource = SubmissionSnapshot.mapFields((fields) => ({
|
|
36
|
+
submissionId: fields.submissionId,
|
|
37
|
+
threadId: fields.threadId,
|
|
38
|
+
queueSequence: fields.queueSequence,
|
|
39
|
+
principal: fields.principal,
|
|
40
|
+
idempotencyKey: fields.idempotencyKey,
|
|
41
|
+
agentId: fields.agentId,
|
|
42
|
+
agentDigests: fields.agentDigests,
|
|
43
|
+
inputPayload: fields.inputPayload,
|
|
44
|
+
inputDigest: fields.inputDigest,
|
|
45
|
+
receiptId: fields.receiptId,
|
|
46
|
+
parentLinkage: fields.parentLinkage,
|
|
47
|
+
workerAdmission: fields.workerAdmission,
|
|
48
|
+
messageAdmission: fields.messageAdmission,
|
|
49
|
+
createdAt: fields.createdAt,
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
export type LifecyclePublicationSource = typeof LifecyclePublicationSource.Type;
|
|
53
|
+
|
|
54
|
+
/** Closed producer facts; model history, Tool payloads and diagnostics are not a publication API. */
|
|
55
|
+
export const LifecyclePublicationFact = Schema.Union([
|
|
56
|
+
Schema.TaggedStruct("DeliveryRetained", {
|
|
57
|
+
key: MessageDeliveryKey,
|
|
58
|
+
envelope: PreparedInput,
|
|
59
|
+
createdAtMillis: Schema.Natural,
|
|
60
|
+
}),
|
|
61
|
+
Schema.TaggedStruct("DeliveryChanged", {
|
|
62
|
+
key: MessageDeliveryKey,
|
|
63
|
+
envelope: PreparedInput,
|
|
64
|
+
status: Schema.Literals(["pending", "accepted", "processed", "refused", "parked"]),
|
|
65
|
+
receipt: Schema.NullOr(Receipt),
|
|
66
|
+
settlement: Schema.NullOr(Settlement),
|
|
67
|
+
version: Schema.Natural,
|
|
68
|
+
}),
|
|
69
|
+
Schema.TaggedStruct("WorkerInboxSealed", {
|
|
70
|
+
threadId: ThreadId,
|
|
71
|
+
activeSubmissionIds: Schema.Array(SubmissionId),
|
|
72
|
+
/** First native seal winner; null is an explicit stop rather than assignment completion. */
|
|
73
|
+
terminal: Schema.NullOr(AssignmentTerminal),
|
|
74
|
+
}),
|
|
75
|
+
Schema.TaggedStruct("SubmissionReady", { submissionId: SubmissionId }),
|
|
76
|
+
Schema.TaggedStruct("SubmissionSuspended", {
|
|
77
|
+
submissionId: SubmissionId,
|
|
78
|
+
reason: SuspensionReason,
|
|
79
|
+
}),
|
|
80
|
+
Schema.TaggedStruct("SubmissionUnknown", { submissionId: SubmissionId }),
|
|
81
|
+
Schema.TaggedStruct("SubmissionResumed", { submissionId: SubmissionId }),
|
|
82
|
+
Schema.TaggedStruct("AbortIntentRecorded", { intent: AbortIntent }),
|
|
83
|
+
UserInputRecorded,
|
|
84
|
+
RunStartedRecord,
|
|
85
|
+
AgentUpdateEmitted,
|
|
86
|
+
ToolApprovalRequested,
|
|
87
|
+
ToolApprovalDecided,
|
|
88
|
+
AbortRequested,
|
|
89
|
+
WorkerInputCompleted,
|
|
90
|
+
WorkerInputRequested,
|
|
91
|
+
WorkerStopRequested,
|
|
92
|
+
SubagentRequested,
|
|
93
|
+
SubagentStarted,
|
|
94
|
+
SubagentJoined,
|
|
95
|
+
SubmissionSettledRecord,
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
export type LifecyclePublicationFact = typeof LifecyclePublicationFact.Type;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* An exact undelivered native fact. `id` is stable across retries and lost acknowledgements.
|
|
102
|
+
* Ordinals order facts only within ownerThreadId. Accepted input order is source.queueSequence;
|
|
103
|
+
* never compare ordinals from the launching Thread and its worker Thread.
|
|
104
|
+
*/
|
|
105
|
+
export class LifecyclePublication extends Schema.Class<LifecyclePublication>(
|
|
106
|
+
"@effect-agent/thread/LifecyclePublication",
|
|
107
|
+
)({
|
|
108
|
+
id: Schema.NonEmptyString.check(Schema.isMaxLength(4096)),
|
|
109
|
+
ownerThreadId: ThreadId,
|
|
110
|
+
ordinal: Schema.Int.check(Schema.isGreaterThan(0)),
|
|
111
|
+
createdAt: Schema.DateTimeUtcFromString,
|
|
112
|
+
canonicalSequence: Schema.optionalKey(CanonicalSequence),
|
|
113
|
+
source: Schema.optionalKey(LifecyclePublicationSource),
|
|
114
|
+
fact: LifecyclePublicationFact,
|
|
115
|
+
}) {}
|
|
116
|
+
|
|
117
|
+
export class LifecyclePublicationError extends Schema.TaggedError<LifecyclePublicationError>()(
|
|
118
|
+
"LifecyclePublicationError",
|
|
119
|
+
{
|
|
120
|
+
reason: Schema.Literals(["unavailable", "conflict", "corrupt", "capacity"]),
|
|
121
|
+
cause: Schema.optionalKey(Schema.Defect()),
|
|
122
|
+
},
|
|
123
|
+
) {}
|
|
124
|
+
|
|
125
|
+
/** Native recovery state, not a second application record or launch queue. */
|
|
126
|
+
export interface LifecyclePublicationStorage {
|
|
127
|
+
readonly pending: (
|
|
128
|
+
nowMillis: number,
|
|
129
|
+
limit: number,
|
|
130
|
+
) => Effect.Effect<ReadonlyArray<LifecyclePublication>, LifecyclePublicationError>;
|
|
131
|
+
readonly acknowledge: (
|
|
132
|
+
publication: LifecyclePublication,
|
|
133
|
+
) => Effect.Effect<void, LifecyclePublicationError>;
|
|
134
|
+
readonly defer: (
|
|
135
|
+
publication: LifecyclePublication,
|
|
136
|
+
untilMillis: number,
|
|
137
|
+
) => Effect.Effect<void, LifecyclePublicationError>;
|
|
138
|
+
readonly pendingDeadline: Effect.Effect<Option.Option<number>, LifecyclePublicationError>;
|
|
139
|
+
/** Earliest retained owner obligation, including deferred facts that still gate execution. */
|
|
140
|
+
readonly pendingDeadlineFor: (
|
|
141
|
+
ownerThreadId: ThreadId,
|
|
142
|
+
) => Effect.Effect<Option.Option<number>, LifecyclePublicationError>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Publish an idempotent domain command, including its authorization, exact receipt and delivery
|
|
147
|
+
* intent. Return only after that command commits. Treat private input/update/result fields as
|
|
148
|
+
* private; the application must select its declared public fields. Revocation/deletion is an
|
|
149
|
+
* acknowledged domain decision, not an indefinitely retryable infrastructure failure.
|
|
150
|
+
*/
|
|
151
|
+
export class LifecyclePublicationHandler extends Context.Service<
|
|
152
|
+
LifecyclePublicationHandler,
|
|
153
|
+
{
|
|
154
|
+
readonly publish: (
|
|
155
|
+
publication: LifecyclePublication,
|
|
156
|
+
) => Effect.Effect<void, LifecyclePublicationError, Scope.Scope>;
|
|
157
|
+
}
|
|
158
|
+
>()("@effect-agent/thread/LifecyclePublicationHandler") {}
|
|
159
|
+
|
|
160
|
+
/** Adapter configuration; the opt-in layer captures its explicit Crypto requirement once. */
|
|
161
|
+
export const LifecyclePublicationConfig = Context.Reference<Option.Option<Crypto.Crypto>>(
|
|
162
|
+
"@effect-agent/thread/LifecyclePublicationConfig",
|
|
163
|
+
{ defaultValue: () => Option.none() },
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
/** Enable native SQL obligations without changing dependencies of disabled storage assemblies. */
|
|
167
|
+
export const lifecyclePublicationLayer = Layer.effect(LifecyclePublicationConfig)(
|
|
168
|
+
Effect.map(Crypto.Crypto, Option.some),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
/** Resolve immutable evidence by its exact native identity, never by scanning execution history. */
|
|
172
|
+
const withSource = Effect.fn("LifecyclePublication.withSource")(
|
|
173
|
+
function* (publication: LifecyclePublication) {
|
|
174
|
+
if (
|
|
175
|
+
publication.source !== undefined ||
|
|
176
|
+
(publication.fact._tag === "WorkerInputCompleted" &&
|
|
177
|
+
publication.fact.workerThreadId !== publication.ownerThreadId)
|
|
178
|
+
)
|
|
179
|
+
return publication;
|
|
180
|
+
const fact = publication.fact;
|
|
181
|
+
|
|
182
|
+
let submissionId =
|
|
183
|
+
fact._tag === "AbortIntentRecorded"
|
|
184
|
+
? fact.intent.submissionId
|
|
185
|
+
: "submissionId" in fact
|
|
186
|
+
? fact.submissionId
|
|
187
|
+
: undefined;
|
|
188
|
+
|
|
189
|
+
const runId =
|
|
190
|
+
fact._tag === "AgentUpdateEmitted"
|
|
191
|
+
? fact.update.runId
|
|
192
|
+
: "runId" in fact
|
|
193
|
+
? fact.runId
|
|
194
|
+
: undefined;
|
|
195
|
+
|
|
196
|
+
if (submissionId === undefined && runId !== undefined) {
|
|
197
|
+
const input = yield* getRunInput({ threadId: publication.ownerThreadId, runId });
|
|
198
|
+
|
|
199
|
+
if (Option.isNone(input) || input.value.record.payload._tag !== "UserInputRecorded")
|
|
200
|
+
return yield* LifecyclePublicationError.make({ reason: "unavailable" });
|
|
201
|
+
submissionId = input.value.record.payload.submissionId;
|
|
202
|
+
}
|
|
203
|
+
if (submissionId === undefined) return publication;
|
|
204
|
+
const ledger = yield* SubmissionLedger;
|
|
205
|
+
const found = yield* ledger.lookup(SubmissionLookupById.make({ submissionId }));
|
|
206
|
+
|
|
207
|
+
if (Option.isNone(found))
|
|
208
|
+
return yield* LifecyclePublicationError.make({ reason: "unavailable" });
|
|
209
|
+
|
|
210
|
+
const source = yield* Schema.decodeEffect(Schema.toType(LifecyclePublicationSource))(
|
|
211
|
+
found.value,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
return LifecyclePublication.make({ ...publication, source });
|
|
215
|
+
},
|
|
216
|
+
Effect.mapError((cause) => LifecyclePublicationError.make({ reason: "unavailable", cause })),
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* One finite wave for the host's existing maintenance coordinator. Persist the next deadline
|
|
221
|
+
* before dispatch, so interruption and process loss retain the same fact without another timer.
|
|
222
|
+
* Acknowledgements are exact and idempotent. No producer or external Tool is re-executed here.
|
|
223
|
+
*/
|
|
224
|
+
export const drainLifecyclePublications = Effect.fn("LifecyclePublication.drain")(function* (
|
|
225
|
+
storage: LifecyclePublicationStorage,
|
|
226
|
+
timeoutMillis = 10_000,
|
|
227
|
+
limit = 4,
|
|
228
|
+
) {
|
|
229
|
+
const handler = yield* LifecyclePublicationHandler;
|
|
230
|
+
const pending = yield* storage.pending(yield* Clock.currentTimeMillis, limit);
|
|
231
|
+
|
|
232
|
+
for (const publication of pending) {
|
|
233
|
+
yield* storage.defer(publication, (yield* Clock.currentTimeMillis) + timeoutMillis);
|
|
234
|
+
yield* Effect.scoped(withSource(publication).pipe(Effect.flatMap(handler.publish))).pipe(
|
|
235
|
+
Effect.timeoutOrElse({
|
|
236
|
+
duration: timeoutMillis,
|
|
237
|
+
orElse: () => LifecyclePublicationError.make({ reason: "unavailable" }),
|
|
238
|
+
}),
|
|
239
|
+
);
|
|
240
|
+
yield* storage.acknowledge(publication);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return pending.length;
|
|
244
|
+
});
|
|
@@ -5,6 +5,7 @@ import { AgentId, DelegationId, ThreadId } from "../core/Identifiers.ts";
|
|
|
5
5
|
import { Receipt } from "../core/Receipt.ts";
|
|
6
6
|
import { WorkerUpdate } from "../core/Worker.ts";
|
|
7
7
|
import { digestJson } from "./Digest.ts";
|
|
8
|
+
import type { LifecyclePublicationStorage } from "./LifecyclePublication.ts";
|
|
8
9
|
import { admitPreparedInput, PreparedInputAdmission } from "./PreparedInputAdmission.ts";
|
|
9
10
|
import { Digest } from "./Records.ts";
|
|
10
11
|
import { ScheduleInstant, ScheduleRetry, ScheduleRetryReason } from "./Schedule.ts";
|
|
@@ -222,6 +223,7 @@ export type MessageDeliveryPage = typeof MessageDeliveryPage.Type;
|
|
|
222
223
|
export class MessageDeliveryStore extends Context.Service<
|
|
223
224
|
MessageDeliveryStore,
|
|
224
225
|
{
|
|
226
|
+
readonly lifecyclePublications?: LifecyclePublicationStorage;
|
|
225
227
|
readonly limits: MessageDeliveryStoreLimits;
|
|
226
228
|
readonly maxStoredValueBytes: number;
|
|
227
229
|
readonly insert: (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Context, Effect, Option, Schema, Stream } from "effect";
|
|
2
2
|
|
|
3
3
|
import { RunId, SubmissionId, ThreadId } from "../core/Identifiers.ts";
|
|
4
|
+
import type { LifecyclePublicationStorage } from "./LifecyclePublication.ts";
|
|
4
5
|
import type { ToolCallPrepared, WorkerInputRequested } from "./Records.ts";
|
|
5
6
|
import {
|
|
6
7
|
BatchId,
|
|
@@ -598,6 +599,7 @@ export interface ThreadRecoveryCheckpoints {
|
|
|
598
599
|
export class ThreadStore extends Context.Service<
|
|
599
600
|
ThreadStore,
|
|
600
601
|
{
|
|
602
|
+
readonly lifecyclePublications?: LifecyclePublicationStorage;
|
|
601
603
|
readonly materialize: (
|
|
602
604
|
request: ThreadMaterialization,
|
|
603
605
|
) => Effect.Effect<void, ThreadStoreError | FenceRejected>;
|
package/src/index.ts
CHANGED
|
@@ -95,3 +95,4 @@ export * as WakeScheduler from "./durable/WakeScheduler.ts";
|
|
|
95
95
|
export * as WorkerHost from "./durable/WorkerHost.ts";
|
|
96
96
|
export * as ThreadContextHistory from "./durable/ThreadContextHistory.ts";
|
|
97
97
|
export * as ThreadContextHistoryProjection from "./durable/ThreadContextHistoryProjection.ts";
|
|
98
|
+
export * as LifecyclePublication from "./durable/LifecyclePublication.ts";
|