agent-lattice 0.19.0 → 0.20.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.
package/README.md CHANGED
@@ -86,7 +86,7 @@ them, so keep expensive handling off the loop itself.
86
86
  | `stream_event` | While the model is responding | Raw provider stream event for incremental rendering. |
87
87
  | `assistant` | After each model turn is assembled | The `AssistantModelMessage` with text / `tool_use` blocks and provider metadata. |
88
88
  | `user` | After a whole tool batch finishes | Tool results as `ToolResultBlock[]`; the prompt is never echoed. |
89
- | `result` | Once, at the end of the query | Final text, `subtype` (`"success"`, `"interrupted"`, or an error variant), and token usage. |
89
+ | `result` | Once, at the end of the query | Final text, `subtype` (`"success"`, `"interrupted"`, or an error variant), optional `structuredResult`, and token usage. |
90
90
 
91
91
  For the exact per-event guarantees see
92
92
  [Streaming Events](https://docs.claude-code-sdk.com/concepts/streaming-events/).
@@ -150,6 +150,14 @@ Anthropic-compatible providers (for example DeepSeek's
150
150
  `https://api.deepseek.com/anthropic` endpoint) default thinking to on —
151
151
  omitting the field would leave it enabled.
152
152
 
153
+ On DeepSeek, the on/off switch is the only thinking control that works:
154
+ DeepSeek accepts `budget_tokens` but ignores the value, treats `adaptive` as
155
+ plain enabled thinking, and does not support `reasoning_effort` at all. Its own
156
+ thinking-strength knob is `output_config.effort`, which the SDK does not expose
157
+ yet. See
158
+ [Provider Compatibility](https://docs.claude-code-sdk.com/reference/provider-compatibility/)
159
+ for the full matrix.
160
+
153
161
  For Kimi K3 through an Anthropic-compatible endpoint or gateway, use
154
162
  `reasoningEffort` to send the provider's top-level `reasoning_effort` parameter:
155
163
 
@@ -415,6 +423,12 @@ const agent = createAgent({
415
423
  });
416
424
  ```
417
425
 
426
+ Pass an explicit `deepseek-*` model name — unknown names are silently mapped to
427
+ `deepseek-v4-flash`. For which SDK options DeepSeek actually honors (thinking
428
+ budgets are ignored; `reasoningEffort` does not apply; structured output is not
429
+ supported), see
430
+ [Provider Compatibility](https://docs.claude-code-sdk.com/reference/provider-compatibility/).
431
+
418
432
  ## Custom Tool
419
433
 
420
434
  ```ts
@@ -462,6 +476,92 @@ started concurrently, their `tool_result` blocks still enter the history, and
462
476
  model call is skipped. When several tools in a batch set `endTurn`, the first
463
477
  one's content becomes the result text.
464
478
 
479
+ A tool can also return `structuredResult` next to `endTurn: true` to carry a
480
+ structured payload to `SDKResultMessage.structuredResult`
481
+ (*requires 0.20.0 or later*). Without `endTurn`, `structuredResult` is
482
+ ignored.
483
+
484
+ ## Structured Output Via submit_output
485
+
486
+ *Requires 0.20.0 or later.*
487
+
488
+ Set `AgentOptions.outputSchema` when a run must deliver a typed result rather
489
+ than free text. The SDK injects a built-in `submit_output` tool (exported as
490
+ `SUBMIT_OUTPUT_TOOL_NAME`) whose input schema is your schema converted to JSON
491
+ Schema — a zod schema works directly, since `OutputSchema<T>` is just
492
+ `{ parse(input: unknown): T }`:
493
+
494
+ ```ts
495
+ import { createAgent } from "agent-lattice";
496
+ import { z } from "zod/v4";
497
+
498
+ const reviewSchema = z.object({
499
+ approved: z.boolean(),
500
+ issues: z.array(z.string()),
501
+ });
502
+
503
+ const reviewer = createAgent({
504
+ model: "claude-sonnet-4-6",
505
+ systemPrompt: "Review the change and submit your verdict.",
506
+ outputSchema: reviewSchema,
507
+ });
508
+
509
+ const result = await reviewer.prompt("Review the patch in this workspace.");
510
+ if (result.subtype === "success") {
511
+ console.log(result.structuredResult); // { approved: false, issues: [...] }
512
+ }
513
+ ```
514
+
515
+ The structure is enforced by the harness, not by prompt discipline:
516
+
517
+ - The model submits its answer by calling `submit_output`. The payload is
518
+ validated against the schema first; a validation failure goes back into the
519
+ loop as an error `tool_result`, so the model can fix it and retry. A valid
520
+ submission ends the run with `subtype: "success"` and the payload on
521
+ `SDKResultMessage.structuredResult`.
522
+ - If the model ends its turn without calling `submit_output`, the run fails
523
+ with `subtype: "error_missing_output"` and a `MissingOutputError`. There is
524
+ no fallback that parses the final text as JSON.
525
+ - `submit_output` must be the only tool call in its batch. A batch that mixes
526
+ it with other calls — or contains two submissions — is rejected with code
527
+ `submit_output_exclusive_batch` and the loop continues.
528
+ - The name is reserved: registering your own `submit_output` tool while
529
+ `outputSchema` is set throws from `createAgent`/`addTools`.
530
+
531
+ Unlike `outputFormat` (which relies on the provider's
532
+ `response_format`/`json_schema` support — DeepSeek ignores it, see
533
+ [Provider Compatibility](https://docs.claude-code-sdk.com/reference/provider-compatibility/)),
534
+ `submit_output` only requires a model that can call tools, so it ports to any
535
+ tool-capable provider.
536
+
537
+ The same schema composes with `agentTool()` for parent/child delegation: pass
538
+ it to the child agent and to `AgentToolOptions.outputSchema` on the parent
539
+ side, and an `ask` call returns the child's validated output as a JSON string:
540
+
541
+ ```ts
542
+ import { agentTool, createAgent } from "agent-lattice";
543
+
544
+ const child = createAgent({
545
+ model: "claude-sonnet-4-5",
546
+ systemPrompt: "You review code and submit a structured verdict.",
547
+ outputSchema: reviewSchema, // child submits via submit_output
548
+ });
549
+
550
+ const parent = createAgent({
551
+ model: "claude-sonnet-4-6",
552
+ tools: [
553
+ agentTool("review", child, {
554
+ description: "Ask the reviewer to audit a change.",
555
+ outputSchema: reviewSchema, // validates the child's submission
556
+ }),
557
+ ],
558
+ });
559
+ ```
560
+
561
+ If the child ends without submitting or submits a payload that fails the
562
+ parent's schema, the tool returns an `is_error` `tool_result` starting with
563
+ `child_output_invalid:`, so the parent model sees the failure and can retry.
564
+
465
565
  ## Concurrent Tool Calls
466
566
 
467
567
  The model requests concurrency by returning multiple `tool_use` blocks in one
package/dist/index.d.ts CHANGED
@@ -70,7 +70,7 @@ export type AgentRuntimeDelegateResult = {
70
70
  workspaceGrants?: WorkspaceGrant[];
71
71
  };
72
72
  export type AgentRuntimeFailure = {
73
- code: "max_turns_exceeded" | "api_error" | "tool_execution_error" | "permission_denied" | "agent_error";
73
+ code: "max_turns_exceeded" | "api_error" | "tool_execution_error" | "permission_denied" | "missing_output" | "agent_error";
74
74
  message: string;
75
75
  name: string;
76
76
  };
@@ -263,6 +263,11 @@ export type ToolResult = {
263
263
  content: string | ContentBlock[];
264
264
  /** End the run after this tool batch: finish with subtype "success" instead of calling the model again. */
265
265
  endTurn?: boolean;
266
+ /**
267
+ * Structured payload carried to `SDKResultMessage.structuredResult` when this
268
+ * tool also ends the run with `endTurn`. Ignored otherwise.
269
+ */
270
+ structuredResult?: unknown;
266
271
  };
267
272
  export type ToolKind = "tool" | "agent_tool";
268
273
  export type ToolBatchCall = {
@@ -581,6 +586,16 @@ export type AgentOptions<TContext = unknown> = {
581
586
  requestTimeoutMs?: number;
582
587
  tools?: Array<ToolDefinition<any, TContext>>;
583
588
  toolBatchPolicy?: ToolBatchPolicy<TContext>;
589
+ /**
590
+ * Declares the run's structured output contract. When set, the SDK injects a
591
+ * `submit_output` tool with this schema: the run only ends successfully once
592
+ * the model submits a payload through it, and the validated payload lands on
593
+ * `SDKResultMessage.structuredResult`. Ending the turn without submitting
594
+ * fails the run with subtype `error_missing_output`. The tool must be the
595
+ * only call in its batch, and its name is reserved (registering a user tool
596
+ * with the same name throws).
597
+ */
598
+ outputSchema?: OutputSchema;
584
599
  /** Lifecycle callbacks that rewrite tool results and outgoing model requests. */
585
600
  hooks?: AgentHooks<TContext>;
586
601
  /**
@@ -682,12 +697,18 @@ export type SDKResultMessage = {
682
697
  * way, keeping completed turns in history so a follow-up query can continue
683
698
  * the conversation. `is_error` stays `false` for it.
684
699
  */
685
- subtype: "success" | "interrupted" | "error" | "error_max_turns" | "error_abort" | "error_timeout";
700
+ subtype: "success" | "interrupted" | "error" | "error_max_turns" | "error_abort" | "error_timeout" | "error_missing_output";
686
701
  is_error: boolean;
687
702
  result: string;
688
703
  session_id: string;
689
704
  num_turns: number;
690
705
  error?: Error;
706
+ /**
707
+ * Validated structured payload submitted via the `submit_output` tool (see
708
+ * `AgentOptions.outputSchema`), or by any tool that ended the run with both
709
+ * `endTurn` and `ToolResult.structuredResult`. Absent otherwise.
710
+ */
711
+ structuredResult?: unknown;
691
712
  /** Summed over every model request in the query. Zeroed when unreported. */
692
713
  usage: TokenUsage;
693
714
  /**
@@ -734,6 +755,12 @@ export declare class ToolExecutionError extends AgentSDKError {
734
755
  }
735
756
  export declare class MaxTurnsError extends AgentSDKError {
736
757
  }
758
+ /**
759
+ * A run with `AgentOptions.outputSchema` ended without the model calling the
760
+ * `submit_output` tool, so no structured output was produced.
761
+ */
762
+ export declare class MissingOutputError extends AgentSDKError {
763
+ }
737
764
  export declare class AbortError extends AgentSDKError {
738
765
  }
739
766
  /** A second query was started on an Agent that was still running one. */
@@ -752,6 +779,20 @@ export declare class ToolPermissionDeniedError extends AgentSDKError {
752
779
  }
753
780
  export declare function tool<TContext = unknown>(): <TSchema>(name: string, description: string, inputSchema: TSchema, handler: ToolHandler<InferInput<TSchema>, TContext>, options?: ToolOptions<InferInput<TSchema>>) => ToolDefinition<InferInput<TSchema>, TContext>;
754
781
  export declare function tool<TSchema, TContext = unknown>(name: string, description: string, inputSchema: TSchema, handler: ToolHandler<InferInput<TSchema>, TContext>, options?: ToolOptions<InferInput<TSchema>>): ToolDefinition<InferInput<TSchema>, TContext>;
782
+ /**
783
+ * A schema describing structured output: a zod schema, or any object with a
784
+ * compatible `parse` method. The SDK validates submitted payloads with it and
785
+ * converts it to the JSON schema shown to the model.
786
+ */
787
+ export type OutputSchema<TOutput = unknown> = {
788
+ parse(input: unknown): TOutput;
789
+ };
790
+ /**
791
+ * Name of the tool the SDK injects when `AgentOptions.outputSchema` is set.
792
+ * Reserved in that configuration: registering a user tool with the same name
793
+ * throws at `createAgent`/`addTools` time.
794
+ */
795
+ export declare const SUBMIT_OUTPUT_TOOL_NAME = "submit_output";
755
796
  export type DelegateToolOptions = {
756
797
  wait?: DelegateWaitMode;
757
798
  targetMailboxId?: string;
@@ -780,6 +821,14 @@ export type AgentToolInput = z.infer<typeof agentToolInputSchema>;
780
821
  export type AgentToolOptions = {
781
822
  description: string;
782
823
  targetMailboxId?: string;
824
+ /**
825
+ * Expected structured output of the target, declared with the same schema
826
+ * the target uses for its own `AgentOptions.outputSchema`. With mode "ask"
827
+ * the tool result is the target's validated structured output as JSON; a
828
+ * target that ends without submitting — or submits a payload that fails this
829
+ * schema — produces a `child_output_invalid` tool error the parent can retry.
830
+ */
831
+ outputSchema?: OutputSchema;
783
832
  };
784
833
  /**
785
834
  * An AgentLike is a live session: it keeps its conversation history across