agent-lattice 0.12.0 → 0.15.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 +94 -0
- package/dist/index.d.ts +78 -5
- package/dist/index.js +7 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -416,6 +416,62 @@ policy, tool execution is unchanged. A policy prevents known bad combinations
|
|
|
416
416
|
inside one model response, but it does not replace database transactions or
|
|
417
417
|
revision checks against concurrent external updates.
|
|
418
418
|
|
|
419
|
+
## Automatic Context Compaction
|
|
420
|
+
|
|
421
|
+
History only grows, so a long-running agent eventually exceeds the model's
|
|
422
|
+
context window. Enable `autoCompact` to replace the older part of the
|
|
423
|
+
conversation with a model-written summary:
|
|
424
|
+
|
|
425
|
+
```ts
|
|
426
|
+
const agent = createAgent({
|
|
427
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
428
|
+
model: "claude-sonnet-4-6",
|
|
429
|
+
autoCompact: true, // or { thresholdTokens: 150_000, keepRecentMessages: 8 }
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Compaction runs between turns, once a response reports more input tokens than
|
|
434
|
+
`thresholdTokens` (default `100000`). Everything except the last
|
|
435
|
+
`keepRecentMessages` messages (default `6`) is summarized, and the history is
|
|
436
|
+
rebuilt as that summary followed by the retained messages. The summary is
|
|
437
|
+
wrapped in an instruction telling the model that compaction just happened and to
|
|
438
|
+
continue from it, so the next turn resumes the task instead of restarting it.
|
|
439
|
+
|
|
440
|
+
Unlike the `onModelRequest` hook, which shapes a single request, this **rewrites
|
|
441
|
+
the stored conversation** — that is what makes the saving persist, but the
|
|
442
|
+
replaced turns are gone.
|
|
443
|
+
|
|
444
|
+
The cut point never separates a `tool_result` from the `tool_use` that produced
|
|
445
|
+
it, because the model API rejects that. If no safe cut leaves anything to
|
|
446
|
+
summarize, compaction is skipped.
|
|
447
|
+
|
|
448
|
+
Compaction costs a model call. Its tokens are folded into `result.usage`, and a
|
|
449
|
+
`system` message with `subtype: "compaction"` reports what happened:
|
|
450
|
+
|
|
451
|
+
```ts
|
|
452
|
+
for await (const message of agent.query("Refactor this module.")) {
|
|
453
|
+
if (message.type === "system" && message.subtype === "compaction") {
|
|
454
|
+
console.log(`compacted ${message.compacted_messages} messages`, message.usage);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
The trigger depends on reported usage, so a custom `ModelClient` that omits
|
|
460
|
+
`usage` never compacts. Override the instruction with `prompt`, or read the
|
|
461
|
+
built-in one from `DEFAULT_COMPACTION_PROMPT`.
|
|
462
|
+
|
|
463
|
+
The threshold is a forecast, so a single large tool result can still carry a
|
|
464
|
+
request past the window. Compaction then runs as a recovery — summarize, then
|
|
465
|
+
retry the same turn — on either `stop_reason: "model_context_window_exceeded"`
|
|
466
|
+
or an API error naming a too-long prompt. It is attempted once per query; if
|
|
467
|
+
summarizing fails or there is nothing left to summarize, the original failure
|
|
468
|
+
surfaces unchanged.
|
|
469
|
+
|
|
470
|
+
`stop_reason: "max_tokens"` deliberately does **not** trigger compaction. It
|
|
471
|
+
means the *output* hit `maxTokens`, not that the input was too large — the model
|
|
472
|
+
had room to read and ran out of room to write, so compacting the history would
|
|
473
|
+
not make the answer complete. Raise `maxTokens` instead.
|
|
474
|
+
|
|
419
475
|
## Hooks
|
|
420
476
|
|
|
421
477
|
`permission` and `toolBatchPolicy` decide whether something runs. Hooks decide
|
|
@@ -644,6 +700,44 @@ type AgentLike<TContext = unknown> = {
|
|
|
644
700
|
That means a team can be used anywhere a callable agent is expected. From the
|
|
645
701
|
outside, a team is an agent; inside, it can contain a whole organization.
|
|
646
702
|
|
|
703
|
+
## Agent Specs (Templates) And Sessions
|
|
704
|
+
|
|
705
|
+
*Requires 0.15.0 or later.*
|
|
706
|
+
|
|
707
|
+
`createAgent()` returns a live session: one conversation, one history, one
|
|
708
|
+
workspace. `defineAgent()` returns an `AgentSpec` — a template carrying the
|
|
709
|
+
same options but no state. `spawn()` creates an independent session from it:
|
|
710
|
+
|
|
711
|
+
```ts
|
|
712
|
+
import { agentTool, defineAgent } from "agent-lattice";
|
|
713
|
+
|
|
714
|
+
const reviewerSpec = defineAgent({
|
|
715
|
+
name: "reviewer",
|
|
716
|
+
model: "claude-sonnet-4-5",
|
|
717
|
+
systemPrompt: "You are a senior code reviewer...",
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
// Register the spec: every tool call spawns a fresh session with no memory
|
|
721
|
+
// of previous calls. This is the safe default for reuse.
|
|
722
|
+
const lead = createAgent({
|
|
723
|
+
model: "claude-sonnet-4-5",
|
|
724
|
+
tools: [
|
|
725
|
+
agentTool("review", reviewerSpec, {
|
|
726
|
+
description: "Ask the reviewer to audit a change.",
|
|
727
|
+
}),
|
|
728
|
+
],
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
// Register a spawned session instead when the target should remember earlier
|
|
732
|
+
// tasks across calls — continuity is an explicit opt-in.
|
|
733
|
+
const reviewSession = reviewerSpec.spawn();
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
The same union applies to `delegateTool()`. The generated tool description
|
|
737
|
+
states which semantics a target has, so the calling agent knows whether each
|
|
738
|
+
task must be self-contained. Existing code that passes an `AgentLike` keeps
|
|
739
|
+
its current behavior: a long-lived session with history.
|
|
740
|
+
|
|
647
741
|
## Team Mailbox Collaboration
|
|
648
742
|
|
|
649
743
|
Use `createTeam()` when you want to talk to one `AgentLike` while it coordinates
|
package/dist/index.d.ts
CHANGED
|
@@ -102,7 +102,7 @@ export type AgentRuntimeContext = {
|
|
|
102
102
|
emit(message: TeamRunnerMessage): void;
|
|
103
103
|
shouldPauseAfterToolBatch?(): boolean;
|
|
104
104
|
};
|
|
105
|
-
export type ContextTraceEventType = "run_start" | "user_message" | "model_request" | "assistant_message" | "tool_use" | "tool_result" | "team_message" | "result" | "error";
|
|
105
|
+
export type ContextTraceEventType = "run_start" | "user_message" | "model_request" | "assistant_message" | "tool_use" | "tool_result" | "team_message" | "result" | "compaction" | "error";
|
|
106
106
|
export type ContextTraceEvent = {
|
|
107
107
|
version: 1;
|
|
108
108
|
timestamp: string;
|
|
@@ -160,7 +160,11 @@ export type TokenUsage = {
|
|
|
160
160
|
* Why the model stopped. `"max_tokens"` means the response was cut off mid-way:
|
|
161
161
|
* the text is a fragment, not an answer. Left open because providers add values.
|
|
162
162
|
*/
|
|
163
|
-
export type StopReason = "end_turn"
|
|
163
|
+
export type StopReason = "end_turn"
|
|
164
|
+
/** Output hit `maxTokens`. The text is a fragment; compaction does not help. */
|
|
165
|
+
| "max_tokens" | "stop_sequence" | "tool_use" | "pause_turn" | "refusal"
|
|
166
|
+
/** The context window ran out mid-request. This is what compaction is for. */
|
|
167
|
+
| "model_context_window_exceeded" | (string & {});
|
|
164
168
|
export type AssistantModelMessage = {
|
|
165
169
|
role: "assistant";
|
|
166
170
|
content: ContentBlock[];
|
|
@@ -254,6 +258,22 @@ export type ModelRequestHookResult = {
|
|
|
254
258
|
messages?: ModelMessage[];
|
|
255
259
|
systemPrompt?: string;
|
|
256
260
|
};
|
|
261
|
+
export declare const DEFAULT_COMPACTION_PROMPT = "Your task is to create a detailed summary of the conversation so far,\npaying close attention to the user's explicit requests and your previous actions.\n\nThis summary should be thorough in capturing:\n- technical details\n- code patterns\n- architectural decisions\n- files that were modified\n- commands that were run\n- errors encountered\n- solutions attempted\n- important context needed to continue the work\n\nPreserve:\n- user's intent\n- important constraints\n- decisions already made\n- reasoning behind decisions\n- unresolved issues\n- next steps\n\nThe summary will replace the conversation history, so include everything\nnecessary for another Claude instance to continue the task successfully.\n\nOutput only the summary.";
|
|
262
|
+
export type AutoCompactOptions = {
|
|
263
|
+
/**
|
|
264
|
+
* Compact once a model response reports more input tokens than this.
|
|
265
|
+
* Defaults to 100000, chosen to leave headroom on a 200k-token model.
|
|
266
|
+
*/
|
|
267
|
+
thresholdTokens?: number;
|
|
268
|
+
/** Trailing messages left verbatim after the summary. Defaults to 6. */
|
|
269
|
+
keepRecentMessages?: number;
|
|
270
|
+
/** Replaces DEFAULT_COMPACTION_PROMPT. */
|
|
271
|
+
prompt?: string;
|
|
272
|
+
/** Model used for the summary. Defaults to the agent's model. */
|
|
273
|
+
model?: string;
|
|
274
|
+
/** Output cap for the summary. Defaults to 8192. */
|
|
275
|
+
maxTokens?: number;
|
|
276
|
+
};
|
|
257
277
|
/**
|
|
258
278
|
* Lifecycle callbacks that can rewrite what crosses the agent loop's boundaries,
|
|
259
279
|
* as opposed to `permission` and `toolBatchPolicy`, which can only allow or deny.
|
|
@@ -493,6 +513,11 @@ export type AgentOptions<TContext = unknown> = {
|
|
|
493
513
|
toolBatchPolicy?: ToolBatchPolicy<TContext>;
|
|
494
514
|
/** Lifecycle callbacks that rewrite tool results and outgoing model requests. */
|
|
495
515
|
hooks?: AgentHooks<TContext>;
|
|
516
|
+
/**
|
|
517
|
+
* Replaces older conversation history with a model-written summary once it
|
|
518
|
+
* grows past a threshold. Off unless set; `true` uses the defaults.
|
|
519
|
+
*/
|
|
520
|
+
autoCompact?: boolean | AutoCompactOptions;
|
|
496
521
|
toolConcurrency?: ToolConcurrencyOptions;
|
|
497
522
|
skills?: SkillDefinition[];
|
|
498
523
|
workspace?: AgentWorkspaceOptions;
|
|
@@ -536,6 +561,19 @@ export type SDKSystemInitMessage = {
|
|
|
536
561
|
tools: string[];
|
|
537
562
|
session_id: string;
|
|
538
563
|
};
|
|
564
|
+
export type SDKSystemCompactionMessage = {
|
|
565
|
+
type: "system";
|
|
566
|
+
subtype: "compaction";
|
|
567
|
+
session_id: string;
|
|
568
|
+
/** Messages replaced by the summary. */
|
|
569
|
+
compacted_messages: number;
|
|
570
|
+
/** Messages kept verbatim after it. */
|
|
571
|
+
retained_messages: number;
|
|
572
|
+
/** Input tokens of the turn that triggered compaction. */
|
|
573
|
+
trigger_input_tokens: number;
|
|
574
|
+
/** Cost of producing the summary, already folded into the result usage. */
|
|
575
|
+
usage: TokenUsage;
|
|
576
|
+
};
|
|
539
577
|
export type SDKAssistantMessage = {
|
|
540
578
|
type: "assistant";
|
|
541
579
|
message: AssistantModelMessage;
|
|
@@ -569,7 +607,7 @@ export type SDKResultMessage = {
|
|
|
569
607
|
*/
|
|
570
608
|
stop_reason?: StopReason;
|
|
571
609
|
};
|
|
572
|
-
export type SDKMessage = SDKSystemInitMessage | SDKStreamEventMessage | SDKAssistantMessage | SDKUserMessage | SDKResultMessage;
|
|
610
|
+
export type SDKMessage = SDKSystemInitMessage | SDKSystemCompactionMessage | SDKStreamEventMessage | SDKAssistantMessage | SDKUserMessage | SDKResultMessage;
|
|
573
611
|
export type TeamRunnerSource = AgentRuntimeSource;
|
|
574
612
|
export type TeamRunnerTeamMessage = {
|
|
575
613
|
type: "team_message";
|
|
@@ -654,12 +692,33 @@ export type AgentToolOptions = {
|
|
|
654
692
|
description: string;
|
|
655
693
|
targetMailboxId?: string;
|
|
656
694
|
};
|
|
657
|
-
|
|
658
|
-
|
|
695
|
+
/**
|
|
696
|
+
* An AgentLike is a live session: it keeps its conversation history across
|
|
697
|
+
* calls. An AgentSpec is a template: each call spawns a fresh session with no
|
|
698
|
+
* memory of previous calls. Prefer a spec unless the parent explicitly wants
|
|
699
|
+
* continuity.
|
|
700
|
+
*/
|
|
701
|
+
export type AgentToolTarget = AgentLike<any> | AgentSpec<any>;
|
|
702
|
+
export declare function agentTool(name: string, agent: AgentToolTarget, options: AgentToolOptions): ToolDefinition<AgentToolInput>;
|
|
703
|
+
export declare function delegateTool(name: string, description: string, agent: AgentToolTarget, options?: DelegateToolOptions): ToolDefinition<{
|
|
659
704
|
task: string;
|
|
660
705
|
}>;
|
|
661
706
|
export declare function createAgent<TContext = unknown>(options: AgentOptions<TContext>): Agent<TContext>;
|
|
662
707
|
export declare function createBareAgent<TContext = unknown>(options: BareAgentOptions<TContext>): Agent<TContext>;
|
|
708
|
+
/**
|
|
709
|
+
* A template describing an agent's identity: model, prompt, tools, skills,
|
|
710
|
+
* and workspace policy. A spec carries no conversation state; `spawn()`
|
|
711
|
+
* creates an independent session (an Agent) that owns its own history and
|
|
712
|
+
* workspace. Register a spec wherever a capability should be reused without
|
|
713
|
+
* leaking memory between tasks; spawn a session when continuity is wanted.
|
|
714
|
+
*/
|
|
715
|
+
export type AgentSpec<TContext = unknown> = {
|
|
716
|
+
readonly name?: string;
|
|
717
|
+
readonly options: AgentOptions<TContext>;
|
|
718
|
+
spawn(overrides?: Partial<AgentOptions<TContext>>): Agent<TContext>;
|
|
719
|
+
};
|
|
720
|
+
export declare function defineAgent<TContext = unknown>(options: AgentOptions<TContext>): AgentSpec<TContext>;
|
|
721
|
+
export declare function isAgentSpec(target: AgentLike<any> | AgentSpec<any>): target is AgentSpec<any>;
|
|
663
722
|
export declare function createBuiltinTools(options?: AgentWorkspaceToolsOptions): Array<ToolDefinition<any, any>>;
|
|
664
723
|
export declare function createJsonlContextTracer(options: JsonlContextTracerOptions): ContextTracer;
|
|
665
724
|
/**
|
|
@@ -710,6 +769,20 @@ export declare class Agent<TContext = unknown> {
|
|
|
710
769
|
private initMessage;
|
|
711
770
|
private resultMessage;
|
|
712
771
|
private modelTools;
|
|
772
|
+
/**
|
|
773
|
+
* Replaces the summarizable head of the conversation with a model-written
|
|
774
|
+
* summary. Returns undefined when there is nothing safe to compact, and lets a
|
|
775
|
+
* failed summarization surface so the caller can decide: compaction is best
|
|
776
|
+
* effort, and continuing with a full history is better than losing it.
|
|
777
|
+
*/
|
|
778
|
+
private compactHistory;
|
|
779
|
+
/**
|
|
780
|
+
* Last-resort compaction after a turn already ran out of context, as opposed
|
|
781
|
+
* to the threshold check that runs between turns. Returns the message to emit
|
|
782
|
+
* so the caller can retry, or undefined when compaction cannot help and the
|
|
783
|
+
* original failure should surface instead.
|
|
784
|
+
*/
|
|
785
|
+
private recoverFromOverflow;
|
|
713
786
|
private messagesForModel;
|
|
714
787
|
private selectSkills;
|
|
715
788
|
private runTool;
|