effect-agent 0.1.0-beta.97 → 0.1.0-beta.99

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.
Files changed (143) hide show
  1. package/dist/{AgentRuntime-B64362Id.d.mts → AgentRuntime-BMzrJos6.d.mts} +2 -2
  2. package/dist/{AgentRuntime-Cdvqw7l_.mjs → AgentRuntime-BWOnNc0d.mjs} +78 -42
  3. package/dist/AgentRuntime-BWOnNc0d.mjs.map +1 -0
  4. package/dist/{DurableAgentRuntime-DbUvZHDZ.mjs → DurableAgentRuntime-BE_OjQjz.mjs} +471 -252
  5. package/dist/DurableAgentRuntime-BE_OjQjz.mjs.map +1 -0
  6. package/dist/{DurableFailpoint-DQJet828.d.mts → DurableFailpoint-mx3-nsxU.d.mts} +3 -3
  7. package/dist/{EventSource-D6PAAEyK.d.mts → EventSource-BZ73J0s5.d.mts} +3 -3
  8. package/dist/{PreparedInputAdmission-omMbwd--.d.mts → PreparedInputAdmission-BkeI-Xvr.d.mts} +4 -4
  9. package/dist/{Records-CV49DgQK.d.mts → Records-BIiRLqlz.d.mts} +92 -128
  10. package/dist/{Records-BAutSSnJ.mjs → Records-DR1xtbXY.mjs} +35 -8
  11. package/dist/Records-DR1xtbXY.mjs.map +1 -0
  12. package/dist/{RunJournal-DRRRZmG7.mjs → RunJournal-DNs3PM4K.mjs} +80 -23
  13. package/dist/RunJournal-DNs3PM4K.mjs.map +1 -0
  14. package/dist/{RunOptions-Clj8cLsp.d.mts → RunOptions-Bruk54SE.d.mts} +13 -4
  15. package/dist/{Schedule-DePbGfgd.d.mts → Schedule-BNFlluAl.d.mts} +2 -2
  16. package/dist/{Scheduling-C3QpYn0W.mjs → Scheduling-HFH0pST1.mjs} +2 -2
  17. package/dist/{Scheduling-C3QpYn0W.mjs.map → Scheduling-HFH0pST1.mjs.map} +1 -1
  18. package/dist/{Subagent-Cbfzylz1.mjs → Subagent-C2s8PFrd.mjs} +2 -2
  19. package/dist/{Subagent-Cbfzylz1.mjs.map → Subagent-C2s8PFrd.mjs.map} +1 -1
  20. package/dist/{Subagent-Bm1dRecN.d.mts → Subagent-m7ywTCe4.d.mts} +3 -3
  21. package/dist/{Subscription-BR_8Cxh3.d.mts → Subscription-BCQ3L9Zp.d.mts} +3 -3
  22. package/dist/{SubscriptionInput-B95sQbuk.d.mts → SubscriptionInput-Cqu4_RW3.d.mts} +3 -3
  23. package/dist/{ToolReconciler-DQVaJfpE.d.mts → ToolReconciler-BmVRAHKs.d.mts} +8 -4
  24. package/dist/agent-registration-cXpB8cBU.mjs +199 -0
  25. package/dist/agent-registration-cXpB8cBU.mjs.map +1 -0
  26. package/dist/capabilities/Commands.d.mts +1 -1
  27. package/dist/capabilities/RunHooks.d.mts +1 -1
  28. package/dist/capabilities/Subagent.d.mts +1 -1
  29. package/dist/capabilities/Subagent.mjs +1 -1
  30. package/dist/durable/ActivityStore.d.mts +1 -1
  31. package/dist/durable/ActivityStore.mjs +1 -1
  32. package/dist/durable/Admin.d.mts +1 -1
  33. package/dist/durable/Admin.mjs +12 -10
  34. package/dist/durable/Admin.mjs.map +1 -1
  35. package/dist/durable/AgentRegistration.d.mts +2 -2
  36. package/dist/durable/AgentRegistration.mjs +4 -8
  37. package/dist/durable/AgentRegistration.mjs.map +1 -1
  38. package/dist/durable/Certification.d.mts +2 -2
  39. package/dist/durable/CommittedActivity.d.mts +1 -1
  40. package/dist/durable/CommittedActivity.mjs +1 -1
  41. package/dist/durable/Digest.d.mts +1 -1
  42. package/dist/durable/Digest.mjs +1 -1
  43. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  44. package/dist/durable/DurableAgentRuntime.mjs +1 -1
  45. package/dist/durable/DurableFailpoint.d.mts +1 -1
  46. package/dist/durable/DurableFailpoint.mjs +2 -0
  47. package/dist/durable/DurableFailpoint.mjs.map +1 -1
  48. package/dist/durable/DurableFailpointTestControl.d.mts +1 -1
  49. package/dist/durable/EventSource.d.mts +1 -1
  50. package/dist/durable/EventSource.mjs +1 -1
  51. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  52. package/dist/durable/GitHubWorkflowSource.mjs +1 -1
  53. package/dist/durable/MessageDelivery.d.mts +3 -3
  54. package/dist/durable/MessageDelivery.mjs +1 -1
  55. package/dist/durable/MessageDeliveryStoreConformance.d.mts +39 -39
  56. package/dist/durable/MessageDeliveryStoreConformance.mjs +1 -1
  57. package/dist/durable/PersistentHistory.d.mts +1 -1
  58. package/dist/durable/PersistentHistory.mjs +2 -2
  59. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  60. package/dist/durable/Records.d.mts +2 -2
  61. package/dist/durable/Records.mjs +2 -2
  62. package/dist/durable/Recovery.d.mts +1 -1
  63. package/dist/durable/Recovery.mjs +9 -9
  64. package/dist/durable/Recovery.mjs.map +1 -1
  65. package/dist/durable/RunJournal.d.mts +2 -2
  66. package/dist/durable/RunJournal.mjs +2 -2
  67. package/dist/durable/Schedule.d.mts +1 -1
  68. package/dist/durable/Schedule.mjs +1 -1
  69. package/dist/durable/ScheduleStoreConformance.d.mts +1 -1
  70. package/dist/durable/ScheduleStoreConformance.mjs +1 -1
  71. package/dist/durable/ScheduleTransition.d.mts +1 -1
  72. package/dist/durable/ScheduleTransition.mjs +1 -1
  73. package/dist/durable/Scheduling.d.mts +2 -2
  74. package/dist/durable/Scheduling.mjs +1 -1
  75. package/dist/durable/SqlStorageV2Upgrade.d.mts +2 -2
  76. package/dist/durable/SqlStorageV2Upgrade.mjs +1 -1
  77. package/dist/durable/SqlSubscriptionStore.d.mts +2 -2
  78. package/dist/durable/SqlSubscriptionStore.mjs +1 -1
  79. package/dist/durable/SubmissionLedger.d.mts +1 -1
  80. package/dist/durable/SubmissionLedger.mjs +14 -13
  81. package/dist/durable/SubmissionLedger.mjs.map +1 -1
  82. package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
  83. package/dist/durable/SubmissionLedgerConformance.mjs +9 -2
  84. package/dist/durable/SubmissionLedgerConformance.mjs.map +1 -1
  85. package/dist/durable/SubmissionStatus.d.mts +1 -1
  86. package/dist/durable/Subscription.d.mts +1 -1
  87. package/dist/durable/Subscription.mjs +1 -1
  88. package/dist/durable/SubscriptionInput.d.mts +1 -1
  89. package/dist/durable/SubscriptionInput.mjs +2 -2
  90. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  91. package/dist/durable/SubscriptionStoreConformance.mjs +1 -1
  92. package/dist/durable/SubscriptionTools.d.mts +3 -3
  93. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  94. package/dist/durable/SubscriptionTransition.mjs +1 -1
  95. package/dist/durable/Subscriptions.d.mts +5 -5
  96. package/dist/durable/Subscriptions.mjs +1 -1
  97. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  98. package/dist/durable/ThreadContextHistory.mjs +1 -1
  99. package/dist/durable/ThreadContextHistoryProjection.d.mts +1 -1
  100. package/dist/durable/ThreadContextHistoryProjection.mjs +1 -1
  101. package/dist/durable/ThreadInvariants.d.mts +4 -2
  102. package/dist/durable/ThreadInvariants.mjs +49 -3
  103. package/dist/durable/ThreadInvariants.mjs.map +1 -1
  104. package/dist/durable/ThreadProjection.d.mts +1 -1
  105. package/dist/durable/ThreadProjection.mjs +1 -1
  106. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  107. package/dist/durable/ThreadStore.d.mts +1 -1
  108. package/dist/durable/ThreadStore.mjs +1 -1
  109. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  110. package/dist/durable/ThreadStoreConformance.mjs +1 -1
  111. package/dist/durable/ToolReconciler.d.mts +1 -1
  112. package/dist/durable/ToolReconciler.mjs +9 -5
  113. package/dist/durable/ToolReconciler.mjs.map +1 -1
  114. package/dist/durable/WorkerHost.d.mts +1 -1
  115. package/dist/engine/AgentRuntime.d.mts +1 -1
  116. package/dist/engine/AgentRuntime.mjs +1 -1
  117. package/dist/engine/RunOptions.d.mts +1 -1
  118. package/dist/engine/RunOptions.mjs.map +1 -1
  119. package/dist/index.d.mts +11 -11
  120. package/dist/index.mjs +6 -6
  121. package/package.json +1 -1
  122. package/src/durable/Admin.ts +20 -11
  123. package/src/durable/AgentRegistration.ts +1 -5
  124. package/src/durable/DurableAgentRuntime.ts +947 -464
  125. package/src/durable/DurableFailpoint.ts +2 -0
  126. package/src/durable/Records.ts +30 -5
  127. package/src/durable/Recovery.ts +8 -8
  128. package/src/durable/RunJournal.ts +128 -51
  129. package/src/durable/SubmissionLedger.ts +13 -12
  130. package/src/durable/SubmissionLedgerConformance.ts +13 -2
  131. package/src/durable/ThreadInvariants.ts +70 -5
  132. package/src/durable/ToolReconciler.ts +7 -3
  133. package/src/durable/internal/agent-registration.ts +65 -414
  134. package/src/durable/internal/messaging-host.ts +0 -2
  135. package/src/durable/internal/worker-host.ts +34 -96
  136. package/src/engine/RunOptions.ts +13 -3
  137. package/src/engine/internal/agent-runtime.ts +160 -106
  138. package/dist/AgentRuntime-Cdvqw7l_.mjs.map +0 -1
  139. package/dist/DurableAgentRuntime-DbUvZHDZ.mjs.map +0 -1
  140. package/dist/Records-BAutSSnJ.mjs.map +0 -1
  141. package/dist/RunJournal-DRRRZmG7.mjs.map +0 -1
  142. package/dist/agent-registration-CXGDwvJH.mjs +0 -379
  143. package/dist/agent-registration-CXGDwvJH.mjs.map +0 -1
