effect-agent 0.1.0-beta.113 → 0.1.0-beta.115
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/Agent-1DMSO_Fl.mjs.map +1 -1
- package/dist/{Agent-DriBcXBN.d.mts → Agent-D9cXqfF3.d.mts} +9 -2
- package/dist/{AgentRuntime-DLwPoEVH.mjs → AgentRuntime-Cp1hAb-W.mjs} +8 -4
- package/dist/AgentRuntime-Cp1hAb-W.mjs.map +1 -0
- package/dist/{AgentRuntime-CWkHuzyt.d.mts → AgentRuntime-DyZv8uu2.d.mts} +6 -6
- package/dist/{DurableAgentRuntime-BIrgxwRl.mjs → DurableAgentRuntime-tNrkJZNo.mjs} +254 -162
- package/dist/DurableAgentRuntime-tNrkJZNo.mjs.map +1 -0
- package/dist/{DurableFailpoint-mx3-nsxU.d.mts → DurableFailpoint-Cnr3wx1L.d.mts} +3 -3
- package/dist/{EventSource-ARs56eBq.d.mts → EventSource-DPRdlvhV.d.mts} +3 -3
- package/dist/{Messaging-DaZDlBqh.d.mts → Messaging-JYYN3oYS.d.mts} +3 -3
- package/dist/{MessagingHost-e1Sbzfao.d.mts → MessagingHost-CqzpxbGz.d.mts} +3 -3
- package/dist/{MessagingHost-D89X7ma8.mjs → MessagingHost-DfsbMPvq.mjs} +2 -2
- package/dist/{MessagingHost-D89X7ma8.mjs.map → MessagingHost-DfsbMPvq.mjs.map} +1 -1
- package/dist/{Output-CAFJTAbP.d.mts → Output-V-6YL1_Z.d.mts} +2 -2
- package/dist/{PreparedInputAdmission-_WYg-D0f.d.mts → PreparedInputAdmission-DqQUjA9k.d.mts} +4 -4
- package/dist/{Records-BtxMy8xD.d.mts → Records-Byeb70tt.d.mts} +89 -31
- package/dist/{RunOptions-BoESJrxw.d.mts → RunOptions-CSBsSj0X.d.mts} +3 -3
- package/dist/{Schedule-DGPyXBzK.d.mts → Schedule-krSnBcbU.d.mts} +2 -2
- package/dist/{Subagent-D9eYdGTl.mjs → Subagent-Di8EEjWZ.mjs} +67 -105
- package/dist/Subagent-Di8EEjWZ.mjs.map +1 -0
- package/dist/{Subagent-F72tuaxy.d.mts → Subagent-gw9KD6eb.d.mts} +310 -35
- package/dist/{SubagentHost-CmiNTOBZ.d.mts → SubagentHost-P9zB_TfV.d.mts} +24 -18
- package/dist/{Subscription-B9VszlF9.d.mts → Subscription-Xnwd42Sm.d.mts} +6 -3
- package/dist/{SubscriptionInput-BJWZqCWP.d.mts → SubscriptionInput-CqtJ58vl.d.mts} +3 -3
- package/dist/{Worker-T_3RbaKc.mjs → Worker-CfwzMZEK.mjs} +66 -5
- package/dist/Worker-CfwzMZEK.mjs.map +1 -0
- package/dist/{Worker-Du9dhVnp.d.mts → Worker-fwWkBg12.d.mts} +199 -6
- package/dist/{agent-registration-B5CI-Ygk.mjs → agent-registration-CKNsrpwo.mjs} +7 -7
- package/dist/agent-registration-CKNsrpwo.mjs.map +1 -0
- package/dist/capabilities/Commands.d.mts +1 -1
- package/dist/capabilities/Messaging.d.mts +1 -1
- package/dist/capabilities/Messaging.mjs +2 -2
- package/dist/capabilities/RunHooks.d.mts +1 -1
- package/dist/capabilities/Subagent.d.mts +3 -3
- package/dist/capabilities/Subagent.mjs +3 -3
- package/dist/core/Agent.d.mts +1 -1
- package/dist/core/Worker.d.mts +2 -2
- package/dist/core/Worker.mjs +2 -2
- package/dist/durable/ActivityStore.d.mts +1 -1
- package/dist/durable/Admin.d.mts +1 -1
- package/dist/durable/AgentRegistration.d.mts +1 -1
- package/dist/durable/AgentRegistration.mjs +1 -1
- package/dist/durable/Certification.d.mts +2 -2
- package/dist/durable/CommittedActivity.d.mts +1 -1
- package/dist/durable/Digest.d.mts +1 -1
- package/dist/durable/DurableAgentRuntime.d.mts +1 -1
- package/dist/durable/DurableAgentRuntime.mjs +1 -1
- package/dist/durable/DurableFailpoint.d.mts +1 -1
- package/dist/durable/DurableFailpoint.mjs +4 -0
- package/dist/durable/DurableFailpoint.mjs.map +1 -1
- package/dist/durable/DurableFailpointTestControl.d.mts +1 -1
- package/dist/durable/EventSource.d.mts +1 -1
- package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
- package/dist/durable/MessageDelivery.d.mts +20 -3
- package/dist/durable/MessageDelivery.mjs +12 -4
- package/dist/durable/MessageDelivery.mjs.map +1 -1
- package/dist/durable/MessageDeliveryStoreConformance.d.mts +41 -39
- package/dist/durable/MessageDeliveryStoreConformance.mjs +1 -1
- package/dist/durable/PersistentHistory.d.mts +1 -1
- package/dist/durable/PreparedInputAdmission.d.mts +1 -1
- package/dist/durable/Records.d.mts +2 -2
- package/dist/durable/Records.mjs +12 -5
- package/dist/durable/Records.mjs.map +1 -1
- package/dist/durable/Recovery.d.mts +1 -1
- package/dist/durable/RunJournal.d.mts +1 -1
- package/dist/durable/Schedule.d.mts +1 -1
- package/dist/durable/ScheduleStoreConformance.d.mts +1 -1
- package/dist/durable/ScheduleTransition.d.mts +1 -1
- package/dist/durable/Scheduling.d.mts +2 -2
- package/dist/durable/SqlMessageDeliveryStore.mjs +7 -1
- package/dist/durable/SqlMessageDeliveryStore.mjs.map +1 -1
- package/dist/durable/SqlStorageV2Upgrade.d.mts +2 -2
- package/dist/durable/SqlSubscriptionStore.d.mts +2 -2
- package/dist/durable/SqlThreadNativeReads.d.mts +5 -1
- package/dist/durable/SqlThreadNativeReads.mjs +6 -0
- package/dist/durable/SqlThreadNativeReads.mjs.map +1 -1
- package/dist/durable/SubmissionLedger.d.mts +2 -2
- package/dist/durable/SubmissionLedger.mjs +34 -4
- package/dist/durable/SubmissionLedger.mjs.map +1 -1
- package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
- package/dist/durable/SubmissionLedgerConformance.mjs +899 -706
- package/dist/durable/SubmissionLedgerConformance.mjs.map +1 -1
- package/dist/durable/SubmissionStatus.d.mts +1 -1
- package/dist/durable/Subscription.d.mts +1 -1
- package/dist/durable/Subscription.mjs +1 -1
- package/dist/durable/SubscriptionInput.d.mts +1 -1
- package/dist/durable/SubscriptionInput.mjs +1 -1
- package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
- package/dist/durable/SubscriptionTools.d.mts +3 -3
- 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/ThreadContextHistoryProjection.d.mts +1 -1
- package/dist/durable/ThreadInvariants.d.mts +1 -1
- package/dist/durable/ThreadProjection.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 +5 -0
- package/dist/durable/ThreadStore.mjs.map +1 -1
- package/dist/durable/ThreadStoreConformance.d.mts +1 -1
- package/dist/durable/WorkerHost.d.mts +3 -3
- package/dist/durable/WorkerHost.mjs +1 -1
- package/dist/engine/AgentRuntime.d.mts +1 -1
- package/dist/engine/AgentRuntime.mjs +1 -1
- package/dist/engine/MessagingHost.d.mts +1 -1
- package/dist/engine/MessagingHost.mjs +1 -1
- package/dist/engine/Output.d.mts +1 -1
- package/dist/engine/RunOptions.d.mts +1 -1
- package/dist/engine/SubagentHost.d.mts +2 -2
- package/dist/engine/SubagentHost.mjs +5 -1
- package/dist/engine/SubagentHost.mjs.map +1 -1
- package/dist/index.d.mts +16 -16
- package/dist/index.mjs +5 -5
- package/package.json +1 -1
- package/src/capabilities/Subagent.ts +2 -1
- package/src/capabilities/internal/subagent-background.ts +86 -57
- package/src/capabilities/internal/subagent-reporting.ts +3 -128
- package/src/core/Agent.ts +8 -1
- package/src/core/Worker.ts +58 -2
- package/src/durable/DurableAgentRuntime.ts +2 -0
- package/src/durable/DurableFailpoint.ts +4 -0
- package/src/durable/MessageDelivery.ts +17 -2
- package/src/durable/Records.ts +12 -2
- package/src/durable/SqlMessageDeliveryStore.ts +15 -1
- package/src/durable/SqlThreadNativeReads.ts +14 -0
- package/src/durable/SubmissionLedger.ts +48 -1
- package/src/durable/SubmissionLedgerConformance.ts +260 -42
- package/src/durable/ThreadStore.ts +5 -0
- package/src/durable/internal/agent-registration.ts +13 -48
- package/src/durable/internal/worker-host.ts +490 -341
- package/src/engine/SubagentHost.ts +37 -13
- package/src/engine/internal/agent-runtime.ts +17 -2
- package/dist/AgentRuntime-DLwPoEVH.mjs.map +0 -1
- package/dist/DurableAgentRuntime-BIrgxwRl.mjs.map +0 -1
- package/dist/Subagent-D9eYdGTl.mjs.map +0 -1
- package/dist/Worker-T_3RbaKc.mjs.map +0 -1
- package/dist/agent-registration-B5CI-Ygk.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Clock, Context, Crypto, Effect, Layer, Result, Schema, Semaphore } from "effect";
|
|
2
2
|
|
|
3
3
|
import * as FailureDiagnostic from "../core/FailureDiagnostic.ts";
|
|
4
|
-
import { ThreadId } from "../core/Identifiers.ts";
|
|
4
|
+
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";
|
|
@@ -181,13 +181,28 @@ export const MessageDeliveryChange = Schema.Union([
|
|
|
181
181
|
|
|
182
182
|
export type MessageDeliveryChange = typeof MessageDeliveryChange.Type;
|
|
183
183
|
|
|
184
|
+
/** Select one native index per page; index filters cannot be combined. */
|
|
184
185
|
export const MessageDeliveryPageRequest = Schema.Struct({
|
|
185
186
|
ownerThreadId: ThreadId,
|
|
186
187
|
limit: Positive.check(Schema.isLessThanOrEqualTo(4096)),
|
|
187
188
|
after: Schema.optionalKey(IdempotencyKey),
|
|
188
189
|
/** Native pending index, including accepted, future-due and parked obligations. */
|
|
189
190
|
pendingOnly: Schema.optionalKey(Schema.Boolean),
|
|
190
|
-
|
|
191
|
+
/** First retained envelopes, including starts not admitted at the destination. */
|
|
192
|
+
workerStarts: Schema.optionalKey(
|
|
193
|
+
Schema.Struct({ delegationId: DelegationId, targetAgentId: AgentId }),
|
|
194
|
+
),
|
|
195
|
+
/** Retained inputs not yet acknowledged by one destination worker. */
|
|
196
|
+
pendingWorker: Schema.optionalKey(ThreadId),
|
|
197
|
+
}).check(
|
|
198
|
+
Schema.makeFilter(
|
|
199
|
+
(request) =>
|
|
200
|
+
Number(request.pendingOnly === true) +
|
|
201
|
+
Number(request.workerStarts !== undefined) +
|
|
202
|
+
Number(request.pendingWorker !== undefined) <=
|
|
203
|
+
1,
|
|
204
|
+
),
|
|
205
|
+
);
|
|
191
206
|
|
|
192
207
|
export type MessageDeliveryPageRequest = typeof MessageDeliveryPageRequest.Type;
|
|
193
208
|
|
package/src/durable/Records.ts
CHANGED
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
import { Selection, Snapshot } from "../core/ToolExposure.ts";
|
|
33
33
|
import { ToolParameterRejection } from "../core/ToolResult.ts";
|
|
34
34
|
import { ModelCallUsage, RunUsageSummary, RunTotals } from "../core/Usage.ts";
|
|
35
|
-
import { WorkerBudgetScope, WorkerRef, WorkerSource } from "../core/Worker.ts";
|
|
35
|
+
import { WorkerBudgetScope, WorkerRef, WorkerSource, WorkerStop } from "../core/Worker.ts";
|
|
36
36
|
import { ContextHandoff } from "../engine/ContextWindow.ts";
|
|
37
37
|
|
|
38
38
|
const identifier = <const Name extends string>(name: Name) =>
|
|
@@ -805,9 +805,10 @@ export class SubagentLineageRecorded extends Schema.TaggedClass<SubagentLineageR
|
|
|
805
805
|
}) {}
|
|
806
806
|
|
|
807
807
|
export const WorkerReportingIntent = Schema.Struct({
|
|
808
|
-
/**
|
|
808
|
+
/** Absent only in historical custom-report origins; these remain readable as evidence. */
|
|
809
809
|
mode: Schema.optionalKey(Schema.Literal("standard")),
|
|
810
810
|
sourceDigests: DefinitionDigests,
|
|
811
|
+
/** Historical custom destination evidence. New worker origins never write this field. */
|
|
811
812
|
destinationDelegationId: Schema.optionalKey(DelegationId),
|
|
812
813
|
/** Frozen by the sender before acceptance. Standard emission never reads the source runtime. */
|
|
813
814
|
returnAddress: Schema.optionalKey(
|
|
@@ -831,6 +832,8 @@ export const WorkerOrigin = Schema.Struct({
|
|
|
831
832
|
source: WorkerSource,
|
|
832
833
|
/** Omitted retains source-subtree funding; worker-run renews only for a new native Run. */
|
|
833
834
|
budgetScope: Schema.optionalKey(WorkerBudgetScope),
|
|
835
|
+
/** Frozen at worker creation; older origins remain reusable. */
|
|
836
|
+
lifecycle: Schema.optionalKey(Schema.Literal("assignment")),
|
|
834
837
|
targetDigests: DefinitionDigests,
|
|
835
838
|
policy: Schema.toCodecJson(AgentPolicy),
|
|
836
839
|
budget: SubagentBudgetReservation,
|
|
@@ -862,6 +865,12 @@ export const WorkerAdmission = Schema.Struct({
|
|
|
862
865
|
|
|
863
866
|
export type WorkerAdmission = typeof WorkerAdmission.Type;
|
|
864
867
|
|
|
868
|
+
/** Owner command retained before the destination inbox is sealed. */
|
|
869
|
+
export class WorkerStopRequested extends Schema.TaggedClass<WorkerStopRequested>()(
|
|
870
|
+
"WorkerStopRequested",
|
|
871
|
+
{ command: WorkerStop },
|
|
872
|
+
) {}
|
|
873
|
+
|
|
865
874
|
/** Source-log capacity reservation, retained independently of the source Run's settlement. */
|
|
866
875
|
export class WorkerInputRequested extends Schema.TaggedClass<WorkerInputRequested>()(
|
|
867
876
|
"WorkerInputRequested",
|
|
@@ -1009,6 +1018,7 @@ export const CanonicalRecordPayload = Schema.Union([
|
|
|
1009
1018
|
SubagentJoined,
|
|
1010
1019
|
SubagentLineageRecorded,
|
|
1011
1020
|
WorkerInputRequested,
|
|
1021
|
+
WorkerStopRequested,
|
|
1012
1022
|
WorkerOriginRecorded,
|
|
1013
1023
|
WorkerInputCompleted,
|
|
1014
1024
|
WorkerReportPrepared,
|
|
@@ -270,7 +270,21 @@ export const makeSqlMessageDeliveryStore = Effect.fn("SqlMessageDeliveryStore.ma
|
|
|
270
270
|
|
|
271
271
|
const rows = yield* query(
|
|
272
272
|
"list",
|
|
273
|
-
sql`SELECT owner_thread_id, message_id, version, state, deadline_at_millis, record_json FROM effect_agent_message_deliveries WHERE owner_thread_id = ${input.ownerThreadId} ${input.after === undefined ? sql`` : sql`AND message_id > ${input.after}`} ${input.pendingOnly ? sql`AND state NOT IN ('processed', 'refused')` : sql``}
|
|
273
|
+
sql`SELECT owner_thread_id, message_id, version, state, deadline_at_millis, record_json FROM effect_agent_message_deliveries WHERE owner_thread_id = ${input.ownerThreadId} ${input.after === undefined ? sql`` : sql`AND message_id > ${input.after}`} ${input.pendingOnly ? sql`AND state NOT IN ('processed', 'refused')` : sql``}
|
|
274
|
+
${
|
|
275
|
+
input.workerStarts === undefined
|
|
276
|
+
? sql``
|
|
277
|
+
: sql`AND json_extract(record_json, '$.envelope.workerAdmission.origin.worker.delegationId') = ${input.workerStarts.delegationId}
|
|
278
|
+
AND json_extract(record_json, '$.envelope.workerAdmission.origin.worker.targetAgentId') = ${input.workerStarts.targetAgentId}
|
|
279
|
+
AND message_id = json_extract(record_json, '$.envelope.workerAdmission.origin.firstMessageId')`
|
|
280
|
+
}
|
|
281
|
+
${
|
|
282
|
+
input.pendingWorker === undefined
|
|
283
|
+
? sql``
|
|
284
|
+
: sql`AND json_extract(record_json, '$.envelope.workerAdmission.origin.worker.threadId') = ${input.pendingWorker}
|
|
285
|
+
AND state IN ('pending', 'parked') AND json_extract(record_json, '$.receipt') IS NULL`
|
|
286
|
+
}
|
|
287
|
+
ORDER BY message_id LIMIT ${input.limit + 1}`,
|
|
274
288
|
);
|
|
275
289
|
|
|
276
290
|
const records = yield* decodeRows(rows);
|
|
@@ -178,6 +178,20 @@ export const makeSelectedReads = Effect.fnUntraced(function* (
|
|
|
178
178
|
rows =
|
|
179
179
|
yield* sql`SELECT thread_id, sequence, record_id, batch_id, record_json, outstanding FROM effect_agent_canonical_records WHERE thread_id = ${request.threadId} AND outstanding <> 0 AND sequence > ${after} ORDER BY sequence LIMIT ${request.page.limit}`;
|
|
180
180
|
break;
|
|
181
|
+
case "WorkerExecution":
|
|
182
|
+
rows = (yield* Effect.forEach(
|
|
183
|
+
["UserInputRecorded", "RunStarted"],
|
|
184
|
+
(tag) =>
|
|
185
|
+
sql`SELECT thread_id, sequence, record_id, batch_id, record_json FROM effect_agent_canonical_records
|
|
186
|
+
WHERE thread_id = ${request.threadId} AND json_extract(record_json, '$.payload._tag') = ${tag}
|
|
187
|
+
AND json_extract(record_json, '$.payload.runId') IS NOT NULL
|
|
188
|
+
ORDER BY sequence DESC LIMIT 1`,
|
|
189
|
+
))
|
|
190
|
+
.flat()
|
|
191
|
+
.filter((row) => Number(row.sequence) > after)
|
|
192
|
+
.sort((a, b) => Number(a.sequence) - Number(b.sequence))
|
|
193
|
+
.slice(0, request.page.limit);
|
|
194
|
+
break;
|
|
181
195
|
case "WorkerState": {
|
|
182
196
|
const runId =
|
|
183
197
|
selection.sourceSubmissionId === undefined
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import type { JoinedToHost } from "../core/Receipt.ts";
|
|
16
16
|
import { IdempotencyKey, QueueSequence, Principal } from "../core/Receipt.ts";
|
|
17
17
|
import { RunUsageSummary } from "../core/Usage.ts";
|
|
18
|
+
import { AssignmentTerminal } from "../core/Worker.ts";
|
|
18
19
|
import {
|
|
19
20
|
AbortRequested,
|
|
20
21
|
ApprovalDecision,
|
|
@@ -241,6 +242,16 @@ export class SubmissionSnapshot extends Schema.Class<SubmissionSnapshot>(
|
|
|
241
242
|
messageAdmission: Schema.optionalKey(InputMessage),
|
|
242
243
|
}) {}
|
|
243
244
|
|
|
245
|
+
/** Bounded native control facts. Canonical input/Run records remain ThreadStore-owned. */
|
|
246
|
+
export const WorkerLedgerState = Schema.Struct({
|
|
247
|
+
latest: Schema.NullOr(SubmissionSnapshot),
|
|
248
|
+
active: Schema.NullOr(SubmissionSnapshot),
|
|
249
|
+
stopped: Schema.Boolean,
|
|
250
|
+
terminal: Schema.optionalKey(AssignmentTerminal),
|
|
251
|
+
}).check(Schema.makeFilter((state) => state.terminal === undefined || state.stopped));
|
|
252
|
+
|
|
253
|
+
export type WorkerLedgerState = typeof WorkerLedgerState.Type;
|
|
254
|
+
|
|
244
255
|
/** The authoritative store proves the scoped idempotency key was never admitted (SUB-031). */
|
|
245
256
|
export class AdmissionNotAdmitted extends Schema.TaggedClass<AdmissionNotAdmitted>(
|
|
246
257
|
"@effect-agent/thread/AdmissionNotAdmitted",
|
|
@@ -418,6 +429,38 @@ export const settlementFailureFromRecord = (
|
|
|
418
429
|
: undefined;
|
|
419
430
|
};
|
|
420
431
|
|
|
432
|
+
/**
|
|
433
|
+
* Assignment intent derived only from frozen admission and exact settlement evidence.
|
|
434
|
+
* Joined receipts and cancellation before a Run starts do not finish an assignment.
|
|
435
|
+
* Adapters suppress completed intent when newer accepted input remains unapplied, then
|
|
436
|
+
* install the first seal and outstanding abort intents atomically before releasing the lane.
|
|
437
|
+
*/
|
|
438
|
+
export const workerTerminalFromRecord = (
|
|
439
|
+
submission: SubmissionSnapshot,
|
|
440
|
+
record: RecordEnvelope,
|
|
441
|
+
): AssignmentTerminal | undefined => {
|
|
442
|
+
const payload = record.payload;
|
|
443
|
+
|
|
444
|
+
if (
|
|
445
|
+
submission.workerAdmission?.origin.lifecycle !== "assignment" ||
|
|
446
|
+
payload._tag !== "SubmissionSettled" ||
|
|
447
|
+
(payload.runId !== undefined && payload.runId !== `run:${submission.submissionId}`)
|
|
448
|
+
)
|
|
449
|
+
return undefined;
|
|
450
|
+
if (payload.outcome === "failed" || payload.finishReason === "budget-exhausted") return "failed";
|
|
451
|
+
if (payload.outcome === "aborted") return payload.runId === undefined ? undefined : "cancelled";
|
|
452
|
+
|
|
453
|
+
return payload.runDisposition === "completed" ? "completed" : undefined;
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
/** Destination-owned permanent inbox seal; recorded atomically with all outstanding abort intents. */
|
|
457
|
+
export const WorkerStopCommand = Schema.Struct({
|
|
458
|
+
threadId: ThreadId,
|
|
459
|
+
author: Principal,
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
export type WorkerStopCommand = typeof WorkerStopCommand.Type;
|
|
463
|
+
|
|
421
464
|
/**
|
|
422
465
|
* A durable abort command (durability §13). Field bounds are exactly those of the canonical
|
|
423
466
|
* `AbortRequested` payload so the intent can become canonical without re-validation.
|
|
@@ -995,7 +1038,8 @@ export type SubmissionLedgerFailure =
|
|
|
995
1038
|
* recovery settles aborted never-claimed queued work immediately, without waiting for it to
|
|
996
1039
|
* head the lane. Both exceptions are outcome- and state-narrow; everything else stays fenced.
|
|
997
1040
|
* - `finalizeSettlement` — idempotent terminal transition (state → settled) after the reserved
|
|
998
|
-
* record is canonical
|
|
1041
|
+
* record is canonical. Native adapters seal opted-in worker assignments atomically before
|
|
1042
|
+
* releasing the lane. Otherwise the next `queueSequence` becomes claimable. It
|
|
999
1043
|
* requires no ownership token: canonical history authorizes finalization (DUR-015). A
|
|
1000
1044
|
* finalization that disagrees with the recorded outcome fails with `SettlementConflict`.
|
|
1001
1045
|
* - `requestAbort` — durable, idempotent by `submissionId`: repeating returns the recorded
|
|
@@ -1100,6 +1144,9 @@ export class SubmissionLedger extends Context.Service<
|
|
|
1100
1144
|
readonly finalizeSettlement: (
|
|
1101
1145
|
request: SettlementFinalization,
|
|
1102
1146
|
) => Effect.Effect<Settlement, SettlementConflict | LedgerError>;
|
|
1147
|
+
/** Optional for third-party adapters. Remaining ownership must reach zero before stop acknowledgement. */
|
|
1148
|
+
readonly inspectWorker?: (threadId: ThreadId) => Effect.Effect<WorkerLedgerState, LedgerError>;
|
|
1149
|
+
readonly stopWorker?: (request: WorkerStopCommand) => Effect.Effect<number, LedgerError>;
|
|
1103
1150
|
readonly requestAbort: (
|
|
1104
1151
|
request: AbortCommand,
|
|
1105
1152
|
) => Effect.Effect<AbortIntent, SettlementConflict | JoinedToHost | LedgerError>;
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
WorkerAdmission,
|
|
27
27
|
type SettlementFailureDiagnostic,
|
|
28
28
|
} from "./Records.ts";
|
|
29
|
+
import { runIdForSubmission } from "./RunJournal.ts";
|
|
29
30
|
import {
|
|
30
31
|
type AdmissionResult,
|
|
31
32
|
AbortCommand,
|
|
@@ -64,6 +65,7 @@ import {
|
|
|
64
65
|
ResolutionSafeToRetry,
|
|
65
66
|
RevertJoiningRequest,
|
|
66
67
|
SettlementConflict,
|
|
68
|
+
Settlement,
|
|
67
69
|
SettlementFinalization,
|
|
68
70
|
SettlementReservation,
|
|
69
71
|
SubmissionLedger,
|
|
@@ -538,6 +540,50 @@ const admissionIdempotency = conformanceCase(
|
|
|
538
540
|
}),
|
|
539
541
|
);
|
|
540
542
|
|
|
543
|
+
const workerMetadata = (base: AdmissionRequest) =>
|
|
544
|
+
WorkerAdmission.make({
|
|
545
|
+
messageId: base.idempotencyKey,
|
|
546
|
+
sourceSubmissionId: Schema.decodeSync(SubmissionId)("source-input"),
|
|
547
|
+
deliveryPrincipal: base.principal,
|
|
548
|
+
parameters: { prompt: "original projection parameters" },
|
|
549
|
+
createdAtMillis: 1,
|
|
550
|
+
origin: {
|
|
551
|
+
worker: {
|
|
552
|
+
schemaVersion: 1,
|
|
553
|
+
threadId: base.threadId,
|
|
554
|
+
targetAgentId: CONFORMANCE_AGENT,
|
|
555
|
+
delegationId: Schema.decodeSync(DelegationId)("worker-research"),
|
|
556
|
+
},
|
|
557
|
+
source: {
|
|
558
|
+
_tag: "programmatic",
|
|
559
|
+
threadId: decodeThreadId("worker-source"),
|
|
560
|
+
agentId: CONFORMANCE_AGENT,
|
|
561
|
+
},
|
|
562
|
+
targetDigests: CONFORMANCE_DIGESTS,
|
|
563
|
+
policy: AgentPolicy.resolve(),
|
|
564
|
+
budget: {
|
|
565
|
+
caps: SubagentDelegationCaps.make({
|
|
566
|
+
maxConcurrentChildren: 1,
|
|
567
|
+
maxTotalChildInvocations: 2,
|
|
568
|
+
}),
|
|
569
|
+
allocation: SubagentReservationAmounts.make({
|
|
570
|
+
turns: 12,
|
|
571
|
+
toolCalls: 24,
|
|
572
|
+
durationMillis: 300_000,
|
|
573
|
+
inputTokens: 0,
|
|
574
|
+
outputTokens: 0,
|
|
575
|
+
costMicrousd: 0,
|
|
576
|
+
resultBytes: 1_000,
|
|
577
|
+
}),
|
|
578
|
+
},
|
|
579
|
+
grant: SubagentGrant.make({ maxDepth: 1, allowedToolNames: [] }),
|
|
580
|
+
depth: 1,
|
|
581
|
+
firstMessageId: base.idempotencyKey,
|
|
582
|
+
createdAtMillis: 1,
|
|
583
|
+
expiresAtMillis: 1_000_000,
|
|
584
|
+
},
|
|
585
|
+
});
|
|
586
|
+
|
|
541
587
|
const workerAdmissionIdentity = conformanceCase(
|
|
542
588
|
"retains immutable worker admission metadata and rejects same-key changes or omission",
|
|
543
589
|
({ ensure, expectFailure, expectSome }) =>
|
|
@@ -550,48 +596,7 @@ const workerAdmissionIdentity = conformanceCase(
|
|
|
550
596
|
{ text: "first" },
|
|
551
597
|
);
|
|
552
598
|
|
|
553
|
-
const metadata =
|
|
554
|
-
messageId: base.idempotencyKey,
|
|
555
|
-
sourceSubmissionId: Schema.decodeSync(SubmissionId)("source-input"),
|
|
556
|
-
deliveryPrincipal: base.principal,
|
|
557
|
-
parameters: { prompt: "original projection parameters" },
|
|
558
|
-
createdAtMillis: 1,
|
|
559
|
-
origin: {
|
|
560
|
-
worker: {
|
|
561
|
-
schemaVersion: 1,
|
|
562
|
-
threadId: base.threadId,
|
|
563
|
-
targetAgentId: CONFORMANCE_AGENT,
|
|
564
|
-
delegationId: Schema.decodeSync(DelegationId)("worker-research"),
|
|
565
|
-
},
|
|
566
|
-
source: {
|
|
567
|
-
_tag: "programmatic",
|
|
568
|
-
threadId: decodeThreadId("worker-source"),
|
|
569
|
-
agentId: CONFORMANCE_AGENT,
|
|
570
|
-
},
|
|
571
|
-
targetDigests: CONFORMANCE_DIGESTS,
|
|
572
|
-
policy: AgentPolicy.resolve(),
|
|
573
|
-
budget: {
|
|
574
|
-
caps: SubagentDelegationCaps.make({
|
|
575
|
-
maxConcurrentChildren: 1,
|
|
576
|
-
maxTotalChildInvocations: 2,
|
|
577
|
-
}),
|
|
578
|
-
allocation: SubagentReservationAmounts.make({
|
|
579
|
-
turns: 12,
|
|
580
|
-
toolCalls: 24,
|
|
581
|
-
durationMillis: 300_000,
|
|
582
|
-
inputTokens: 0,
|
|
583
|
-
outputTokens: 0,
|
|
584
|
-
costMicrousd: 0,
|
|
585
|
-
resultBytes: 1_000,
|
|
586
|
-
}),
|
|
587
|
-
},
|
|
588
|
-
grant: SubagentGrant.make({ maxDepth: 1, allowedToolNames: [] }),
|
|
589
|
-
depth: 1,
|
|
590
|
-
firstMessageId: base.idempotencyKey,
|
|
591
|
-
createdAtMillis: 1,
|
|
592
|
-
expiresAtMillis: 1_000_000,
|
|
593
|
-
},
|
|
594
|
-
});
|
|
599
|
+
const metadata = workerMetadata(base);
|
|
595
600
|
|
|
596
601
|
const admitted = yield* ledger.admit(
|
|
597
602
|
AdmissionRequest.make({ ...base, workerAdmission: metadata }),
|
|
@@ -4553,6 +4558,218 @@ const abortedSettledRowIsNotAJoiningGap = conformanceCase(
|
|
|
4553
4558
|
}),
|
|
4554
4559
|
);
|
|
4555
4560
|
|
|
4561
|
+
const assignmentSettlement = conformanceCase(
|
|
4562
|
+
"seals terminal assignments atomically while waiting and unapplied corrections remain steerable",
|
|
4563
|
+
({ ensure, expectSome, expectFailure }) =>
|
|
4564
|
+
Effect.gen(function* () {
|
|
4565
|
+
const ledger = yield* SubmissionLedger;
|
|
4566
|
+
|
|
4567
|
+
if (ledger.inspectWorker === undefined) return;
|
|
4568
|
+
|
|
4569
|
+
// Each row isolates one ownership rule. The same cases run on every native adapter.
|
|
4570
|
+
const cases = [
|
|
4571
|
+
{
|
|
4572
|
+
name: "completed",
|
|
4573
|
+
outcome: "completed",
|
|
4574
|
+
disposition: "completed",
|
|
4575
|
+
terminal: "completed",
|
|
4576
|
+
},
|
|
4577
|
+
{ name: "waiting", outcome: "completed", disposition: "waiting", terminal: undefined },
|
|
4578
|
+
{ name: "failure", outcome: "failed", queued: true, terminal: "failed" },
|
|
4579
|
+
{
|
|
4580
|
+
name: "failure-joining",
|
|
4581
|
+
outcome: "failed",
|
|
4582
|
+
queued: true,
|
|
4583
|
+
joining: true,
|
|
4584
|
+
terminal: "failed",
|
|
4585
|
+
},
|
|
4586
|
+
{ name: "exhaustion", outcome: "completed", exhausted: true, terminal: "failed" },
|
|
4587
|
+
{ name: "active-abort", outcome: "aborted", terminal: "cancelled" },
|
|
4588
|
+
{ name: "queued-abort", outcome: "aborted", noRun: true, terminal: undefined },
|
|
4589
|
+
{
|
|
4590
|
+
name: "reusable",
|
|
4591
|
+
outcome: "completed",
|
|
4592
|
+
disposition: "completed",
|
|
4593
|
+
reusable: true,
|
|
4594
|
+
terminal: undefined,
|
|
4595
|
+
},
|
|
4596
|
+
{
|
|
4597
|
+
name: "late-correction",
|
|
4598
|
+
outcome: "completed",
|
|
4599
|
+
disposition: "completed",
|
|
4600
|
+
queued: true,
|
|
4601
|
+
terminal: undefined,
|
|
4602
|
+
},
|
|
4603
|
+
{
|
|
4604
|
+
name: "applied-correction",
|
|
4605
|
+
outcome: "completed",
|
|
4606
|
+
disposition: "completed",
|
|
4607
|
+
queued: true,
|
|
4608
|
+
joined: true,
|
|
4609
|
+
terminal: "completed",
|
|
4610
|
+
},
|
|
4611
|
+
] as const;
|
|
4612
|
+
|
|
4613
|
+
for (const scenario of cases) {
|
|
4614
|
+
const threadId = decodeThreadId(`assignment-${scenario.name}`);
|
|
4615
|
+
const base = yield* admissionRequest(threadId, "first", { text: "first" });
|
|
4616
|
+
const metadata = workerMetadata(base);
|
|
4617
|
+
|
|
4618
|
+
const workerAdmission = WorkerAdmission.make({
|
|
4619
|
+
...metadata,
|
|
4620
|
+
origin: {
|
|
4621
|
+
...metadata.origin,
|
|
4622
|
+
...("reusable" in scenario ? {} : { lifecycle: "assignment" as const }),
|
|
4623
|
+
},
|
|
4624
|
+
});
|
|
4625
|
+
|
|
4626
|
+
const request = AdmissionRequest.make({ ...base, workerAdmission });
|
|
4627
|
+
|
|
4628
|
+
const first = yield* ledger.admit(request);
|
|
4629
|
+
|
|
4630
|
+
yield* ledger.markReady(MarkReadyRequest.make({ submissionId: first.submissionId }));
|
|
4631
|
+
const claim = yield* expectSome("assignment claim", yield* claimLane(threadId, PRODUCER_A));
|
|
4632
|
+
const runId = runIdForSubmission(first.submissionId);
|
|
4633
|
+
|
|
4634
|
+
const payload = yield* Schema.decodeEffect(SubmissionSettledRecord)(
|
|
4635
|
+
SubmissionSettled.make({
|
|
4636
|
+
submissionId: first.submissionId,
|
|
4637
|
+
settlementId: submissionSettlementId(first.submissionId),
|
|
4638
|
+
receiptId: first.receiptId,
|
|
4639
|
+
outcome: scenario.outcome,
|
|
4640
|
+
...("noRun" in scenario ? {} : { runId }),
|
|
4641
|
+
...(scenario.outcome === "aborted"
|
|
4642
|
+
? {}
|
|
4643
|
+
: {
|
|
4644
|
+
result:
|
|
4645
|
+
scenario.outcome === "failed"
|
|
4646
|
+
? { errorTag: "TestFailure", message: "Failed task" }
|
|
4647
|
+
: { answer: "done" },
|
|
4648
|
+
}),
|
|
4649
|
+
...("disposition" in scenario ? { runDisposition: scenario.disposition } : {}),
|
|
4650
|
+
...("exhausted" in scenario
|
|
4651
|
+
? { finishReason: "budget-exhausted" as const, exhausted: "turns" as const }
|
|
4652
|
+
: {}),
|
|
4653
|
+
}),
|
|
4654
|
+
).pipe(Effect.orDie);
|
|
4655
|
+
|
|
4656
|
+
const record = RecordEnvelope.make({
|
|
4657
|
+
recordId: submissionSettlementRecordId(first.submissionId),
|
|
4658
|
+
family: "thread",
|
|
4659
|
+
schemaVersion: 1,
|
|
4660
|
+
createdAt: CONFORMANCE_CREATED_AT,
|
|
4661
|
+
deploymentId: CONFORMANCE_DEPLOYMENT,
|
|
4662
|
+
payload,
|
|
4663
|
+
});
|
|
4664
|
+
|
|
4665
|
+
let queued: AdmissionResult | undefined;
|
|
4666
|
+
|
|
4667
|
+
if ("queued" in scenario) {
|
|
4668
|
+
const next = yield* admissionRequest(threadId, "correction", { text: "correction" });
|
|
4669
|
+
|
|
4670
|
+
queued = yield* ledger.admit(
|
|
4671
|
+
AdmissionRequest.make({
|
|
4672
|
+
...next,
|
|
4673
|
+
workerAdmission: { ...workerAdmission, messageId: next.idempotencyKey },
|
|
4674
|
+
}),
|
|
4675
|
+
);
|
|
4676
|
+
yield* ledger.markReady(MarkReadyRequest.make({ submissionId: queued.submissionId }));
|
|
4677
|
+
if ("joined" in scenario || "joining" in scenario) {
|
|
4678
|
+
yield* ledger.claimJoining(
|
|
4679
|
+
ClaimJoiningRequest.make({
|
|
4680
|
+
threadId,
|
|
4681
|
+
hostSubmissionId: first.submissionId,
|
|
4682
|
+
ownershipToken: claim.ownershipToken,
|
|
4683
|
+
maxCount: 1,
|
|
4684
|
+
}),
|
|
4685
|
+
);
|
|
4686
|
+
}
|
|
4687
|
+
if ("joined" in scenario) {
|
|
4688
|
+
yield* ledger.markJoined(
|
|
4689
|
+
MarkJoinedRequest.make({
|
|
4690
|
+
submissionId: queued.submissionId,
|
|
4691
|
+
ownershipToken: claim.ownershipToken,
|
|
4692
|
+
recordId: submissionInputRecordId(queued.submissionId),
|
|
4693
|
+
sequence: Schema.decodeSync(CanonicalSequence)(2),
|
|
4694
|
+
}),
|
|
4695
|
+
);
|
|
4696
|
+
}
|
|
4697
|
+
}
|
|
4698
|
+
yield* ledger.reserveSettlement(
|
|
4699
|
+
SettlementReservation.make({
|
|
4700
|
+
submissionId: first.submissionId,
|
|
4701
|
+
ownershipToken: claim.ownershipToken,
|
|
4702
|
+
settlementId: payload.settlementId,
|
|
4703
|
+
outcome: scenario.outcome,
|
|
4704
|
+
record,
|
|
4705
|
+
recordDigest: yield* digestJson(
|
|
4706
|
+
yield* Schema.encodeEffect(RecordEnvelope)(record).pipe(Effect.orDie),
|
|
4707
|
+
),
|
|
4708
|
+
}),
|
|
4709
|
+
);
|
|
4710
|
+
|
|
4711
|
+
const finalization = SettlementFinalization.make({
|
|
4712
|
+
submissionId: first.submissionId,
|
|
4713
|
+
settlementId: payload.settlementId,
|
|
4714
|
+
});
|
|
4715
|
+
|
|
4716
|
+
const settled = yield* ledger.finalizeSettlement(finalization);
|
|
4717
|
+
|
|
4718
|
+
yield* ensure(
|
|
4719
|
+
Schema.toEquivalence(Settlement)(settled, yield* ledger.finalizeSettlement(finalization)),
|
|
4720
|
+
"Finalization replay must preserve its exact outcome",
|
|
4721
|
+
);
|
|
4722
|
+
const control = yield* ledger.inspectWorker(threadId);
|
|
4723
|
+
|
|
4724
|
+
yield* ensure(
|
|
4725
|
+
control.terminal === scenario.terminal &&
|
|
4726
|
+
control.stopped === (scenario.terminal !== undefined),
|
|
4727
|
+
`Wrong assignment state for ${scenario.name}`,
|
|
4728
|
+
);
|
|
4729
|
+
yield* ensure(
|
|
4730
|
+
(yield* ledger.admit(request)).receiptId === first.receiptId,
|
|
4731
|
+
"A seal must preserve same-command receipt replay",
|
|
4732
|
+
);
|
|
4733
|
+
const next = yield* admissionRequest(threadId, "later", { text: "later" });
|
|
4734
|
+
|
|
4735
|
+
const later = AdmissionRequest.make({
|
|
4736
|
+
...next,
|
|
4737
|
+
workerAdmission: { ...workerAdmission, messageId: next.idempotencyKey },
|
|
4738
|
+
});
|
|
4739
|
+
|
|
4740
|
+
if (scenario.terminal !== undefined) {
|
|
4741
|
+
const refusal = yield* expectFailure("terminal admission", ledger.admit(later));
|
|
4742
|
+
|
|
4743
|
+
yield* ensure(
|
|
4744
|
+
Schema.is(AdmissionPolicyError)(refusal) && refusal.code === "worker-stopped",
|
|
4745
|
+
"New instructions cannot reopen a terminal assignment",
|
|
4746
|
+
);
|
|
4747
|
+
} else {
|
|
4748
|
+
yield* ledger.admit(later);
|
|
4749
|
+
}
|
|
4750
|
+
if (queued !== undefined) {
|
|
4751
|
+
if ("joining" in scenario)
|
|
4752
|
+
yield* ledger.revertJoining(
|
|
4753
|
+
RevertJoiningRequest.make({ submissionId: queued.submissionId }),
|
|
4754
|
+
);
|
|
4755
|
+
const state = yield* recoverySnapshot(queued.submissionId);
|
|
4756
|
+
|
|
4757
|
+
yield* ensure(
|
|
4758
|
+
(state.abortIntent !== undefined) === (scenario.outcome === "failed"),
|
|
4759
|
+
"Failure aborts queued inputs; completion never cancels an accepted correction or joined member",
|
|
4760
|
+
);
|
|
4761
|
+
}
|
|
4762
|
+
if (scenario.terminal !== undefined && ledger.stopWorker !== undefined) {
|
|
4763
|
+
yield* ledger.stopWorker({ threadId, author: CONFORMANCE_PRINCIPAL });
|
|
4764
|
+
yield* ensure(
|
|
4765
|
+
(yield* ledger.inspectWorker(threadId)).terminal === scenario.terminal,
|
|
4766
|
+
"Explicit stop cannot replace an assignment's first terminal outcome",
|
|
4767
|
+
);
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
}),
|
|
4771
|
+
);
|
|
4772
|
+
|
|
4556
4773
|
/**
|
|
4557
4774
|
* The shared, adapter-parameterized SubmissionLedger contract suite (STORE-010). Every durable
|
|
4558
4775
|
* ledger adapter test suite must execute each case against its own ledger provisioning, inside
|
|
@@ -4561,6 +4778,7 @@ const abortedSettledRowIsNotAJoiningGap = conformanceCase(
|
|
|
4561
4778
|
export const submissionLedgerConformanceCases: ReadonlyArray<SubmissionLedgerConformanceCase> = [
|
|
4562
4779
|
admissionIdempotency,
|
|
4563
4780
|
workerAdmissionIdentity,
|
|
4781
|
+
assignmentSettlement,
|
|
4564
4782
|
messageAdmissionIdentity,
|
|
4565
4783
|
workerCompletionIdentity,
|
|
4566
4784
|
workerUpdateIdentity,
|
|
@@ -324,6 +324,11 @@ export const ThreadSelection = Schema.Union([
|
|
|
324
324
|
expectedTailSequence: CanonicalSequence,
|
|
325
325
|
expectedTailDigest: Digest,
|
|
326
326
|
}),
|
|
327
|
+
Schema.Struct({
|
|
328
|
+
_tag: Schema.Literal("WorkerExecution"),
|
|
329
|
+
expectedTailSequence: CanonicalSequence,
|
|
330
|
+
expectedTailDigest: Digest,
|
|
331
|
+
}),
|
|
327
332
|
Schema.Struct({
|
|
328
333
|
_tag: Schema.Literal("WorkerState"),
|
|
329
334
|
expectedTailSequence: CanonicalSequence,
|
|
@@ -230,15 +230,6 @@ interface CapturedBinding {
|
|
|
230
230
|
) => Effect.Effect<Option.Option<Settlement>, DurableWorkerFailure>;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
type ReportRequirements<Reports> =
|
|
234
|
-
Reports extends ReadonlyArray<WorkerReporting<unknown, unknown>>
|
|
235
|
-
? [Reports[number]] extends [never]
|
|
236
|
-
? never
|
|
237
|
-
: Reports[number] extends WorkerReporting<unknown, infer R>
|
|
238
|
-
? Exclude<R, Scope.Scope>
|
|
239
|
-
: never
|
|
240
|
-
: never;
|
|
241
|
-
|
|
242
233
|
// Registrations outlive their construction span. Dependencies remain captured,
|
|
243
234
|
// while every attempt/report inherits the invoking fiber's tracing state.
|
|
244
235
|
const omitTraceContext = Context.omit(
|
|
@@ -352,32 +343,16 @@ const capture = <A extends ExecutableAgentBinding, Provides = never, Requires =
|
|
|
352
343
|
* may supply a previously computed digest triple directly.
|
|
353
344
|
*/
|
|
354
345
|
export const DurableWorkerBinding = {
|
|
355
|
-
make: <
|
|
356
|
-
A extends ExecutableAgentBinding,
|
|
357
|
-
const Reports extends ReadonlyArray<WorkerReporting<unknown, unknown>> = readonly [],
|
|
358
|
-
>(
|
|
346
|
+
make: <A extends ExecutableAgentBinding>(
|
|
359
347
|
agent: A,
|
|
360
348
|
digests: DefinitionDigests,
|
|
361
|
-
|
|
362
|
-
): Effect.Effect<
|
|
363
|
-
ResolvedBinding,
|
|
364
|
-
never,
|
|
365
|
-
DurableWorkerRequirements<A> | ReportRequirements<Reports>
|
|
366
|
-
> =>
|
|
349
|
+
): Effect.Effect<ResolvedBinding, never, DurableWorkerRequirements<A>> =>
|
|
367
350
|
Effect.gen(function* () {
|
|
368
351
|
const binding = yield* capture(agent);
|
|
352
|
+
const reporting = yield* captureReporting(backgroundReports(agent.definition));
|
|
369
353
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
...backgroundReports(agent.definition),
|
|
373
|
-
]);
|
|
374
|
-
|
|
375
|
-
return { ...binding, digests, reporting: reports };
|
|
376
|
-
}) as Effect.Effect<
|
|
377
|
-
ResolvedBinding,
|
|
378
|
-
never,
|
|
379
|
-
DurableWorkerRequirements<A> | ReportRequirements<Reports>
|
|
380
|
-
>,
|
|
354
|
+
return { ...binding, digests, reporting };
|
|
355
|
+
}),
|
|
381
356
|
} as const;
|
|
382
357
|
|
|
383
358
|
/** INTERNAL identity-only capture retained for the legacy direct worker path. */
|
|
@@ -442,12 +417,6 @@ export type AgentRegistration<A extends ExecutableAgentBinding = ExecutableAgent
|
|
|
442
417
|
readonly continuity?: {
|
|
443
418
|
readonly versions: ReplayVersions;
|
|
444
419
|
};
|
|
445
|
-
/**
|
|
446
|
-
* Source-owned reports capture services outside attemptLayer and open a fresh Scope per
|
|
447
|
-
* preparation. Version changes with this registration. Missing exact code and preparation
|
|
448
|
-
* failures become retained report refusals; durable delivery retries reuse the frozen input.
|
|
449
|
-
*/
|
|
450
|
-
readonly reporting?: ReadonlyArray<WorkerReporting<unknown, unknown>>;
|
|
451
420
|
/**
|
|
452
421
|
* Build fresh services for exactly one fenced Attempt, across all its Tool/model turns.
|
|
453
422
|
* Finalizes on completion, suspension, failure and interruption. Never reused after eviction.
|
|
@@ -477,12 +446,10 @@ type AttemptLayerRequirements<Requirements, AttemptLayer> = AttemptLayer extends
|
|
|
477
446
|
// Conditional options retain every service they may consume. An absent attempt Layer
|
|
478
447
|
// still needs the original worker services; only a definite Layer can remove them.
|
|
479
448
|
type EntryRequirements<Entry> = Entry extends unknown
|
|
480
|
-
?
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
>
|
|
485
|
-
| ("reporting" extends keyof Entry ? ReportRequirements<Entry["reporting"]> : never)
|
|
449
|
+
? AttemptLayerRequirements<
|
|
450
|
+
EntryWorkerRequirements<Entry>,
|
|
451
|
+
"attemptLayer" extends keyof Entry ? Entry["attemptLayer"] : undefined
|
|
452
|
+
>
|
|
486
453
|
: never;
|
|
487
454
|
|
|
488
455
|
type RegistrationRequirements<Entries extends ReadonlyArray<AgentRegistration>> = [
|
|
@@ -519,8 +486,9 @@ const registrationDefinitions = (entry: AgentRegistration): DefinitionDigestInpu
|
|
|
519
486
|
backgroundReporting: automatic.map((report) => ({
|
|
520
487
|
delegationId: report.delegationId,
|
|
521
488
|
targetAgentId: report.target.id,
|
|
522
|
-
mode:
|
|
523
|
-
|
|
489
|
+
mode: "standard",
|
|
490
|
+
// Preserve the fingerprint of existing standard registrations.
|
|
491
|
+
destinationDelegationId: null,
|
|
524
492
|
})),
|
|
525
493
|
},
|
|
526
494
|
};
|
|
@@ -579,10 +547,7 @@ const compileRegistration = <Entry extends AgentRegistration>(
|
|
|
579
547
|
entry.attemptLayer,
|
|
580
548
|
);
|
|
581
549
|
|
|
582
|
-
const reporting = yield* captureReporting(
|
|
583
|
-
...(entry.reporting ?? []),
|
|
584
|
-
...backgroundReports(binding.definition),
|
|
585
|
-
]);
|
|
550
|
+
const reporting = yield* captureReporting(backgroundReports(binding.definition));
|
|
586
551
|
|
|
587
552
|
return {
|
|
588
553
|
...binding,
|