effect-agent 0.1.0-beta.96 → 0.1.0-beta.97
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/{DurableAgentRuntime-BGwGUIT-.mjs → DurableAgentRuntime-DbUvZHDZ.mjs} +34 -30
- package/dist/DurableAgentRuntime-DbUvZHDZ.mjs.map +1 -0
- package/dist/{EventSource-tCO_dAff.d.mts → EventSource-D6PAAEyK.d.mts} +3 -3
- package/dist/{PreparedInputAdmission-CFSddiT8.d.mts → PreparedInputAdmission-omMbwd--.d.mts} +4 -4
- package/dist/{Records-BEz5IFUu.mjs → Records-BAutSSnJ.mjs} +3 -1
- package/dist/Records-BAutSSnJ.mjs.map +1 -0
- package/dist/{Records-CMByd1eD.d.mts → Records-CV49DgQK.d.mts} +37 -9
- package/dist/{RunJournal-B_fxe57c.mjs → RunJournal-DRRRZmG7.mjs} +2 -2
- package/dist/{RunJournal-B_fxe57c.mjs.map → RunJournal-DRRRZmG7.mjs.map} +1 -1
- package/dist/{Schedule-BxCfnYr0.d.mts → Schedule-DePbGfgd.d.mts} +2 -2
- package/dist/{Scheduling-BW6BD_TP.mjs → Scheduling-C3QpYn0W.mjs} +2 -2
- package/dist/{Scheduling-BW6BD_TP.mjs.map → Scheduling-C3QpYn0W.mjs.map} +1 -1
- package/dist/{Subscription-CrTIQKZR.d.mts → Subscription-BR_8Cxh3.d.mts} +3 -3
- package/dist/{SubscriptionInput-BVOeFF1L.d.mts → SubscriptionInput-B95sQbuk.d.mts} +3 -3
- package/dist/{agent-registration-BTa5vmQF.mjs → agent-registration-CXGDwvJH.mjs} +151 -26
- package/dist/agent-registration-CXGDwvJH.mjs.map +1 -0
- package/dist/durable/ActivityStore.d.mts +1 -1
- package/dist/durable/ActivityStore.mjs +1 -1
- package/dist/durable/Admin.d.mts +1 -1
- package/dist/durable/Admin.mjs +1 -1
- package/dist/durable/AgentRegistration.d.mts +2 -2
- package/dist/durable/AgentRegistration.mjs +3 -2
- package/dist/durable/AgentRegistration.mjs.map +1 -1
- package/dist/durable/Certification.d.mts +1 -1
- package/dist/durable/CommittedActivity.d.mts +1 -1
- package/dist/durable/CommittedActivity.mjs +1 -1
- package/dist/durable/Digest.d.mts +1 -1
- package/dist/durable/Digest.mjs +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/EventSource.mjs +1 -1
- package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
- package/dist/durable/GitHubWorkflowSource.mjs +1 -1
- package/dist/durable/MessageDelivery.d.mts +3 -3
- package/dist/durable/MessageDelivery.mjs +1 -1
- package/dist/durable/MessageDeliveryStoreConformance.d.mts +1 -1
- package/dist/durable/MessageDeliveryStoreConformance.mjs +1 -1
- package/dist/durable/PersistentHistory.d.mts +1 -1
- package/dist/durable/PersistentHistory.mjs +2 -2
- package/dist/durable/PreparedInputAdmission.d.mts +1 -1
- package/dist/durable/Records.d.mts +1 -1
- package/dist/durable/Records.mjs +1 -1
- package/dist/durable/Recovery.d.mts +1 -1
- package/dist/durable/Recovery.mjs +1 -1
- package/dist/durable/RunJournal.d.mts +1 -1
- package/dist/durable/RunJournal.mjs +1 -1
- package/dist/durable/Schedule.d.mts +1 -1
- package/dist/durable/Schedule.mjs +1 -1
- package/dist/durable/ScheduleStoreConformance.d.mts +1 -1
- package/dist/durable/ScheduleStoreConformance.mjs +1 -1
- package/dist/durable/ScheduleTransition.d.mts +1 -1
- package/dist/durable/ScheduleTransition.mjs +1 -1
- package/dist/durable/Scheduling.d.mts +2 -2
- package/dist/durable/Scheduling.mjs +1 -1
- package/dist/durable/SqlStorageV2Upgrade.d.mts +2 -2
- package/dist/durable/SqlStorageV2Upgrade.mjs +1 -1
- package/dist/durable/SqlSubscriptionStore.d.mts +2 -2
- package/dist/durable/SqlSubscriptionStore.mjs +1 -1
- package/dist/durable/SubmissionLedger.d.mts +1 -1
- package/dist/durable/SubmissionLedger.mjs +1 -1
- package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
- package/dist/durable/SubmissionLedgerConformance.mjs +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 +2 -2
- package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
- package/dist/durable/SubscriptionStoreConformance.mjs +1 -1
- package/dist/durable/SubscriptionTools.d.mts +3 -3
- package/dist/durable/SubscriptionTransition.d.mts +1 -1
- package/dist/durable/SubscriptionTransition.mjs +1 -1
- package/dist/durable/Subscriptions.d.mts +5 -5
- package/dist/durable/Subscriptions.mjs +1 -1
- package/dist/durable/ThreadContextHistory.d.mts +1 -1
- package/dist/durable/ThreadContextHistory.mjs +1 -1
- package/dist/durable/ThreadContextHistoryProjection.d.mts +1 -1
- package/dist/durable/ThreadContextHistoryProjection.mjs +1 -1
- package/dist/durable/ThreadInvariants.d.mts +1 -1
- package/dist/durable/ThreadInvariants.mjs +1 -1
- package/dist/durable/ThreadProjection.d.mts +1 -1
- package/dist/durable/ThreadProjection.mjs +1 -1
- package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
- package/dist/durable/ThreadStore.d.mts +1 -1
- package/dist/durable/ThreadStore.mjs +1 -1
- package/dist/durable/ThreadStoreConformance.d.mts +1 -1
- package/dist/durable/ThreadStoreConformance.mjs +1 -1
- package/dist/durable/ToolReconciler.mjs +1 -1
- package/dist/durable/WorkerHost.d.mts +1 -1
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +4 -4
- package/package.json +1 -1
- package/src/durable/AgentRegistration.ts +1 -0
- package/src/durable/DurableAgentRuntime.ts +3 -1
- package/src/durable/Records.ts +2 -0
- package/src/durable/internal/agent-registration.ts +274 -36
- package/src/durable/internal/messaging-host.ts +2 -5
- package/src/durable/internal/worker-host.ts +119 -85
- package/dist/DurableAgentRuntime-BGwGUIT-.mjs.map +0 -1
- package/dist/Records-BEz5IFUu.mjs.map +0 -1
- package/dist/agent-registration-BTa5vmQF.mjs.map +0 -1
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type Crypto,
|
|
3
|
+
type Option,
|
|
4
|
+
type Scope,
|
|
5
|
+
Array,
|
|
6
|
+
Context,
|
|
7
|
+
Effect,
|
|
8
|
+
Layer,
|
|
9
|
+
Schema,
|
|
10
|
+
} from "effect";
|
|
2
11
|
import { Tool } from "effect/unstable/ai";
|
|
3
12
|
|
|
4
13
|
import type * as Agent from "../../core/Agent.ts";
|
|
@@ -30,7 +39,7 @@ import {
|
|
|
30
39
|
DefinitionDigestInput,
|
|
31
40
|
DefinitionDigests,
|
|
32
41
|
ReplayContract,
|
|
33
|
-
|
|
42
|
+
PersistedJson,
|
|
34
43
|
} from "../Records.ts";
|
|
35
44
|
import type { Claim, Settlement } from "../SubmissionLedger.ts";
|
|
36
45
|
|
|
@@ -88,10 +97,34 @@ export interface ReplayVersions {
|
|
|
88
97
|
* manifest from the original release for work admitted before replay contracts were retained.
|
|
89
98
|
* Its original declarations must hash to that admission; a bare old digest is not a manifest.
|
|
90
99
|
*/
|
|
100
|
+
const ToolReplayDeclaration = Schema.Struct({
|
|
101
|
+
version: PersistedJson,
|
|
102
|
+
parameters: PersistedJson,
|
|
103
|
+
success: PersistedJson,
|
|
104
|
+
failure: PersistedJson,
|
|
105
|
+
failureMode: Schema.String,
|
|
106
|
+
needsApproval: Schema.Union([Schema.Boolean, Schema.Literal("dynamic")]),
|
|
107
|
+
executionClass: Schema.String,
|
|
108
|
+
executionKind: Schema.String,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const AgentReplayDeclaration = Schema.Struct({
|
|
112
|
+
version: PersistedJson,
|
|
113
|
+
id: AgentId,
|
|
114
|
+
input: PersistedJson,
|
|
115
|
+
output: PersistedJson,
|
|
116
|
+
updates: Schema.NullOr(PersistedJson),
|
|
117
|
+
completion: Schema.NullOr(Schema.Struct({ tool: Schema.String, required: Schema.Boolean })),
|
|
118
|
+
completionFromTools: Schema.Array(Schema.String),
|
|
119
|
+
disposition: Schema.NullOr(PersistedJson),
|
|
120
|
+
});
|
|
121
|
+
|
|
91
122
|
export class BindingManifest extends Schema.Class<BindingManifest>("BindingManifest")({
|
|
92
123
|
agentId: AgentId,
|
|
93
124
|
definitions: DefinitionDigestInput,
|
|
94
125
|
replay: ReplayContract,
|
|
126
|
+
agentContract: Schema.optionalKey(AgentReplayDeclaration),
|
|
127
|
+
toolContracts: Schema.optionalKey(Schema.Record(Schema.String, ToolReplayDeclaration)),
|
|
95
128
|
}) {}
|
|
96
129
|
|
|
97
130
|
export const makeBindingManifest = Effect.fn("AgentRegistration.makeBindingManifest")(function* (
|
|
@@ -154,6 +187,7 @@ export const makeBindingManifest = Effect.fn("AgentRegistration.makeBindingManif
|
|
|
154
187
|
});
|
|
155
188
|
|
|
156
189
|
const agent = yield* digestJson(contracts.agent);
|
|
190
|
+
const agentBehavior = yield* digestJson({ ...contracts.agent, input: null });
|
|
157
191
|
|
|
158
192
|
const tools = yield* Effect.forEach(contracts.tools, ({ name, contract }) =>
|
|
159
193
|
digestJson(contract).pipe(Effect.map((digest) => [name, digest] as const)),
|
|
@@ -162,37 +196,249 @@ export const makeBindingManifest = Effect.fn("AgentRegistration.makeBindingManif
|
|
|
162
196
|
return BindingManifest.make({
|
|
163
197
|
agentId: definition.id,
|
|
164
198
|
definitions,
|
|
165
|
-
replay: ReplayContract.make({ agent, tools: Object.fromEntries(tools) }),
|
|
199
|
+
replay: ReplayContract.make({ agent, agentBehavior, tools: Object.fromEntries(tools) }),
|
|
200
|
+
agentContract: contracts.agent,
|
|
201
|
+
toolContracts: Object.fromEntries(
|
|
202
|
+
contracts.tools.map(({ name, contract }) => [name, contract]),
|
|
203
|
+
),
|
|
166
204
|
});
|
|
167
205
|
});
|
|
168
206
|
|
|
207
|
+
const jsonEqual = Schema.toEquivalence(PersistedJson);
|
|
208
|
+
|
|
209
|
+
// JSON Schema generation can widen object openness without changing an Effect codec.
|
|
210
|
+
// Prove only this widening; all other codec changes still require an exact contract.
|
|
211
|
+
const schemasCompatible = (previous: PersistedJson, current: PersistedJson): boolean => {
|
|
212
|
+
if (jsonEqual(previous, current)) return true;
|
|
213
|
+
|
|
214
|
+
const hasNegativeSchema = (value: PersistedJson): boolean => {
|
|
215
|
+
if (Array.isArray<PersistedJson>(value)) return value.some(hasNegativeSchema);
|
|
216
|
+
if (value === null || typeof value !== "object") return false;
|
|
217
|
+
|
|
218
|
+
return Object.entries(value).some(
|
|
219
|
+
([key, child]) =>
|
|
220
|
+
key === "oneOf" ||
|
|
221
|
+
key === "maxContains" ||
|
|
222
|
+
key === "unevaluatedProperties" ||
|
|
223
|
+
key === "unevaluatedItems" ||
|
|
224
|
+
key === "if" ||
|
|
225
|
+
key === "then" ||
|
|
226
|
+
key === "else" ||
|
|
227
|
+
key === "$dynamicRef" ||
|
|
228
|
+
key === "$recursiveRef" ||
|
|
229
|
+
(key === "not" && !jsonEqual(child, {})) ||
|
|
230
|
+
hasNegativeSchema(child),
|
|
231
|
+
);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
if (hasNegativeSchema(previous) || hasNegativeSchema(current)) return false;
|
|
235
|
+
|
|
236
|
+
const compare = (before: PersistedJson, after: PersistedJson): boolean => {
|
|
237
|
+
if (jsonEqual(before, after)) return true;
|
|
238
|
+
if (
|
|
239
|
+
before === null ||
|
|
240
|
+
typeof before !== "object" ||
|
|
241
|
+
Array.isArray<PersistedJson>(before) ||
|
|
242
|
+
after === null ||
|
|
243
|
+
typeof after !== "object" ||
|
|
244
|
+
Array.isArray<PersistedJson>(after)
|
|
245
|
+
)
|
|
246
|
+
return false;
|
|
247
|
+
if (Object.keys(before).length !== Object.keys(after).length) return false;
|
|
248
|
+
|
|
249
|
+
return Object.entries(before).every(([key, value]) => {
|
|
250
|
+
if (!Object.hasOwn(after, key)) return false;
|
|
251
|
+
const next = after[key]!;
|
|
252
|
+
|
|
253
|
+
if (jsonEqual(value, next)) return true;
|
|
254
|
+
if (key === "additionalProperties") return value === false && next === true;
|
|
255
|
+
if (key === "items") return compare(value, next);
|
|
256
|
+
if (
|
|
257
|
+
(key === "anyOf" || key === "allOf") &&
|
|
258
|
+
Array.isArray<PersistedJson>(value) &&
|
|
259
|
+
Array.isArray<PersistedJson>(next)
|
|
260
|
+
)
|
|
261
|
+
return (
|
|
262
|
+
value.length === next.length &&
|
|
263
|
+
value.every((entry, index) => compare(entry, next[index]!))
|
|
264
|
+
);
|
|
265
|
+
if (
|
|
266
|
+
(key === "properties" || key === "$defs" || key === "definitions") &&
|
|
267
|
+
value !== null &&
|
|
268
|
+
typeof value === "object" &&
|
|
269
|
+
!Array.isArray<PersistedJson>(value) &&
|
|
270
|
+
next !== null &&
|
|
271
|
+
typeof next === "object" &&
|
|
272
|
+
!Array.isArray<PersistedJson>(next)
|
|
273
|
+
)
|
|
274
|
+
return (
|
|
275
|
+
Object.keys(value).length === Object.keys(next).length &&
|
|
276
|
+
Object.entries(value).every(
|
|
277
|
+
([name, schema]) => Object.hasOwn(next, name) && compare(schema, next[name]!),
|
|
278
|
+
)
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
return false;
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
return compare(previous, current);
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
/** Compile audited declaration data once, without acquiring an executable or rewriting receipts.
|
|
289
|
+
* Full input schemas stay in registration metadata, never in each admission's replay contract. */
|
|
290
|
+
export const compileBindingManifest = Effect.fn("AgentRegistration.compileBindingManifest")(
|
|
291
|
+
function* (
|
|
292
|
+
definition: Agent.AnyDefinition,
|
|
293
|
+
definitions: DefinitionDigestInput,
|
|
294
|
+
versions: ReplayVersions,
|
|
295
|
+
retained: ReadonlyArray<BindingManifest> = [],
|
|
296
|
+
): Effect.fn.Return<
|
|
297
|
+
Pick<ResolvedBinding, "digests" | "manifest" | "retainedManifests" | "acceptsInput">,
|
|
298
|
+
DigestError,
|
|
299
|
+
Crypto.Crypto
|
|
300
|
+
> {
|
|
301
|
+
const manifest = yield* makeBindingManifest(definition, definitions, versions);
|
|
302
|
+
|
|
303
|
+
const validContract = Effect.fnUntraced(function* (entry: BindingManifest) {
|
|
304
|
+
if (entry.agentContract === undefined) return undefined;
|
|
305
|
+
if (
|
|
306
|
+
entry.agentContract.id !== entry.agentId ||
|
|
307
|
+
(yield* digestJson(entry.agentContract)) !== entry.replay.agent ||
|
|
308
|
+
(entry.replay.agentBehavior !== undefined &&
|
|
309
|
+
(yield* digestJson({ ...entry.agentContract, input: null })) !==
|
|
310
|
+
entry.replay.agentBehavior)
|
|
311
|
+
)
|
|
312
|
+
return yield* DigestError.make({
|
|
313
|
+
message: "A manifest's Agent contract does not match its replay digest",
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
return entry.agentContract;
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const current = manifest.agentContract!;
|
|
320
|
+
const acceptsInput = Schema.is(Schema.toEncoded(definition.input));
|
|
321
|
+
|
|
322
|
+
const validateTools = Effect.fnUntraced(function* (entry: BindingManifest) {
|
|
323
|
+
for (const [name, contract] of Object.entries(entry.toolContracts ?? {}))
|
|
324
|
+
if ((yield* digestJson(contract)) !== entry.replay.tools[name])
|
|
325
|
+
return yield* DigestError.make({
|
|
326
|
+
message: "A manifest's Tool contract does not match its replay digest",
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const digests = yield* digestDefinitions(manifest.definitions);
|
|
331
|
+
|
|
332
|
+
const retainedManifests = yield* Effect.forEach(
|
|
333
|
+
retained,
|
|
334
|
+
Effect.fnUntraced(function* (entry) {
|
|
335
|
+
if (entry.agentId !== manifest.agentId)
|
|
336
|
+
return yield* DigestError.make({
|
|
337
|
+
message: "A retained manifest belongs to another Agent",
|
|
338
|
+
});
|
|
339
|
+
const previous = yield* validContract(entry);
|
|
340
|
+
|
|
341
|
+
yield* validateTools(entry);
|
|
342
|
+
|
|
343
|
+
const compatible =
|
|
344
|
+
previous !== undefined &&
|
|
345
|
+
jsonEqual(
|
|
346
|
+
{ ...previous, input: null, output: null, updates: null, disposition: null },
|
|
347
|
+
{ ...current, input: null, output: null, updates: null, disposition: null },
|
|
348
|
+
) &&
|
|
349
|
+
schemasCompatible(previous.output, current.output) &&
|
|
350
|
+
schemasCompatible(previous.updates, current.updates) &&
|
|
351
|
+
schemasCompatible(previous.disposition, current.disposition);
|
|
352
|
+
|
|
353
|
+
const compatibleTools: Record<string, string> = {};
|
|
354
|
+
|
|
355
|
+
for (const [name, oldTool] of Object.entries(entry.toolContracts ?? {})) {
|
|
356
|
+
const newTool = manifest.toolContracts?.[name];
|
|
357
|
+
|
|
358
|
+
if (
|
|
359
|
+
newTool !== undefined &&
|
|
360
|
+
jsonEqual(
|
|
361
|
+
{ ...oldTool, parameters: null, success: null, failure: null },
|
|
362
|
+
{ ...newTool, parameters: null, success: null, failure: null },
|
|
363
|
+
) &&
|
|
364
|
+
schemasCompatible(oldTool.parameters, newTool.parameters) &&
|
|
365
|
+
schemasCompatible(oldTool.success, newTool.success) &&
|
|
366
|
+
schemasCompatible(oldTool.failure, newTool.failure)
|
|
367
|
+
)
|
|
368
|
+
compatibleTools[name] = manifest.replay.tools[name]!;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return {
|
|
372
|
+
digests: yield* digestDefinitions(entry.definitions),
|
|
373
|
+
replay: entry.replay,
|
|
374
|
+
compatibleTools,
|
|
375
|
+
...(compatible ? { compatibleAgent: manifest.replay.agent } : {}),
|
|
376
|
+
};
|
|
377
|
+
}),
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
return {
|
|
381
|
+
digests: DefinitionDigests.make({ ...digests, replay: manifest.replay }),
|
|
382
|
+
manifest,
|
|
383
|
+
retainedManifests,
|
|
384
|
+
acceptsInput: (input: PersistedJson) => {
|
|
385
|
+
// Async checks and defective guards cannot establish synchronous replay proof.
|
|
386
|
+
try {
|
|
387
|
+
return acceptsInput(input);
|
|
388
|
+
} catch {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
};
|
|
393
|
+
},
|
|
394
|
+
);
|
|
395
|
+
|
|
169
396
|
const replaySupports = (
|
|
170
397
|
current: ReplayContract,
|
|
171
398
|
retained: ReplayContract,
|
|
172
399
|
requiredTools?: ReadonlyArray<string>,
|
|
400
|
+
inputCompatible = false,
|
|
401
|
+
compatibleTools: Readonly<Record<string, string>> = {},
|
|
173
402
|
): boolean =>
|
|
174
|
-
current.agent === retained.agent &&
|
|
403
|
+
(current.agent === retained.agent || inputCompatible) &&
|
|
175
404
|
(requiredTools ?? Object.keys(retained.tools)).every(
|
|
176
|
-
(name) =>
|
|
405
|
+
(name) =>
|
|
406
|
+
retained.tools[name] !== undefined &&
|
|
407
|
+
(current.tools[name] === retained.tools[name] ||
|
|
408
|
+
(current.tools[name] !== undefined && compatibleTools[name] === current.tools[name])),
|
|
177
409
|
);
|
|
178
410
|
|
|
179
411
|
/** Resolve executable code only. Admission, delivery, lineage and receipt comparisons stay exact. */
|
|
180
412
|
export const bindingSupports = (
|
|
181
|
-
binding: Pick<ResolvedBinding, "digests" | "retainedManifests">,
|
|
413
|
+
binding: Pick<ResolvedBinding, "digests" | "retainedManifests" | "acceptsInput">,
|
|
182
414
|
digests: DefinitionDigests,
|
|
183
415
|
requiredTools?: ReadonlyArray<string>,
|
|
416
|
+
input?: PersistedJson,
|
|
184
417
|
): boolean => {
|
|
185
418
|
if (definitionDigestsEqual(binding.digests, digests)) return true;
|
|
186
419
|
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
420
|
+
const manifest = binding.retainedManifests?.find(
|
|
421
|
+
(entry) =>
|
|
422
|
+
definitionDigestsEqual(entry.digests, digests) ||
|
|
423
|
+
definitionDigestsEqual({ ...entry.digests, replay: entry.replay }, digests),
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
const retained = digests.replay ?? manifest?.replay;
|
|
191
427
|
|
|
192
428
|
return (
|
|
193
429
|
binding.digests.replay !== undefined &&
|
|
194
430
|
retained !== undefined &&
|
|
195
|
-
replaySupports(
|
|
431
|
+
replaySupports(
|
|
432
|
+
binding.digests.replay,
|
|
433
|
+
retained,
|
|
434
|
+
requiredTools,
|
|
435
|
+
input !== undefined &&
|
|
436
|
+
binding.acceptsInput?.(input) === true &&
|
|
437
|
+
((binding.digests.replay.agentBehavior !== undefined &&
|
|
438
|
+
binding.digests.replay.agentBehavior === retained.agentBehavior) ||
|
|
439
|
+
manifest?.compatibleAgent === binding.digests.replay.agent),
|
|
440
|
+
manifest?.compatibleTools,
|
|
441
|
+
)
|
|
196
442
|
);
|
|
197
443
|
};
|
|
198
444
|
|
|
@@ -327,9 +573,14 @@ const backgroundReports = (definition: Agent.AnyDefinition) => [
|
|
|
327
573
|
export interface ResolvedBinding extends CapturedBinding {
|
|
328
574
|
readonly digests: DefinitionDigests;
|
|
329
575
|
readonly manifest?: BindingManifest;
|
|
576
|
+
/** Current encoded input guard, acquired without executable services. */
|
|
577
|
+
readonly acceptsInput?: (input: PersistedJson) => boolean;
|
|
330
578
|
readonly retainedManifests?: ReadonlyArray<{
|
|
331
579
|
readonly digests: DefinitionDigests;
|
|
332
580
|
readonly replay: ReplayContract;
|
|
581
|
+
readonly compatibleTools?: Readonly<Record<string, string>>;
|
|
582
|
+
/** Original declarations prove unchanged semantics and output/completion contracts. */
|
|
583
|
+
readonly compatibleAgent?: string;
|
|
333
584
|
}>;
|
|
334
585
|
}
|
|
335
586
|
|
|
@@ -429,6 +680,7 @@ export const resolveWorkerBinding = (
|
|
|
429
680
|
agentId: AgentId,
|
|
430
681
|
digests: DefinitionDigests,
|
|
431
682
|
requiredTools?: ReadonlyArray<string>,
|
|
683
|
+
input?: PersistedJson,
|
|
432
684
|
): Effect.Effect<ResolvedBinding, DurableBindingFailure> => {
|
|
433
685
|
const registered = bindings.filter((binding) => binding.agentId === agentId);
|
|
434
686
|
|
|
@@ -444,7 +696,7 @@ export const resolveWorkerBinding = (
|
|
|
444
696
|
|
|
445
697
|
const compatible =
|
|
446
698
|
exact ??
|
|
447
|
-
(registered.length === 1 && bindingSupports(registered[0]!, digests, requiredTools)
|
|
699
|
+
(registered.length === 1 && bindingSupports(registered[0]!, digests, requiredTools, input)
|
|
448
700
|
? registered[0]
|
|
449
701
|
: undefined);
|
|
450
702
|
|
|
@@ -632,8 +884,8 @@ const compileRegistration = <Entry extends AgentRegistration>(
|
|
|
632
884
|
try: () => registrationDefinitions(entry),
|
|
633
885
|
catch: () =>
|
|
634
886
|
DigestError.make({ message: "Agent update schema has no serializable wire contract" }),
|
|
635
|
-
})
|
|
636
|
-
(
|
|
887
|
+
}),
|
|
888
|
+
(definitions) =>
|
|
637
889
|
Effect.gen(function* () {
|
|
638
890
|
const binding = yield* capture(
|
|
639
891
|
entry.model === undefined ? entry.agent : { definition: entry.agent, model: entry.model },
|
|
@@ -645,32 +897,18 @@ const compileRegistration = <Entry extends AgentRegistration>(
|
|
|
645
897
|
...backgroundReports(binding.definition),
|
|
646
898
|
]);
|
|
647
899
|
|
|
648
|
-
if (entry.continuity === undefined)
|
|
649
|
-
|
|
650
|
-
const manifest = yield* makeBindingManifest(
|
|
651
|
-
binding.definition,
|
|
652
|
-
registrationDefinitions(entry),
|
|
653
|
-
entry.continuity.versions,
|
|
654
|
-
);
|
|
655
|
-
|
|
656
|
-
const retainedManifests = yield* Effect.forEach(
|
|
657
|
-
entry.continuity.retainedManifests ?? [],
|
|
658
|
-
(retained) =>
|
|
659
|
-
retained.agentId === binding.agentId
|
|
660
|
-
? digestDefinitions(retained.definitions).pipe(
|
|
661
|
-
Effect.map((digests) => ({ digests, replay: retained.replay })),
|
|
662
|
-
)
|
|
663
|
-
: Effect.fail(
|
|
664
|
-
DigestError.make({ message: "A retained manifest belongs to another Agent" }),
|
|
665
|
-
),
|
|
666
|
-
);
|
|
900
|
+
if (entry.continuity === undefined)
|
|
901
|
+
return { ...binding, digests: yield* digestDefinitions(definitions), reporting };
|
|
667
902
|
|
|
668
903
|
return {
|
|
669
904
|
...binding,
|
|
670
|
-
|
|
905
|
+
...(yield* compileBindingManifest(
|
|
906
|
+
binding.definition,
|
|
907
|
+
definitions,
|
|
908
|
+
entry.continuity.versions,
|
|
909
|
+
entry.continuity.retainedManifests,
|
|
910
|
+
)),
|
|
671
911
|
reporting,
|
|
672
|
-
manifest,
|
|
673
|
-
retainedManifests,
|
|
674
912
|
};
|
|
675
913
|
}),
|
|
676
914
|
// The erased descriptor accepts arbitrary provided services. Restore the concrete entry's
|
|
@@ -103,10 +103,7 @@ export const makeMessagingRuntime = Effect.fn("MessagingHost.make")(function* (
|
|
|
103
103
|
|
|
104
104
|
if (created?._tag !== "ThreadCreated") return yield* failure("context", "not-found");
|
|
105
105
|
|
|
106
|
-
const matching = deps.bindings.filter(
|
|
107
|
-
(binding) =>
|
|
108
|
-
binding.agentId === created.agentId && bindingSupports(binding, created.definitions),
|
|
109
|
-
);
|
|
106
|
+
const matching = deps.bindings.filter((binding) => binding.agentId === created.agentId);
|
|
110
107
|
|
|
111
108
|
if (matching.length !== 1) return yield* failure("context", "binding-mismatch");
|
|
112
109
|
|
|
@@ -310,7 +307,7 @@ export const makeMessagingRuntime = Effect.fn("MessagingHost.make")(function* (
|
|
|
310
307
|
!Schema.is(MessageAdmission)(metadata) ||
|
|
311
308
|
metadata.peerName !== request.name ||
|
|
312
309
|
saved.envelope.agentId !== target.agentId ||
|
|
313
|
-
!bindingSupports(target, saved.envelope.definitions) ||
|
|
310
|
+
!bindingSupports(target, saved.envelope.definitions, undefined, saved.envelope.input) ||
|
|
314
311
|
!sameJson(saved.envelope.input, input) ||
|
|
315
312
|
(inReplyTo === undefined || metadata.inReplyTo === undefined
|
|
316
313
|
? inReplyTo !== metadata.inReplyTo
|