agent-lattice 0.9.25 → 0.12.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 +144 -0
- package/dist/index.d.ts +102 -1
- package/dist/index.js +7 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,6 +7,11 @@ Install it from npm as `agent-lattice`. It works with Anthropic and
|
|
|
7
7
|
Anthropic-compatible providers such as DeepSeek, without installing the Claude
|
|
8
8
|
Code CLI runtime.
|
|
9
9
|
|
|
10
|
+
> **Integrating this SDK from an AI agent?** Start at
|
|
11
|
+
> <https://docs.claude-code-sdk.com/llms.txt> for an index of the documentation,
|
|
12
|
+
> where every page is served as clean Markdown. Read
|
|
13
|
+
> <https://docs.claude-code-sdk.com/llms-full.txt> to take it all in one request.
|
|
14
|
+
|
|
10
15
|
## Install
|
|
11
16
|
|
|
12
17
|
```bash
|
|
@@ -50,6 +55,24 @@ const agent = createBareAgent({
|
|
|
50
55
|
});
|
|
51
56
|
```
|
|
52
57
|
|
|
58
|
+
`agent.query()` yields `stream_event` messages while the model is still
|
|
59
|
+
responding, so a host can render output incrementally:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
for await (const message of agent.query("Say hello")) {
|
|
63
|
+
if (message.type === "stream_event") {
|
|
64
|
+
const event = message.event as { type: string; delta?: { text?: string } };
|
|
65
|
+
if (event.type === "content_block_delta" && event.delta?.text) {
|
|
66
|
+
process.stdout.write(event.delta.text);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The SDK produces the next event only after the loop takes the current one.
|
|
73
|
+
Slow work in the loop body delays later events without dropping or reordering
|
|
74
|
+
them, so keep expensive handling off the loop itself.
|
|
75
|
+
|
|
53
76
|
Pass `{ stream: false }` to disable model streaming for a query:
|
|
54
77
|
|
|
55
78
|
```ts
|
|
@@ -103,6 +126,30 @@ Use `{ type: "adaptive" }` for models that support adaptive thinking. Use
|
|
|
103
126
|
budget is capped at `maxTokens - 1` to satisfy the Anthropic API constraint.
|
|
104
127
|
When omitted, the SDK does not send a thinking configuration.
|
|
105
128
|
|
|
129
|
+
For Kimi K3 through an Anthropic-compatible endpoint or gateway, use
|
|
130
|
+
`reasoningEffort` to send the provider's top-level `reasoning_effort` parameter:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const agent = createAgent({
|
|
134
|
+
apiKey: process.env.MOONSHOT_API_KEY,
|
|
135
|
+
baseURL: process.env.KIMI_ANTHROPIC_BASE_URL,
|
|
136
|
+
model: "kimi-k3",
|
|
137
|
+
reasoningEffort: "high",
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const result = await agent.prompt("Solve this carefully.", {
|
|
141
|
+
reasoningEffort: "low",
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The supported values are `"low"`, `"high"`, and `"max"`. A query-level value
|
|
146
|
+
overrides the agent default. When omitted, the SDK does not send
|
|
147
|
+
`reasoning_effort`, so the provider applies its own default (`"max"` for Kimi
|
|
148
|
+
K3). Kimi K3 does not accept `thinkingConfig`; use `reasoningEffort` instead.
|
|
149
|
+
The Kimi Open Platform endpoint at `https://api.moonshot.cn/v1` uses the OpenAI
|
|
150
|
+
Chat Completions protocol and is not a valid `baseURL` for the SDK's built-in
|
|
151
|
+
Anthropic client.
|
|
152
|
+
|
|
106
153
|
## Multimodal Input
|
|
107
154
|
|
|
108
155
|
Pass Anthropic-compatible content blocks for image or document prompts:
|
|
@@ -369,6 +416,44 @@ policy, tool execution is unchanged. A policy prevents known bad combinations
|
|
|
369
416
|
inside one model response, but it does not replace database transactions or
|
|
370
417
|
revision checks against concurrent external updates.
|
|
371
418
|
|
|
419
|
+
## Hooks
|
|
420
|
+
|
|
421
|
+
`permission` and `toolBatchPolicy` decide whether something runs. Hooks decide
|
|
422
|
+
what it looks like — redacting tool output, trimming context before a request,
|
|
423
|
+
or injecting retrieved documents:
|
|
424
|
+
|
|
425
|
+
```ts
|
|
426
|
+
const agent = createAgent({
|
|
427
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
428
|
+
model: "claude-sonnet-4-6",
|
|
429
|
+
tools: [queryDatabase],
|
|
430
|
+
hooks: {
|
|
431
|
+
async onToolResult({ toolName, result, error }) {
|
|
432
|
+
if (toolName !== "queryDatabase") return; // undefined: leave unchanged
|
|
433
|
+
return { ...result, content: await redact(result.content) };
|
|
434
|
+
},
|
|
435
|
+
onModelRequest({ messages, turn }) {
|
|
436
|
+
if (messages.length < 40) return;
|
|
437
|
+
return { messages: compact(messages) };
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
`onToolResult` sees every result on its way to the model, including handler
|
|
444
|
+
failures, aborted calls, and calls blocked by `toolBatchPolicy`. `onModelRequest`
|
|
445
|
+
shapes a single request; the stored conversation is untouched, so trimming
|
|
446
|
+
context does not destroy history.
|
|
447
|
+
|
|
448
|
+
A hook returns a replacement or nothing, and must not mutate what it receives. A
|
|
449
|
+
hook that throws propagates out of `query()` rather than becoming an error
|
|
450
|
+
`result` — a redaction hook that failed quietly would leak the data it exists to
|
|
451
|
+
protect. Hooks run before the matching trace event, so traces record what was
|
|
452
|
+
actually sent.
|
|
453
|
+
|
|
454
|
+
Compose independent concerns with `createCompositeAgentHooks([a, b, c])`, which
|
|
455
|
+
chains them in order, each receiving the previous one's output.
|
|
456
|
+
|
|
372
457
|
## Business Context For Tools
|
|
373
458
|
|
|
374
459
|
Pass host application data through `context`. The SDK gives that context to
|
|
@@ -897,3 +982,62 @@ console.log(result.result);
|
|
|
897
982
|
The SDK stores conversation state in memory for the lifetime of the `Agent`
|
|
898
983
|
instance. Persistent transcripts and resume support are intentionally out of
|
|
899
984
|
scope for the first release.
|
|
985
|
+
|
|
986
|
+
An `Agent` is a conversation, not a reusable client. Because the history is
|
|
987
|
+
instance state, starting a query while another is still running would interleave
|
|
988
|
+
both conversations; the SDK rejects the second one with `ConcurrentQueryError`.
|
|
989
|
+
Create one Agent per concurrent conversation — in a server, per request or per
|
|
990
|
+
user session rather than a shared module-level instance. Sequential reuse, as
|
|
991
|
+
above, is the intended pattern.
|
|
992
|
+
|
|
993
|
+
## Deadlines
|
|
994
|
+
|
|
995
|
+
`QueryOptions.signal` bounds a whole query — every model request, tool call, and
|
|
996
|
+
turn together. `requestTimeoutMs` bounds each single model request, so an agent
|
|
997
|
+
that legitimately runs many tool-using turns does not have to fit them all into
|
|
998
|
+
one budget:
|
|
999
|
+
|
|
1000
|
+
```ts
|
|
1001
|
+
const agent = createAgent({
|
|
1002
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
1003
|
+
model: "claude-sonnet-4-6",
|
|
1004
|
+
requestTimeoutMs: 120_000,
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
const result = await agent.prompt("Audit this repository.", {
|
|
1008
|
+
signal: AbortSignal.timeout(600_000),
|
|
1009
|
+
requestTimeoutMs: 60_000, // overrides the agent default for this query
|
|
1010
|
+
});
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
A request deadline produces `subtype: "error_timeout"` with a `TimeoutError`,
|
|
1014
|
+
distinct from the `"error_abort"` of a caller-initiated cancellation, so hosts
|
|
1015
|
+
can retry timeouts without retrying deliberate cancellations.
|
|
1016
|
+
|
|
1017
|
+
Both limits are enforced by the SDK rather than delegated. `ModelRequest` carries
|
|
1018
|
+
`signal` and `timeoutMs` so a client can cancel its own work, but the agent loop
|
|
1019
|
+
also races the call, so a `ModelClient` that honours neither cannot stall the
|
|
1020
|
+
loop indefinitely. Losing that race abandons the call rather than cancelling it.
|
|
1021
|
+
|
|
1022
|
+
## Token Usage And Truncation
|
|
1023
|
+
|
|
1024
|
+
Every `result` message reports `usage`, summed over the model requests in that
|
|
1025
|
+
query, plus the `stop_reason` of the last response:
|
|
1026
|
+
|
|
1027
|
+
```ts
|
|
1028
|
+
const result = await agent.prompt("Summarize this file.");
|
|
1029
|
+
console.log(result.usage);
|
|
1030
|
+
// { input_tokens: 1200, output_tokens: 512, cache_read_input_tokens: 800 }
|
|
1031
|
+
|
|
1032
|
+
if (result.stop_reason === "max_tokens") {
|
|
1033
|
+
// subtype is still "success", but result is a fragment, not an answer.
|
|
1034
|
+
}
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
`stop_reason: "max_tokens"` means the model hit its output budget mid-response.
|
|
1038
|
+
The SDK does not treat that as an error, so checking this field is the only way
|
|
1039
|
+
to distinguish a complete answer from a truncated one.
|
|
1040
|
+
|
|
1041
|
+
Usage comes from the model client. The built-in Anthropic client fills it in from
|
|
1042
|
+
the response, including the streaming path; a custom `ModelClient` that omits
|
|
1043
|
+
`usage` produces zeroed counts rather than an error.
|
package/dist/index.d.ts
CHANGED
|
@@ -150,9 +150,23 @@ export type ModelMessage = {
|
|
|
150
150
|
role: "user" | "assistant";
|
|
151
151
|
content: string | ContentBlock[];
|
|
152
152
|
};
|
|
153
|
+
export type TokenUsage = {
|
|
154
|
+
input_tokens: number;
|
|
155
|
+
output_tokens: number;
|
|
156
|
+
cache_creation_input_tokens?: number;
|
|
157
|
+
cache_read_input_tokens?: number;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Why the model stopped. `"max_tokens"` means the response was cut off mid-way:
|
|
161
|
+
* the text is a fragment, not an answer. Left open because providers add values.
|
|
162
|
+
*/
|
|
163
|
+
export type StopReason = "end_turn" | "max_tokens" | "stop_sequence" | "tool_use" | "pause_turn" | "refusal" | (string & {});
|
|
153
164
|
export type AssistantModelMessage = {
|
|
154
165
|
role: "assistant";
|
|
155
166
|
content: ContentBlock[];
|
|
167
|
+
/** Absent when a custom ModelClient does not report it. */
|
|
168
|
+
usage?: TokenUsage;
|
|
169
|
+
stopReason?: StopReason;
|
|
156
170
|
};
|
|
157
171
|
export type ModelToolDefinition = {
|
|
158
172
|
name: string;
|
|
@@ -168,6 +182,9 @@ export type ModelRequest = {
|
|
|
168
182
|
stream: boolean;
|
|
169
183
|
outputFormat?: OutputFormat;
|
|
170
184
|
thinkingConfig?: ThinkingConfig;
|
|
185
|
+
reasoningEffort?: ReasoningEffort;
|
|
186
|
+
/** Deadline for this single request. Clients should honour it; the SDK also enforces it. */
|
|
187
|
+
timeoutMs?: number;
|
|
171
188
|
onStreamEvent?: (event: Record<string, unknown>) => void;
|
|
172
189
|
signal?: AbortSignal;
|
|
173
190
|
};
|
|
@@ -210,6 +227,54 @@ export type ToolExecutionContext<TContext = unknown> = {
|
|
|
210
227
|
agentRuntime?: AgentRuntimeContext;
|
|
211
228
|
permissions?: RuntimePermissions;
|
|
212
229
|
};
|
|
230
|
+
export type ToolResultHookContext<TContext = unknown> = {
|
|
231
|
+
toolName: string;
|
|
232
|
+
toolUseId: string;
|
|
233
|
+
/** Raw input as the model sent it, before the tool schema parsed it. */
|
|
234
|
+
input: unknown;
|
|
235
|
+
/** What the SDK would send back to the model. */
|
|
236
|
+
result: ToolResultBlock;
|
|
237
|
+
/** Present when the handler failed, was denied, or was cancelled. */
|
|
238
|
+
error?: Error;
|
|
239
|
+
context?: TContext;
|
|
240
|
+
source?: AgentRuntimeSource;
|
|
241
|
+
signal?: AbortSignal;
|
|
242
|
+
};
|
|
243
|
+
export type ModelRequestHookContext<TContext = unknown> = {
|
|
244
|
+
/** What the SDK would send. Not the stored history; see AgentHooks. */
|
|
245
|
+
messages: ModelMessage[];
|
|
246
|
+
systemPrompt?: string;
|
|
247
|
+
/** 1 for the first model request of the query. */
|
|
248
|
+
turn: number;
|
|
249
|
+
context?: TContext;
|
|
250
|
+
source?: AgentRuntimeSource;
|
|
251
|
+
signal?: AbortSignal;
|
|
252
|
+
};
|
|
253
|
+
export type ModelRequestHookResult = {
|
|
254
|
+
messages?: ModelMessage[];
|
|
255
|
+
systemPrompt?: string;
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Lifecycle callbacks that can rewrite what crosses the agent loop's boundaries,
|
|
259
|
+
* as opposed to `permission` and `toolBatchPolicy`, which can only allow or deny.
|
|
260
|
+
*
|
|
261
|
+
* A hook returns a replacement, or nothing to leave the value unchanged; it must
|
|
262
|
+
* not mutate what it receives.
|
|
263
|
+
*
|
|
264
|
+
* A hook that throws propagates out of `query()` rather than being swallowed the
|
|
265
|
+
* way a tracer error is, or being reported as an error `result`. A hook failure
|
|
266
|
+
* is host code failing, like `ConcurrentQueryError`, not an upstream failure the
|
|
267
|
+
* loop can describe to the model — and a redaction hook that failed quietly
|
|
268
|
+
* would leak the data it exists to protect.
|
|
269
|
+
*
|
|
270
|
+
* Hooks run before the matching trace event, so traces record what was actually
|
|
271
|
+
* sent. `onModelRequest` shapes one request only and never edits the stored
|
|
272
|
+
* conversation, so trimming context for a long turn does not destroy history.
|
|
273
|
+
*/
|
|
274
|
+
export type AgentHooks<TContext = unknown> = {
|
|
275
|
+
onToolResult?(context: ToolResultHookContext<TContext>): ToolResultBlock | void | Promise<ToolResultBlock | void>;
|
|
276
|
+
onModelRequest?(context: ModelRequestHookContext<TContext>): ModelRequestHookResult | void | Promise<ModelRequestHookResult | void>;
|
|
277
|
+
};
|
|
213
278
|
export type ToolHandler<TInput = unknown, TContext = unknown> = (input: TInput, context: ToolExecutionContext<TContext>) => Promise<ToolResult> | ToolResult;
|
|
214
279
|
export type ToolOptions<TInput = unknown> = {
|
|
215
280
|
isConcurrencySafe?: (input: TInput) => boolean;
|
|
@@ -421,8 +486,13 @@ export type AgentOptions<TContext = unknown> = {
|
|
|
421
486
|
maxTokens?: number;
|
|
422
487
|
maxTurns?: number;
|
|
423
488
|
thinkingConfig?: ThinkingConfig;
|
|
489
|
+
reasoningEffort?: ReasoningEffort;
|
|
490
|
+
/** Deadline for each single model request, in milliseconds. Unset means no SDK-side limit. */
|
|
491
|
+
requestTimeoutMs?: number;
|
|
424
492
|
tools?: Array<ToolDefinition<any, TContext>>;
|
|
425
493
|
toolBatchPolicy?: ToolBatchPolicy<TContext>;
|
|
494
|
+
/** Lifecycle callbacks that rewrite tool results and outgoing model requests. */
|
|
495
|
+
hooks?: AgentHooks<TContext>;
|
|
426
496
|
toolConcurrency?: ToolConcurrencyOptions;
|
|
427
497
|
skills?: SkillDefinition[];
|
|
428
498
|
workspace?: AgentWorkspaceOptions;
|
|
@@ -440,6 +510,9 @@ export type QueryOptions<TContext = unknown> = {
|
|
|
440
510
|
stream?: boolean;
|
|
441
511
|
outputFormat?: OutputFormat;
|
|
442
512
|
thinkingConfig?: ThinkingConfig;
|
|
513
|
+
reasoningEffort?: ReasoningEffort;
|
|
514
|
+
/** Overrides the agent's per-request deadline for this query. */
|
|
515
|
+
requestTimeoutMs?: number;
|
|
443
516
|
signal?: AbortSignal;
|
|
444
517
|
context?: TContext;
|
|
445
518
|
agentRuntime?: AgentRuntimeContext;
|
|
@@ -455,6 +528,7 @@ export type ThinkingConfig = {
|
|
|
455
528
|
} | {
|
|
456
529
|
type: "disabled";
|
|
457
530
|
};
|
|
531
|
+
export type ReasoningEffort = "low" | "high" | "max";
|
|
458
532
|
export type SDKSystemInitMessage = {
|
|
459
533
|
type: "system";
|
|
460
534
|
subtype: "init";
|
|
@@ -481,12 +555,19 @@ export type SDKStreamEventMessage = {
|
|
|
481
555
|
};
|
|
482
556
|
export type SDKResultMessage = {
|
|
483
557
|
type: "result";
|
|
484
|
-
subtype: "success" | "error" | "error_max_turns" | "error_abort";
|
|
558
|
+
subtype: "success" | "error" | "error_max_turns" | "error_abort" | "error_timeout";
|
|
485
559
|
is_error: boolean;
|
|
486
560
|
result: string;
|
|
487
561
|
session_id: string;
|
|
488
562
|
num_turns: number;
|
|
489
563
|
error?: Error;
|
|
564
|
+
/** Summed over every model request in the query. Zeroed when unreported. */
|
|
565
|
+
usage: TokenUsage;
|
|
566
|
+
/**
|
|
567
|
+
* From the last model response. Check for `"max_tokens"`: `subtype` is still
|
|
568
|
+
* `"success"` there, but `result` is a truncated fragment.
|
|
569
|
+
*/
|
|
570
|
+
stop_reason?: StopReason;
|
|
490
571
|
};
|
|
491
572
|
export type SDKMessage = SDKSystemInitMessage | SDKStreamEventMessage | SDKAssistantMessage | SDKUserMessage | SDKResultMessage;
|
|
492
573
|
export type TeamRunnerSource = AgentRuntimeSource;
|
|
@@ -528,6 +609,12 @@ export declare class MaxTurnsError extends AgentSDKError {
|
|
|
528
609
|
}
|
|
529
610
|
export declare class AbortError extends AgentSDKError {
|
|
530
611
|
}
|
|
612
|
+
/** A second query was started on an Agent that was still running one. */
|
|
613
|
+
export declare class ConcurrentQueryError extends AgentSDKError {
|
|
614
|
+
}
|
|
615
|
+
/** A model request exceeded `requestTimeoutMs`. */
|
|
616
|
+
export declare class TimeoutError extends AgentSDKError {
|
|
617
|
+
}
|
|
531
618
|
export declare class ToolBatchRejectedError extends AgentSDKError {
|
|
532
619
|
readonly rejection: ToolBatchPolicyRejection;
|
|
533
620
|
constructor(rejection: ToolBatchPolicyRejection);
|
|
@@ -575,6 +662,12 @@ export declare function createAgent<TContext = unknown>(options: AgentOptions<TC
|
|
|
575
662
|
export declare function createBareAgent<TContext = unknown>(options: BareAgentOptions<TContext>): Agent<TContext>;
|
|
576
663
|
export declare function createBuiltinTools(options?: AgentWorkspaceToolsOptions): Array<ToolDefinition<any, any>>;
|
|
577
664
|
export declare function createJsonlContextTracer(options: JsonlContextTracerOptions): ContextTracer;
|
|
665
|
+
/**
|
|
666
|
+
* Chains hooks in array order: each one sees the previous one's output, so
|
|
667
|
+
* redaction, truncation, and auditing can be written separately and combined.
|
|
668
|
+
* Unlike the composite tracer, a failure is not swallowed — see `AgentHooks`.
|
|
669
|
+
*/
|
|
670
|
+
export declare function createCompositeAgentHooks<TContext = unknown>(hooks: Array<AgentHooks<TContext> | undefined | null>): AgentHooks<TContext>;
|
|
578
671
|
export declare function createCompositeContextTracer(tracers: Array<ContextTracer | undefined | null>): ContextTracer;
|
|
579
672
|
export declare function createLangSmithContextTracer(options: LangSmithContextTracerOptions): ContextTracer;
|
|
580
673
|
export declare function skill(input: SkillInput): SkillDefinition;
|
|
@@ -593,6 +686,7 @@ export declare function query<TContext = unknown>(params: AgentOptions<TContext>
|
|
|
593
686
|
stream?: boolean;
|
|
594
687
|
outputFormat?: OutputFormat;
|
|
595
688
|
thinkingConfig?: ThinkingConfig;
|
|
689
|
+
reasoningEffort?: ReasoningEffort;
|
|
596
690
|
signal?: AbortSignal;
|
|
597
691
|
context?: TContext;
|
|
598
692
|
}): AsyncGenerator<SDKMessage>;
|
|
@@ -602,8 +696,15 @@ export declare class Agent<TContext = unknown> {
|
|
|
602
696
|
private readonly messages;
|
|
603
697
|
private readonly sessionId;
|
|
604
698
|
private readonly toolConcurrency;
|
|
699
|
+
private running;
|
|
605
700
|
constructor(options: AgentOptions<TContext>);
|
|
701
|
+
/**
|
|
702
|
+
* One Agent owns one conversation. Overlapping queries would interleave writes
|
|
703
|
+
* into the shared history, so the second caller is rejected rather than served
|
|
704
|
+
* a conversation containing someone else's turns.
|
|
705
|
+
*/
|
|
606
706
|
query(prompt: string | ContentBlock[], options?: QueryOptions<TContext>): AsyncGenerator<SDKMessage>;
|
|
707
|
+
private runQuery;
|
|
607
708
|
prompt(prompt: string | ContentBlock[], options?: QueryOptions<TContext>): Promise<SDKResultMessage>;
|
|
608
709
|
addTools(tools: Array<ToolDefinition<any, TContext>>): void;
|
|
609
710
|
private initMessage;
|