@@ -1,13 +1,4 @@
1
- import {
2
- type Crypto,
3
- type Option,
4
- type Scope,
5
- Array,
6
- Context,
7
- Effect,
8
- Layer,
9
- Schema,
10
- } from "effect";
1
+ import { type Crypto, type Option, type Scope, Context, Effect, Layer, Schema } from "effect";
11
2
  import { Tool } from "effect/unstable/ai";
12
3
 
13
4
  import type * as Agent from "../../core/Agent.ts";
@@ -27,6 +18,7 @@ import {
27
18
  ToolNamespace,
28
19
  } from "../../core/ToolExposure.ts";
29
20
  import { type RuntimeBinding } from "../../engine/AgentRuntime.ts";
21
+ import { ContextRolloverTool } from "../../engine/ContextWindow.ts";
30
22
  import { getToolExecutionClass } from "../../engine/DurableStep.ts";
31
23
  import {
32
24
  BackgroundReporting,
@@ -35,44 +27,19 @@ import {
35
27
  } from "../../engine/SubagentHost.ts";
36
28
  import { digestDefinitions, digestJson, DigestError } from "../Digest.ts";
37
29
  import type { DurableWorkerFailure, DurableWorkerRequirements } from "../DurableAgentRuntime.ts";
38
- import {
39
- DefinitionDigestInput,
40
- DefinitionDigests,
41
- ReplayContract,
42
- PersistedJson,
43
- } from "../Records.ts";
30
+ import type { DefinitionDigestInput, PersistedJson } from "../Records.ts";
31
+ import { DefinitionDigests, ReplayContract } from "../Records.ts";
44
32
  import type { Claim, Settlement } from "../SubmissionLedger.ts";
45
33
 
46
- /**
47
- * No Agent Binding is registered for the requested stable identity. Recovery
48
- * fails closed: it never substitutes the latest Binding or runs different
49
- * code.
50
- */
34
+ /** No unique current executable is registered for the stable Agent identity. */
51
35
  export class BindingUnavailable extends Schema.TaggedError<BindingUnavailable>()(
52
36
  "BindingUnavailable",
53
- {
54
- agentId: AgentId,
55
- message: Schema.String,
56
- },
57
- ) {}
58
-
59
- /**
60
- * A Binding is registered for the identity but neither the exact admission fingerprints
61
- * nor its declared replay contract support the retained pending operations. Refusal leaves
62
- * the original Submission pending until compatible code is available.
63
- */
64
- export class BindingDigestMismatch extends Schema.TaggedError<BindingDigestMismatch>()(
65
- "BindingDigestMismatch",
66
- {
67
- agentId: AgentId,
68
- message: Schema.String,
69
- },
37
+ { agentId: AgentId, message: Schema.String },
70
38
  ) {}
71
39
 
72
- /** The typed refusal family of durable Binding resolution (spec §11, SUB-032). */
73
- export type DurableBindingFailure = BindingUnavailable | BindingDigestMismatch;
40
+ export type DurableBindingFailure = BindingUnavailable;
74
41
 
75
- /** Byte-for-byte equality of two stored definition digest triples (SUB-023). */
42
+ /** Exact immutable admission, lineage and delivery evidence; never an executable-code gate. */
76
43
  export const definitionDigestsEqual = (
77
44
  left: DefinitionDigests,
78
45
  right: DefinitionDigests,
@@ -82,91 +49,35 @@ export const definitionDigestsEqual = (
82
49
  left.tools === right.tools &&
83
50
  Schema.toEquivalence(Schema.UndefinedOr(ReplayContract))(left.replay, right.replay);
84
51
 
85
- /**
86
- * Host-owned semantic versions, independent of deployment and tool inventory. Change a version
87
- * when the meaning of an operation, its durable Step names or external idempotency keys changes.
88
- * Codecs and execution class are included automatically; JSON Schema alone cannot prove semantics.
89
- */
52
+ /** Change an operation version when its meaning, durable Steps or idempotency keys change. */
90
53
  export interface ReplayVersions {
91
- readonly agent: PersistedJson;
92
54
  readonly tools: Readonly<Record<string, PersistedJson>>;
93
55
  }
94
56
 
95
- /**
96
- * Data exported from a definition, never an old executable Binding. A host may supply an audited
97
- * manifest from the original release for work admitted before replay contracts were retained.
98
- * Its original declarations must hash to that admission; a bare old digest is not a manifest.
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
-
122
- export class BindingManifest extends Schema.Class<BindingManifest>("BindingManifest")({
123
- agentId: AgentId,
124
- definitions: DefinitionDigestInput,
125
- replay: ReplayContract,
126
- agentContract: Schema.optionalKey(AgentReplayDeclaration),
127
- toolContracts: Schema.optionalKey(Schema.Record(Schema.String, ToolReplayDeclaration)),
128
- }) {}
129
-
130
- export const makeBindingManifest = Effect.fn("AgentRegistration.makeBindingManifest")(function* (
57
+ /** Hash current operation contracts; no historical definitions or schemas are retained. */
58
+ export const toolReplayContracts = Effect.fn("AgentRegistration.toolReplayContracts")(function* (
131
59
  definition: Agent.AnyDefinition,
132
- definitions: DefinitionDigestInput,
133
- versions: ReplayVersions,
60
+ versions?: ReplayVersions,
61
+ fallbackVersion: PersistedJson = null,
134
62
  ) {
135
- const jsonSchema = (schema: Schema.Top) =>
136
- Schema.decodeUnknownSync(Schema.Json)(Tool.getJsonSchemaFromSchema(schema));
137
-
138
- const contracts = yield* Effect.try({
63
+ const declarations = yield* Effect.try({
139
64
  try: () => {
140
- const tools = Object.values(definition.toolkit.tools);
141
-
142
- if (tools.some((tool) => !Object.hasOwn(versions.tools, tool.name)))
143
- throw new Error("Every Tool needs an explicit replay semantic version");
144
-
145
- return {
146
- agent: {
147
- version: versions.agent,
148
- id: definition.id,
149
- input: jsonSchema(definition.input),
150
- output: jsonSchema(definition.output),
151
- updates: definition.updates === undefined ? null : jsonSchema(definition.updates),
152
- completion:
153
- definition.completion === undefined
154
- ? null
155
- : {
156
- tool: definition.completion.tool,
157
- required: definition.completion.required ?? false,
158
- },
159
- completionFromTools:
160
- definition.completionFromTools?.map((entry) => entry.tool).sort() ?? [],
161
- disposition:
162
- definition.runDisposition === undefined
163
- ? null
164
- : jsonSchema(definition.runDisposition.schema),
165
- },
166
- tools: tools.map((tool) => ({
65
+ const jsonSchema = (schema: Schema.Top) =>
66
+ Schema.decodeUnknownSync(Schema.Json)(Tool.getJsonSchemaFromSchema(schema));
67
+
68
+ return Object.values(definition.toolkit.tools).map((tool) => {
69
+ const version = versions === undefined ? fallbackVersion : versions.tools[tool.name];
70
+
71
+ if (
72
+ version === undefined ||
73
+ (versions !== undefined && !Object.hasOwn(versions.tools, tool.name))
74
+ )
75
+ throw new Error("Every Tool needs an explicit replay semantic version");
76
+
77
+ return {
167
78
  name: tool.name,
168
79
  contract: {
169
- version: versions.tools[tool.name]!,
80
+ version,
170
81
  parameters: jsonSchema(tool.parametersSchema),
171
82
  success: jsonSchema(tool.successSchema),
172
83
  failure: jsonSchema(tool.failureSchema),
@@ -175,273 +86,48 @@ export const makeBindingManifest = Effect.fn("AgentRegistration.makeBindingManif
175
86
  typeof tool.needsApproval === "function" ? "dynamic" : (tool.needsApproval ?? false),
176
87
  executionClass: getToolExecutionClass(tool),
177
88
  executionKind: getToolExecutionKind(tool.annotations),
89
+ contextRollover: Context.get(tool.annotations, ContextRolloverTool),
90
+ completion: definition.completion?.tool === tool.name,
91
+ completionFromTool:
92
+ definition.completionFromTools?.some(
93
+ (declaration) => declaration.tool === tool.name,
94
+ ) ?? false,
178
95
  },
179
- })),
180
- };
96
+ };
97
+ });
181
98
  },
182
99
  catch: () =>
183
100
  DigestError.make({
184
- message:
185
- "Replay contracts require serializable codecs and an explicit semantic version for every Tool",
101
+ message: "Operation contracts require serializable codecs and a version for every Tool",
186
102
  }),
187
103
  });
188
104
 
189
- const agent = yield* digestJson(contracts.agent);
190
- const agentBehavior = yield* digestJson({ ...contracts.agent, input: null });
191
-
192
- const tools = yield* Effect.forEach(contracts.tools, ({ name, contract }) =>
193
- digestJson(contract).pipe(Effect.map((digest) => [name, digest] as const)),
194
- );
195
-
196
- return BindingManifest.make({
197
- agentId: definition.id,
198
- definitions,
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]),
105
+ return Object.fromEntries(
106
+ yield* Effect.forEach(declarations, ({ name, contract }) =>
107
+ digestJson(contract).pipe(Effect.map((digest) => [name, digest] as const)),
203
108
  ),
204
- });
109
+ );
205
110
  });
206
111
 
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")(
112
+ /** Compile current metadata without acquiring executable services. */
113
+ export const compileBindingContracts = Effect.fn("AgentRegistration.compileBindingContracts")(
291
114
  function* (
292
115
  definition: Agent.AnyDefinition,
293
116
  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
- );
117
+ versions?: ReplayVersions,
118
+ ): Effect.fn.Return<Pick<ResolvedBinding, "digests">, DigestError, Crypto.Crypto> {
119
+ const digests = yield* digestDefinitions(definitions);
120
+ const tools = yield* toolReplayContracts(definition, versions, definitions.tools);
379
121
 
380
122
  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
- },
123
+ digests: DefinitionDigests.make({
124
+ ...digests,
125
+ replay: ReplayContract.make({ agent: digests.agent, tools }),
126
+ }),
392
127
  };
393
128
  },
394
129
  );
395
130
 
396
- const replaySupports = (
397
- current: ReplayContract,
398
- retained: ReplayContract,
399
- requiredTools?: ReadonlyArray<string>,
400
- inputCompatible = false,
401
- compatibleTools: Readonly<Record<string, string>> = {},
402
- ): boolean =>
403
- (current.agent === retained.agent || inputCompatible) &&
404
- (requiredTools ?? Object.keys(retained.tools)).every(
405
- (name) =>
406
- retained.tools[name] !== undefined &&
407
- (current.tools[name] === retained.tools[name] ||
408
- (current.tools[name] !== undefined && compatibleTools[name] === current.tools[name])),
409
- );
410
-
411
- /** Resolve executable code only. Admission, delivery, lineage and receipt comparisons stay exact. */
412
- export const bindingSupports = (
413
- binding: Pick<ResolvedBinding, "digests" | "retainedManifests" | "acceptsInput">,
414
- digests: DefinitionDigests,
415
- requiredTools?: ReadonlyArray<string>,
416
- input?: PersistedJson,
417
- ): boolean => {
418
- if (definitionDigestsEqual(binding.digests, digests)) return true;
419
-
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;
427
-
428
- return (
429
- binding.digests.replay !== undefined &&
430
- retained !== undefined &&
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
- )
442
- );
443
- };
444
-
445
131
  type ExecutableDefinition = Agent.AnyDefinition & {
446
132
  readonly instructions: InstructionSource<never, unknown, unknown>;
447
133
  readonly inputPrompt?: InputPromptSource<never, unknown, unknown> | undefined;
@@ -572,16 +258,6 @@ const backgroundReports = (definition: Agent.AnyDefinition) => [
572
258
  /** One exact executable registration used by durable claim-time resolution. */
573
259
  export interface ResolvedBinding extends CapturedBinding {
574
260
  readonly digests: DefinitionDigests;
575
- readonly manifest?: BindingManifest;
576
- /** Current encoded input guard, acquired without executable services. */
577
- readonly acceptsInput?: (input: PersistedJson) => boolean;
578
- readonly retainedManifests?: ReadonlyArray<{
579
- readonly digests: DefinitionDigests;
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;
584
- }>;
585
261
  }
586
262
 
587
263
  /** Trusted claim identity for resolving per-Attempt application services. Not model input. */
@@ -678,38 +354,18 @@ export const makeLegacyWorkerBinding = capture;
678
354
  export const resolveWorkerBinding = (
679
355
  bindings: ReadonlyArray<ResolvedBinding>,
680
356
  agentId: AgentId,
681
- digests: DefinitionDigests,
682
- requiredTools?: ReadonlyArray<string>,
683
- input?: PersistedJson,
684
- ): Effect.Effect<ResolvedBinding, DurableBindingFailure> => {
357
+ ): Effect.Effect<ResolvedBinding, BindingUnavailable> => {
685
358
  const registered = bindings.filter((binding) => binding.agentId === agentId);
686
-
687
- if (registered.length === 0) {
688
- return Effect.fail(
689
- BindingUnavailable.make({
690
- agentId,
691
- message: `No Agent Binding is registered for ${agentId}; recovery never substitutes different code (SUB-023)`,
692
- }),
693
- );
694
- }
695
- const exact = registered.find((binding) => definitionDigestsEqual(binding.digests, digests));
696
-
697
- const compatible =
698
- exact ??
699
- (registered.length === 1 && bindingSupports(registered[0]!, digests, requiredTools, input)
700
- ? registered[0]
701
- : undefined);
702
-
703
- if (compatible === undefined) {
704
- return Effect.fail(
705
- BindingDigestMismatch.make({
706
- agentId,
707
- message: `No registered Binding proves the retained replay contracts for ${agentId}; the original request and receipts remain pending`,
708
- }),
709
- );
710
- }
711
-
712
- return Effect.succeed(compatible);
359
+ const binding = registered[0];
360
+
361
+ return registered.length === 1 && binding !== undefined
362
+ ? Effect.succeed(binding)
363
+ : Effect.fail(
364
+ BindingUnavailable.make({
365
+ agentId,
366
+ message: `Exactly one current Agent Binding must be registered for ${agentId}`,
367
+ }),
368
+ );
713
369
  };
714
370
 
715
371
  /** Resolve authoring-time admission through the one exact host registration, never by identity alone. */
@@ -750,10 +406,9 @@ export type AgentRegistration<A extends ExecutableAgentBinding = ExecutableAgent
750
406
  readonly definitions: DefinitionDigestInput;
751
407
  }
752
408
  ) & {
753
- /** Opt into contract-checked deployment continuity. Omit to require an exact registration. */
409
+ /** Explicit operation versions let unchanged unfinished handlers survive toolbox changes. */
754
410
  readonly continuity?: {
755
411
  readonly versions: ReplayVersions;
756
- readonly retainedManifests?: ReadonlyArray<BindingManifest>;
757
412
  };
758
413
  /**
759
414
  * Source-owned reports capture services outside attemptLayer and open a fresh Scope per
@@ -897,16 +552,12 @@ const compileRegistration = <Entry extends AgentRegistration>(
897
552
  ...backgroundReports(binding.definition),
898
553
  ]);
899
554
 
900
- if (entry.continuity === undefined)
901
- return { ...binding, digests: yield* digestDefinitions(definitions), reporting };
902
-
903
555
  return {
904
556
  ...binding,
905
- ...(yield* compileBindingManifest(
557
+ ...(yield* compileBindingContracts(
906
558
  binding.definition,
907
559
  definitions,
908
- entry.continuity.versions,
909
- entry.continuity.retainedManifests,
560
+ entry.continuity?.versions,
910
561
  )),
911
562
  reporting,
912
563
  };
@@ -52,7 +52,6 @@ import {
52
52
  } from "../ThreadStore.ts";
53
53
  import {
54
54
  definitionDigestsEqual,
55
- bindingSupports,
56
55
  resolveDefinitionBinding,
57
56
  type ResolvedBinding,
58
57
  } from "./agent-registration.ts";
@@ -307,7 +306,6 @@ export const makeMessagingRuntime = Effect.fn("MessagingHost.make")(function* (
307
306
  !Schema.is(MessageAdmission)(metadata) ||
308
307
  metadata.peerName !== request.name ||
309
308
  saved.envelope.agentId !== target.agentId ||
310
- !bindingSupports(target, saved.envelope.definitions, undefined, saved.envelope.input) ||
311
309
  !sameJson(saved.envelope.input, input) ||
312
310
  (inReplyTo === undefined || metadata.inReplyTo === undefined
313
311
  ? inReplyTo !== metadata.inReplyTo