Skip to content

Commit a2ae6f2

Browse files
authored
Dev/0.4.54 (#683)
* smart agent improvements, websocket improvements * smart agent enhance, websocket performance, parallel bash handling * add claude-sonnet-4-6, sonnet46 alias, --smart CLI flag, update docs * feat(agent-cards): add runtime MCP targets via mcp_connect * chore(docs): bump docs submodule for mcp_connect docs * web search for anthropic history webclear command improve smart agent handling
1 parent 367e17d commit a2ae6f2

File tree

98 files changed

+6146
-416
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+6146
-416
lines changed

plan/agent-card-rfc.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ A loader validates fields based on `type` and loads a single file or a directory
77
optional/experimental and described in a separate spec.
88
AgentCards now support an optional `description` field used for tool descriptions when
99
agents are exposed as tools (MCP or agent-as-tool wiring).
10+
AgentCards may declare runtime MCP targets via `mcp_connect` (`target` + optional `name`).
1011
AgentCards may enable local shell execution via `shell: true` with optional `cwd`.
1112
AgentCards support `tool_only: true` to prevent exposure as first-class agents while
1213
still allowing use as tools (useful for helper agents in `.fast-agent/tool-cards/`).
@@ -115,6 +116,7 @@ Allowed fields:
115116
- `name`, `instruction`, `description`, `default`, `tool_only`
116117
- `agents` (agents-as-tools)
117118
- `servers`, `tools`, `resources`, `prompts`, `skills`
119+
- `mcp_connect` (runtime MCP targets; `target` required, `name` optional)
118120
- `model`, `use_history`, `request_params`, `human_input`, `api_key`
119121
- `history_source`, `history_merge_target`
120122
- `max_parallel`, `child_timeout_sec`, `max_display_instances`

plan/session-size.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
---
2+
title: "Session Size Indicator in /session list"
3+
status: draft
4+
---
5+
6+
# Session Size Indicator in `/session list`
7+
8+
## Goal
9+
10+
Add a compact visual size indicator to session list output (interactive + ACP markdown),
11+
similar in spirit to history density shading.
12+
13+
Example target UX:
14+
15+
- small session: ``
16+
- medium session: ``
17+
- large session: ``
18+
- very large session: ``
19+
20+
This should make large sessions obvious at a glance without opening each one.
21+
22+
---
23+
24+
## Current behavior and constraints
25+
26+
1. Session metadata is saved to `session.json` via `Session._save_metadata()`.
27+
2. Session history autosave runs from the `after_turn_complete` hook (`save_session_history`).
28+
3. `/session list` rendering paths:
29+
- interactive/TUI: `commands/handlers/sessions.py::_build_session_entries(...)`
30+
- ACP markdown: `commands/renderers/session_markdown.py`, fed by
31+
`session/formatting.py::format_session_entries(...)`
32+
4. Current list rows already include timestamp, pin state, agent count, summary.
33+
34+
Design implication: compute/store a lightweight turn count during autosave and read it from
35+
`session.json` during listing (no history file parsing during list).
36+
37+
---
38+
39+
## Proposed metadata additions
40+
41+
Add optional metadata keys in `session.json`:
42+
43+
- `turn_count_by_agent: dict[str, int]`
44+
- `turn_count_total: int`
45+
46+
Notes:
47+
48+
- `turn_count_total` is the value used for list glyphs.
49+
- Missing keys are valid for old sessions (fallback indicator).
50+
51+
---
52+
53+
## Turn counting rule
54+
55+
Use the same turn boundary semantics as conversation summaries:
56+
57+
- a turn starts on a `user` message with no `tool_results`
58+
59+
Implementation should use `split_into_turns(...)` from
60+
`fast_agent.types.conversation_summary` for consistency.
61+
62+
---
63+
64+
## Indicator mapping (initial thresholds)
65+
66+
Define a small helper (in `session/formatting.py` or nearby) that maps `turn_count_total`
67+
to a single character.
68+
69+
Initial mapping proposal:
70+
71+
- missing/unknown: `·`
72+
- `0`: `·`
73+
- `1-4`: ``
74+
- `5-14`: ``
75+
- `15-39`: ``
76+
- `40+`: ``
77+
78+
Thresholds can be tuned later based on real usage.
79+
80+
---
81+
82+
## Implementation plan
83+
84+
## 1) Persist turn counts during save
85+
86+
Files:
87+
88+
- `src/fast_agent/session/session_manager.py`
89+
90+
Changes:
91+
92+
1. In `Session.save_history(...)` after saving history file and resolving `agent_name`:
93+
- compute `agent_turn_count` from `agent.message_history`
94+
- update `metadata["turn_count_by_agent"][agent_name]`
95+
- compute/update `metadata["turn_count_total"]` as sum of per-agent counts
96+
2. Keep behavior safe when agent has no name or malformed metadata.
97+
3. Ensure this remains part of existing `_save_metadata()` call path.
98+
99+
## 2) Preserve counts on fork
100+
101+
Files:
102+
103+
- `src/fast_agent/session/session_manager.py`
104+
105+
Changes:
106+
107+
1. In `fork_current_session(...)`, copy over `turn_count_by_agent` and `turn_count_total`
108+
(or recompute from copied map if safer).
109+
110+
## 3) Thread counts into list summaries
111+
112+
Files:
113+
114+
- `src/fast_agent/session/formatting.py`
115+
116+
Changes:
117+
118+
1. Extend `SessionEntrySummary` with optional `turn_count` and `size_indicator`.
119+
2. In `build_session_entry_summaries(...)`, read `turn_count_total` from metadata.
120+
3. Compute `size_indicator` from helper mapping.
121+
4. For old sessions without metadata, default to unknown indicator.
122+
123+
## 4) Render indicator in interactive list
124+
125+
Files:
126+
127+
- `src/fast_agent/commands/handlers/sessions.py`
128+
129+
Changes:
130+
131+
1. In `_build_session_entries(...)`, add `entry.size_indicator` before session name,
132+
dim-styled so it is informative but not noisy.
133+
134+
## 5) Render indicator in compact/markdown list
135+
136+
Files:
137+
138+
- `src/fast_agent/session/formatting.py`
139+
140+
Changes:
141+
142+
1. In `format_session_entries(..., mode="compact")`, prepend indicator to each line
143+
so ACP markdown rendering automatically includes it.
144+
145+
No direct change should be required in `session_markdown.py` if compact lines already
146+
contain the indicator.
147+
148+
---
149+
150+
## Backward compatibility
151+
152+
1. Existing sessions without new metadata continue to list correctly.
153+
2. Unknown/missing count shows neutral marker (`·`).
154+
3. No migration step required.
155+
156+
---
157+
158+
## Testing plan
159+
160+
## Unit tests
161+
162+
1. `tests/unit/fast_agent/session/test_session_formatting.py`
163+
- add/adjust assertions for indicator presence in compact/verbose outputs
164+
- include edge cases around threshold boundaries
165+
2. Add/extend `session_manager` unit test(s)
166+
- verify `turn_count_by_agent` and `turn_count_total` are written on save
167+
- verify malformed metadata is handled robustly
168+
169+
## Integration tests
170+
171+
1. `tests/integration/sessions/test_sessions.py`
172+
- assert new metadata keys appear in `session.json` after autosave
173+
2. ACP session list integration test
174+
- ensure response contains indicator in list lines
175+
176+
---
177+
178+
## Risks / trade-offs
179+
180+
1. **Accuracy vs cost:** storing turn counts is fast and cheap; parsing all histories on list
181+
would be more accurate for legacy edge cases but slower.
182+
2. **Definition drift:** if turn definition changes elsewhere, this feature must stay aligned
183+
by relying on shared split helper.
184+
3. **Visual noise:** indicators should be dim and stable; avoid overloading line with too many
185+
symbols.
186+
187+
---
188+
189+
## Rollout sequence
190+
191+
1. Land metadata persistence + tests.
192+
2. Land formatting/rendering + tests.
193+
3. Validate in both interactive prompt and ACP `/session list`.
194+
4. Tune thresholds if needed after initial usage.
195+

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ dependencies = [
2121
"pyyaml>=6.0.2",
2222
"rich>=14.3.1",
2323
"typer>=0.21.1",
24-
"anthropic>=0.78.0",
24+
"anthropic>=0.80.0",
2525
"openai[aiohttp]>=2.17.0",
2626
"prompt-toolkit>=3.0.52",
2727
"aiohttp>=3.13.2",
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<AgentCards>
2+
---
3+
4+
# Agent Card (type: `agent`)
5+
6+
## Format
7+
- **Markdown** with YAML frontmatter + body, or **YAML** only.
8+
- **Body = system/developer instruction.**
9+
- Optional first non-empty line `---SYSTEM` is stripped.
10+
- `instruction:` field **or** body may define the instruction (not both).
11+
- If neither is present, the **default instruction** is used.
12+
- **Invocation:** the card defines the agent; you invoke it later with a **user message** (first user turn).
13+
- `messages:` is **history files**, not the invocation message.
14+
15+
---
16+
17+
## Main fields (frontmatter, type = `agent`)
18+
- `name` — string; defaults to file stem.
19+
- `description` — optional summary.
20+
- `type``"agent"` (default if omitted).
21+
- `model` — model ID.
22+
- `instruction` — system/developer prompt string (mutually exclusive with body).
23+
- `skills` — list of skills. **Disable all skills:** `skills: []`.
24+
- `servers` — list of configured MCP server names.
25+
- `tools` / `resources` / `prompts` — map: `server_name -> [allowed_items]`.
26+
- `mcp_connect` — optional runtime MCP targets resolved at startup.
27+
- entries require `target` and may include optional `name`.
28+
- target forms: `https://...`, `@scope/pkg`, `npx ...`, `uvx ...`, or stdio command.
29+
- `agents` — list of child agents (Agents-as-Tools).
30+
- `use_history` — bool (default `true`).
31+
- `messages` — path or list of history files (relative to card directory).
32+
- `request_params` — request/model overrides.
33+
- `human_input` — bool (enable human input tool).
34+
- `shell` — bool (enable shell); `cwd` optional.
35+
- `default` — marks this agent as the `smart` tool target when the path resolves multiple cards. First `default: true` non-`tool_only` agent wins; if none, the first non-`tool_only` agent is used.
36+
- `tool_only` — excludes this agent from default selection; it can only be invoked by other agents as a tool.
37+
38+
---
39+
40+
## Instruction templates (placeholders)
41+
You can insert these in the **body** or `instruction:`.
42+
43+
| Placeholder | Meaning |
44+
|---|---|
45+
| `\{{currentDate}}` | Current date (e.g., “17 December 2025”) |
46+
| `\{{hostPlatform}}` | Host platform string |
47+
| `\{{pythonVer}}` | Python version |
48+
| `\{{workspaceRoot}}` | Workspace root path (if available) |
49+
| `\{{env}}` | Environment summary (client, host, workspace) |
50+
| `\{{agentName}}` | Current agent name |
51+
| `\{{agentType}}` | Current agent type |
52+
| `\{{agentCardPath}}` | Source AgentCard path (if loaded from card) |
53+
| `\{{agentCardDir}}` | Directory containing the source AgentCard |
54+
| `\{{serverInstructions}}` | MCP server instructions (if any) |
55+
| `\{{agentSkills}}` | Formatted skill descriptions |
56+
57+
---
58+
59+
## Content includes (inline)
60+
- `\{{url:https://...}}` — fetch and inline URL content.
61+
- `\{{file:relative/path}}` — inline file content (error if missing).
62+
- `\{{file_silent:relative/path}}` — inline file content, **empty if missing**.
63+
64+
**Note:** file paths are **relative** (resolved against `workspaceRoot` when available).
65+
66+
---
67+
68+
## Minimal example (Markdown)
69+
70+
```md
71+
---
72+
name: my_agent
73+
description: Focused helper
74+
model: gpt-oss
75+
skills: [] # disable skills
76+
use_history: true
77+
---
78+
79+
You are a concise assistant.
80+
81+
\{{env}}
82+
\{{currentDate}}
83+
\{{file:docs/house-style.md}}
84+
```
85+
86+
---
87+
88+
</AgentCards>

resources/shared/smart_prompt.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
You are a helpful AI Agent.
2+
3+
You have the ability to create sub-agents and delegate tasks to them.
4+
5+
Information about how to do so is below. Pre-existing cards may be in the `fast-agent environment` directories. You may issue
6+
multiple calls in parallel to new or existing AgentCard definitions.
7+
8+
{{internal:smart_agent_cards}}
9+
10+
{{serverInstructions}}
11+
{{agentSkills}}
12+
{{file_silent:AGENTS.md}}
13+
{{env}}
14+
15+
fast-agent environment paths:
16+
- Environment root: {{environmentDir}}
17+
- Agent cards: {{environmentAgentCardsDir}}
18+
- Tool cards: {{environmentToolCardsDir}}
19+
20+
Current agent identity:
21+
- Name: {{agentName}}
22+
- Type: {{agentType}}
23+
- AgentCard path: {{agentCardPath}}
24+
- AgentCard directory: {{agentCardDir}}
25+
26+
Use the smart tool to load AgentCards temporarily when you need extra agents.
27+
Use validate to check AgentCard files before running them.
28+
When a card needs MCP servers that are not preconfigured in `fastagent.config.yaml`,
29+
declare them with `mcp_connect` entries (`target` + optional `name`). Prefer explicit
30+
`name` values when collisions are possible.
31+
32+
The current date is {{currentDate}}.

src/fast_agent/acp/server/agent_acp_server.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@
8888
from fast_agent.context import Context
8989
from fast_agent.core.fastagent import AgentInstance
9090
from fast_agent.core.instruction_refresh import McpInstructionCapable, build_instruction
91-
from fast_agent.core.instruction_utils import get_instruction_template
91+
from fast_agent.core.instruction_utils import (
92+
build_agent_instruction_context,
93+
get_instruction_template,
94+
)
9295
from fast_agent.core.logging.logger import get_logger
9396
from fast_agent.core.prompt_templates import enrich_with_environment_context
9497
from fast_agent.interfaces import (
@@ -660,6 +663,8 @@ async def _resolve_instruction_for_session(
660663
if agent.instruction_context:
661664
effective_context = dict(agent.instruction_context)
662665

666+
effective_context = build_agent_instruction_context(agent, effective_context)
667+
663668
return await build_instruction(
664669
template,
665670
aggregator=aggregator,
@@ -780,7 +785,8 @@ async def _refresh_session_state(
780785
for agent_name, agent in instance.agents.items():
781786
if isinstance(agent, InstructionContextCapable):
782787
try:
783-
agent.set_instruction_context(prompt_context)
788+
context_with_agent = build_agent_instruction_context(agent, prompt_context)
789+
agent.set_instruction_context(context_with_agent)
784790
except Exception as exc:
785791
logger.warning(
786792
"Failed to set instruction context on agent",
@@ -1474,7 +1480,8 @@ async def _initialize_session_state(
14741480
for agent_name, agent in instance.agents.items():
14751481
if isinstance(agent, InstructionContextCapable):
14761482
try:
1477-
agent.set_instruction_context(session_context)
1483+
context_with_agent = build_agent_instruction_context(agent, session_context)
1484+
agent.set_instruction_context(context_with_agent)
14781485
except Exception as e:
14791486
logger.warning(f"Failed to set instruction context on agent {agent_name}: {e}")
14801487

0 commit comments

Comments
 (0)