effect-agent 0.1.0-beta.143 → 0.1.0-beta.148
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-CQMppCcy.d.mts → Admin-BzPfXvzC.d.mts} +37 -22
- package/dist/Agent-1DMSO_Fl.mjs.map +1 -1
- package/dist/{Agent-BWhD4Dvu.d.mts → Agent-Cjd_ZveM.d.mts} +7 -3
- package/dist/{AgentRuntime-xeCUXutN.mjs → AgentRuntime-D8U9gl9Q.mjs} +23 -18
- package/dist/AgentRuntime-D8U9gl9Q.mjs.map +1 -0
- package/dist/{AgentRuntime-B4kIzmSF.d.mts → AgentRuntime-DZdgNcBk.d.mts} +5 -5
- package/dist/{DurableAgentRuntime-D8GI66mP.mjs → DurableAgentRuntime-N5Meflyt.mjs} +70 -42
- package/dist/DurableAgentRuntime-N5Meflyt.mjs.map +1 -0
- package/dist/{EventSource-w9PonN9X.d.mts → EventSource-DJW_1kPY.d.mts} +2 -2
- package/dist/{Messaging-Dnmk31yq.d.mts → Messaging-ccQURKCf.d.mts} +3 -3
- package/dist/{MessagingHost-CajpPDPG.d.mts → MessagingHost-D04wE-7W.d.mts} +2 -2
- package/dist/{Output-DhtWbBcw.d.mts → Output-CFD1CPfU.d.mts} +2 -2
- package/dist/{PreparedInputAdmission-BfcLy_k4.d.mts → PreparedInputAdmission-CNRSDoYu.d.mts} +3 -3
- package/dist/{RunOptions-IlEXLG5F.d.mts → RunOptions-Cy7CjrjS.d.mts} +2 -2
- package/dist/{Subagent-8RzJkqJ9.mjs → Subagent-hG9lJ7tJ.mjs} +2 -2
- package/dist/{Subagent-8RzJkqJ9.mjs.map → Subagent-hG9lJ7tJ.mjs.map} +1 -1
- package/dist/{Subagent-Cl8D1mFM.d.mts → Subagent-zS6prSK6.d.mts} +5 -5
- package/dist/{SubagentHost-B6f4PBC2.d.mts → SubagentHost-C-4A7tA2.d.mts} +2 -2
- package/dist/{Subscription-ClkC4_T0.d.mts → Subscription-C279AjM6.d.mts} +2 -2
- package/dist/{SubscriptionInput-CMjEKL1z.d.mts → SubscriptionInput-7R-Xrpxd.d.mts} +2 -2
- package/dist/capabilities/Commands.d.mts +1 -1
- package/dist/capabilities/Messaging.d.mts +1 -1
- package/dist/capabilities/RunHooks.d.mts +1 -1
- package/dist/capabilities/Subagent.d.mts +1 -1
- package/dist/capabilities/Subagent.mjs +1 -1
- package/dist/core/Agent.d.mts +1 -1
- 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 -2
- package/dist/durable/LifecyclePublication.mjs +32 -12
- package/dist/durable/LifecyclePublication.mjs.map +1 -1
- package/dist/durable/MessageDelivery.d.mts +41 -41
- package/dist/durable/MessageDeliveryStoreConformance.d.mts +39 -39
- 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/ThreadStoreConformance.d.mts +1 -1
- package/dist/durable/WorkerHost.d.mts +25 -5
- package/dist/durable/WorkerHost.mjs.map +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/Output.d.mts +1 -1
- package/dist/engine/Output.mjs +1 -1
- package/dist/engine/RunOptions.d.mts +1 -1
- package/dist/engine/SubagentHost.d.mts +1 -1
- package/dist/index.d.mts +13 -13
- package/dist/index.mjs +3 -3
- package/dist/{output-contract-CANJJGA5.mjs → output-contract-DS-S8mTv.mjs} +46 -10
- package/dist/{output-contract-CANJJGA5.mjs.map → output-contract-DS-S8mTv.mjs.map} +1 -1
- package/package.json +1 -1
- package/src/core/Agent.ts +6 -2
- package/src/durable/DurableAgentRuntime.ts +50 -58
- package/src/durable/LifecyclePublication.ts +79 -25
- package/src/durable/WorkerHost.ts +23 -1
- package/src/durable/internal/worker-host.ts +169 -17
- package/src/engine/internal/agent-runtime.ts +80 -18
- package/src/engine/internal/output-contract.ts +67 -8
- package/dist/AgentRuntime-xeCUXutN.mjs.map +0 -1
- package/dist/DurableAgentRuntime-D8GI66mP.mjs.map +0 -1
|
@@ -128,6 +128,7 @@ import {
|
|
|
128
128
|
WorkerHostConfig,
|
|
129
129
|
WorkerPolicyResolver,
|
|
130
130
|
type WorkerPolicyTarget,
|
|
131
|
+
type WorkerStartAdmission,
|
|
131
132
|
} from "../WorkerHost.ts";
|
|
132
133
|
import {
|
|
133
134
|
definitionDigestsEqual,
|
|
@@ -263,6 +264,20 @@ export class WorkerInputControl extends Context.Service<
|
|
|
263
264
|
}
|
|
264
265
|
>()("@effect-agent/thread/internal/WorkerInputControl") {}
|
|
265
266
|
|
|
267
|
+
const CurrentStartPreparation = Context.Reference<WorkerStartAdmission | undefined>(
|
|
268
|
+
"@effect-agent/thread/internal/CurrentStartPreparation",
|
|
269
|
+
{ defaultValue: () => undefined },
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
const CurrentStartDelivery = Context.Reference<
|
|
273
|
+
| {
|
|
274
|
+
readonly authorization: WorkerStartAdmission;
|
|
275
|
+
readonly admission: WorkerAdmission;
|
|
276
|
+
readonly inputDigest: Digest;
|
|
277
|
+
}
|
|
278
|
+
| undefined
|
|
279
|
+
>("@effect-agent/thread/internal/CurrentStartDelivery", { defaultValue: () => undefined });
|
|
280
|
+
|
|
266
281
|
/** No in-memory ownership: every mutation is reserved in source canonical history with CAS. */
|
|
267
282
|
export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
268
283
|
options: WorkerRuntimeOptions,
|
|
@@ -290,9 +305,40 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
290
305
|
const withCrypto = <A, E>(effect: Effect.Effect<A, E, Crypto.Crypto>) =>
|
|
291
306
|
Effect.provideService(effect, Crypto.Crypto, deps.crypto);
|
|
292
307
|
|
|
308
|
+
const currentStart = Effect.fnUntraced(function* (
|
|
309
|
+
admission: WorkerStartAdmission | undefined,
|
|
310
|
+
sourceThreadId: ThreadId,
|
|
311
|
+
sourceSubmissionId: SubmissionId | undefined,
|
|
312
|
+
principal: Principal,
|
|
313
|
+
targetAgentId: AgentId,
|
|
314
|
+
continuationOf: WorkerContinuation | undefined,
|
|
315
|
+
) {
|
|
316
|
+
if (
|
|
317
|
+
admission === undefined ||
|
|
318
|
+
admission.sourceThreadId !== sourceThreadId ||
|
|
319
|
+
admission.sourceSubmissionId !== sourceSubmissionId ||
|
|
320
|
+
(admission.principal !== principal && admission.requestedPrincipal !== principal) ||
|
|
321
|
+
admission.targetAgentId !== targetAgentId ||
|
|
322
|
+
!Schema.toEquivalence(Schema.UndefinedOr(WorkerContinuation))(
|
|
323
|
+
admission.continuationOf,
|
|
324
|
+
continuationOf,
|
|
325
|
+
)
|
|
326
|
+
)
|
|
327
|
+
return undefined;
|
|
328
|
+
|
|
329
|
+
const tail = yield* deps.store
|
|
330
|
+
.inspectTail(ThreadTailRequest.make({ threadId: sourceThreadId }))
|
|
331
|
+
.pipe(Effect.mapError(storageFailure("start")));
|
|
332
|
+
|
|
333
|
+
return tail.tailSequence === admission.tailSequence && tail.tailDigest === admission.tailDigest
|
|
334
|
+
? admission
|
|
335
|
+
: undefined;
|
|
336
|
+
});
|
|
337
|
+
|
|
293
338
|
const authorizeBudget = Effect.fn("WorkerHost.authorizeBudget")(function* (
|
|
294
339
|
origin: WorkerOrigin,
|
|
295
340
|
principal: Principal,
|
|
341
|
+
admission?: WorkerStartAdmission,
|
|
296
342
|
) {
|
|
297
343
|
yield* verifyContinuation(origin);
|
|
298
344
|
if (origin.budgetScope !== "worker-run") return;
|
|
@@ -300,6 +346,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
300
346
|
if (origin.depth !== 1) return yield* failure("start", "denied");
|
|
301
347
|
|
|
302
348
|
yield* deps.budgetAuthorizer.authorize({
|
|
349
|
+
...(admission === undefined ? {} : { admission }),
|
|
303
350
|
source: origin.source,
|
|
304
351
|
principal,
|
|
305
352
|
worker: origin.worker,
|
|
@@ -1049,6 +1096,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1049
1096
|
input: PersistedJson,
|
|
1050
1097
|
principal: Principal,
|
|
1051
1098
|
retainedFrameworkInput: boolean,
|
|
1099
|
+
authorization?: WorkerStartAdmission,
|
|
1052
1100
|
): Effect.fn.Return<void, WorkerError> {
|
|
1053
1101
|
const origin = admission.origin;
|
|
1054
1102
|
|
|
@@ -1133,6 +1181,11 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1133
1181
|
? {
|
|
1134
1182
|
...targetRequest,
|
|
1135
1183
|
_tag: "InitialInput",
|
|
1184
|
+
...(authorization === undefined ||
|
|
1185
|
+
authorization.tailSequence !== current.tailSequence ||
|
|
1186
|
+
authorization.tailDigest !== current.tailDigest
|
|
1187
|
+
? {}
|
|
1188
|
+
: { admission: authorization }),
|
|
1136
1189
|
...(origin.continuationOf === undefined
|
|
1137
1190
|
? {}
|
|
1138
1191
|
: { continuationOf: origin.continuationOf }),
|
|
@@ -1351,7 +1404,24 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1351
1404
|
}
|
|
1352
1405
|
|
|
1353
1406
|
yield* verifyContinuation(admission.origin);
|
|
1407
|
+
const delivery = yield* CurrentStartDelivery;
|
|
1408
|
+
|
|
1409
|
+
const authorization =
|
|
1410
|
+
delivery !== undefined &&
|
|
1411
|
+
sameAdmission(delivery.admission, admission) &&
|
|
1412
|
+
delivery.inputDigest === inputDigest
|
|
1413
|
+
? yield* currentStart(
|
|
1414
|
+
delivery.authorization,
|
|
1415
|
+
admission.origin.source.threadId,
|
|
1416
|
+
admission.sourceSubmissionId,
|
|
1417
|
+
options.principal,
|
|
1418
|
+
agentId,
|
|
1419
|
+
admission.origin.continuationOf,
|
|
1420
|
+
)
|
|
1421
|
+
: undefined;
|
|
1422
|
+
|
|
1354
1423
|
yield* deps.authorizer.authorize({
|
|
1424
|
+
...(authorization === undefined ? {} : { admission: authorization }),
|
|
1355
1425
|
sourceThreadId: admission.origin.source.threadId,
|
|
1356
1426
|
...(admission.sourceSubmissionId === undefined
|
|
1357
1427
|
? {}
|
|
@@ -1364,7 +1434,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1364
1434
|
? {}
|
|
1365
1435
|
: { continuationOf: admission.origin.continuationOf }),
|
|
1366
1436
|
});
|
|
1367
|
-
yield* authorizeBudget(admission.origin, options.principal);
|
|
1437
|
+
yield* authorizeBudget(admission.origin, options.principal, authorization);
|
|
1368
1438
|
if (
|
|
1369
1439
|
admission.origin.worker.threadId !== options.threadId ||
|
|
1370
1440
|
admission.origin.worker.targetAgentId !== agentId ||
|
|
@@ -1378,7 +1448,14 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1378
1448
|
|
|
1379
1449
|
if (retainedFrameworkInput && admission.reportKind !== "update")
|
|
1380
1450
|
yield* validateCompletion(options.messageAdmission, options, agentId, inputDigest);
|
|
1381
|
-
yield* reservation(
|
|
1451
|
+
yield* reservation(
|
|
1452
|
+
admission,
|
|
1453
|
+
inputDigest,
|
|
1454
|
+
input,
|
|
1455
|
+
options.principal,
|
|
1456
|
+
retainedFrameworkInput,
|
|
1457
|
+
authorization,
|
|
1458
|
+
);
|
|
1382
1459
|
|
|
1383
1460
|
return admission;
|
|
1384
1461
|
});
|
|
@@ -1866,13 +1943,17 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1866
1943
|
principal: Principal,
|
|
1867
1944
|
sourceSubmissionId?: SubmissionId,
|
|
1868
1945
|
): SubagentHost["Service"] => {
|
|
1946
|
+
const requestedPrincipal = principal;
|
|
1947
|
+
|
|
1869
1948
|
const authorize = (
|
|
1870
1949
|
operation: WorkerError["operation"],
|
|
1871
1950
|
access: "context" | "read" | "send" | "control",
|
|
1872
1951
|
worker?: WorkerRef,
|
|
1873
1952
|
continuationOf?: WorkerContinuation,
|
|
1953
|
+
admission?: WorkerStartAdmission,
|
|
1874
1954
|
) =>
|
|
1875
1955
|
deps.authorizer.authorize({
|
|
1956
|
+
...(admission === undefined ? {} : { admission }),
|
|
1876
1957
|
sourceThreadId: context.source.threadId,
|
|
1877
1958
|
...(sourceSubmissionId === undefined ? {} : { sourceSubmissionId }),
|
|
1878
1959
|
principal,
|
|
@@ -1894,6 +1975,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1894
1975
|
target: Agent.AnyDefinition,
|
|
1895
1976
|
encodedInput: unknown,
|
|
1896
1977
|
continuationOf?: WorkerContinuation,
|
|
1978
|
+
authorization?: WorkerStartAdmission,
|
|
1897
1979
|
) {
|
|
1898
1980
|
const resolved = yield* binding(target, "start");
|
|
1899
1981
|
|
|
@@ -1908,8 +1990,18 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
1908
1990
|
|
|
1909
1991
|
const source = yield* sourceAuthority(context.source.threadId, sourceSubmissionId);
|
|
1910
1992
|
|
|
1993
|
+
const admission = yield* currentStart(
|
|
1994
|
+
authorization,
|
|
1995
|
+
context.source.threadId,
|
|
1996
|
+
sourceSubmissionId,
|
|
1997
|
+
principal,
|
|
1998
|
+
target.id,
|
|
1999
|
+
continuationOf,
|
|
2000
|
+
);
|
|
2001
|
+
|
|
1911
2002
|
const policy = yield* resolveTargetPolicy({
|
|
1912
2003
|
_tag: "InitialInput",
|
|
2004
|
+
...(admission === undefined ? {} : { admission }),
|
|
1913
2005
|
...(continuationOf === undefined ? {} : { continuationOf }),
|
|
1914
2006
|
definition: resolved.definition,
|
|
1915
2007
|
definitions: resolved.digests,
|
|
@@ -2064,6 +2156,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
2064
2156
|
encodedParameters: unknown,
|
|
2065
2157
|
principal: Principal,
|
|
2066
2158
|
operation: "start" | "followUp",
|
|
2159
|
+
authorization?: WorkerStartAdmission,
|
|
2067
2160
|
) {
|
|
2068
2161
|
if (Option.isNone(deps.deliveries)) return yield* failure(operation, "unavailable");
|
|
2069
2162
|
const deliveries = deps.deliveries.value;
|
|
@@ -2146,17 +2239,25 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
2146
2239
|
Effect.provideService(MessageDeliveryStore, deliveries),
|
|
2147
2240
|
Effect.provideService(PreparedInputAdmission, {
|
|
2148
2241
|
submit: (prepared) =>
|
|
2149
|
-
deps
|
|
2150
|
-
.
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2242
|
+
deps.submit(prepared).pipe(
|
|
2243
|
+
Effect.provideService(
|
|
2244
|
+
CurrentStartDelivery,
|
|
2245
|
+
authorization !== undefined &&
|
|
2246
|
+
prepared.workerAdmission !== undefined &&
|
|
2247
|
+
prepared.admissionKey === messageId &&
|
|
2248
|
+
prepared.inputDigest === inputDigest &&
|
|
2249
|
+
sameOrigin(prepared.workerAdmission.origin, origin)
|
|
2250
|
+
? { authorization, admission: prepared.workerAdmission, inputDigest }
|
|
2251
|
+
: undefined,
|
|
2159
2252
|
),
|
|
2253
|
+
Effect.mapError((error) =>
|
|
2254
|
+
error._tag === "AdmissionConflict" || error._tag === "AgentInputError"
|
|
2255
|
+
? ScheduledInputRefused.make({ code: error._tag, cause: error })
|
|
2256
|
+
: error._tag === "AdmissionPolicyError" && error.reason === "refused"
|
|
2257
|
+
? ScheduledInputRefused.make({ code: error.code, cause: error })
|
|
2258
|
+
: ScheduledInputRetryable.make({ reason: "storage", cause: error }),
|
|
2259
|
+
),
|
|
2260
|
+
),
|
|
2160
2261
|
submissionStatus: (receipt) =>
|
|
2161
2262
|
deps
|
|
2162
2263
|
.status(receipt)
|
|
@@ -2393,10 +2494,23 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
2393
2494
|
request.continuationOf.delegationId,
|
|
2394
2495
|
);
|
|
2395
2496
|
|
|
2396
|
-
yield*
|
|
2497
|
+
const admission = yield* currentStart(
|
|
2498
|
+
yield* CurrentStartPreparation,
|
|
2499
|
+
context.source.threadId,
|
|
2500
|
+
sourceSubmissionId,
|
|
2501
|
+
principal,
|
|
2502
|
+
request.target.id,
|
|
2503
|
+
previous?.evidence,
|
|
2504
|
+
);
|
|
2505
|
+
|
|
2506
|
+
yield* authorize("start", "send", undefined, previous?.evidence, admission);
|
|
2397
2507
|
|
|
2398
|
-
return (yield* preparedTarget(
|
|
2399
|
-
.
|
|
2508
|
+
return (yield* preparedTarget(
|
|
2509
|
+
request.target,
|
|
2510
|
+
request.encodedInput,
|
|
2511
|
+
previous?.evidence,
|
|
2512
|
+
admission,
|
|
2513
|
+
)).policy;
|
|
2400
2514
|
}),
|
|
2401
2515
|
start: Effect.fn("WorkerHost.start")(function* <E = never, R = never>(
|
|
2402
2516
|
command: StartWorkerRequest | DeferredStartWorkerRequest<E, R>,
|
|
@@ -2411,8 +2525,26 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
2411
2525
|
command.delegationId,
|
|
2412
2526
|
);
|
|
2413
2527
|
|
|
2528
|
+
// Bind the decision to the tail before authorization. If it changes while the
|
|
2529
|
+
// authorizer reads current state, currentStart rejects reuse of this decision.
|
|
2530
|
+
const tail = yield* deps.store
|
|
2531
|
+
.inspectTail(ThreadTailRequest.make({ threadId: context.source.threadId }))
|
|
2532
|
+
.pipe(Effect.mapError(storageFailure("start")));
|
|
2533
|
+
|
|
2414
2534
|
const principal = yield* authorize("start", "send", undefined, previous?.evidence);
|
|
2415
2535
|
|
|
2536
|
+
const startAdmission: WorkerStartAdmission = {
|
|
2537
|
+
sourceThreadId: context.source.threadId,
|
|
2538
|
+
...(sourceSubmissionId === undefined ? {} : { sourceSubmissionId }),
|
|
2539
|
+
requestedPrincipal,
|
|
2540
|
+
principal,
|
|
2541
|
+
targetAgentId: command.target.id,
|
|
2542
|
+
...(previous === undefined ? {} : { continuationOf: previous.evidence }),
|
|
2543
|
+
access: "send",
|
|
2544
|
+
tailSequence: tail.tailSequence,
|
|
2545
|
+
tailDigest: tail.tailDigest,
|
|
2546
|
+
};
|
|
2547
|
+
|
|
2416
2548
|
if (context.depth !== 0 && sourceSubmissionId === undefined)
|
|
2417
2549
|
return yield* failure("start", "denied");
|
|
2418
2550
|
|
|
@@ -2507,7 +2639,14 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
2507
2639
|
|
|
2508
2640
|
return yield* Effect.gen(function* () {
|
|
2509
2641
|
const request =
|
|
2510
|
-
"prepare" in command
|
|
2642
|
+
"prepare" in command
|
|
2643
|
+
? {
|
|
2644
|
+
...(yield* command.prepare.pipe(
|
|
2645
|
+
Effect.provideService(CurrentStartPreparation, startAdmission),
|
|
2646
|
+
)),
|
|
2647
|
+
...command,
|
|
2648
|
+
}
|
|
2649
|
+
: command;
|
|
2511
2650
|
|
|
2512
2651
|
const currentPrevious =
|
|
2513
2652
|
command.continuationOf === undefined
|
|
@@ -2535,6 +2674,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
2535
2674
|
request.target,
|
|
2536
2675
|
request.encodedInput,
|
|
2537
2676
|
currentPrevious?.evidence,
|
|
2677
|
+
startAdmission,
|
|
2538
2678
|
);
|
|
2539
2679
|
|
|
2540
2680
|
const resolved = prepared.resolved;
|
|
@@ -2596,7 +2736,18 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
2596
2736
|
"start",
|
|
2597
2737
|
);
|
|
2598
2738
|
|
|
2599
|
-
yield* authorizeBudget(
|
|
2739
|
+
yield* authorizeBudget(
|
|
2740
|
+
origin,
|
|
2741
|
+
principal,
|
|
2742
|
+
yield* currentStart(
|
|
2743
|
+
startAdmission,
|
|
2744
|
+
context.source.threadId,
|
|
2745
|
+
sourceSubmissionId,
|
|
2746
|
+
principal,
|
|
2747
|
+
request.target.id,
|
|
2748
|
+
currentPrevious?.evidence,
|
|
2749
|
+
),
|
|
2750
|
+
);
|
|
2600
2751
|
|
|
2601
2752
|
const targetPolicy = Option.isSome(prepared.policy)
|
|
2602
2753
|
? prepared.policy.value
|
|
@@ -2623,6 +2774,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
|
|
|
2623
2774
|
request.encodedParameters,
|
|
2624
2775
|
principal,
|
|
2625
2776
|
"start",
|
|
2777
|
+
startAdmission,
|
|
2626
2778
|
);
|
|
2627
2779
|
|
|
2628
2780
|
return { worker: origin.worker, delivery };
|
|
@@ -3248,8 +3248,8 @@ export const formatRunStatus = (view: RunStatusView): string => {
|
|
|
3248
3248
|
/**
|
|
3249
3249
|
* The run-status message is derived per request and appended only to the
|
|
3250
3250
|
* OUTGOING prompt: official history and durable commits never carry it, so it
|
|
3251
|
-
* can never accumulate or replay.
|
|
3252
|
-
* leaving the
|
|
3251
|
+
* can never accumulate or replay. Capable adapters receive trailing system guidance,
|
|
3252
|
+
* leaving the preceding user/tool result available as a cache-write boundary.
|
|
3253
3253
|
*/
|
|
3254
3254
|
const outgoingModelPrompt = (
|
|
3255
3255
|
policy: AgentPolicy,
|
|
@@ -3257,13 +3257,13 @@ const outgoingModelPrompt = (
|
|
|
3257
3257
|
prepared: Prompt.Prompt,
|
|
3258
3258
|
turn: number,
|
|
3259
3259
|
declaredToolCalls: number,
|
|
3260
|
-
|
|
3260
|
+
systemMessagesInHistory: boolean,
|
|
3261
|
+
): Effect.Effect<Prompt.Prompt> =>
|
|
3261
3262
|
Effect.gen(function* () {
|
|
3262
3263
|
if (policy.runStatus !== "appended") {
|
|
3263
3264
|
return prepared;
|
|
3264
3265
|
}
|
|
3265
3266
|
const now = yield* Clock.currentTimeMillis;
|
|
3266
|
-
const provider = yield* Model.ProviderName;
|
|
3267
3267
|
|
|
3268
3268
|
const status = formatRunStatus({
|
|
3269
3269
|
turn,
|
|
@@ -3280,7 +3280,7 @@ const outgoingModelPrompt = (
|
|
|
3280
3280
|
|
|
3281
3281
|
return Prompt.fromMessages([
|
|
3282
3282
|
...prepared.content,
|
|
3283
|
-
|
|
3283
|
+
systemMessagesInHistory
|
|
3284
3284
|
? Prompt.systemMessage({ content: status })
|
|
3285
3285
|
: Prompt.userMessage({ content: [Prompt.textPart({ text: status })] }),
|
|
3286
3286
|
]);
|
|
@@ -3880,6 +3880,8 @@ const estimateContextTokens = Effect.fn("AgentRuntime.estimateContextTokens")(fu
|
|
|
3880
3880
|
const nextContextEstimate = Effect.fn("AgentRuntime.nextContextEstimate")(function* (
|
|
3881
3881
|
context: RunContext,
|
|
3882
3882
|
view: ReadonlyArray<Prompt.Message>,
|
|
3883
|
+
systemMessagesInHistory: boolean,
|
|
3884
|
+
staticInstructions: Prompt.RawInput | undefined,
|
|
3883
3885
|
) {
|
|
3884
3886
|
const state = context.compaction;
|
|
3885
3887
|
|
|
@@ -3896,7 +3898,12 @@ const nextContextEstimate = Effect.fn("AgentRuntime.nextContextEstimate")(functi
|
|
|
3896
3898
|
}
|
|
3897
3899
|
|
|
3898
3900
|
return yield* estimateContextTokens(
|
|
3899
|
-
prepareModelPrompt(
|
|
3901
|
+
prepareModelPrompt(
|
|
3902
|
+
Prompt.fromMessages(view),
|
|
3903
|
+
undefined,
|
|
3904
|
+
systemMessagesInHistory,
|
|
3905
|
+
staticInstructions,
|
|
3906
|
+
).content,
|
|
3900
3907
|
);
|
|
3901
3908
|
});
|
|
3902
3909
|
|
|
@@ -5551,6 +5558,25 @@ const makeTurn = <
|
|
|
5551
5558
|
const withCallModel = <A, E, R>(operation: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
|
|
5552
5559
|
modelServices === undefined ? operation : Effect.provide(operation, modelServices);
|
|
5553
5560
|
|
|
5561
|
+
const provider = yield* withCallModel(Model.ProviderName);
|
|
5562
|
+
|
|
5563
|
+
// Older adapters do not expose this native capability. Keep their safe
|
|
5564
|
+
// projection; only the established OpenAI and xAI transports default to it.
|
|
5565
|
+
// Evaluate against this call's services so scoped model overrides are honored.
|
|
5566
|
+
const callModel: LanguageModel.LanguageModel & {
|
|
5567
|
+
readonly supportsSystemMessagesInHistory?: Effect.Effect<boolean> | undefined;
|
|
5568
|
+
} = yield* withCallModel(LanguageModel.LanguageModel);
|
|
5569
|
+
|
|
5570
|
+
const systemMessagesInHistory =
|
|
5571
|
+
callModel.supportsSystemMessagesInHistory === undefined
|
|
5572
|
+
? provider === "openai" || provider === "xai"
|
|
5573
|
+
: yield* withCallModel(callModel.supportsSystemMessagesInHistory);
|
|
5574
|
+
|
|
5575
|
+
const staticInstructions =
|
|
5576
|
+
typeof agent.definition.instructions === "function"
|
|
5577
|
+
? undefined
|
|
5578
|
+
: agent.definition.instructions;
|
|
5579
|
+
|
|
5554
5580
|
const priorCompactionPrefix = context.preparedCompactionSource?.prefix;
|
|
5555
5581
|
|
|
5556
5582
|
if (priorCompactionPrefix !== undefined && priorCompactionPrefix.length > 0) {
|
|
@@ -5690,9 +5716,14 @@ const makeTurn = <
|
|
|
5690
5716
|
|
|
5691
5717
|
const canonicalDecoration = !admissionRequired
|
|
5692
5718
|
? Prompt.empty
|
|
5693
|
-
: yield* outgoingModelPrompt(
|
|
5694
|
-
|
|
5695
|
-
|
|
5719
|
+
: yield* outgoingModelPrompt(
|
|
5720
|
+
policy,
|
|
5721
|
+
context,
|
|
5722
|
+
Prompt.empty,
|
|
5723
|
+
turn,
|
|
5724
|
+
priorToolCalls,
|
|
5725
|
+
systemMessagesInHistory,
|
|
5726
|
+
).pipe(withCallModel);
|
|
5696
5727
|
|
|
5697
5728
|
const estimateToolSchemaTokens = Effect.suspend(() => {
|
|
5698
5729
|
const choice = modelToolChoice();
|
|
@@ -5752,8 +5783,15 @@ const makeTurn = <
|
|
|
5752
5783
|
// canonical indices still locate appended content and compaction coverage.
|
|
5753
5784
|
const estimateSourceContext = (view: ReadonlyArray<Prompt.Message>) =>
|
|
5754
5785
|
options.context === undefined && options.transientContext === undefined
|
|
5755
|
-
? nextContextEstimate(context, view)
|
|
5756
|
-
: estimateCallTokens(
|
|
5786
|
+
? nextContextEstimate(context, view, systemMessagesInHistory, staticInstructions)
|
|
5787
|
+
: estimateCallTokens(
|
|
5788
|
+
prepareModelPrompt(
|
|
5789
|
+
Prompt.fromMessages(view),
|
|
5790
|
+
undefined,
|
|
5791
|
+
systemMessagesInHistory,
|
|
5792
|
+
staticInstructions,
|
|
5793
|
+
).content,
|
|
5794
|
+
);
|
|
5757
5795
|
|
|
5758
5796
|
let prepared = buildCompactedView(modelContext.prompt.content, context.compaction);
|
|
5759
5797
|
let sourceTokens: number | undefined;
|
|
@@ -6005,6 +6043,7 @@ const makeTurn = <
|
|
|
6005
6043
|
transientContext,
|
|
6006
6044
|
turn,
|
|
6007
6045
|
priorToolCalls,
|
|
6046
|
+
systemMessagesInHistory,
|
|
6008
6047
|
).pipe(withCallModel);
|
|
6009
6048
|
|
|
6010
6049
|
const derivedPromptContentTokens = !admissionRequired
|
|
@@ -6186,9 +6225,12 @@ const makeTurn = <
|
|
|
6186
6225
|
prepareModelPrompt(
|
|
6187
6226
|
Prompt.fromMessages([...basis.content, ...transientContext.content]),
|
|
6188
6227
|
outputContract._tag === "rendered" ? outputContract.part : undefined,
|
|
6228
|
+
systemMessagesInHistory,
|
|
6229
|
+
staticInstructions,
|
|
6189
6230
|
),
|
|
6190
6231
|
turn,
|
|
6191
6232
|
priorToolCalls,
|
|
6233
|
+
systemMessagesInHistory,
|
|
6192
6234
|
).pipe(
|
|
6193
6235
|
withCallModel,
|
|
6194
6236
|
Effect.flatMap((providerPrompt) =>
|
|
@@ -6759,7 +6801,16 @@ const makeTurn = <
|
|
|
6759
6801
|
const settleOrFollowUp = (history: Prompt.Prompt) =>
|
|
6760
6802
|
Effect.gen(function* () {
|
|
6761
6803
|
yield* advanceHistory(context, history, options);
|
|
6762
|
-
|
|
6804
|
+
|
|
6805
|
+
// Do not consume durable receipts that this completed Turn cannot cover.
|
|
6806
|
+
// Final-answer mode permits one grace Turn, but never a second finalization.
|
|
6807
|
+
const turnsBlocked =
|
|
6808
|
+
policy.onExhaustion === "fail" ? turn >= bounds.maxTurns : turn > bounds.maxTurns;
|
|
6809
|
+
|
|
6810
|
+
const steering =
|
|
6811
|
+
turnsBlocked || context.finalizationUsed
|
|
6812
|
+
? []
|
|
6813
|
+
: yield* drainInputs(context, options);
|
|
6763
6814
|
|
|
6764
6815
|
const queued =
|
|
6765
6816
|
steering.length > 0
|
|
@@ -6771,9 +6822,6 @@ const makeTurn = <
|
|
|
6771
6822
|
// `maxTurns` (RUN-019): `turn > maxTurns` can only be
|
|
6772
6823
|
// `maxTurns + 1`, so a second grace is structurally
|
|
6773
6824
|
// impossible.
|
|
6774
|
-
const turnsBlocked =
|
|
6775
|
-
policy.onExhaustion === "fail" ? turn >= bounds.maxTurns : turn > bounds.maxTurns;
|
|
6776
|
-
|
|
6777
6825
|
if (turnsBlocked) {
|
|
6778
6826
|
return failRunEventStream(
|
|
6779
6827
|
AgentPolicyError.make({
|
|
@@ -7187,9 +7235,24 @@ const toolBatchContinuation = <
|
|
|
7187
7235
|
}
|
|
7188
7236
|
}
|
|
7189
7237
|
|
|
7190
|
-
|
|
7238
|
+
// A completion Tool can admit steering while its handler runs. Only claim it
|
|
7239
|
+
// when another ordinary Turn can cover it; otherwise keep it queued for a new Run.
|
|
7240
|
+
const bounds = effectiveRunBounds(agent.definition.policy, options);
|
|
7241
|
+
|
|
7242
|
+
const canContinue =
|
|
7243
|
+
turn < bounds.maxTurns &&
|
|
7244
|
+
(completion?.required === true
|
|
7245
|
+
? toolCalls + context.programmaticToolCalls < bounds.maxToolCalls
|
|
7246
|
+
: toolCalls + context.programmaticToolCalls <= bounds.maxToolCalls) &&
|
|
7247
|
+
!context.tokenExhausted &&
|
|
7248
|
+
!context.finalizationUsed &&
|
|
7249
|
+
(yield* Clock.currentTimeMillis) < context.durationDeadlineMillis;
|
|
7250
|
+
|
|
7251
|
+
const steering =
|
|
7252
|
+
Option.isSome(selectedOutput) && !canContinue ? [] : yield* drainInputs(context, options);
|
|
7253
|
+
|
|
7254
|
+
if (Option.isSome(selectedOutput) && steering.length === 0) {
|
|
7191
7255
|
const output = selectedOutput.value;
|
|
7192
|
-
const bounds = effectiveRunBounds(agent.definition.policy, options);
|
|
7193
7256
|
|
|
7194
7257
|
const exhausted = context.tokenExhausted
|
|
7195
7258
|
? "tokens"
|
|
@@ -7225,7 +7288,6 @@ const toolBatchContinuation = <
|
|
|
7225
7288
|
),
|
|
7226
7289
|
);
|
|
7227
7290
|
}
|
|
7228
|
-
const steering = yield* drainInputs(context, options);
|
|
7229
7291
|
const nextPrompt = yield* appendInputs(context, history, steering, options);
|
|
7230
7292
|
|
|
7231
7293
|
return nextTurn(nextPrompt, turn + 1, toolCalls);
|
|
@@ -147,22 +147,81 @@ export const outputSchemaContract = (definition: Agent.AnyDefinition): OutputCon
|
|
|
147
147
|
const sameSystemMessage = Schema.toEquivalence(Prompt.SystemMessage);
|
|
148
148
|
|
|
149
149
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
150
|
+
* Keep supported system instructions in conversation order so changing late guidance
|
|
151
|
+
* cannot invalidate the preceding user/tool cache prefix. Omit an exact repeat
|
|
152
|
+
* only when no distinct system instruction intervened; returning to an earlier
|
|
153
|
+
* instruction after a different one must preserve the new directive's precedence.
|
|
154
|
+
* Anchor the immutable output contract after the initial system block.
|
|
155
|
+
* Restore leading static instructions for caller-supplied conversation-only history.
|
|
155
156
|
*
|
|
156
157
|
* This runs after preparation and compaction. Canonical messages, protected
|
|
157
158
|
* instruction/input spans and compaction coverage retain their original positions.
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
159
|
+
* Adapters without this capability retain the grouped-system projection: keep the
|
|
160
|
+
* last equivalent instruction with its native options, then the contract and
|
|
161
|
+
* conversation. This prevents older Anthropic adapters from discarding system groups.
|
|
161
162
|
*/
|
|
162
163
|
export const prepareModelPrompt = (
|
|
163
164
|
prompt: Prompt.Prompt,
|
|
164
165
|
contract: Prompt.SystemMessage | undefined,
|
|
166
|
+
systemMessagesInHistory: boolean,
|
|
167
|
+
staticInstructions: Prompt.RawInput | undefined,
|
|
165
168
|
): Prompt.Prompt => {
|
|
169
|
+
if (systemMessagesInHistory) {
|
|
170
|
+
const content: Array<Prompt.Message> = [];
|
|
171
|
+
let lastSystem: Prompt.SystemMessage | undefined;
|
|
172
|
+
let ordered = prompt.content;
|
|
173
|
+
|
|
174
|
+
if (staticInstructions !== undefined) {
|
|
175
|
+
const firstConversation = prompt.content.findIndex((message) => message.role !== "system");
|
|
176
|
+
|
|
177
|
+
const leading = prompt.content.slice(
|
|
178
|
+
0,
|
|
179
|
+
firstConversation === -1 ? prompt.content.length : firstConversation,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const restored: Array<Prompt.Message> = [];
|
|
183
|
+
|
|
184
|
+
const instructions =
|
|
185
|
+
typeof staticInstructions === "string"
|
|
186
|
+
? [Prompt.systemMessage({ content: staticInstructions })]
|
|
187
|
+
: Prompt.make(staticInstructions).content;
|
|
188
|
+
|
|
189
|
+
for (const message of instructions) {
|
|
190
|
+
if (message.role !== "system") break;
|
|
191
|
+
if (
|
|
192
|
+
leading.some(
|
|
193
|
+
(candidate) => candidate.role === "system" && sameSystemMessage(candidate, message),
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
break;
|
|
197
|
+
|
|
198
|
+
const original = prompt.content.find(
|
|
199
|
+
(candidate) => candidate.role === "system" && sameSystemMessage(candidate, message),
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
if (original === undefined) break;
|
|
203
|
+
restored.push(original);
|
|
204
|
+
}
|
|
205
|
+
if (restored.length > 0)
|
|
206
|
+
ordered = [...leading, ...restored, ...prompt.content.slice(leading.length)];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
for (const message of ordered) {
|
|
210
|
+
if (message.role === "system") {
|
|
211
|
+
if (lastSystem !== undefined && sameSystemMessage(lastSystem, message)) continue;
|
|
212
|
+
lastSystem = message;
|
|
213
|
+
}
|
|
214
|
+
content.push(message);
|
|
215
|
+
}
|
|
216
|
+
if (contract !== undefined) {
|
|
217
|
+
const firstConversation = content.findIndex((message) => message.role !== "system");
|
|
218
|
+
|
|
219
|
+
content.splice(firstConversation === -1 ? content.length : firstConversation, 0, contract);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return Prompt.fromMessages(content);
|
|
223
|
+
}
|
|
224
|
+
|
|
166
225
|
const systems: Array<Prompt.SystemMessage> = [];
|
|
167
226
|
const conversation: Array<Prompt.Message> = [];
|
|
168
227
|
const seen = new Map<string, Array<Prompt.SystemMessage>>();
|