会话
Agent 持有配置和服务提供商状态。Session 把这个智能体绑定到一个工作区和一次
对话生命周期。
界面通常从这里开始接入:订阅 Session 产生的 AgentEvent,再把事件映射为进度、
工具调用、权限确认和结果。
session.ts TypeScript 换行 复制 import { Agent } from ' @a3s-lab/code ';
const agent = await Agent . create (' agent.acl ');
const session = agent . session (' /repo ', {
model : ' provider/model-id ',
planningMode : ' enabled ',
goalTracking : true ,
autoDelegation : { enabled : true , maxTasks : 4 },
autoParallel : false ,
});
Rust 构建路径
Rust 会话构建以异步为先,因为默认内存存储、文件型存储、队列、轨迹记录与
MCP 发现都可能需要 I/O:
Rust 换行 复制 let session = agent
. session_builder (" /repo ")
. options ( options )
. build ()
. await ? ;
session_async、resume_session_async、session_for_agent_async 与
session_for_worker_async 是直接的异步入口。Node 和 Python 保留现有工厂
命名,由原生绑定在内部委托给同一个异步构建内核。
同步 Rust Agent::session 是严格兼容路径:只接受已经显式初始化好的资源,绝不
启动或阻塞异步运行时。仍需初始化的默认或文件型内存存储、文件会话存储、队列、
轨迹记录器,以及 SessionOptions 中的任何宿主 MCP 管理器都会返回
CodeError::AsyncSessionBuildRequired;会话选项中的 MCP 能力发现始终是异步的。
应改用构建器,不要捕获错误后悄悄更换后端。
同步路径只能继承智能体初始化时已经缓存的全局 MCP 工具。
planningMode 是显式三态:'auto' 使用默认结构化预分析,'enabled'
强制规划,'disabled' 在低延迟调用中关闭规划。旧的布尔 planning 选项仍保留兼容。
规划会写入运行级状态。宿主应用可以把这些状态渲染成任务列表,并随着运行事件更新
每一项,而不是从文本令牌中猜测进度。
单任务操作约束
同一个会话同时只准入一个会影响对话记录的操作。send、stream、它们的附件变体、
斜杠命令与 resumeRun 共用快速失败准入门。重叠调用会在读取历史或派发命令前返回
CodeError::SessionBusy,不会排队
等待当前操作。
开始下一次对话操作前,应等待活动结果、把事件流消费到结束,或先取消它。
即使公开的事件流句柄被丢弃或中止,运行时也会在生产者真正停止前继续
持有租约。直接调用宿主工具的辅助方法不改变对话记录,因此不占用这把租约。
Node 与 Python 的事件流迭代器会在终止边界等待这段生命周期清理。迭代器完整消费
并报告结束后,立即开始下一次对话操作不会继承上一个事件流留下的过期忙碌状态。
发送消息
Rust 换行 复制 let result = session
. send (" 审查这个仓库并列出发布阻塞项 ", None )
. await ? ;
println! ("{}", result . text );
println! ("{}", result . usage . total_tokens );
println! ("{ :? }", result . verification_summary () . status );
TypeScript 换行 复制 const result = await session . send (' 检查仓库并列出发布阻塞项 ');
console . log ( result . text );
console . log ( result . totalTokens );
console . log ( result . verificationStatus );
Python 换行 复制 result = session . send (" 审查这个仓库并列出发布阻塞项 ")
print ( result . text )
print ( result . total_tokens )
print ( result . verification_status )
Go 换行 复制 result , err := session . Run ( ctx , " 检查仓库并列出发布阻塞项 ")
if err != nil {
return err
}
fmt . Println ( result . Text )
fmt . Println ( result . Usage . TotalTokens )
fmt . Println ( result . VerificationSummary . Status )
流式输出
Rust 换行 复制 use a3s_code_core :: { AgentEvent , CodeError };
let ( mut events , lifecycle ) = session
. stream (" 运行相关测试并解释失败原因 ", None )
. await ? ;
while let Some ( event ) = events . recv () . await {
match event {
AgentEvent :: TextDelta { text } => print! ("{ text }"),
AgentEvent :: ToolStart { name , .. } => println! (" \n 工具: { name }"),
AgentEvent :: End { .. } => break ,
AgentEvent :: Error { message } => return Err ( CodeError :: Llm ( message )),
_ => {}
}
}
lifecycle
. await
. map_err ( | error | CodeError :: Internal ( error . into ())) ?? ;
TypeScript 换行 复制 const stream = await session . stream (' 运行聚焦测试并解释失败 ');
while ( true ) {
const { value : event , done } = await stream . next ();
if ( done ) break ;
if ( ! event ) continue ;
if ( event . text ) process . stdout . write ( event . text );
if ( event . toolName ) console . log (' tool: ', event . toolName );
}
Python 换行 复制 for event in session . stream (" 运行相关测试并解释失败原因 "):
if event . type == " text_delta " and event . text :
print ( event . text , end = "", flush = True )
elif event . type == " tool_start ":
print ( f " \n 工具: { event . tool_name or ' 未知 ' } " )
elif event . type == " error ":
raise RuntimeError ( event . error or " 流式执行出错 ")
Go 换行 复制 stream , err := session . Stream ( ctx , " 运行聚焦测试并解释失败 ", nil )
if err != nil {
return err
}
for event := range stream . Events {
if event . Type != code . EventTextDelta {
continue
}
var payload struct {
Text string ` json:"text" `
}
if err := event . DecodePayload ( & payload ); err != nil {
return err
}
fmt . Print ( payload . Text )
}
if err := <- stream . Done ; err != nil {
return err
}
每个 SDK 事件都是 EventEnvelopeV1 投影,包含 version === 1、开放的 type
字符串、完整 payload 与可选 metadata。text、toolName 等便捷字段
由信封统一派生。消费端应保留默认分支,并为未来的事件类型保存原始载荷。
临时提问
SDK 没有专用的临时提问辅助方法。要提出临时问题,可以先快照当前历史,再把它
显式传给 send 或 stream。显式历史只服务这一次调用,不会把答案写回
会话历史。
TypeScript 换行 复制 const snapshot = session . history ();
const answer = await session . send (' 这个 session 已经看过哪些文件? ', snapshot );
console . log ( answer . text );
console . log ( session . history (). length === snapshot . length );
恢复会话
Rust 换行 复制 use a3s_code_core :: { Agent , SessionOptions };
let session = agent
. resume_session_async (
" release-review ",
SessionOptions :: new () . with_file_session_store (" ./.a3s/sessions "),
)
. await ? ;
TypeScript 换行 复制 import { Agent , FileSessionStore } from ' @a3s-lab/code ';
const agent = await Agent . create (' agent.acl ');
const session = agent . resumeSession (' release-review ', {
sessionStore : new FileSessionStore (' ./.a3s/sessions '),
});
Python 换行 复制 from a3s_code import FileSessionStore , SessionOptions
opts = SessionOptions ()
opts . session_store = FileSessionStore (" ./.a3s/sessions ")
session = agent . resume_session (" release-review ", opts )
Go 换行 复制 options := & code . SessionOptions {
FileSessionStoreDir : " .a3s/sessions ",
}
session , err := agent . ResumeSession ( ctx , " release-review ", options )
使用会话存储时设置 autoSave: true,或显式调用 await session.save()。
这里恢复的是已保存的会话快照;中断运行的检查点通过
session.resumeRun(runId) 恢复。Go 使用 Save、ResumeSession 和
ResumeRun(ctx, runID) 对应同样的两层持久化。参见
持久化 。
生命周期与关闭
session.close() 是一次完整的优雅停止。首次调用会把会话切换到
已关闭 状态——之后的 send/stream 调用会以 CodeError::SessionClosed
快速失败,而不会启动新运行——然后取消正在执行的运行、所有正在进行的委派子
智能体任务,以及所有挂起的人工确认。后续调用不会重复操作,且保证
不会触发 panic。用 session.isClosed()(Node)、session.is_closed()(Python)
或 session.IsClosed(ctx)(Go)查询关闭状态。
TypeScript 换行 复制 session . close ();
if ( session . isClosed ()) {
// send/stream 现在会以 CodeError::SessionClosed 拒绝
}
Python 换行 复制 session . close ()
if session . is_closed ():
# send/stream 现在会以 CodeError::SessionClosed 拒绝
pass
Go 换行 复制 if err := session . Close ( ctx ); err != nil {
return err
}
closed , err := session . IsClosed ( ctx )
取消令牌
每个运行都通过 child_token() 从同一个会话级父令牌派生出自己的
逐操作取消令牌,因此 close() 会一次性级联到所有正在进行的工作。需要原始
令牌的嵌入方——例如把它接入宿主侧的 select!,或者绕过 close() 的
运行存储和钩子副作用直接中止会话——可以通过
AgentSession::session_cancel_token() 克隆它。
智能体侧会话注册表
所属的 Agent 通过 Weak 引用跟踪它的存活会话(惰性回收),这样控制面
就能在不持有会话句柄的情况下驱动生命周期:
Agent::list_sessions() 返回存活的会话 ID(已排序,稳定)。
Agent::close_session(id) 按 ID 关闭单个会话——与
AgentSession::close() 相同的清理流程,从带外调用。
Agent::close() 关闭每个存活会话并拆除智能体持有的后台资源(同时
断开全局 MCP 连接)。返回后,新的 session / resumeSession 调用会以
CodeError::SessionClosed 快速失败。
Agent::is_closed() 报告智能体自身是否已被关闭。
TypeScript 换行 复制 const ids = await agent . listSessions ();
await agent . closeSession ( ids [ 0 ]);
await agent . close (); // 关闭所有剩余会话和全局 MCP
console . log ( agent . isClosed ());
Python 换行 复制 ids = agent . list_sessions ()
agent . close_session ( ids [ 0 ])
agent . close () # 关闭所有剩余会话和全局 MCP
print ( agent . is_closed ())
Go 换行 复制 ids , err := agent . ListSessions ( ctx )
if err == nil && len ( ids ) > 0 {
_ , err = agent . CloseSession ( ctx , ids [ 0 ])
}
err = agent . Close ( ctx )
参见更新日志 [3.3.0] 中的“会话与智能体生命周期控制”。
宿主身份标签
SessionOptions 携带四个不透明的身份字段,宿主可以在创建会话时附加。
框架只负责传递它们——从不解释或强制执行。它们会被传播进 SessionData、
钩子和追踪,并在恢复时还原,因此宿主可以据此驱动多租户聚合、计费
和分布式追踪:
TypeScript 换行 复制 const session = agent . session (' /repo ', {
tenantId : ' tenant-example ',
principal : ' principal-example ',
agentTemplateId : ' agent-template-example ',
correlationId : ' trace-example ',
});
Python 换行 复制 opts = SessionOptions ()
opts . tenant_id = ' tenant-example '
opts . principal = ' principal-example '
opts . agent_template_id = ' agent-template-example '
opts . correlation_id = ' trace-example '
session = agent . session (' /repo ', opts )
print ( session . tenant_id , session . principal )
Go 换行 复制 session , err := agent . Session ( ctx , " /repo ", & code . SessionOptions {
TenantID : " tenant-example ",
Principal : " principal-example ",
AgentTemplateID : " agent-template-example ",
CorrelationID : " trace-example ",
})
参见更新日志 [3.3.0] 中的“宿主提供的身份标签”。
运行记录与回放
每次 send() 或 stream() 都会创建运行记录。应用可以用这些记录实现界面状态、
审计、回放、取消和测试断言:
TypeScript 换行 复制 const runs = await session . runs ();
const latest = runs . at ( - 1 );
if ( latest ) {
console . log ( await session . runSnapshot ( latest . id ));
console . log ( await session . runEvents ( latest . id ));
}
Go 换行 复制 runs , err := session . Runs ( ctx )
if err == nil && len ( runs ) > 0 {
latest := runs [ len ( runs ) - 1 ]
snapshot , snapshotErr := session . RunSnapshot ( ctx , latest . ID )
events , eventsErr := session . RunEvents ( ctx , latest . ID )
_ , _ , _ = snapshot , snapshotErr , eventsErr
_ = events
}
currentRun() 用来读取调用当下的当前运行。send() 或 stream() 仍在
执行时,可以把它的 id 传给 cancelRun(id) 请求取消。空闲时,currentRun()
可能返回 null,也可能保留一个运行快照;已完成历史应使用 runs(),
取消前必须检查 status:
TypeScript 换行 复制 const current = await session . currentRun ();
if ( current ?. id && current . status === ' running ') {
await session . cancelRun ( current . id );
}
智能体定义
sessionForAgent() 应用一个命名的智能体定义,来源是内置智能体、
.a3s/agents 或配置的 agentDirs。
TypeScript 复制 const session = agent . sessionForAgent (' /repo ', ' explore ', [' ./agents '], {
planningMode : ' auto ',
});
Go 换行 复制 session , err := agent . SessionForAgent (
ctx ,
" /repo ",
" explore ",
[] string {" ./agents "},
& code . SessionOptions { PlanningMode : code . PlanningAuto },
)
对于通过值定义的一次性 worker,可使用 SessionForWorker;两种方法都返回通用的
Go Session API。