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
|
@@ -19,6 +19,8 @@ import { MessageRef } from "../../core/Messaging.ts";
|
|
|
19
19
|
import { IdempotencyKey, Receipt } from "../../core/Receipt.ts";
|
|
20
20
|
import { SubagentDelegationCaps, SubagentGrant } from "../../core/SubagentContract.ts";
|
|
21
21
|
import {
|
|
22
|
+
type WorkerSummary,
|
|
23
|
+
type WorkerStarted,
|
|
22
24
|
WorkerCompletion,
|
|
23
25
|
FrameworkMessage,
|
|
24
26
|
WorkerUpdate,
|
|
@@ -26,9 +28,12 @@ import {
|
|
|
26
28
|
WorkerHistoryEntry,
|
|
27
29
|
type WorkerContext,
|
|
28
30
|
WorkerRef,
|
|
31
|
+
WorkerStop,
|
|
29
32
|
} from "../../core/Worker.ts";
|
|
30
33
|
import {
|
|
31
34
|
type SubagentHost,
|
|
35
|
+
type DeferredStartWorkerRequest,
|
|
36
|
+
type StartWorkerRequest,
|
|
32
37
|
type WorkerObservation,
|
|
33
38
|
type WorkerReceiptRequest,
|
|
34
39
|
type WorkerRunReport,
|
|
@@ -62,6 +67,7 @@ import {
|
|
|
62
67
|
RecordId,
|
|
63
68
|
WorkerAdmission,
|
|
64
69
|
WorkerInputRequested,
|
|
70
|
+
WorkerStopRequested,
|
|
65
71
|
WorkerOrigin,
|
|
66
72
|
WorkerOriginRecorded,
|
|
67
73
|
WorkerInputCompleted,
|
|
@@ -86,13 +92,14 @@ import {
|
|
|
86
92
|
} from "../Schedule.ts";
|
|
87
93
|
import {
|
|
88
94
|
SubmissionLedger,
|
|
95
|
+
WorkerLedgerState,
|
|
89
96
|
LedgerError,
|
|
90
97
|
AbortCommand,
|
|
91
98
|
type AbortIntent,
|
|
92
99
|
type Principal,
|
|
93
100
|
type Settlement,
|
|
94
101
|
SubmissionLookupById,
|
|
95
|
-
|
|
102
|
+
submissionSettlementRecordId,
|
|
96
103
|
type SubmissionSnapshot,
|
|
97
104
|
} from "../SubmissionLedger.ts";
|
|
98
105
|
import type { SubmissionStatus } from "../SubmissionStatus.ts";
|
|
@@ -100,6 +107,7 @@ import { PreparedInput } from "../Subscription.ts";
|
|
|
100
107
|
import {
|
|
101
108
|
ThreadStore,
|
|
102
109
|
getRecord,
|
|
110
|
+
getRunInput,
|
|
103
111
|
readWorkerState as readNativeWorkerState,
|
|
104
112
|
FencedAppendRequest,
|
|
105
113
|
ThreadExportRequest,
|
|
@@ -352,6 +360,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
352
360
|
threadId: ThreadId,
|
|
353
361
|
id: string,
|
|
354
362
|
payload:
|
|
363
|
+
| WorkerStopRequested
|
|
355
364
|
| WorkerInputRequested
|
|
356
365
|
| WorkerOriginRecorded
|
|
357
366
|
| WorkerInputCompleted
|
|
@@ -359,7 +368,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
359
368
|
| WorkerReportRefused
|
|
360
369
|
| SubtreeBudgetReserved,
|
|
361
370
|
current: Pick<ThreadExport, "tailSequence" | "tailDigest" | "records">,
|
|
362
|
-
phase: "source" | "origin" | "completion" | "subtree" | "report",
|
|
371
|
+
phase: "source" | "origin" | "completion" | "subtree" | "report" | "stop",
|
|
363
372
|
acknowledgements: ReadonlyArray<WorkerInputCompleted> = [],
|
|
364
373
|
) {
|
|
365
374
|
const operation = "start";
|
|
@@ -487,50 +496,36 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
487
496
|
const report = reports[0];
|
|
488
497
|
|
|
489
498
|
if (report === undefined) return undefined;
|
|
490
|
-
if (
|
|
491
|
-
reports.length !== 1 ||
|
|
492
|
-
!Object.is(report.target, target.definition) ||
|
|
493
|
-
(report.mode !== "standard" && !Object.is(report.input, source.definition.input)) ||
|
|
494
|
-
(report.destination !== undefined && !Object.is(report.destination.target, source.definition))
|
|
495
|
-
)
|
|
499
|
+
if (reports.length !== 1 || !Object.is(report.target, target.definition))
|
|
496
500
|
return yield* failure("start", "declaration-unavailable");
|
|
497
501
|
|
|
498
|
-
|
|
502
|
+
const submission = authority.submission;
|
|
499
503
|
|
|
500
|
-
if (
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
(authority.depth !== 0 && submission.workerAdmission === undefined)
|
|
506
|
-
)
|
|
507
|
-
return yield* failure("start", "denied");
|
|
504
|
+
if (
|
|
505
|
+
submission === undefined ||
|
|
506
|
+
(authority.depth !== 0 && submission.workerAdmission === undefined)
|
|
507
|
+
)
|
|
508
|
+
return yield* failure("start", "denied");
|
|
508
509
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
510
|
+
const retained =
|
|
511
|
+
submission.workerAdmission === undefined
|
|
512
|
+
? undefined
|
|
513
|
+
: yield* Schema.encodeEffect(WorkerAdmission)(submission.workerAdmission).pipe(
|
|
514
|
+
Effect.flatMap(Schema.decodeEffect(PersistedJson)),
|
|
515
|
+
Effect.mapError(storageFailure("start")),
|
|
516
|
+
);
|
|
516
517
|
|
|
517
|
-
|
|
518
|
+
return {
|
|
519
|
+
sourceDigests: source.digests,
|
|
520
|
+
mode: "standard",
|
|
521
|
+
returnAddress: {
|
|
518
522
|
input: submission.inputPayload,
|
|
519
523
|
principal: submission.principal,
|
|
520
524
|
policy: authority.policy,
|
|
521
525
|
depth: authority.depth,
|
|
522
526
|
...(authority.grant === undefined ? {} : { grant: authority.grant }),
|
|
523
527
|
...(retained === undefined ? {} : { workerAdmission: retained }),
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
return {
|
|
528
|
-
sourceDigests: source.digests,
|
|
529
|
-
...(report.mode === undefined ? {} : { mode: report.mode }),
|
|
530
|
-
...(returnAddress === undefined ? {} : { returnAddress }),
|
|
531
|
-
...(report.destination === undefined
|
|
532
|
-
? {}
|
|
533
|
-
: { destinationDelegationId: report.destination.delegationId }),
|
|
528
|
+
},
|
|
534
529
|
};
|
|
535
530
|
});
|
|
536
531
|
|
|
@@ -1232,7 +1227,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1232
1227
|
sourceBinding === undefined ||
|
|
1233
1228
|
targetBinding === undefined ||
|
|
1234
1229
|
reports.length !== 1 ||
|
|
1235
|
-
reports[0]
|
|
1230
|
+
reports[0] === undefined ||
|
|
1236
1231
|
reports[0].target !== targetBinding.definition ||
|
|
1237
1232
|
!definitionDigestsEqual(submission.agentDigests, origin.targetDigests)
|
|
1238
1233
|
)
|
|
@@ -1351,6 +1346,9 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1351
1346
|
const refused = (reason: WorkerReportRefused["reason"]) =>
|
|
1352
1347
|
WorkerReportRefused.make({ runId, messageId, reason });
|
|
1353
1348
|
|
|
1349
|
+
// Retired custom origins remain readable; already prepared envelopes bypass preparation.
|
|
1350
|
+
if (intent.mode !== "standard") return refused("declaration-unavailable");
|
|
1351
|
+
|
|
1354
1352
|
const selected = yield* deps.ledger
|
|
1355
1353
|
.lookup(SubmissionLookupById.make({ submissionId: host.submissionId }))
|
|
1356
1354
|
.pipe(Effect.mapError(storageFailure("inspect")));
|
|
@@ -1366,84 +1364,18 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1366
1364
|
const hostSubmission = selected.value;
|
|
1367
1365
|
const hostAdmission = selected.value.workerAdmission;
|
|
1368
1366
|
|
|
1369
|
-
|
|
1370
|
-
// projections retain their explicit source-context contract.
|
|
1371
|
-
const source = yield* Effect.gen(function* () {
|
|
1372
|
-
if (intent.mode === "standard") {
|
|
1373
|
-
const address = intent.returnAddress;
|
|
1367
|
+
const address = intent.returnAddress;
|
|
1374
1368
|
|
|
1375
|
-
|
|
1369
|
+
if (address === undefined) return yield* failure("inspect", "corrupt");
|
|
1376
1370
|
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
return { ...address, admission: retained, sourceSubmissionId: undefined };
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
const first = Option.getOrUndefined(
|
|
1388
|
-
yield* exactRecord(
|
|
1389
|
-
origin.source.threadId,
|
|
1390
|
-
workerInputRecordId(origin.firstMessageId),
|
|
1391
|
-
"inspect",
|
|
1392
|
-
),
|
|
1393
|
-
)?.record.payload;
|
|
1394
|
-
|
|
1395
|
-
if (
|
|
1396
|
-
first?._tag !== "WorkerInputRequested" ||
|
|
1397
|
-
first.admission.messageId !== origin.firstMessageId ||
|
|
1398
|
-
!sameOrigin(first.admission.origin, origin)
|
|
1399
|
-
)
|
|
1400
|
-
return yield* failure("inspect", "corrupt");
|
|
1401
|
-
|
|
1402
|
-
const authority = yield* sourceAuthority(
|
|
1403
|
-
origin.source.threadId,
|
|
1404
|
-
first.admission.sourceSubmissionId,
|
|
1405
|
-
);
|
|
1406
|
-
|
|
1407
|
-
const recorded = authority.current.records.find(
|
|
1408
|
-
({ record }) => record.payload._tag === "WorkerOriginRecorded",
|
|
1409
|
-
)?.record.payload;
|
|
1410
|
-
|
|
1411
|
-
let retained: WorkerAdmission | undefined;
|
|
1412
|
-
let principal = hostSubmission.principal;
|
|
1413
|
-
|
|
1414
|
-
if (recorded?._tag === "WorkerOriginRecorded") {
|
|
1415
|
-
if (hostAdmission.sourceSubmissionId === undefined)
|
|
1416
|
-
return yield* failure("inspect", "denied");
|
|
1417
|
-
|
|
1418
|
-
const snapshot = yield* deps.ledger
|
|
1419
|
-
.lookup(
|
|
1420
|
-
SubmissionLookupById.make({
|
|
1421
|
-
submissionId: hostAdmission.sourceSubmissionId,
|
|
1422
|
-
}),
|
|
1423
|
-
)
|
|
1424
|
-
.pipe(Effect.mapError(storageFailure("inspect")));
|
|
1425
|
-
|
|
1426
|
-
if (
|
|
1427
|
-
Option.isNone(snapshot) ||
|
|
1428
|
-
snapshot.value.threadId !== origin.source.threadId ||
|
|
1429
|
-
snapshot.value.workerAdmission === undefined ||
|
|
1430
|
-
!sameOrigin(snapshot.value.workerAdmission.origin, recorded.origin)
|
|
1431
|
-
)
|
|
1432
|
-
return yield* failure("inspect", "denied");
|
|
1433
|
-
retained = snapshot.value.workerAdmission;
|
|
1434
|
-
principal = snapshot.value.principal;
|
|
1435
|
-
}
|
|
1371
|
+
const retainedAdmission =
|
|
1372
|
+
address.workerAdmission === undefined
|
|
1373
|
+
? undefined
|
|
1374
|
+
: yield* Schema.decodeUnknownEffect(WorkerAdmission)(address.workerAdmission).pipe(
|
|
1375
|
+
Effect.mapError((cause) => failure("inspect", "corrupt", cause)),
|
|
1376
|
+
);
|
|
1436
1377
|
|
|
1437
|
-
|
|
1438
|
-
policy: authority.policy,
|
|
1439
|
-
depth: authority.depth,
|
|
1440
|
-
grant: authority.grant,
|
|
1441
|
-
input: authority.submission?.inputPayload,
|
|
1442
|
-
admission: retained,
|
|
1443
|
-
principal,
|
|
1444
|
-
sourceSubmissionId: first.admission.sourceSubmissionId,
|
|
1445
|
-
};
|
|
1446
|
-
});
|
|
1378
|
+
const source = { ...address, admission: retainedAdmission };
|
|
1447
1379
|
|
|
1448
1380
|
const sourceBinding = currentBinding(origin.source.agentId);
|
|
1449
1381
|
const targetBinding = currentBinding(origin.worker.targetAgentId);
|
|
@@ -1460,29 +1392,13 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1460
1392
|
targetBinding === undefined ||
|
|
1461
1393
|
descriptor === undefined ||
|
|
1462
1394
|
reports.length !== 1 ||
|
|
1463
|
-
!Object.is(descriptor.target, targetBinding.definition)
|
|
1464
|
-
(descriptor.mode !== "standard" &&
|
|
1465
|
-
!Object.is(descriptor.input, sourceBinding.definition.input)) ||
|
|
1466
|
-
descriptor.mode !== intent.mode ||
|
|
1467
|
-
descriptor.destination?.delegationId !== intent.destinationDelegationId ||
|
|
1468
|
-
(descriptor.destination !== undefined &&
|
|
1469
|
-
!Object.is(descriptor.destination.target, sourceBinding.definition))
|
|
1395
|
+
!Object.is(descriptor.target, targetBinding.definition)
|
|
1470
1396
|
)
|
|
1471
1397
|
return refused("declaration-unavailable");
|
|
1472
1398
|
|
|
1473
1399
|
const sourceOrigin = source.admission?.origin;
|
|
1474
1400
|
|
|
1475
1401
|
if (source.depth !== 0 && sourceOrigin === undefined) return refused("destination");
|
|
1476
|
-
if (
|
|
1477
|
-
descriptor.mode !== "standard" &&
|
|
1478
|
-
sourceOrigin !== undefined &&
|
|
1479
|
-
(descriptor.destination === undefined ||
|
|
1480
|
-
descriptor.destination.delegationId !== sourceOrigin.worker.delegationId ||
|
|
1481
|
-
descriptor.destination.target.id !== sourceOrigin.worker.targetAgentId)
|
|
1482
|
-
)
|
|
1483
|
-
return refused("destination");
|
|
1484
|
-
if (sourceOrigin === undefined && descriptor.destination !== undefined)
|
|
1485
|
-
return refused("destination");
|
|
1486
1402
|
const now = yield* Clock.currentTimeMillis;
|
|
1487
1403
|
|
|
1488
1404
|
const deadlineAtMillis = Math.min(
|
|
@@ -1537,32 +1453,20 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1537
1453
|
|
|
1538
1454
|
if (projection._tag === "WorkerReportRefused") return projection;
|
|
1539
1455
|
|
|
1540
|
-
|
|
1541
|
-
const message = projection.value.message;
|
|
1456
|
+
const message = projection.value.message;
|
|
1542
1457
|
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
)
|
|
1553
|
-
return refused("input");
|
|
1554
|
-
} else if (projection.value.message !== undefined) return refused("input");
|
|
1458
|
+
if (!Schema.is(WorkerCompletion)(message)) return refused("input");
|
|
1459
|
+
if (
|
|
1460
|
+
!Schema.toEquivalence(WorkerRef)(message.report.worker, origin.worker) ||
|
|
1461
|
+
!Schema.toEquivalence(Receipt)(message.report.receipt, report.observation.receipt) ||
|
|
1462
|
+
message.report.runId !== runId ||
|
|
1463
|
+
message.report.settlementId !== host.settlementId ||
|
|
1464
|
+
message.report.outcome !== host.outcome ||
|
|
1465
|
+
message.budgetExhausted !== report.observation.budgetExhausted
|
|
1466
|
+
)
|
|
1467
|
+
return refused("input");
|
|
1555
1468
|
|
|
1556
|
-
const
|
|
1557
|
-
descriptor.mode === "standard" ? source.input : projection.value.encodedInput;
|
|
1558
|
-
|
|
1559
|
-
const validated = yield* (
|
|
1560
|
-
descriptor.mode === "standard"
|
|
1561
|
-
? Schema.decodeUnknownEffect(PersistedJson)(reportInput)
|
|
1562
|
-
: Schema.decodeEffect(Schema.toEncoded(sourceBinding.definition.input))(reportInput).pipe(
|
|
1563
|
-
Effect.flatMap(() => Schema.decodeUnknownEffect(PersistedJson)(reportInput)),
|
|
1564
|
-
)
|
|
1565
|
-
).pipe(Effect.option);
|
|
1469
|
+
const validated = yield* Schema.decodeEffect(PersistedJson)(source.input).pipe(Effect.option);
|
|
1566
1470
|
|
|
1567
1471
|
if (Option.isNone(validated)) return refused("input");
|
|
1568
1472
|
const input = validated.value;
|
|
@@ -1579,9 +1483,9 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1579
1483
|
|
|
1580
1484
|
if (retained === undefined) return refused("destination");
|
|
1581
1485
|
|
|
1582
|
-
const parameters = yield* Schema.
|
|
1583
|
-
|
|
1584
|
-
)
|
|
1486
|
+
const parameters = yield* Schema.decodeEffect(PersistedJson)(retained.parameters).pipe(
|
|
1487
|
+
Effect.option,
|
|
1488
|
+
);
|
|
1585
1489
|
|
|
1586
1490
|
if (Option.isNone(parameters)) return refused("destination");
|
|
1587
1491
|
workerAdmission = {
|
|
@@ -1595,56 +1499,21 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1595
1499
|
};
|
|
1596
1500
|
}
|
|
1597
1501
|
|
|
1598
|
-
//
|
|
1599
|
-
// or starts another Run. Nested reports authorize the enclosing worker's source owner.
|
|
1600
|
-
const authorizationSourceSubmissionId =
|
|
1601
|
-
sourceOrigin === undefined
|
|
1602
|
-
? source.sourceSubmissionId
|
|
1603
|
-
: workerAdmission?.sourceSubmissionId;
|
|
1604
|
-
|
|
1605
|
-
// Standard framework messages are reauthorized by validateCompletion at
|
|
1606
|
-
// their destination. Persisting an outbound report does not admit input there.
|
|
1607
|
-
const authorized =
|
|
1608
|
-
intent.mode === "standard"
|
|
1609
|
-
? Option.some(principal)
|
|
1610
|
-
: yield* deps.authorizer
|
|
1611
|
-
.authorize({
|
|
1612
|
-
sourceThreadId: sourceOrigin?.source.threadId ?? origin.source.threadId,
|
|
1613
|
-
...(authorizationSourceSubmissionId === undefined
|
|
1614
|
-
? {}
|
|
1615
|
-
: { sourceSubmissionId: authorizationSourceSubmissionId }),
|
|
1616
|
-
principal,
|
|
1617
|
-
operation: "followUp",
|
|
1618
|
-
access: "send",
|
|
1619
|
-
worker: sourceOrigin?.worker ?? origin.worker,
|
|
1620
|
-
})
|
|
1621
|
-
.pipe(
|
|
1622
|
-
Effect.map(Option.some),
|
|
1623
|
-
Effect.catchTag("WorkerError", (error) =>
|
|
1624
|
-
error.reason === "storage" || error.reason === "unavailable"
|
|
1625
|
-
? Effect.fail(error)
|
|
1626
|
-
: Effect.succeed(Option.none<Principal>()),
|
|
1627
|
-
),
|
|
1628
|
-
);
|
|
1629
|
-
|
|
1630
|
-
if (Option.isNone(authorized)) return refused("denied");
|
|
1631
|
-
|
|
1502
|
+
// The destination reauthorizes standard messages through validateCompletion.
|
|
1632
1503
|
const envelope: PreparedInput = {
|
|
1633
1504
|
schemaVersion: 1,
|
|
1634
1505
|
threadId: origin.source.threadId,
|
|
1635
|
-
deliveryPrincipal:
|
|
1506
|
+
deliveryPrincipal: principal,
|
|
1636
1507
|
agentId: origin.source.agentId,
|
|
1637
1508
|
definitions: intent.sourceDigests,
|
|
1638
1509
|
input,
|
|
1639
1510
|
inputDigest,
|
|
1640
1511
|
admissionKey: messageId,
|
|
1641
1512
|
authorization: { policyId: "worker-report", decisionId: messageId },
|
|
1642
|
-
|
|
1643
|
-
? {}
|
|
1644
|
-
: { messageAdmission: projection.value.message }),
|
|
1513
|
+
messageAdmission: message,
|
|
1645
1514
|
...(workerAdmission === undefined
|
|
1646
1515
|
? {}
|
|
1647
|
-
: { workerAdmission: { ...workerAdmission, deliveryPrincipal:
|
|
1516
|
+
: { workerAdmission: { ...workerAdmission, deliveryPrincipal: principal } }),
|
|
1648
1517
|
};
|
|
1649
1518
|
|
|
1650
1519
|
const encoded = yield* Schema.encodeEffect(PreparedInput)(envelope).pipe(
|
|
@@ -1868,7 +1737,28 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1868
1737
|
|
|
1869
1738
|
const origin = first?._tag === "WorkerInputRequested" ? first.admission.origin : undefined;
|
|
1870
1739
|
|
|
1871
|
-
if (origin === undefined)
|
|
1740
|
+
if (origin === undefined) {
|
|
1741
|
+
const retained = Option.isNone(deps.deliveries)
|
|
1742
|
+
? null
|
|
1743
|
+
: yield* deps.deliveries.value
|
|
1744
|
+
.get({
|
|
1745
|
+
ownerThreadId: context.source.threadId,
|
|
1746
|
+
messageId: Schema.decodeSync(IdempotencyKey)(worker.threadId),
|
|
1747
|
+
})
|
|
1748
|
+
.pipe(Effect.mapError(storageFailure(operation)));
|
|
1749
|
+
|
|
1750
|
+
const pendingOrigin = retained?.envelope.workerAdmission?.origin;
|
|
1751
|
+
|
|
1752
|
+
if (pendingOrigin === undefined) return yield* failure(operation, "not-found");
|
|
1753
|
+
if (
|
|
1754
|
+
!Schema.toEquivalence(WorkerRef)(pendingOrigin.worker, worker) ||
|
|
1755
|
+
pendingOrigin.firstMessageId !== retained?.key.messageId ||
|
|
1756
|
+
pendingOrigin.source.threadId !== context.source.threadId
|
|
1757
|
+
)
|
|
1758
|
+
return yield* failure(operation, "worker-mismatch");
|
|
1759
|
+
|
|
1760
|
+
return pendingOrigin;
|
|
1761
|
+
}
|
|
1872
1762
|
if (
|
|
1873
1763
|
!Schema.toEquivalence(WorkerRef)(origin.worker, worker) ||
|
|
1874
1764
|
first?._tag !== "WorkerInputRequested" ||
|
|
@@ -2091,65 +1981,194 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
2091
1981
|
|
|
2092
1982
|
const summarize = Effect.fn("WorkerHost.summarize")(function* (
|
|
2093
1983
|
origin: WorkerOrigin,
|
|
2094
|
-
all: ReadonlyArray<WorkerInputRequested>,
|
|
2095
1984
|
operation: "list" | "inspect",
|
|
2096
|
-
) {
|
|
1985
|
+
): Effect.fn.Return<WorkerSummary, WorkerError> {
|
|
2097
1986
|
yield* authorize(operation, "read", origin.worker);
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
1987
|
+
if (deps.ledger.inspectWorker === undefined || Option.isNone(deps.deliveries))
|
|
1988
|
+
return yield* failure(operation, "unavailable");
|
|
1989
|
+
const threadId = origin.worker.threadId;
|
|
1990
|
+
const deliveries = deps.deliveries.value;
|
|
2101
1991
|
|
|
2102
|
-
|
|
2103
|
-
|
|
1992
|
+
const pending = () =>
|
|
1993
|
+
deliveries
|
|
1994
|
+
.list({ ownerThreadId: context.source.threadId, pendingWorker: threadId, limit: 1 })
|
|
1995
|
+
.pipe(
|
|
1996
|
+
Effect.mapError(storageFailure(operation)),
|
|
1997
|
+
Effect.map((page) => page.items[0]),
|
|
1998
|
+
);
|
|
2104
1999
|
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
messageId: row.admission.messageId,
|
|
2111
|
-
})
|
|
2112
|
-
.pipe(Effect.mapError(storageFailure(operation)));
|
|
2000
|
+
const tail = () =>
|
|
2001
|
+
deps.store.inspectTail(ThreadTailRequest.make({ threadId })).pipe(
|
|
2002
|
+
Effect.catchTag("ThreadNotMaterialized", () => Effect.succeed(undefined)),
|
|
2003
|
+
Effect.mapError(storageFailure(operation)),
|
|
2004
|
+
);
|
|
2113
2005
|
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
.lookup(
|
|
2121
|
-
SubmissionLookupByKey.make({
|
|
2122
|
-
threadId: origin.worker.threadId,
|
|
2123
|
-
principal,
|
|
2124
|
-
idempotencyKey: row.admission.messageId,
|
|
2125
|
-
}),
|
|
2126
|
-
)
|
|
2127
|
-
.pipe(Effect.mapError(storageFailure(operation)));
|
|
2006
|
+
const receipt = (row: SubmissionSnapshot): Receipt => ({
|
|
2007
|
+
threadId: row.threadId,
|
|
2008
|
+
submissionId: row.submissionId,
|
|
2009
|
+
receiptId: row.receiptId,
|
|
2010
|
+
queueSequence: row.queueSequence,
|
|
2011
|
+
});
|
|
2128
2012
|
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2013
|
+
const input = (row: SubmissionSnapshot) => {
|
|
2014
|
+
if (row.workerAdmission === undefined || !sameOrigin(row.workerAdmission.origin, origin))
|
|
2015
|
+
return failure(operation, "corrupt");
|
|
2016
|
+
|
|
2017
|
+
return Effect.succeed({ receipt: receipt(row), messageId: row.workerAdmission.messageId });
|
|
2018
|
+
};
|
|
2019
|
+
|
|
2020
|
+
const submission = (submissionId: SubmissionId) =>
|
|
2021
|
+
deps.ledger.lookup(SubmissionLookupById.make({ submissionId })).pipe(
|
|
2022
|
+
Effect.mapError(storageFailure(operation)),
|
|
2023
|
+
Effect.flatMap((row) =>
|
|
2024
|
+
Option.isNone(row) ? failure(operation, "corrupt") : Effect.succeed(row.value),
|
|
2025
|
+
),
|
|
2026
|
+
);
|
|
2027
|
+
|
|
2028
|
+
// Native pages and exact lookups only. Retry changing watermarks instead of combining
|
|
2029
|
+
// acceptance, application and completion facts that never coexisted.
|
|
2030
|
+
for (let attempt = 0; attempt < 16; attempt++) {
|
|
2031
|
+
const delivery = yield* pending();
|
|
2032
|
+
|
|
2033
|
+
const control = yield* deps.ledger
|
|
2034
|
+
.inspectWorker(threadId)
|
|
2035
|
+
.pipe(Effect.mapError(storageFailure(operation)));
|
|
2036
|
+
|
|
2037
|
+
const current = yield* tail();
|
|
2038
|
+
|
|
2039
|
+
const records =
|
|
2040
|
+
current === undefined
|
|
2041
|
+
? []
|
|
2042
|
+
: yield* deps.store
|
|
2043
|
+
.read({
|
|
2044
|
+
threadId,
|
|
2045
|
+
selection: {
|
|
2046
|
+
_tag: "WorkerExecution",
|
|
2047
|
+
expectedTailSequence: current.tailSequence,
|
|
2048
|
+
expectedTailDigest: current.tailDigest,
|
|
2049
|
+
},
|
|
2050
|
+
page: { limit: 2 },
|
|
2051
|
+
})
|
|
2052
|
+
.pipe(
|
|
2053
|
+
Stream.runCollect,
|
|
2054
|
+
Effect.mapError(storageFailure(operation)),
|
|
2055
|
+
Effect.catch((error) =>
|
|
2056
|
+
Effect.gen(function* () {
|
|
2057
|
+
// An unavailable recheck cannot justify discarding the original error.
|
|
2058
|
+
const after = yield* tail().pipe(Effect.orElseSucceed(() => current));
|
|
2059
|
+
|
|
2060
|
+
if (
|
|
2061
|
+
after !== undefined &&
|
|
2062
|
+
(current.tailSequence !== after.tailSequence ||
|
|
2063
|
+
current.tailDigest !== after.tailDigest)
|
|
2064
|
+
)
|
|
2065
|
+
return undefined;
|
|
2066
|
+
|
|
2067
|
+
return yield* error;
|
|
2068
|
+
}),
|
|
2069
|
+
),
|
|
2070
|
+
);
|
|
2071
|
+
|
|
2072
|
+
if (records === undefined) continue;
|
|
2073
|
+
|
|
2074
|
+
const applied = records.find((entry) => entry.record.payload._tag === "UserInputRecorded");
|
|
2075
|
+
|
|
2076
|
+
const started = records.find((entry) => entry.record.payload._tag === "RunStarted")?.record
|
|
2077
|
+
.payload;
|
|
2078
|
+
|
|
2079
|
+
const acceptedInput = control.latest === null ? null : yield* input(control.latest);
|
|
2080
|
+
let appliedInput: WorkerSummary["appliedInput"] = null;
|
|
2081
|
+
let run: WorkerSummary["run"] = null;
|
|
2082
|
+
|
|
2083
|
+
if (applied?.record.payload._tag === "UserInputRecorded") {
|
|
2084
|
+
const value = applied.record.payload;
|
|
2085
|
+
|
|
2086
|
+
if (value.submissionId === undefined || value.runId === undefined)
|
|
2087
|
+
return yield* failure(operation, "corrupt");
|
|
2088
|
+
appliedInput = {
|
|
2089
|
+
...(yield* input(yield* submission(value.submissionId))),
|
|
2090
|
+
runId: value.runId,
|
|
2091
|
+
sequence: applied.sequence,
|
|
2092
|
+
};
|
|
2132
2093
|
}
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2094
|
+
if (started?._tag === "RunStarted") {
|
|
2095
|
+
const initial = Option.getOrUndefined(
|
|
2096
|
+
yield* getRunInput({ threadId, runId: started.runId }).pipe(
|
|
2097
|
+
Effect.provideService(ThreadStore, deps.store),
|
|
2098
|
+
Effect.mapError(storageFailure(operation)),
|
|
2099
|
+
),
|
|
2100
|
+
)?.record.payload;
|
|
2101
|
+
|
|
2102
|
+
if (initial?._tag !== "UserInputRecorded" || initial.submissionId === undefined)
|
|
2103
|
+
return yield* failure(operation, "corrupt");
|
|
2104
|
+
const host = yield* input(yield* submission(initial.submissionId));
|
|
2105
|
+
|
|
2106
|
+
const terminal = Option.getOrUndefined(
|
|
2107
|
+
yield* exactRecord(
|
|
2108
|
+
threadId,
|
|
2109
|
+
submissionSettlementRecordId(initial.submissionId),
|
|
2110
|
+
operation,
|
|
2111
|
+
),
|
|
2112
|
+
)?.record.payload;
|
|
2113
|
+
|
|
2114
|
+
if (
|
|
2115
|
+
terminal !== undefined &&
|
|
2116
|
+
(terminal._tag !== "SubmissionSettled" || terminal.runId !== started.runId)
|
|
2117
|
+
)
|
|
2118
|
+
return yield* failure(operation, "corrupt");
|
|
2119
|
+
|
|
2120
|
+
run = {
|
|
2121
|
+
runId: started.runId,
|
|
2122
|
+
hostReceipt: host.receipt,
|
|
2123
|
+
outcome: terminal?.outcome ?? null,
|
|
2124
|
+
disposition: terminal?.runDisposition ?? null,
|
|
2140
2125
|
};
|
|
2141
2126
|
}
|
|
2127
|
+
|
|
2128
|
+
const after = yield* deps.ledger
|
|
2129
|
+
.inspectWorker(threadId)
|
|
2130
|
+
.pipe(Effect.mapError(storageFailure(operation)));
|
|
2131
|
+
|
|
2132
|
+
const afterTail = yield* tail();
|
|
2133
|
+
const afterDelivery = yield* pending();
|
|
2134
|
+
|
|
2135
|
+
if (
|
|
2136
|
+
!Schema.toEquivalence(WorkerLedgerState)(control, after) ||
|
|
2137
|
+
current?.tailSequence !== afterTail?.tailSequence ||
|
|
2138
|
+
current?.tailDigest !== afterTail?.tailDigest ||
|
|
2139
|
+
delivery?.key.messageId !== afterDelivery?.key.messageId ||
|
|
2140
|
+
delivery?.version !== afterDelivery?.version
|
|
2141
|
+
)
|
|
2142
|
+
continue;
|
|
2143
|
+
|
|
2144
|
+
return {
|
|
2145
|
+
worker: origin.worker,
|
|
2146
|
+
latestReceipt: acceptedInput?.receipt ?? null,
|
|
2147
|
+
state:
|
|
2148
|
+
control.terminal ??
|
|
2149
|
+
(control.stopped
|
|
2150
|
+
? control.active === null
|
|
2151
|
+
? "stopped"
|
|
2152
|
+
: "stopping"
|
|
2153
|
+
: control.active !== null
|
|
2154
|
+
? "active"
|
|
2155
|
+
: delivery !== undefined
|
|
2156
|
+
? "starting"
|
|
2157
|
+
: "idle"),
|
|
2158
|
+
acceptedInput,
|
|
2159
|
+
appliedInput,
|
|
2160
|
+
run,
|
|
2161
|
+
pendingDelivery:
|
|
2162
|
+
delivery === undefined ? null : yield* deliveryStatus(delivery, operation),
|
|
2163
|
+
watermark: {
|
|
2164
|
+
canonicalSequence: current?.tailSequence ?? 0,
|
|
2165
|
+
acceptedQueueSequence: control.latest?.queueSequence ?? 0,
|
|
2166
|
+
pendingDeliveryVersion: delivery?.version ?? null,
|
|
2167
|
+
},
|
|
2168
|
+
};
|
|
2142
2169
|
}
|
|
2143
2170
|
|
|
2144
|
-
return
|
|
2145
|
-
worker: origin.worker,
|
|
2146
|
-
latestReceipt,
|
|
2147
|
-
state: active
|
|
2148
|
-
? ("active" as const)
|
|
2149
|
-
: starting || latestReceipt === null
|
|
2150
|
-
? ("starting" as const)
|
|
2151
|
-
: ("idle" as const),
|
|
2152
|
-
};
|
|
2171
|
+
return yield* failure(operation, "storage");
|
|
2153
2172
|
});
|
|
2154
2173
|
|
|
2155
2174
|
return {
|
|
@@ -2168,104 +2187,181 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
2168
2187
|
|
|
2169
2188
|
return (yield* preparedTarget(request.target, request.encodedInput)).policy;
|
|
2170
2189
|
}),
|
|
2171
|
-
start: Effect.fn("WorkerHost.start")(function* (
|
|
2190
|
+
start: Effect.fn("WorkerHost.start")(function* <E = never, R = never>(
|
|
2191
|
+
command: StartWorkerRequest | DeferredStartWorkerRequest<E, R>,
|
|
2192
|
+
): Effect.fn.Return<WorkerStarted, WorkerError | E, R> {
|
|
2172
2193
|
const principal = yield* authorize("start", "send");
|
|
2173
2194
|
|
|
2174
2195
|
if (context.depth !== 0 && sourceSubmissionId === undefined)
|
|
2175
2196
|
return yield* failure("start", "denied");
|
|
2176
|
-
const prepared = yield* preparedTarget(request.target, request.encodedInput);
|
|
2177
|
-
const resolved = prepared.resolved;
|
|
2178
|
-
const sourcePolicy = Option.getOrElse(prepared.source.policyOverride, () => context.policy);
|
|
2179
2197
|
|
|
2180
|
-
const grant = yield* Schema.decodeUnknownEffect(SubagentGrant)(
|
|
2198
|
+
const grant = yield* Schema.decodeUnknownEffect(SubagentGrant)(command.encodedGrant).pipe(
|
|
2181
2199
|
Effect.mapError((cause) => failure("start", "corrupt", cause)),
|
|
2182
2200
|
);
|
|
2183
2201
|
|
|
2202
|
+
const parameters = yield* decode(PersistedJson, command.encodedParameters, "start");
|
|
2203
|
+
|
|
2184
2204
|
const messageId = yield* messageIdFor([
|
|
2185
2205
|
context.source.threadId,
|
|
2186
|
-
|
|
2187
|
-
|
|
2206
|
+
command.delegationId,
|
|
2207
|
+
command.idempotencyKey,
|
|
2188
2208
|
]);
|
|
2189
2209
|
|
|
2190
2210
|
if (Option.isNone(deps.deliveries)) return yield* failure("start", "unavailable");
|
|
2211
|
+
const deliveries = deps.deliveries.value;
|
|
2191
2212
|
|
|
2192
|
-
const
|
|
2193
|
-
|
|
2194
|
-
|
|
2213
|
+
const retained = () =>
|
|
2214
|
+
deliveries
|
|
2215
|
+
.get({ ownerThreadId: context.source.threadId, messageId })
|
|
2216
|
+
.pipe(Effect.mapError(storageFailure("start")));
|
|
2195
2217
|
|
|
2196
|
-
const
|
|
2197
|
-
|
|
2198
|
-
|
|
2218
|
+
const replay = (saved: MessageDeliveryRecord) =>
|
|
2219
|
+
Effect.gen(function* () {
|
|
2220
|
+
const replayPrincipal = yield* authorize("start", "send");
|
|
2221
|
+
|
|
2222
|
+
yield* binding(command.target, "start");
|
|
2223
|
+
const metadata = saved.envelope.workerAdmission;
|
|
2224
|
+
|
|
2225
|
+
if (metadata === undefined) return yield* failure("start", "idempotency-conflict");
|
|
2226
|
+
const origin = metadata.origin;
|
|
2227
|
+
|
|
2228
|
+
if (
|
|
2229
|
+
origin.firstMessageId !== messageId ||
|
|
2230
|
+
metadata.messageId !== messageId ||
|
|
2231
|
+
origin.source.threadId !== context.source.threadId ||
|
|
2232
|
+
origin.worker.delegationId !== command.delegationId ||
|
|
2233
|
+
origin.worker.targetAgentId !== command.target.id ||
|
|
2234
|
+
!sameJson(metadata.parameters, parameters) ||
|
|
2235
|
+
!Schema.toEquivalence(SubagentGrant)(origin.grant, grant) ||
|
|
2236
|
+
origin.budgetScope !== command.budgetScope ||
|
|
2237
|
+
origin.depth !== context.depth + 1 ||
|
|
2238
|
+
(!("prepare" in command) &&
|
|
2239
|
+
(!samePolicy(origin.policy, command.policy) ||
|
|
2240
|
+
!Schema.toEquivalence(WorkerOrigin.fields.budget)(
|
|
2241
|
+
origin.budget,
|
|
2242
|
+
command.budget,
|
|
2243
|
+
) ||
|
|
2244
|
+
origin.toolCallAllowance !== command.toolCallAllowance))
|
|
2245
|
+
)
|
|
2246
|
+
return yield* failure("start", "idempotency-conflict");
|
|
2247
|
+
|
|
2248
|
+
return {
|
|
2249
|
+
worker: origin.worker,
|
|
2250
|
+
delivery: yield* send(
|
|
2251
|
+
origin,
|
|
2252
|
+
messageId,
|
|
2253
|
+
"prepare" in command ? saved.envelope.input : command.encodedInput,
|
|
2254
|
+
parameters,
|
|
2255
|
+
replayPrincipal,
|
|
2256
|
+
"start",
|
|
2257
|
+
),
|
|
2258
|
+
};
|
|
2259
|
+
});
|
|
2199
2260
|
|
|
2200
|
-
|
|
2261
|
+
const existing = yield* retained();
|
|
2201
2262
|
|
|
2202
|
-
|
|
2203
|
-
previousOrigin === undefined
|
|
2204
|
-
? yield* reportIntent(sourceBinding, resolved, request.delegationId, prepared.source)
|
|
2205
|
-
: previousOrigin.reporting;
|
|
2263
|
+
if (existing !== null) return yield* replay(existing);
|
|
2206
2264
|
|
|
2207
|
-
|
|
2208
|
-
|
|
2265
|
+
return yield* Effect.gen(function* () {
|
|
2266
|
+
const request =
|
|
2267
|
+
"prepare" in command ? { ...(yield* command.prepare), ...command } : command;
|
|
2268
|
+
|
|
2269
|
+
const prepared = yield* preparedTarget(request.target, request.encodedInput);
|
|
2270
|
+
const resolved = prepared.resolved;
|
|
2271
|
+
|
|
2272
|
+
const sourcePolicy = Option.getOrElse(
|
|
2273
|
+
prepared.source.policyOverride,
|
|
2274
|
+
() => context.policy,
|
|
2275
|
+
);
|
|
2276
|
+
|
|
2277
|
+
const now = yield* Clock.currentTimeMillis;
|
|
2278
|
+
const sourceBinding = prepared.source.binding;
|
|
2279
|
+
|
|
2280
|
+
if (sourceBinding === undefined)
|
|
2281
|
+
return yield* failure("start", "declaration-unavailable");
|
|
2209
2282
|
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2283
|
+
const reporting = yield* reportIntent(
|
|
2284
|
+
sourceBinding,
|
|
2285
|
+
resolved,
|
|
2286
|
+
request.delegationId,
|
|
2287
|
+
prepared.source,
|
|
2288
|
+
);
|
|
2289
|
+
|
|
2290
|
+
if (reporting?.mode === "standard" && sourceSubmissionId === undefined)
|
|
2291
|
+
return yield* failure("start", "denied");
|
|
2292
|
+
|
|
2293
|
+
const origin = yield* decode(
|
|
2294
|
+
WorkerOrigin,
|
|
2295
|
+
{
|
|
2296
|
+
worker: {
|
|
2297
|
+
schemaVersion: 1,
|
|
2298
|
+
delegationId: request.delegationId,
|
|
2299
|
+
targetAgentId: request.target.id,
|
|
2300
|
+
threadId: Schema.decodeSync(ThreadId)(messageId),
|
|
2301
|
+
},
|
|
2302
|
+
source: context.source,
|
|
2303
|
+
...(resolved.definition.runDisposition?.workerLifecycle === "assignment"
|
|
2304
|
+
? { lifecycle: "assignment" }
|
|
2305
|
+
: {}),
|
|
2306
|
+
targetDigests: resolved.digests,
|
|
2307
|
+
policy: request.policy,
|
|
2308
|
+
budget: request.budget,
|
|
2309
|
+
...(request.budgetScope === undefined ? {} : { budgetScope: request.budgetScope }),
|
|
2310
|
+
grant,
|
|
2311
|
+
depth: context.depth + 1,
|
|
2312
|
+
firstMessageId: messageId,
|
|
2313
|
+
createdAtMillis: now,
|
|
2314
|
+
expiresAtMillis: now + deps.limits.lifetimeMillis,
|
|
2315
|
+
...(reporting === undefined ? {} : { reporting }),
|
|
2316
|
+
...(request.toolCallAllowance === undefined
|
|
2317
|
+
? {}
|
|
2318
|
+
: { toolCallAllowance: request.toolCallAllowance }),
|
|
2218
2319
|
},
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
policy: request.policy,
|
|
2222
|
-
budget: request.budget,
|
|
2223
|
-
...(request.budgetScope === undefined ? {} : { budgetScope: request.budgetScope }),
|
|
2224
|
-
grant,
|
|
2225
|
-
depth: context.depth + 1,
|
|
2226
|
-
firstMessageId: messageId,
|
|
2227
|
-
createdAtMillis: existing?.envelope.workerAdmission?.origin.createdAtMillis ?? now,
|
|
2228
|
-
expiresAtMillis:
|
|
2229
|
-
existing?.envelope.workerAdmission?.origin.expiresAtMillis ??
|
|
2230
|
-
now + deps.limits.lifetimeMillis,
|
|
2231
|
-
...(reporting === undefined ? {} : { reporting }),
|
|
2232
|
-
...(request.toolCallAllowance === undefined
|
|
2233
|
-
? {}
|
|
2234
|
-
: { toolCallAllowance: request.toolCallAllowance }),
|
|
2235
|
-
},
|
|
2236
|
-
"start",
|
|
2237
|
-
);
|
|
2320
|
+
"start",
|
|
2321
|
+
);
|
|
2238
2322
|
|
|
2239
|
-
|
|
2323
|
+
yield* authorizeBudget(origin, principal);
|
|
2240
2324
|
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2325
|
+
const targetPolicy = Option.isSome(prepared.policy)
|
|
2326
|
+
? prepared.policy.value
|
|
2327
|
+
: origin.budgetScope === "worker-run"
|
|
2328
|
+
? resolved.definition.policy
|
|
2329
|
+
: AgentPolicy.resolve(
|
|
2330
|
+
resolved.definition.policyOverrides ?? resolved.definition.policy,
|
|
2331
|
+
sourcePolicy,
|
|
2332
|
+
);
|
|
2249
2333
|
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2334
|
+
if (
|
|
2335
|
+
!withinPolicy(
|
|
2336
|
+
origin,
|
|
2337
|
+
origin.budgetScope === "worker-run" ? targetPolicy : sourcePolicy,
|
|
2338
|
+
targetPolicy,
|
|
2339
|
+
)
|
|
2255
2340
|
)
|
|
2256
|
-
|
|
2257
|
-
return yield* failure("start", "capacity");
|
|
2341
|
+
return yield* failure("start", "capacity");
|
|
2258
2342
|
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2343
|
+
const delivery = yield* send(
|
|
2344
|
+
origin,
|
|
2345
|
+
messageId,
|
|
2346
|
+
request.encodedInput,
|
|
2347
|
+
request.encodedParameters,
|
|
2348
|
+
principal,
|
|
2349
|
+
"start",
|
|
2350
|
+
);
|
|
2267
2351
|
|
|
2268
|
-
|
|
2352
|
+
return { worker: origin.worker, delivery };
|
|
2353
|
+
}).pipe(
|
|
2354
|
+
Effect.catch((error) =>
|
|
2355
|
+
Effect.gen(function* () {
|
|
2356
|
+
if (!("prepare" in command)) return yield* Effect.fail(error);
|
|
2357
|
+
// A concurrent first writer may have retained the command during preparation or insert.
|
|
2358
|
+
// Only its exact declared arguments permit replay; absent proof preserves the failure.
|
|
2359
|
+
const saved = yield* retained();
|
|
2360
|
+
|
|
2361
|
+
return saved === null ? yield* Effect.fail(error) : yield* replay(saved);
|
|
2362
|
+
}),
|
|
2363
|
+
),
|
|
2364
|
+
);
|
|
2269
2365
|
}),
|
|
2270
2366
|
followUp: Effect.fn("WorkerHost.followUp")(function* (request) {
|
|
2271
2367
|
const principal = yield* authorize("followUp", "send", request.worker);
|
|
@@ -2351,9 +2447,8 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
2351
2447
|
summary: Effect.fn("WorkerHost.inspectWorker")(function* (request) {
|
|
2352
2448
|
yield* authorize("inspect", "read", request.worker);
|
|
2353
2449
|
const origin = yield* findOrigin(request.worker, request.target, "inspect");
|
|
2354
|
-
const source = yield* readWorkerState(context.source.threadId, "inspect");
|
|
2355
2450
|
|
|
2356
|
-
return yield* summarize(origin,
|
|
2451
|
+
return yield* summarize(origin, "inspect");
|
|
2357
2452
|
}),
|
|
2358
2453
|
observe: (request) =>
|
|
2359
2454
|
Stream.unwrap(
|
|
@@ -2438,33 +2533,87 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
2438
2533
|
yield* binding(request.target, "list");
|
|
2439
2534
|
if (!Number.isSafeInteger(request.limit) || request.limit < 1 || request.limit > 100)
|
|
2440
2535
|
return yield* failure("list", "capacity");
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
(request.after === undefined || origin.worker.threadId > request.after),
|
|
2454
|
-
)
|
|
2455
|
-
.sort((left, right) => left.worker.threadId.localeCompare(right.worker.threadId));
|
|
2536
|
+
if (Option.isNone(deps.deliveries)) return yield* failure("list", "unavailable");
|
|
2537
|
+
|
|
2538
|
+
const page = yield* deps.deliveries.value
|
|
2539
|
+
.list({
|
|
2540
|
+
ownerThreadId: context.source.threadId,
|
|
2541
|
+
limit: request.limit,
|
|
2542
|
+
workerStarts: { delegationId: request.delegationId, targetAgentId: request.target.id },
|
|
2543
|
+
...(request.after === undefined
|
|
2544
|
+
? {}
|
|
2545
|
+
: { after: Schema.decodeSync(IdempotencyKey)(request.after) }),
|
|
2546
|
+
})
|
|
2547
|
+
.pipe(Effect.mapError(storageFailure("list")));
|
|
2456
2548
|
|
|
2457
|
-
const
|
|
2549
|
+
const items = yield* Effect.forEach(page.items, (row) => {
|
|
2550
|
+
const origin = row.envelope.workerAdmission?.origin;
|
|
2458
2551
|
|
|
2459
|
-
|
|
2552
|
+
return origin === undefined || origin.firstMessageId !== row.key.messageId
|
|
2553
|
+
? failure("list", "corrupt")
|
|
2554
|
+
: summarize(origin, "list");
|
|
2555
|
+
});
|
|
2460
2556
|
|
|
2461
|
-
return {
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2557
|
+
return { items, next: page.next === null ? null : Schema.decodeSync(ThreadId)(page.next) };
|
|
2558
|
+
}),
|
|
2559
|
+
stop: Effect.fn("WorkerHost.stop")(function* (request) {
|
|
2560
|
+
const principal = yield* authorize("stop", "control", request.worker);
|
|
2561
|
+
|
|
2562
|
+
yield* findOrigin(request.worker, request.target, "stop");
|
|
2563
|
+
if (deps.ledger.stopWorker === undefined) return yield* failure("stop", "unavailable");
|
|
2564
|
+
const command = yield* decode(WorkerStop, request, "stop");
|
|
2565
|
+
|
|
2566
|
+
const id = Schema.decodeSync(RecordId)(
|
|
2567
|
+
`worker-stop:${yield* messageIdFor([context.source.threadId, request.idempotencyKey])}`,
|
|
2568
|
+
);
|
|
2569
|
+
|
|
2570
|
+
let retained = false;
|
|
2571
|
+
|
|
2572
|
+
for (let attempt = 0; attempt < 16; attempt++) {
|
|
2573
|
+
const current = yield* deps.store
|
|
2574
|
+
.inspectTail(ThreadTailRequest.make({ threadId: context.source.threadId }))
|
|
2575
|
+
.pipe(Effect.mapError(storageFailure("stop")));
|
|
2576
|
+
|
|
2577
|
+
const prior = Option.getOrUndefined(
|
|
2578
|
+
yield* exactRecord(context.source.threadId, id, "stop"),
|
|
2579
|
+
)?.record.payload;
|
|
2580
|
+
|
|
2581
|
+
if (prior !== undefined) {
|
|
2582
|
+
if (
|
|
2583
|
+
prior._tag !== "WorkerStopRequested" ||
|
|
2584
|
+
!Schema.toEquivalence(WorkerStop)(prior.command, command)
|
|
2585
|
+
)
|
|
2586
|
+
return yield* failure("stop", "idempotency-conflict");
|
|
2587
|
+
retained = true;
|
|
2588
|
+
break;
|
|
2589
|
+
}
|
|
2590
|
+
if (
|
|
2591
|
+
yield* append(
|
|
2592
|
+
context.source.threadId,
|
|
2593
|
+
id,
|
|
2594
|
+
WorkerStopRequested.make({ command }),
|
|
2595
|
+
{ ...current, records: [] },
|
|
2596
|
+
"stop",
|
|
2597
|
+
)
|
|
2598
|
+
) {
|
|
2599
|
+
retained = true;
|
|
2600
|
+
break;
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
if (!retained) return yield* failure("stop", "storage");
|
|
2604
|
+
// The destination serializes this seal with admission. Replays retain every earlier
|
|
2605
|
+
// abort intent and wait for active handlers/finalizers, rather than acknowledging a hint.
|
|
2606
|
+
while (true) {
|
|
2607
|
+
yield* hit("worker:before-stop-seal", "stop");
|
|
2608
|
+
|
|
2609
|
+
const owned = yield* deps.ledger
|
|
2610
|
+
.stopWorker({ threadId: request.worker.threadId, author: principal })
|
|
2611
|
+
.pipe(Effect.mapError(storageFailure("stop")));
|
|
2612
|
+
|
|
2613
|
+
yield* hit("worker:after-stop-seal", "stop");
|
|
2614
|
+
if (owned === 0) return command;
|
|
2615
|
+
yield* Effect.sleep(deps.settlementPollInterval);
|
|
2616
|
+
}
|
|
2468
2617
|
}),
|
|
2469
2618
|
cancel: Effect.fn("WorkerHost.cancel")(function* (request) {
|
|
2470
2619
|
const principal = yield* authorize("cancel", "control", request.worker);
|