agent-lattice 0.16.0 → 0.17.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
@@ -55,6 +55,11 @@ const agent = createBareAgent({
55
55
  });
56
56
  ```
57
57
 
58
+ `Agent` is a type-only export — instances come from these factories (or
59
+ `AgentSpec.spawn()`), never from `new Agent()`, because the factories also
60
+ generate the session id and install the workspace. (Breaking in 0.17.0: the
61
+ `Agent` class constructor is no longer exported.)
62
+
58
63
  `agent.query()` yields `stream_event` messages while the model is still
59
64
  responding, so a host can render output incrementally:
60
65
 
@@ -221,9 +226,9 @@ for await (const event of team.query("Ask engineering to investigate.", {
221
226
 
222
227
  ## LangSmith Context Tracing
223
228
 
224
- Pass LangSmith's `RunTree` constructor to the SDK tracer. The SDK depends on
225
- `langsmith` directly and uses its official `RunTree` / `RunTreeConfig` types for
226
- this adapter.
229
+ The SDK depends on `langsmith` directly and uses its official `RunTree` /
230
+ `RunTreeConfig` types for this adapter, so the tracer works out of the box —
231
+ no constructor wiring needed.
227
232
 
228
233
  Configure LangSmith with its standard environment variables:
229
234
 
@@ -237,7 +242,6 @@ LANGSMITH_WORKSPACE_ID=<your-langsmith-workspace-id>
237
242
  ```
238
243
 
239
244
  ```ts
240
- import { RunTree } from "langsmith/run_trees";
241
245
  import {
242
246
  createAgent,
243
247
  createCompositeContextTracer,
@@ -248,7 +252,6 @@ import {
248
252
  const tracer = createCompositeContextTracer([
249
253
  createJsonlContextTracer({ path: ".agent-runs/session.jsonl" }),
250
254
  createLangSmithContextTracer({
251
- RunTree,
252
255
  projectName: process.env.LANGSMITH_PROJECT,
253
256
  workspaceId: process.env.LANGSMITH_WORKSPACE_ID,
254
257
  tags: ["local-debug"],
@@ -277,11 +280,9 @@ tracer. `workspaceId` is optional and only selects a LangSmith workspace; it is
277
280
  not the tracing project name.
278
281
 
279
282
  ```ts
280
- import { RunTree } from "langsmith/run_trees";
281
283
  import { createLangSmithContextTracer } from "agent-lattice";
282
284
 
