effect-agent 0.1.0-beta.144 → 0.1.0-beta.149

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 (83) hide show
  1. package/dist/{Admin-CQMppCcy.d.mts → Admin-B8ISZyYf.d.mts} +37 -22
  2. package/dist/Agent-1DMSO_Fl.mjs.map +1 -1
  3. package/dist/{Agent-BWhD4Dvu.d.mts → Agent-Cjd_ZveM.d.mts} +7 -3
  4. package/dist/{AgentRuntime-xeCUXutN.mjs → AgentRuntime-D8U9gl9Q.mjs} +23 -18
  5. package/dist/AgentRuntime-D8U9gl9Q.mjs.map +1 -0
  6. package/dist/{AgentRuntime-B4kIzmSF.d.mts → AgentRuntime-sXpKcyxu.d.mts} +5 -5
  7. package/dist/{DurableAgentRuntime-D8GI66mP.mjs → DurableAgentRuntime-DLa4IwFi.mjs} +87 -49
  8. package/dist/DurableAgentRuntime-DLa4IwFi.mjs.map +1 -0
  9. package/dist/{EventSource-w9PonN9X.d.mts → EventSource-CkN05NT3.d.mts} +2 -2
  10. package/dist/{Messaging-Dnmk31yq.d.mts → Messaging-ccQURKCf.d.mts} +3 -3
  11. package/dist/{MessagingHost-CajpPDPG.d.mts → MessagingHost-D04wE-7W.d.mts} +2 -2
  12. package/dist/{Output-DhtWbBcw.d.mts → Output-CFD1CPfU.d.mts} +2 -2
  13. package/dist/{PreparedInputAdmission-BfcLy_k4.d.mts → PreparedInputAdmission-fctZOTnn.d.mts} +3 -3
  14. package/dist/{RunOptions-IlEXLG5F.d.mts → RunOptions-Bv9NpkPZ.d.mts} +10 -2
  15. package/dist/{Subagent-Cl8D1mFM.d.mts → Subagent-VHrANXFy.d.mts} +5 -5
  16. package/dist/{Subagent-8RzJkqJ9.mjs → Subagent-hG9lJ7tJ.mjs} +2 -2
  17. package/dist/{Subagent-8RzJkqJ9.mjs.map → Subagent-hG9lJ7tJ.mjs.map} +1 -1
  18. package/dist/{SubagentHost-B6f4PBC2.d.mts → SubagentHost-C-4A7tA2.d.mts} +2 -2
  19. package/dist/{Subscription-ClkC4_T0.d.mts → Subscription-B16A-48F.d.mts} +2 -2
  20. package/dist/{SubscriptionInput-CMjEKL1z.d.mts → SubscriptionInput-DIU7CiXE.d.mts} +2 -2
  21. package/dist/capabilities/Commands.d.mts +1 -1
  22. package/dist/capabilities/Messaging.d.mts +1 -1
  23. package/dist/capabilities/RunHooks.d.mts +1 -1
  24. package/dist/capabilities/Subagent.d.mts +1 -1
  25. package/dist/capabilities/Subagent.mjs +1 -1
  26. package/dist/core/Agent.d.mts +1 -1
  27. package/dist/durable/Admin.d.mts +1 -1
  28. package/dist/durable/AgentRegistration.d.mts +1 -1
  29. package/dist/durable/Certification.d.mts +1 -1
  30. package/dist/durable/CommittedActivity.d.mts +1 -1
  31. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  32. package/dist/durable/DurableAgentRuntime.mjs +1 -1
  33. package/dist/durable/EventSource.d.mts +1 -1
  34. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  35. package/dist/durable/LifecyclePublication.d.mts +2 -2
  36. package/dist/durable/LifecyclePublication.mjs +32 -12
  37. package/dist/durable/LifecyclePublication.mjs.map +1 -1
  38. package/dist/durable/MessageDelivery.d.mts +3 -3
  39. package/dist/durable/MessageDeliveryStoreConformance.d.mts +1 -1
  40. package/dist/durable/PersistentHistory.d.mts +1 -1
  41. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  42. package/dist/durable/Recovery.d.mts +1 -1
  43. package/dist/durable/RunJournal.d.mts +1 -1
  44. package/dist/durable/Scheduling.d.mts +1 -1
  45. package/dist/durable/SubmissionLedger.d.mts +1 -1
  46. package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
  47. package/dist/durable/SubmissionStatus.d.mts +1 -1
  48. package/dist/durable/Subscription.d.mts +1 -1
  49. package/dist/durable/SubscriptionInput.d.mts +1 -1
  50. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  51. package/dist/durable/SubscriptionTools.d.mts +2 -2
  52. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  53. package/dist/durable/Subscriptions.d.mts +5 -5
  54. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  55. package/dist/durable/ThreadInvariants.d.mts +1 -1
  56. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  57. package/dist/durable/ThreadStore.d.mts +1 -1
  58. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  59. package/dist/durable/WorkerHost.d.mts +25 -5
  60. package/dist/durable/WorkerHost.mjs.map +1 -1
  61. package/dist/engine/AgentRuntime.d.mts +1 -1
  62. package/dist/engine/AgentRuntime.mjs +1 -1
  63. package/dist/engine/MessagingHost.d.mts +1 -1
  64. package/dist/engine/Output.d.mts +1 -1
  65. package/dist/engine/Output.mjs +1 -1
  66. package/dist/engine/RunOptions.d.mts +1 -1
  67. package/dist/engine/RunOptions.mjs.map +1 -1
  68. package/dist/engine/SubagentHost.d.mts +1 -1
  69. package/dist/index.d.mts +13 -13
  70. package/dist/index.mjs +3 -3
  71. package/dist/{output-contract-CANJJGA5.mjs → output-contract-DS-S8mTv.mjs} +46 -10
  72. package/dist/{output-contract-CANJJGA5.mjs.map → output-contract-DS-S8mTv.mjs.map} +1 -1
  73. package/package.json +1 -1
  74. package/src/core/Agent.ts +6 -2
  75. package/src/durable/DurableAgentRuntime.ts +86 -78
  76. package/src/durable/LifecyclePublication.ts +79 -25
  77. package/src/durable/WorkerHost.ts +23 -1
  78. package/src/durable/internal/worker-host.ts +169 -17
  79. package/src/engine/RunOptions.ts +8 -0
  80. package/src/engine/internal/agent-runtime.ts +80 -18
  81. package/src/engine/internal/output-contract.ts +67 -8
  82. package/dist/AgentRuntime-xeCUXutN.mjs.map +0 -1
  83. package/dist/DurableAgentRuntime-D8GI66mP.mjs.map +0 -1
@@ -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. OpenAI receives it as trailing system guidance,
3252
- * leaving the last user/tool result as the implicit cache-write boundary.
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
- ): Effect.Effect<Prompt.Prompt, never, Model.ProviderName> =>
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
- provider === "openai"
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(Prompt.fromMessages(view), undefined).content,
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(policy, context, Prompt.empty, turn, priorToolCalls).pipe(
5694
- withCallModel,
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(prepareModelPrompt(Prompt.fromMessages(view), undefined).content);
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
- const steering = yield* drainInputs(context, options);
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
- if (Option.isSome(selectedOutput)) {
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
- * Project system instructions and the output contract into one leading block.
151
- * Repeated Runs append instructions to canonical history; letting those copies
152
- * move the contract breaks the preceding user/tool prefix used by provider caches.
153
- * Keep the last occurrence of each equivalent system message, preserving the
154
- * precedence of distinct instructions and their native provider options.
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
- * Conversation order and message identities are unchanged. A single system block
159
- * also prevents Anthropic's last-system-group conversion from discarding earlier
160
- * application instructions or the output contract.
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>>();