@anthropic-ai/sdk 0.109.1 → 0.111.0

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 (77) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/lib/tools/SessionToolRunner.d.mts +29 -2
  3. package/lib/tools/SessionToolRunner.d.mts.map +1 -1
  4. package/lib/tools/SessionToolRunner.d.ts +29 -2
  5. package/lib/tools/SessionToolRunner.d.ts.map +1 -1
  6. package/lib/tools/SessionToolRunner.js +265 -36
  7. package/lib/tools/SessionToolRunner.js.map +1 -1
  8. package/lib/tools/SessionToolRunner.mjs +265 -36
  9. package/lib/tools/SessionToolRunner.mjs.map +1 -1
  10. package/package.json +1 -1
  11. package/resources/beta/agents/agents.d.mts +1 -1
  12. package/resources/beta/agents/agents.d.ts +1 -1
  13. package/resources/beta/beta.d.mts +5 -1
  14. package/resources/beta/beta.d.mts.map +1 -1
  15. package/resources/beta/beta.d.ts +5 -1
  16. package/resources/beta/beta.d.ts.map +1 -1
  17. package/resources/beta/beta.js +4 -0
  18. package/resources/beta/beta.js.map +1 -1
  19. package/resources/beta/beta.mjs +4 -0
  20. package/resources/beta/beta.mjs.map +1 -1
  21. package/resources/beta/dreams.d.mts +269 -0
  22. package/resources/beta/dreams.d.mts.map +1 -0
  23. package/resources/beta/dreams.d.ts +269 -0
  24. package/resources/beta/dreams.d.ts.map +1 -0
  25. package/resources/beta/dreams.js +116 -0
  26. package/resources/beta/dreams.js.map +1 -0
  27. package/resources/beta/dreams.mjs +112 -0
  28. package/resources/beta/dreams.mjs.map +1 -0
  29. package/resources/beta/index.d.mts +1 -0
  30. package/resources/beta/index.d.mts.map +1 -1
  31. package/resources/beta/index.d.ts +1 -0
  32. package/resources/beta/index.d.ts.map +1 -1
  33. package/resources/beta/index.js +3 -1
  34. package/resources/beta/index.js.map +1 -1
  35. package/resources/beta/index.mjs +1 -0
  36. package/resources/beta/index.mjs.map +1 -1
  37. package/resources/beta/memory-stores/memories.d.mts +0 -8
  38. package/resources/beta/memory-stores/memories.d.mts.map +1 -1
  39. package/resources/beta/memory-stores/memories.d.ts +0 -8
  40. package/resources/beta/memory-stores/memories.d.ts.map +1 -1
  41. package/resources/beta/memory-stores/memories.js +5 -5
  42. package/resources/beta/memory-stores/memories.js.map +1 -1
  43. package/resources/beta/memory-stores/memories.mjs +5 -5
  44. package/resources/beta/memory-stores/memories.mjs.map +1 -1
  45. package/resources/beta/memory-stores/memory-stores.js +6 -6
  46. package/resources/beta/memory-stores/memory-stores.js.map +1 -1
  47. package/resources/beta/memory-stores/memory-stores.mjs +6 -6
  48. package/resources/beta/memory-stores/memory-stores.mjs.map +1 -1
  49. package/resources/beta/memory-stores/memory-versions.js +3 -3
  50. package/resources/beta/memory-stores/memory-versions.js.map +1 -1
  51. package/resources/beta/memory-stores/memory-versions.mjs +3 -3
  52. package/resources/beta/memory-stores/memory-versions.mjs.map +1 -1
  53. package/resources/beta/messages/batches.d.mts +3 -2
  54. package/resources/beta/messages/batches.d.mts.map +1 -1
  55. package/resources/beta/messages/batches.d.ts +3 -2
  56. package/resources/beta/messages/batches.d.ts.map +1 -1
  57. package/resources/beta/messages/messages.d.mts +18 -9
  58. package/resources/beta/messages/messages.d.mts.map +1 -1
  59. package/resources/beta/messages/messages.d.ts +18 -9
  60. package/resources/beta/messages/messages.d.ts.map +1 -1
  61. package/resources/beta/messages/messages.js.map +1 -1
  62. package/resources/beta/messages/messages.mjs.map +1 -1
  63. package/src/lib/tools/SessionToolRunner.ts +280 -36
  64. package/src/resources/beta/agents/agents.ts +1 -1
  65. package/src/resources/beta/beta.ts +47 -1
  66. package/src/resources/beta/dreams.ts +404 -0
  67. package/src/resources/beta/index.ts +20 -0
  68. package/src/resources/beta/memory-stores/memories.ts +5 -15
  69. package/src/resources/beta/memory-stores/memory-stores.ts +6 -6
  70. package/src/resources/beta/memory-stores/memory-versions.ts +3 -3
  71. package/src/resources/beta/messages/batches.ts +3 -2
  72. package/src/resources/beta/messages/messages.ts +18 -9
  73. package/src/version.ts +1 -1
  74. package/version.d.mts +1 -1
  75. package/version.d.ts +1 -1
  76. package/version.js +1 -1
  77. package/version.mjs +1 -1