283
285
  const tracer = createLangSmithContextTracer({
284
- RunTree,
285
286
  apiKey: process.env.LANGSMITH_API_KEY,
286
287
  apiUrl: process.env.LANGSMITH_ENDPOINT,
287
288
  projectName: process.env.LANGSMITH_PROJECT,
@@ -290,6 +291,9 @@ const tracer = createLangSmithContextTracer({
290
291
  });
291
292
  ```
292
293
 
294
+ `RunTree` defaults to the bundled langsmith constructor since 0.17.0; pass
295
+ `RunTree` or `runTree` only to inject a custom runtime or a test fake.
296
+
293
297
  LangSmith receives one root `chain` run per SDK query. For an `Agent` query,
294
298
  model turns and SDK tool calls appear as child `llm` and `tool` runs. For a
295
299
  `Team` query, the root represents the complete Team invocation; the initial
@@ -299,15 +303,21 @@ recorded as `agent_session_id` metadata, so tracing does not change Agent state
299
303
  or returned SDK messages.
300
304
 
301
305
  Custom sinks can implement the same interface for SQLite, OpenTelemetry, object
302
- storage, or host-specific observability. The functions below are application
303
- code you provide, not SDK exports:
306
+ storage, or host-specific observability. A `ContextTracer` port object exposes
307
+ methods only — `failOnError` is bound when the factory creates the tracer, not
308
+ set as a field afterwards. Implement a custom sink with `defineContextTracer()`.
309
+ *Requires 0.17.0 or later.* (Breaking in 0.17.0: the public `failOnError` field
310
+ was removed from `ContextTracer`; custom tracers created before 0.17.0 must go
311
+ through `defineContextTracer()`.)
304
312
 
305
313
  ```ts
306
- const tracer = {
314
+ import { defineContextTracer } from "agent-lattice";
315
+
316
+ const tracer = defineContextTracer({
307
317
  async onEvent(event) {
308
318
  // TODO: Replace with your own storage/logging code.
309
319
  },
310
- };
320
+ });
311
321
  ```
312
322
 
313
323
  ## DeepSeek Anthropic-compatible API
@@ -730,13 +740,15 @@ const mcp = await connectMCPStreamableHTTPServer("https://mcp.example.com/mcp",
730
740
  type AgentLike<TContext = unknown> = {
731
741
  query(prompt, options?): AsyncGenerator<SDKMessage | TeamRunnerMessage>;
732
742
  prompt(prompt, options?): Promise<SDKResultMessage>;
733
- interrupt(): void;
743
+ interrupt(): boolean;
734
744
  };
735
745
  ```
736
746
 
737
747
  `interrupt()` ends the in-flight model request with an `"interrupted"` result
738
748
  (see [Interrupting A Query](#interrupting-a-query)); on a `Team` or
739
- `TeamRunner` it delegates to the lead/root agent. *Requires 0.16.0 or later.*
749
+ `TeamRunner` it delegates to the lead/root agent. It returns `true` when a
750
+ query was interrupted and `false` when idle. *Requires 0.16.0 or later; the
751
+ `boolean` return requires 0.17.0 or later.*
740
752
 
741
753
  That means a team can be used anywhere a callable agent is expected. From the
742
754
  outside, a team is an agent; inside, it can contain a whole organization.
@@ -1170,8 +1182,42 @@ The store contract is three methods — `load()`, `append(message)`, and
1170
1182
  The JSONL store's `load()` skips malformed lines rather than failing, so a
1171
1183
  torn final write does not lose the rest of the transcript. By default a
1172
1184
  failing store is swallowed and the conversation continues in memory only,
1173
- mirroring the tracer's failure semantics; set `failOnError: true` on the store
1174
- to propagate store errors out of `query()` instead.
1185
+ mirroring the tracer's failure semantics; bind `failOnError: true` when the
1186
+ store is created (via `createJsonlHistoryStore()` options or
1187
+ `defineHistoryStore()`) to propagate store errors out of `query()` instead.
1188
+
1189
+ Implement a custom store with `defineHistoryStore()`, which validates the
1190
+ three methods and binds `failOnError`; the returned port object exposes
1191
+ methods only. *Requires 0.17.0 or later.* (Breaking in 0.17.0: the public
1192
+ `failOnError` field was removed from `HistoryStore`; custom stores created
1193
+ before 0.17.0 must go through `defineHistoryStore()`.)
1194
+
1195
+ ```ts
1196
+ import { defineHistoryStore } from "agent-lattice";
1197
+
1198
+ const historyStore = defineHistoryStore({
1199
+ load: () => loadMessagesFromYourDatabase(),
1200
+ append: message => appendMessageToYourDatabase(message),
1201
+ replace: messages => replaceMessagesInYourDatabase(messages),
1202
+ });
1203
+ ```
1204
+
1205
+ To rewrite the history from the host instead of from compaction, use
1206
+ `agent.replaceHistory(messages)`. *Requires 0.17.0 or later.* It is idle-only:
1207
+ calling it while a query is running throws `ConcurrentQueryError`, the same
1208
+ guard as a concurrent `query()`. When a `historyStore` is configured the store
1209
+ is replaced too, so persistence stays in sync, and the replacement also
1210
+ suppresses the lazy `load()` — seeding never overwrites what the host just
1211
+ installed. The SDK does not validate the content: the host owns it, and the
1212
+ history must be well-formed (e.g. no dangling `tool_use` without its matching
1213
+ `tool_result`).
1214
+
1215
+ ```ts
1216
+ await agent.replaceHistory([
1217
+ { role: "user", content: "My name is Ada." },
1218
+ { role: "assistant", content: [{ type: "text", text: "Nice to meet you, Ada." }] },
1219
+ ]);
1220
+ ```
1175
1221
 
1176
1222
  ## Deadlines
1177
1223
 
@@ -1204,7 +1250,7 @@ loop indefinitely. Losing that race abandons the call rather than cancelling it.
1204
1250
 
1205
1251
  ## Interrupting A Query
1206
1252
 
1207
- *Requires 0.16.0 or later.*
1253
+ *Requires 0.16.0 or later; `interrupt()` returns `boolean` from 0.17.0.*
1208
1254
 
1209
1255
  `agent.interrupt()` ends the current query without tearing the conversation
1210
1256
  down. Where `QueryOptions.signal` terminates the query with `"error_abort"`,
@@ -1225,8 +1271,10 @@ await agent.prompt("Actually, skip 0.15.x and cover 0.16.0 only.");
1225
1271
 
1226
1272
  An interrupt that lands while a tool batch is executing takes effect once the
1227
1273
  batch completes: its tool results are written to history first, and the query
1228
- ends `"interrupted"` before the next model call. `interrupt()` is a no-op when
1229
- no query is running.
1274
+ ends `"interrupted"` before the next model call. `interrupt()` returns `true`
1275
+ when a query was running and is now interrupted, and `false` when idle — a
1276
+ `false` tells the host there is nothing to wait for, so it can send its next
1277
+ query directly.
1230
1278
 
1231
1279
  ## Token Usage And Truncation
1232
1280
 
package/dist/index.d.ts CHANGED
@@ -34,8 +34,12 @@ export type AgentLikeEvent = SDKMessage | TeamRunnerMessage;
34
34
  export type AgentLike<TContext = unknown> = {
35
35
  query(prompt: string | ContentBlock[], options?: QueryOptions<TContext>): AsyncGenerator<AgentLikeEvent>;
36
36
  prompt(prompt: string | ContentBlock[], options?: QueryOptions<TContext>): Promise<SDKResultMessage>;
37
- /** End the in-flight model request with an "interrupted" result; follow up with a new query(). No-op when idle. */
38
- interrupt(): void;
37
+ /**
38
+ * End the in-flight model request with an "interrupted" result; follow up
39
+ * with a new query(). Returns `true` when a query was interrupted, `false`
40
+ * when idle — a `false` means the host can send its next query directly.
41
+ */
42
+ interrupt(): boolean;
39
43
  };
40
44
  export type DelegateWaitMode = "result" | "accepted";
41
45
  export type AgentRuntimeSource = {
@@ -117,7 +121,6 @@ export type ContextTraceEvent = {
117
121
  data: Record<string, unknown>;
118
122
  };
119
123
  export type ContextTracer = {
120
- failOnError?: boolean;
121
124
  onEvent(event: ContextTraceEvent): Promise<void> | void;
122
125
  flush?(): Promise<void>;
123
126
  close?(): Promise<void>;
@@ -135,7 +138,9 @@ export type LangSmithRunTreeLike = RunTree;
135
138
  export type LangSmithRunTreeConstructor = new (config: LangSmithRunTreeConfig) => LangSmithRunTreeLike;
136
139
  export type LangSmithWriteReplicaConfig = NonNullable<LangSmithRunTreeConfig["replicas"]>[number];
137
140
  export type LangSmithContextTracerOptions = {
141
+ /** Defaults to the bundled langsmith RunTree; inject a constructor for custom runtimes. */
138
142
  RunTree?: LangSmithRunTreeConstructor;
143
+ /** Factory form of RunTree, mainly for tests. Takes precedence over RunTree. */
139
144
  runTree?: (config: LangSmithRunTreeConfig) => LangSmithRunTreeLike;
140
145
  projectName?: string;
141
146
  name?: string;
@@ -159,11 +164,18 @@ export type ModelMessage = {
159
164
  * whole history — a store must support full replacement. Methods may be
160
165
  * synchronous or return a promise; the Agent awaits them to preserve order.
161
166
  * Like `ContextTracer`, a failing store is swallowed by default and the
162
- * conversation continues in memory only; set `failOnError` to propagate store
163
- * errors out of `query()` instead.
167
+ * conversation continues in memory only; pass `failOnError` to the factory
168
+ * that creates the store (`createJsonlHistoryStore`, `defineHistoryStore`)
169
+ * to propagate store errors out of `query()` instead.
170
+ *
171
+ * The objects passed to `append()` and `replace()` are guaranteed to carry at
172
+ * least `role` and `content`. Runtime objects may carry extra fields (`usage`,
173
+ * `stopReason`, `providerResponseId`, `model`) that are not part of the
174
+ * contract and must not be relied on; hosts that need response metadata for
175
+ * reconciliation or analytics should use the ContextTracer's
176
+ * `assistant_message` events instead.
164
177
  */
165
178
  export type HistoryStore = {
166
- failOnError?: boolean;
167
179
  load(): ModelMessage[] | Promise<ModelMessage[]>;
168
180
  append(message: ModelMessage): void | Promise<void>;
169
181
  replace(messages: ModelMessage[]): void | Promise<void>;
@@ -489,8 +501,8 @@ export type Team = {
489
501
  drain(options?: TeamDrainOptions): Promise<TeamDrainResult>;
490
502
  query(prompt: string | ContentBlock[], options?: QueryOptions): AsyncGenerator<TeamRunnerMessage>;
491
503
  prompt(prompt: string | ContentBlock[], options?: QueryOptions): Promise<SDKResultMessage>;
492
- /** Interrupts the lead agent's in-flight model request. */
493
- interrupt(): void;
504
+ /** Interrupts the lead agent's in-flight model request; returns `true` when a query was interrupted, `false` when idle. */
505
+ interrupt(): boolean;
494
506
  };
495
507
  export type TeamDrainOptions = {
496
508
  maxRounds?: number;
@@ -513,8 +525,8 @@ export type TeamRunner = {
513
525
  mailbox: TeamMailbox;
514
526
  query(prompt: string | ContentBlock[], options?: QueryOptions): AsyncGenerator<TeamRunnerMessage>;
515
527
  prompt(prompt: string | ContentBlock[], options?: QueryOptions): Promise<SDKResultMessage>;
516
- /** Interrupts the root agent's in-flight model request. */
517
- interrupt(): void;
528
+ /** Interrupts the root agent's in-flight model request; returns `true` when a query was interrupted, `false` when idle. */
529
+ interrupt(): boolean;
518
530
  };
519
531
  export type PermissionRequest = {
520
532
  toolName: string;
@@ -774,6 +786,15 @@ export type AgentSpec<TContext = unknown> = {
774
786
  export declare function defineAgent<TContext = unknown>(options: AgentOptions<TContext>): AgentSpec<TContext>;
775
787
  export declare function isAgentSpec(target: AgentLike<any> | AgentSpec<any>): target is AgentSpec<any>;
776
788
  export declare function createBuiltinTools(options?: AgentWorkspaceToolsOptions): Array<ToolDefinition<any, any>>;
789
+ /**
790
+ * Entry point for implementing a custom trace sink. Validates that the
791
+ * required `onEvent` method exists and binds `failOnError` at creation time —
792
+ * a `ContextTracer` exposes methods only; the flag travels with the returned
793
+ * object as internal metadata the runtime reads when a call throws.
794
+ */
795
+ export declare function defineContextTracer(impl: ContextTracer & {
796
+ failOnError?: boolean;
797
+ }): ContextTracer;
777
798
  export declare function createJsonlContextTracer(options: JsonlContextTracerOptions): ContextTracer;
778
799
  /**
779
800
  * Chains hooks in array order: each one sees the previous one's output, so
@@ -782,7 +803,7 @@ export declare function createJsonlContextTracer(options: JsonlContextTracerOpti
782
803
  */
783
804
  export declare function createCompositeAgentHooks<TContext = unknown>(hooks: Array<AgentHooks<TContext> | undefined | null>): AgentHooks<TContext>;
784
805
  export declare function createCompositeContextTracer(tracers: Array<ContextTracer | undefined | null>): ContextTracer;
785
- export declare function createLangSmithContextTracer(options: LangSmithContextTracerOptions): ContextTracer;
806
+ export declare function createLangSmithContextTracer(options?: LangSmithContextTracerOptions): ContextTracer;
786
807
  export declare function skill(input: SkillInput): SkillDefinition;
787
808
  export declare function loadSkill(path: string): Promise<SkillDefinition>;
788
809
  export declare function createMCPTools(client: MCPClient, options?: MCPToolsOptions): Promise<Array<ToolDefinition<Record<string, unknown>>>>;
@@ -796,6 +817,16 @@ export declare function connectMCPStreamableHTTPServer(url: string | URL, option
796
817
  * Writes are serialized through an internal queue, so callers may fire them
797
818
  * without waiting for ordering.
798
819
  */
820
+ /**
821
+ * Entry point for implementing a custom history store. Validates that the
822
+ * required `load`/`append`/`replace` methods exist and binds `failOnError` at
823
+ * creation time — a `HistoryStore` exposes methods only; the flag travels
824
+ * with the returned object as internal metadata the runtime reads when a call
825
+ * throws.
826
+ */
827
+ export declare function defineHistoryStore(impl: HistoryStore & {
828
+ failOnError?: boolean;
829
+ }): HistoryStore;
799
830
  export declare function createJsonlHistoryStore(options: JsonlHistoryStoreOptions): HistoryStore;
800
831
  export declare function teamMember(input: TeamMemberInput): TeamMemberDefinition;
801
832
  export declare function createMemoryMailbox(): TeamMailbox;
@@ -812,7 +843,13 @@ export declare function query<TContext = unknown>(params: AgentOptions<TContext>
812
843
  signal?: AbortSignal;
813
844
  context?: TContext;
814
845
  }): AsyncGenerator<SDKMessage>;
815
- export declare class Agent<TContext = unknown> {
846
+ /**
847
+ * An Agent owns one conversation: history, workspace, and session identity.
848
+ * Create instances with `createAgent()` or `createBareAgent()` (or spawn from
849
+ * an `AgentSpec`) — the constructor is intentionally not exported, because the
850
+ * factories also generate the session id and install workspace tools.
851
+ */
852
+ declare class Agent<TContext = unknown> {
816
853
  private readonly options;
817
854
  private readonly modelClient;
818
855
  private readonly messages;
@@ -833,14 +870,17 @@ export declare class Agent<TContext = unknown> {
833
870
  * "interrupted". Completed turns stay in history; follow up with a new
834
871
  * query() to continue the conversation. Unlike `QueryOptions.signal`, which
835
872
  * terminates the query as an error, an interrupt is normal control flow.
836
- * A no-op when no query is running.
873
+ * Returns `true` when a query was running and is now interrupted, `false`
874
+ * when idle — a `false` tells the host it can send its next query directly
875
+ * instead of waiting for an "interrupted" result.
837
876
  */
838
- interrupt(): void;
877
+ interrupt(): boolean;
839
878
  /**
840
879
  * The store is read once per Agent lifetime, lazily on the first query or
841
880
  * getHistory() call — the constructor cannot be async. A failed load follows
842
- * the same rule as a failed write: swallowed unless `failOnError` is set, in
843
- * which case the error propagates and the Agent starts empty.
881
+ * the same rule as a failed write: swallowed unless `failOnError` was bound
882
+ * by the store's factory, in which case the error propagates and the Agent
883
+ * starts empty.
844
884
  */
845
885
  private ensureHistoryLoaded;
846
886
  private loadHistory;
@@ -854,6 +894,16 @@ export declare class Agent<TContext = unknown> {
854
894
  * mutating the result cannot corrupt the live conversation.
855
895
  */
856
896
  getHistory(): Promise<ModelMessage[]>;
897
+ /**
898
+ * Replace the conversation history. Idle only: throws ConcurrentQueryError
899
+ * while a query is running. When a historyStore is configured the store is
900
+ * replaced too, so persistence stays in sync.
901
+ *
902
+ * The host owns the content: the SDK does not validate the messages, so the
903
+ * replacement must be a well-formed history — e.g. no dangling `tool_use`
904
+ * without its matching `tool_result`.
905
+ */
906
+ replaceHistory(messages: ModelMessage[]): Promise<void>;
857
907
  addTools(tools: Array<ToolDefinition<any, TContext>>): void;
858
908
  private initMessage;
859
909
  private resultMessage;
@@ -878,7 +928,7 @@ export declare class Agent<TContext = unknown> {
878
928
  private executeToolBatch;
879
929
  private isToolCallConcurrencySafe;
880
930
  }
931
+ export type { Agent };
881
932
  type InferInput<TSchema> = TSchema extends {
882
933
  parse(input: unknown): infer TInput;
883
934
  } ? TInput : Record<string, unknown>;
884
- export {};