Skip to content

Commit 6c7cac4

Browse files
RoyLinRoyLin
authored andcommitted
fix(docs/sdk): fix stale examples and add sessionId/autoSave to SessionOptions
Documentation fixes: - README Quick Start TypeScript: defaultSecurity → securityProvider: new DefaultSecurityProvider() - README Quick Start Python: fix constructor misuse and removed default_security field - README Node.js/Python SDK sections: add typed class imports, show sessionId/autoSave persistence flow - SUBAGENT_PERMISSIVE_MODE.md: add missing await on all Agent.create() calls (3 occurrences) SDK additions (Node.js + Python): - SessionOptions.sessionId / session_id: set a stable ID for save-and-resume workflows - SessionOptions.autoSave / auto_save: persist session after each turn automatically - Both fields wired through js_session_options_to_rust() and build_rust_session_options() Example files updated to use current API: - memoryDir/memory_dir → memoryStore: new FileMemoryStore(dir) / opts.memory_store = FileMemoryStore(dir) - defaultSecurity/default_security → securityProvider: new DefaultSecurityProvider() / opts.security_provider = DefaultSecurityProvider() - Imports updated in all four files
1 parent 8ffdc50 commit 6c7cac4

9 files changed

Lines changed: 131 additions & 34 deletions

File tree