@@ -63,7 +63,9 @@ export interface SessionToolRunnerOptions {
63
63
  /**
64
64
  * Once the session goes idle with `stop_reason.type === "end_turn"`, the
65
65
  * runner keeps running for this many milliseconds before stopping; any new
66
- * event resets the countdown and it re-arms on the next `end_turn` idle.
66
+ * event resets the countdown and it re-arms on the next `end_turn` idle. The
67
+ * countdown is deferred while a confirmation-gated call is held or still
68
+ * dispatching, and starts fresh once the last one resolves.
67
69
  * Defaults to {@link DEFAULT_MAX_IDLE_MS} (60s). `0` (or negative) disables
68
70
  * it — the runner then only stops on session termination or the consumer
69
71
  * breaking out / aborting.
@@ -128,9 +130,22 @@ export interface DispatchedToolCall {
128
130
  * post itself failed (typically a permanent 4xx or send-retry exhaustion)
129
131
  * and also `false` — with no `result` event ever built — for a tool name
130
132
  * this runner does not own when it deliberately posts nothing and leaves the
131
- * id pending for its owner (the split-client behavior).
133
+ * id pending for its owner (the split-client behavior), or when the call
134
+ * was denied and never executed (see `confirmation`).
132
135
  */
133
136
  readonly posted: boolean;
137
+ /**
138
+ * The confirmation verdict that gated this call, if any.
139
+ *
140
+ * `'allow'` — the call required user confirmation (the server evaluated
141
+ * its permission to `ask`, e.g. under an `always_ask` policy) and the
142
+ * matching `user.tool_confirmation` approved it before the tool ran.
143
+ * `'deny'` — the user denied it, or the server itself evaluated the
144
+ * permission to `deny`; the tool was never executed and nothing was posted
145
+ * (`result=undefined`, `posted=false`, `isError=false`).
146
+ * `undefined` — the call needed no confirmation.
147
+ */
148
+ readonly confirmation: 'allow' | 'deny' | undefined;
134
149
  }
135
150
 
136
151
  /** Returns true if `ev` is a `session.status_idle` with `stop_reason` `end_turn`. */
@@ -138,6 +153,95 @@ function isEndTurnIdle(ev: { type?: string; stop_reason?: { type?: string } }):
138
153
  return ev.type === 'session.status_idle' && ev.stop_reason?.type === 'end_turn';
139
154
  }
140
155
 
156
+ /**
157
+ * The `maxIdleMs` stop-countdown, including its deferral. {@link noteEvent}
158
+ * arms on `session.status_idle` with `stop_reason: end_turn` and disarms on
159
+ * anything else. Gated tool work registered via {@link block} — a call held for
160
+ * user confirmation, or a user-approved call still dispatching — keeps
161
+ * {@link arm} pending until {@link unblock} retires the last blocker, at which
162
+ * point the countdown starts. Event-driven — there is no polling watchdog.
163
+ */
164
+ class IdleClock {
165
+ readonly #maxIdleMs: number;
166
+ readonly #onExpire: () => void;
167
+ readonly #blockers = new Set<string>();
168
+ // Set when arm() found blockers outstanding; the unblock that retires the
169
+ // last blocker applies it. Cleared by any disarm.
170
+ #armPending = false;
171
+ #timer: ReturnType<typeof setTimeout> | undefined;
172
+
173
+ constructor(maxIdleMs: number, onExpire: () => void) {
174
+ this.#maxIdleMs = maxIdleMs;
175
+ this.#onExpire = onExpire;
176
+ }
177
+
178
+ /**
179
+ * Arm on `status_idle{end_turn}`; disarm otherwise. `user.tool_confirmation`
180
+ * is neutral: it signals neither agent activity nor an idle, and its effect
181
+ * on the clock flows through {@link block} / {@link unblock} instead —
182
+ * disarming here would discard the pending arm the verdict is about to
183
+ * settle.
184
+ */
185
+ noteEvent(ev: { type: string; stop_reason?: { type?: string } }): void {
186
+ if (ev.type === 'user.tool_confirmation') return;
187
+ if (isEndTurnIdle(ev)) this.arm();
188
+ else this.disarm();
189
+ }
190
+
191
+ /** Register gated work that must resolve before an idle countdown starts. */
192
+ block(toolUseId: string): void {
193
+ this.#blockers.add(toolUseId);
194
+ if (this.#timer !== undefined) {
195
+ // Defensive: every caller disarms first (any event that routes a tool
196
+ // call is itself a disarming event), but a countdown running when gated
197
+ // work appears is stale evidence — the session is not idly waiting to
198
+ // stop. Convert it into a pending arm rather than let it fire over the
199
+ // gated call.
200
+ this.#armPending = true;
201
+ clearTimeout(this.#timer);
202
+ this.#timer = undefined;
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Retire gated work (a no-op for ids never blocked); applies a pending arm —
208
+ * with a fresh full `maxIdleMs` window — once the last blocker retires.
209
+ */
210
+ unblock(toolUseId: string): void {
211
+ this.#blockers.delete(toolUseId);
212
+ if (this.#blockers.size === 0 && this.#armPending) this.arm();
213
+ }
214
+
215
+ /**
216
+ * (Re)start the idle countdown — or, while blockers are outstanding, hold
217
+ * the arm pending instead. Stopping then would drop a held call when its
218
+ * verdict later arrives, or cut the runner off before a released call's
219
+ * result can drive the next turn.
220
+ */
221
+ arm(): void {
222
+ if (this.#maxIdleMs <= 0) return;
223
+ if (this.#blockers.size > 0) {
224
+ this.#armPending = true;
225
+ return;
226
+ }
227
+ this.#armPending = false;
228
+ if (this.#timer !== undefined) clearTimeout(this.#timer);
229
+ this.#timer = setTimeout(this.#onExpire, this.#maxIdleMs);
230
+ }
231
+
232
+ /**
233
+ * Cancel the idle countdown and any pending arm. Blockers persist — they
234
+ * track real outstanding work, retired only by {@link unblock}.
235
+ */
236
+ disarm(): void {
237
+ this.#armPending = false;
238
+ if (this.#timer !== undefined) {
239
+ clearTimeout(this.#timer);
240
+ this.#timer = undefined;
241
+ }
242
+ }
243
+ }
244
+
141
245
  /**
142
246
  * The sessions-side counterpart to `client.beta.messages.toolRunner`: an
143
247
  * async-iterable that attaches to a managed-agents session, executes every
@@ -148,6 +252,18 @@ function isEndTurnIdle(ev: { type?: string; stop_reason?: { type?: string } }):
148
252
  * calls are not dispatched. Internally drives event-stream reconnect and result
149
253
  * posting.
150
254
  *
255
+ * A call the server gated with `evaluated_permission: "ask"` (the `always_ask`
256
+ * policy — or any value this SDK doesn't recognize, which fails closed) is held
257
+ * until its `user.tool_confirmation` arrives: only an explicit `allow` runs it;
258
+ * `deny` — or any verdict this SDK doesn't recognize, failing closed — is never
259
+ * executed and posts nothing (the denial resolves the call server-side), but is
260
+ * still yielded (`confirmation="deny"`, `posted=false`, `result=undefined`) so
261
+ * the consumer can observe it. A held call — and a user-approved one still
262
+ * dispatching — defers the `maxIdleMs` countdown, so an `end_turn` idle
263
+ * observed in the meantime cannot stop the runner: it waits until the verdict
264
+ * arrives, the session terminates, or the abort signal fires — pass
265
+ * `AbortSignal.timeout(...)` for a wall-clock bound.
266
+ *
151
267
  * Iteration ends when the session terminates (`session.status_terminated` /
152
268
  * `session.deleted`), when the consumer `break`s out of the loop or aborts the
153
269
  * supplied signal, or — once the session has gone idle with
@@ -183,12 +299,19 @@ export class SessionToolRunner implements AsyncIterable<DispatchedToolCall> {
183
299
  readonly #logger: Logger;
184
300
  readonly #seen = new Set<string>();
185
301
  readonly #answered = new Set<string>();
302
+ // Confirmation gating (`always_ask` tools): `#confirmationVerdicts` records
303
+ // every `user.tool_confirmation` verdict by `tool_use_id`;
304
+ // `#awaitingConfirmation` holds the tool-call events whose
305
+ // `evaluated_permission` is `ask` and whose verdict has not arrived —
306
+ // released (or resolved as denied) by `#noteConfirmation` / the next
307
+ // reconcile pass. Like `#seen` and `#answered`, `#confirmationVerdicts` is
308
+ // per-session O(tool calls): recorded verdicts persist for the life of the run.
309
+ readonly #confirmationVerdicts = new Map<string, 'allow' | 'deny'>();
310
+ readonly #awaitingConfirmation = new Map<string, DispatchedToolUseEvent>();
186
311
  readonly #results = new AsyncQueue<DispatchedToolCall>();
187
312
  #inFlightCount = 0;
188
313
  #onIdle: (() => void) | null = null;
189
- // When the session is idle past an `end_turn`, the pending stop timer; cleared
190
- // by any new event. Event-driven — there is no polling watchdog.
191
- #idleTimer: ReturnType<typeof setTimeout> | undefined;
314
+ readonly #idleClock: IdleClock;
192
315
 
193
316
  constructor(sessionId: string, opts: SessionToolRunnerOptions) {
194
317
  this.client = opts.client;
@@ -200,6 +323,14 @@ export class SessionToolRunner implements AsyncIterable<DispatchedToolCall> {
200
323
  this.#controller = new AbortController();
201
324
  this.#detachExternal = linkAbort(opts.signal, this.#controller);
202
325
  this.#requestOpts = opts.requestOptions;
326
+ this.#idleClock = new IdleClock(this.maxIdleMs, () => {
327
+ this.#logger.info('session idle after end_turn; stopping', {
328
+ component: 'session-tool-runner',
329
+ session_id: this.sessionId,
330
+ max_idle_ms: this.maxIdleMs,
331
+ });
332
+ this.#controller.abort();
333
+ });
203
334
  }
204
335
 
205
336
  /** Read-only view of this runner's abort signal. */
@@ -250,7 +381,7 @@ export class SessionToolRunner implements AsyncIterable<DispatchedToolCall> {
250
381
  }
251
382
  } finally {
252
383
  this.#controller.abort();
253
- this.#disarmIdleTimer();
384
+ this.#idleClock.disarm();
254
385
  // Re-await defensively in case the consumer broke out of phase 1 before
255
386
  // phase 2 ran — a no-op if it already settled.
256
387
  await streamPromise;
@@ -362,12 +493,29 @@ export class SessionToolRunner implements AsyncIterable<DispatchedToolCall> {
362
493
  return;
363
494
  }
364
495
  const unanswered = pending.filter((ev) => !this.#answered.has(ev.id));
365
- // If the most recent event in history is an `end_turn` idle and there's no
366
- // outstanding tool work, the session is done arm the idle timer so the
367
- // runner stops even if that `end_turn` arrived during a disconnect.
368
- if (lastWasEndTurn && unanswered.length === 0) this.#armIdleTimer();
369
- else this.#disarmIdleTimer();
370
- for (const ev of unanswered) await this.#execute(ev);
496
+ // Disarm before routing: `#execute` runs inline here, so a timer left armed
497
+ // from before the reconnect could fire over an in-flight tool.
498
+ this.#idleClock.disarm();
499
+ for (const ev of unanswered) await this.#routeToolEvent(ev);
500
+ // A held call's verdict is normally applied by the routing pass above; if
501
+ // its tool_use fell outside the listed window the pass never saw it, so
502
+ // apply the verdict to the held copy here.
503
+ for (const held of [...this.#awaitingConfirmation.values()]) {
504
+ const verdict = this.#confirmationVerdicts.get(held.id);
505
+ if (verdict !== undefined) await this.#applyVerdict(held, verdict);
506
+ }
507
+ // Routing resolves denied calls in place (marking them answered) and holds
508
+ // ask-gated calls for their `user.tool_confirmation`. If the most recent
509
+ // event in history is an `end_turn` idle and no tool work is outstanding,
510
+ // the session is done — arm the idle clock so the runner stops even if that
511
+ // `end_turn` arrived during a disconnect. A held call is not outstanding
512
+ // here: it blocks the clock, so this arm stays pending until the verdict
513
+ // (and, for an allow, the dispatch it releases) resolves it.
514
+ const outstanding = unanswered.filter(
515
+ (ev) => !this.#answered.has(ev.id) && !this.#awaitingConfirmation.has(ev.id),
516
+ );
517
+ if (lastWasEndTurn && outstanding.length === 0) this.#idleClock.arm();
518
+ else this.#idleClock.disarm();
371
519
  }
372
520
 
373
521
  #ingestHistory(ev: BetaManagedAgentsSessionEvent, pending: DispatchedToolUseEvent[]): void {
@@ -382,23 +530,29 @@ export class SessionToolRunner implements AsyncIterable<DispatchedToolCall> {
382
530
  this.#answered.add(ev.tool_use_id);
383
531
  } else if (ev.type === 'user.custom_tool_result') {
384
532
  this.#answered.add(ev.custom_tool_use_id);
533
+ } else if (ev.type === 'user.tool_confirmation') {
534
+ // Record the verdict only, before the pending pass, so a call whose
535
+ // confirmation appears later in the same history routes with its verdict
536
+ // already known. Releasing a held call here as well would dispatch it a
537
+ // second time when the routing pass reaches its tool_use event.
538
+ if (!this.#answered.has(ev.tool_use_id)) this.#confirmationVerdicts.set(ev.tool_use_id, ev.result);
385
539
  }
386
540
  }
387
541
 
388
542
  /** Returns true when the runner should exit. */
389
543
  async #handleStreamEvent(ev: BetaManagedAgentsStreamSessionEvents): Promise<boolean> {
390
- // Arm/disarm the idle timer: an `end_turn` idle starts the grace countdown;
391
- // any other event cancels it.
392
- if (isEndTurnIdle(ev)) this.#armIdleTimer();
393
- else this.#disarmIdleTimer();
544
+ this.#idleClock.noteEvent(ev);
394
545
  switch (ev.type) {
395
546
  case 'agent.tool_use':
396
547
  case 'agent.custom_tool_use':
397
548
  if (!this.#seen.has(ev.id)) {
398
549
  this.#seen.add(ev.id);
399
- await this.#execute(ev);
550
+ await this.#routeToolEvent(ev);
400
551
  }
401
552
  return false;
553
+ case 'user.tool_confirmation':
554
+ await this.#noteConfirmation(ev);
555
+ return false;
402
556
  case 'user.tool_result':
403
557
  this.#answered.add(ev.tool_use_id);
404
558
  return false;
@@ -418,33 +572,115 @@ export class SessionToolRunner implements AsyncIterable<DispatchedToolCall> {
418
572
  }
419
573
  }
420
574
 
421
- // ===== idle timer =====
575
+ // ===== confirmation gating (always_ask tools) =====
422
576
 
423
- /** (Re)start the grace countdown that stops the runner after `maxIdleMs` of idle. */
424
- #armIdleTimer(): void {
425
- this.#disarmIdleTimer();
426
- if (this.maxIdleMs <= 0) return;
427
- this.#idleTimer = setTimeout(() => {
428
- this.#logger.info('session idle after end_turn; stopping', {
577
+ /**
578
+ * Dispatch `ev`, honoring its evaluated permission. A call the server gated
579
+ * (`evaluated_permission == "ask"`) is held until its `user.tool_confirmation`
580
+ * arrives. Fails closed: only an explicit `allow` verdict releases a gated
581
+ * call; a server-side `deny` overrides any recorded verdict; an unrecognized
582
+ * permission is held like `ask` and an unrecognized verdict is denied.
583
+ */
584
+ async #routeToolEvent(ev: DispatchedToolUseEvent): Promise<void> {
585
+ // `getattr`-style read: today only `agent.tool_use` carries
586
+ // `evaluated_permission`, but if it ever lands on `agent.custom_tool_use`
587
+ // the gate must keep failing closed rather than dispatch by event type.
588
+ const permission = (ev as { evaluated_permission?: 'allow' | 'ask' | 'deny' }).evaluated_permission;
589
+ // A server-side `deny` overrides any (stray) recorded verdict.
590
+ const verdict = permission === 'deny' ? 'deny' : this.#confirmationVerdicts.get(ev.id);
591
+ if (verdict === undefined) {
592
+ if (permission === undefined || permission === 'allow') {
593
+ await this.#execute(ev, undefined);
594
+ } else if (!this.#awaitingConfirmation.has(ev.id)) {
595
+ // "ask" — or a permission this SDK does not recognize, which must not
596
+ // dispatch unconfirmed — waits for the user's verdict. (Already-held: a
597
+ // reconcile after reconnect re-routes the call; keep the existing hold.)
598
+ this.#logger.info('tool call awaiting confirmation; holding', {
599
+ component: 'session-tool-runner',
600
+ session_id: this.sessionId,
601
+ tool: ev.name,
602
+ tool_use_id: ev.id,
603
+ });
604
+ this.#awaitingConfirmation.set(ev.id, ev);
605
+ this.#idleClock.block(ev.id);
606
+ }
607
+ return;
608
+ }
609
+ await this.#applyVerdict(ev, verdict);
610
+ }
611
+
612
+ /** Record an allow/deny verdict and release the held call it gates, if any. */
613
+ async #noteConfirmation(ev: { tool_use_id: string; result: 'allow' | 'deny' }): Promise<void> {
614
+ this.#confirmationVerdicts.set(ev.tool_use_id, ev.result);
615
+ const held = this.#awaitingConfirmation.get(ev.tool_use_id);
616
+ // Nothing held: the verdict gates a call this runner has not seen yet (or
617
+ // one it never gates, e.g. an `agent.mcp_tool_use`). Keeping it in
618
+ // `#confirmationVerdicts` lets a later route of that call resolve instantly.
619
+ if (held === undefined) return;
620
+ await this.#applyVerdict(held, ev.result);
621
+ }
622
+
623
+ /**
624
+ * Dispatch or resolve a gated call according to its verdict.
625
+ *
626
+ * The idle-clock blocker accounting lives here: a denial retires the held
627
+ * call's blocker, while an allow keeps one on the call — taking it now if the
628
+ * verdict was already known when the call was routed, so it was never held —
629
+ * until `#execute` has finished with it. The countdown must not run over
630
+ * gated work that is still in flight.
631
+ */
632
+ async #applyVerdict(ev: DispatchedToolUseEvent, verdict: 'allow' | 'deny'): Promise<void> {
633
+ const wasHeld = this.#awaitingConfirmation.delete(ev.id);
634
+ if (verdict === 'allow') {
635
+ this.#logger.info('tool call confirmed', {
429
636
  component: 'session-tool-runner',
430
637
  session_id: this.sessionId,
431
- max_idle_ms: this.maxIdleMs,
638
+ tool: ev.name,
639
+ tool_use_id: ev.id,
432
640
  });
433
- this.#controller.abort();
434
- }, this.maxIdleMs);
641
+ if (!wasHeld) this.#idleClock.block(ev.id);
642
+ try {
643
+ await this.#execute(ev, 'allow');
644
+ } finally {
645
+ // The approved call is fully disposed of (executed, or moot because it
646
+ // was answered elsewhere) — the sole place an allow's blocker retires.
647
+ this.#idleClock.unblock(ev.id);
648
+ }
649
+ return;
650
+ }
651
+ // "deny" — or any value other than an explicit "allow" (fail closed). The
652
+ // denial resolves the call server-side, so mark it answered and yield it
653
+ // (nothing ran, nothing posted).
654
+ if (wasHeld) this.#idleClock.unblock(ev.id);
655
+ this.#answered.add(ev.id);
656
+ this.#logger.info('tool call denied; not executing', {
657
+ component: 'session-tool-runner',
658
+ session_id: this.sessionId,
659
+ tool: ev.name,
660
+ tool_use_id: ev.id,
661
+ });
662
+ this.#surfaceCall({
663
+ event: ev,
664
+ toolUseId: ev.id,
665
+ name: ev.name,
666
+ isError: false,
667
+ posted: false,
668
+ confirmation: 'deny',
669
+ });
435
670
  }