README.md

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,11 @@ async fn main() -> anyhow::Result<()> {
8585
<summary><b>TypeScript</b></summary>
8686

8787
```typescript
88-
import { Agent } from '@a3s-lab/code';
88+
import { Agent, DefaultSecurityProvider } from '@a3s-lab/code';
8989

9090
const agent = await Agent.create('agent.hcl');
9191
const session = agent.session('.', {
92-
defaultSecurity: true,
92+
securityProvider: new DefaultSecurityProvider(),
9393
builtinSkills: true,
9494
planning: true,
9595
});
@@ -104,14 +104,13 @@ console.log(result.text);
104104
<summary><b>Python</b></summary>
105105

106106
```python
107-
from a3s_code import Agent, SessionOptions
107+
from a3s_code import Agent, SessionOptions, DefaultSecurityProvider
108108

109109
agent = Agent("agent.hcl")
110-
session = agent.session(".", SessionOptions(
111-
default_security=True,
112-
builtin_skills=True,
113-
planning=True,
114-
))
110+
opts = SessionOptions()
111+
opts.security_provider = DefaultSecurityProvider()
112+
opts.builtin_skills = True
113+
session = agent.session(".", opts, planning=True)
115114

116115
result = session.send("Refactor auth + update tests")
117116
print(result.text)
@@ -135,6 +134,7 @@ print(result.text)
135134
| **Git** | `git_worktree` | Create/list/remove/status git worktrees for parallel work |
136135
| **Subagents** | `task` | Delegate to a named agent; blocks until the child agent replies |
137136
| **Parallel subagents** | `parallel_task` | Fan-out to multiple named agents concurrently |
137+
| **Team workflow** | `run_team` | Lead → Worker → Reviewer team with dynamic decomposition and quality review |
138138
| **Parallel tools** | `batch` | Execute multiple tools concurrently in one call |
139139

140140
---
@@ -556,6 +556,28 @@ for task in result.done_tasks:
556556

557557
Supports Lead/Worker/Reviewer roles, `mpsc` peer messaging, broadcast, and a full task lifecycle (Open → InProgress → InReview → Done/Rejected).
558558

559+
**`run_team` built-in tool** — The LLM can also trigger the same Lead → Worker → Reviewer workflow autonomously at runtime via the `run_team` tool (no SDK wiring required):
560+
561+
```python
562+
# Python — call directly from SDK code
563+
result = session.tool("run_team", {
564+
"goal": "Audit the auth module for security issues and produce a remediation plan",
565+
"max_steps": 10, # per-member agent; lead/worker/reviewer all default to "general"
566+
})
567+
print(result.output)
568+
```
569+
570+
```typescript
571+
// TypeScript — call directly from SDK code
572+
const result = await session.tool('run_team', {
573+
goal: 'Audit the auth module for security issues and produce a remediation plan',
574+
maxSteps: 10,
575+
});
576+
console.log(result.output);
577+
```
578+
579+
The LLM can also call `run_team` on its own when the `delegate-task` skill is loaded — it selects it automatically for goals with an unknown number of subtasks or that require reviewer sign-off.
580+
559581
---
560582

561583
### 🎛️ Agent Orchestrator (Master-SubAgent Coordination)
@@ -1064,7 +1086,7 @@ tasks = board.by_status("done") # "open"|"in_progress"|"in_review"|"done"|"
10641086
### Python SDK
10651087

10661088
```python
1067-
from a3s_code import Agent, SessionOptions, builtin_skills
1089+
from a3s_code import Agent, SessionOptions, builtin_skills, DefaultSecurityProvider, FileMemoryStore, FileSessionStore, MemorySessionStore
10681090

10691091
# Create agent
10701092
agent = Agent("agent.hcl")
@@ -1108,9 +1130,13 @@ agent.refresh_mcp_tools() # refresh global MCP tool cache
11081130
stats = session.queue_stats()
11091131
dead = session.dead_letters()
11101132

1111-
# Persistence
1112-
session.save()
1113-
resumed = agent.resume_session(session.session_id, opts)
1133+
# Persistence — set ID + auto-save, then resume later
1134+
opts2 = SessionOptions()
1135+
opts2.session_store = FileSessionStore('./sessions')
1136+
opts2.session_id = 'my-session'
1137+
opts2.auto_save = True
1138+
session2 = agent.session(".", opts2)
1139+
resumed = agent.resume_session('my-session', opts2)
11141140
```
11151141

11161142
### Node.js SDK — Agent Teams
@@ -1149,7 +1175,7 @@ const stats = board.stats(); // { open, inProgress, inReview, done, reje
11491175
### Node.js SDK
11501176

11511177
```typescript
1152-
import { Agent } from '@a3s-lab/code';
1178+
import { Agent, DefaultSecurityProvider, FileMemoryStore, FileSessionStore, MemorySessionStore } from '@a3s-lab/code';
11531179

11541180
// Create agent
11551181
const agent = await Agent.create('agent.hcl');
@@ -1194,9 +1220,13 @@ await agent.refreshMcpTools(); // refresh global MCP tool cache
11941220
const stats = await session.queueStats();
11951221
const dead = await session.deadLetters();
11961222

1197-
// Persistence
1198-
await session.save();
1199-
const resumed = agent.resumeSession(session.sessionId, options);
1223+
// Persistence — set ID + auto-save, then resume later
1224+
const session2 = agent.session('.', {
1225+
sessionStore: new FileSessionStore('./sessions'),
1226+
sessionId: 'my-session',
1227+
autoSave: true,
1228+
});
1229+
const resumed = agent.resumeSession('my-session', { sessionStore: new FileSessionStore('./sessions') });
12001230
```
12011231

12021232
---

sdk/node/examples/SUBAGENT_PERMISSIVE_MODE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Version 1.0.3 introduces permissive mode support for sub-agents, allowing them t
1111
```typescript
1212
import { Agent } from '@a3s-lab/code';
1313

14-
const agent = Agent.create('~/.a3s/config.hcl');
14+
const agent = await Agent.create('~/.a3s/config.hcl');
1515
const session = agent.session('.', { permissive: true });
1616

1717
// Spawn a sub-agent with permissive mode
@@ -32,7 +32,7 @@ console.log(result.output);
3232
const { Agent } = require('@a3s-lab/code');
3333

3434
async function main() {
35-
const agent = Agent.create('~/.a3s/config.hcl');
35+
const agent = await Agent.create('~/.a3s/config.hcl');
3636
const session = agent.session('.', { permissive: true });
3737

3838
// Spawn a sub-agent with permissive mode
@@ -55,7 +55,7 @@ main();
5555
```typescript
5656
import { Agent } from '@a3s-lab/code';
5757

58-
const agent = Agent.create('~/.a3s/config.hcl');
58+
const agent = await Agent.create('~/.a3s/config.hcl');
5959
const session = agent.session('.', { permissive: true });
6060

6161
// Spawn multiple sub-agents in parallel
@@ -88,7 +88,7 @@ Monitor internal SubAgent events (tool calls, LLM responses):
8888
```typescript
8989
import { Agent } from '@a3s-lab/code';
9090

91-
const agent = Agent.create('~/.a3s/config.hcl');
91+
const agent = await Agent.create('~/.a3s/config.hcl');
9292
const session = agent.session('.', { permissive: true });
9393

9494
// Stream events and monitor SubAgent activity

sdk/node/examples/advanced_features_demo.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import {
2424
AgentResult,
2525
ToolResult,
2626
builtinSkills,
27+
FileMemoryStore,
28+
DefaultSecurityProvider,
2729
} from '../index.js';
2830
import * as fs from 'fs';
2931
import * as path from 'path';
@@ -239,7 +241,7 @@ class AdvancedFeaturesDemo {
239241
'AWS_SECRET=AKIAIOSFODNN7EXAMPLE\n'
240242
);
241243

242-
const session: Session = this.agent.session(workspace, { defaultSecurity: true, permissive: true });
244+
const session: Session = this.agent.session(workspace, { securityProvider: new DefaultSecurityProvider(), permissive: true });
243245

244246
console.log(' Security: DefaultSecurityProvider enabled');
245247
console.log(' Features: taint tracking + output sanitization\n');
@@ -308,7 +310,7 @@ class AdvancedFeaturesDemo {
308310
const workspace: string = AdvancedFeaturesDemo.makeTempDir();
309311
const memoryDir: string = AdvancedFeaturesDemo.makeTempDir();
310312
try {
311-
const session: Session = this.agent.session(workspace, { memoryDir, permissive: true });
313+
const session: Session = this.agent.session(workspace, { memoryStore: new FileMemoryStore(memoryDir), permissive: true });
312314

313315
console.log(` Memory dir: ${memoryDir}\n`);
314316

sdk/node/examples/test_advanced_features.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* Requires a valid LLM API key in ~/.a3s/config.hcl or $A3S_CONFIG.
1414
*/
1515

16-
import { Agent, Session, AgentResult } from '../index.js';
16+
import { Agent, Session, AgentResult, FileMemoryStore, DefaultSecurityProvider } from '../index.js';
1717
import * as fs from 'fs';
1818
import * as path from 'path';
1919
import * as os from 'os';
@@ -68,7 +68,7 @@ class AdvancedFeaturesTest {
6868
const memDir: string = fs.mkdtempSync(path.join(os.tmpdir(), 'a3s-mem-'));
6969

7070
const session: Session = agent.session(os.tmpdir(), {
71-
memoryDir: memDir,
71+
memoryStore: new FileMemoryStore(memDir),
7272
});
7373

7474
const result: AgentResult = await session.send('Remember: my favorite language is Rust.');
@@ -88,7 +88,7 @@ class AdvancedFeaturesTest {
8888

8989
// Enable default security: taint-tracks input, sanitizes output
9090
const session: Session = agent.session(os.tmpdir(), {
91-
defaultSecurity: true,
91+
securityProvider: new DefaultSecurityProvider(),
9292
});
9393

9494
const result: AgentResult = await session.send('What is 2 + 2?');
@@ -181,10 +181,10 @@ class AdvancedFeaturesTest {
181181
const memDir: string = fs.mkdtempSync(path.join(os.tmpdir(), 'a3s-combined-'));
182182

183183
const session: Session = agent.session(os.tmpdir(), {
184-
defaultSecurity: true,
184+
securityProvider: new DefaultSecurityProvider(),
185185
autoCompact: true,
186186
autoCompactThreshold: 0.9,
187-
memoryDir: memDir,
187+
memoryStore: new FileMemoryStore(memDir),
188188
permissive: true,
189189
});
190190

sdk/node/index.d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,19 @@ export interface SessionOptions {
138138
inlineSkills?: Array<InlineSkill>
139139
/** Override maximum number of tool-call rounds for this session. */
140140
maxToolRounds?: number
141+
/**
142+
* Session ID (auto-generated if not set).
143+
*
144+
* Set a stable ID so the session can be saved and resumed later:
145+
* ```js
146+
* agent.session('.', { sessionId: 'my-session', sessionStore: new FileSessionStore('./sessions'), autoSave: true });
147+
* // Later:
148+
* agent.resumeSession('my-session', { sessionStore: new FileSessionStore('./sessions') });
149+
* ```
150+
*/
151+
sessionId?: string
152+
/** Automatically save the session to the configured store after each turn (default: false). */
153+
autoSave?: boolean
141154
}
142155
/** A single message in conversation history. */
143156
export interface MessageObject {

sdk/node/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,12 @@ fn js_session_options_to_rust(options: Option<SessionOptions>) -> RustSessionOpt
794794
if let Some(r) = o.max_tool_rounds {
795795
opts = opts.with_max_tool_rounds(r as usize);
796796
}
797+
if let Some(id) = o.session_id {
798+
opts = opts.with_session_id(id);
799+
}
800+
if o.auto_save.unwrap_or(false) {
801+
opts = opts.with_auto_save(true);
802+
}
797803
opts
798804
}
799805

sdk/python/examples/advanced_features_demo.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import os
1919
import tempfile
2020
from pathlib import Path
21-
from a3s_code import Agent, SessionOptions, SessionQueueConfig, builtin_skills
21+
from a3s_code import Agent, SessionOptions, SessionQueueConfig, builtin_skills, DefaultSecurityProvider, FileMemoryStore
2222

2323

2424
class AdvancedFeaturesDemo:
@@ -200,7 +200,7 @@ def demo_security(self) -> None:
200200
)
201201

202202
opts = SessionOptions()
203-
opts.default_security = True
203+
opts.security_provider = DefaultSecurityProvider()
204204
session = self.agent.session(workspace, options=opts, permissive=True)
205205

206206
print(" Security: DefaultSecurityProvider enabled")
@@ -266,7 +266,7 @@ def demo_memory(self) -> None:
266266
with tempfile.TemporaryDirectory() as workspace:
267267
with tempfile.TemporaryDirectory() as memory_dir:
268268
opts = SessionOptions()
269-
opts.memory_dir = memory_dir
269+
opts.memory_store = FileMemoryStore(memory_dir)
270270

271271
session = self.agent.session(workspace, options=opts, permissive=True)
272272

sdk/python/examples/test_advanced_features.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ async def test_memory(self) -> None:
6363

6464
try:
6565
opts = a3s_code.SessionOptions()
66-
opts.memory_dir = mem_dir
66+
opts.memory_store = a3s_code.FileMemoryStore(mem_dir)
6767

6868
session = agent.session(tempfile.gettempdir(), options=opts)
6969
result = await session.send("Remember: my favorite language is Rust.")
@@ -80,7 +80,7 @@ async def test_security(self) -> None:
8080
agent = await a3s_code.Agent.create(self.config_path)
8181

8282
opts = a3s_code.SessionOptions()
83-
opts.default_security = True
83+
opts.security_provider = a3s_code.DefaultSecurityProvider()
8484

8585
session = agent.session(tempfile.gettempdir(), options=opts)
8686
result = await session.send("What is 2 + 2?")
@@ -164,10 +164,10 @@ async def test_combined(self) -> None:
164164

165165
try:
166166
opts = a3s_code.SessionOptions()
167-
opts.default_security = True
167+
opts.security_provider = a3s_code.DefaultSecurityProvider()
168168
opts.auto_compact = True
169169
opts.auto_compact_threshold = 0.9
170-
opts.memory_dir = mem_dir
170+
opts.memory_store = a3s_code.FileMemoryStore(mem_dir)
171171

172172
session = agent.session(tempfile.gettempdir(), options=opts, permissive=True)
173173

sdk/python/src/lib.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,6 +1931,22 @@ struct PySessionOptions {
19311931
inline_skills: Vec<(String, String, String)>,
19321932
/// Override maximum number of tool-call rounds per session.
19331933
max_tool_rounds: Option<usize>,
1934+
/// Session ID for this session (auto-generated if not set).
1935+
///
1936+
/// Set a stable ID to save and resume the session later:
1937+
///
1938+
/// .. code-block:: python
1939+
///
1940+
/// opts = SessionOptions()
1941+
/// opts.session_store = FileSessionStore('./sessions')
1942+
/// opts.session_id = 'my-session'
1943+
/// opts.auto_save = True
1944+
/// session = agent.session('.', opts)
1945+
/// # Later:
1946+
/// resumed = agent.resume_session('my-session', opts)
1947+
session_id: Option<String>,
1948+
/// Automatically save the session to the configured store after each turn (default: False).
1949+
auto_save: bool,
19341950
}
19351951

19361952
#[pymethods]
@@ -1954,6 +1970,8 @@ impl PySessionOptions {
19541970
extra: None,
19551971
inline_skills: vec![],
19561972
max_tool_rounds: None,
1973+
session_id: None,
1974+
auto_save: false,
19571975
}
19581976
}
19591977

@@ -2143,6 +2161,28 @@ impl PySessionOptions {
21432161
self.max_tool_rounds = value;
21442162
}
21452163

2164+
/// Session ID (auto-generated if not set). Set to save and resume sessions by name.
2165+
#[getter]
2166+
fn get_session_id(&self) -> Option<String> {
2167+
self.session_id.clone()
2168+
}
2169+
2170+
#[setter]
2171+
fn set_session_id(&mut self, value: Option<String>) {
2172+
self.session_id = value;
2173+
}
2174+
2175+
/// Automatically save the session after each turn (default: False).
2176+
#[getter]
2177+
fn get_auto_save(&self) -> bool {
2178+
self.auto_save
2179+
}
2180+
2181+
#[setter]
2182+
fn set_auto_save(&mut self, value: bool) {
2183+
self.auto_save = value;
2184+
}
2185+
21462186
/// Register an instruction skill programmatically.
21472187
///
21482188
/// Instructions are injected into the system prompt at session start.
@@ -2489,6 +2529,12 @@ fn build_rust_session_options(so: PySessionOptions) -> RustSessionOptions {
24892529
if let Some(r) = so.max_tool_rounds {
24902530
o = o.with_max_tool_rounds(r);
24912531
}
2532+
if let Some(id) = so.session_id {
2533+
o = o.with_session_id(id);
2534+
}
2535+
if so.auto_save {
2536+
o = o.with_auto_save(true);
2537+
}
24922538
o
24932539
}
24942540

0 commit comments

Comments
 (0)