436
671
 
437
- /** Cancel a pending idle countdown, if any. */
438
- #disarmIdleTimer(): void {
439
- if (this.#idleTimer !== undefined) {
440
- clearTimeout(this.#idleTimer);
441
- this.#idleTimer = undefined;
442
- }
672
+ /**
673
+ * Yield `call` to the consumer. A closed queue — the consumer broke out of
674
+ * the iterator makes `push` a no-op returning false; the underlying work
675
+ * already happened, so only the observability event is lost.
676
+ */
677
+ #surfaceCall(call: DispatchedToolCall): void {
678
+ this.#results.push(call);
443
679
  }
444
680
 
445
681
  // ===== tool execution =====
446
682
 
447
- async #execute(ev: DispatchedToolUseEvent): Promise<void> {
683
+ async #execute(ev: DispatchedToolUseEvent, confirmation: 'allow' | undefined): Promise<void> {
448
684
  if (this.#answered.has(ev.id)) return;
449
685
  this.#logger.info('executing tool', {
450
686
  component: 'session-tool-runner',
@@ -472,7 +708,14 @@ export class SessionToolRunner implements AsyncIterable<DispatchedToolCall> {
472
708
  tool: ev.name,
473
709
  tool_use_id: ev.id,
474
710
  });
475
- this.#results.push({ event: ev, toolUseId: ev.id, name: ev.name, isError: false, posted: false });
711
+ this.#surfaceCall({
712
+ event: ev,
713
+ toolUseId: ev.id,
714
+ name: ev.name,
715
+ isError: false,
716
+ posted: false,
717
+ confirmation,
718
+ });
476
719
  return;
477
720
  }
478
721
  let content: string | Array<BetaToolResultContentBlockParam>;
@@ -504,13 +747,14 @@ export class SessionToolRunner implements AsyncIterable<DispatchedToolCall> {
504
747
  // unanswered and the session stuck.
505
748
  const result = buildResultEvent(ev, isError, toSessionContent(content));
506
749
  const posted = await this.#sendResult(result, ev.id);
507
- this.#results.push({
750
+ this.#surfaceCall({
508
751
  event: ev,
509
752
  result,
510
753
  toolUseId: ev.id,
511
754
  name: ev.name,
512
755
  isError,
513
756
  posted,
757
+ confirmation,
514
758
  });
515
759
  } finally {
516
760
  this.#inFlightCount--;
@@ -537,7 +537,7 @@ export interface BetaManagedAgentsCustomToolInputSchema {
537
537
  export interface BetaManagedAgentsCustomToolParams {
538
538
  /**
539
539
  * Description of what the tool does, shown to the agent to help it decide when to
540
- * use the tool. 1-1024 characters.
540
+ * use the tool. 1-4096 characters.
541
541
  */
542
542
  description: string;
543
543
 
@@ -75,6 +75,27 @@ import {
75
75
  DeploymentUpdateParams,
76
76
  Deployments,
77
77
  } from './deployments';
78
+ import * as DreamsAPI from './dreams';
79
+ import {
80
+ BetaDream,
81
+ BetaDreamError,
82
+ BetaDreamInput,
83
+ BetaDreamMemoryStoreInput,
84
+ BetaDreamMemoryStoreOutput,
85
+ BetaDreamModelConfig,
86
+ BetaDreamModelConfigParam,
87
+ BetaDreamOutput,
88
+ BetaDreamSessionsInput,
89
+ BetaDreamStatus,
90
+ BetaDreamUsage,
91
+ BetaDreamsPageCursor,
92
+ DreamArchiveParams,
93
+ DreamCancelParams,
94
+ DreamCreateParams,
95
+ DreamListParams,
96
+ DreamRetrieveParams,
97
+ Dreams,
98
+ } from './dreams';
78
99
  import * as FilesAPI from './files';
79
100
  import {
80
101
  BetaFileScope,
@@ -553,6 +574,7 @@ export class Beta extends APIResource {
553
574
  skills: SkillsAPI.Skills = new SkillsAPI.Skills(this._client);
554
575
  webhooks: WebhooksAPI.Webhooks = new WebhooksAPI.Webhooks(this._client);
555
576
  userProfiles: UserProfilesAPI.UserProfiles = new UserProfilesAPI.UserProfiles(this._client);
577
+ dreams: DreamsAPI.Dreams = new DreamsAPI.Dreams(this._client);
556
578
  }
557
579
 
558
580
  export type AnthropicBeta =
@@ -582,9 +604,11 @@ export type AnthropicBeta =
582
604
  | 'advisor-tool-2026-03-01'
583
605
  | 'managed-agents-2026-04-01'
584
606
  | 'cache-diagnosis-2026-04-07'
607
+ | 'dreaming-2026-04-21'
585
608
  | 'thinking-token-count-2026-05-13'
586
609
  | 'server-side-fallback-2026-06-01'
587
- | 'fallback-credit-2026-06-01';
610
+ | 'fallback-credit-2026-06-01'
611
+ | 'agent-memory-2026-07-22';
588
612
 
589
613
  export interface BetaAPIError {
590
614
  message: string;
@@ -672,6 +696,7 @@ Beta.Files = Files;
672
696
  Beta.Skills = Skills;
673
697
  Beta.Webhooks = Webhooks;
674
698
  Beta.UserProfiles = UserProfiles;
699
+ Beta.Dreams = Dreams;
675
700
 
676
701
  export declare namespace Beta {
677
702
  export {
@@ -1225,4 +1250,25 @@ export declare namespace Beta {
1225
1250
  type UserProfileListParams as UserProfileListParams,
1226
1251
  type UserProfileCreateEnrollmentURLParams as UserProfileCreateEnrollmentURLParams,
1227
1252
  };
1253
+
1254
+ export {
1255
+ Dreams as Dreams,
1256
+ type BetaDream as BetaDream,
1257
+ type BetaDreamError as BetaDreamError,
1258
+ type BetaDreamInput as BetaDreamInput,
1259
+ type BetaDreamMemoryStoreInput as BetaDreamMemoryStoreInput,
1260
+ type BetaDreamMemoryStoreOutput as BetaDreamMemoryStoreOutput,
1261
+ type BetaDreamModelConfig as BetaDreamModelConfig,
1262
+ type BetaDreamModelConfigParam as BetaDreamModelConfigParam,
1263
+ type BetaDreamOutput as BetaDreamOutput,
1264
+ type BetaDreamSessionsInput as BetaDreamSessionsInput,
1265
+ type BetaDreamStatus as BetaDreamStatus,
1266
+ type BetaDreamUsage as BetaDreamUsage,
1267
+ type BetaDreamsPageCursor as BetaDreamsPageCursor,
1268
+ type DreamCreateParams as DreamCreateParams,
1269
+ type DreamRetrieveParams as DreamRetrieveParams,
1270
+ type DreamListParams as DreamListParams,
1271
+ type DreamArchiveParams as DreamArchiveParams,
1272
+ type DreamCancelParams as DreamCancelParams,
1273
+ };
1228
1274
  }