Skip to content
This repository was archived by the owner on Aug 15, 2026. It is now read-only.

fix(core): harden skill loading and session persistence - #23

Closed
srimon12 wants to merge 4 commits into
AstraBert:mainfrom
srimon12:feat/skill-session-hardening
Closed

fix(core): harden skill loading and session persistence#23
srimon12 wants to merge 4 commits into
AstraBert:mainfrom
srimon12:feat/skill-session-hardening

Conversation

@srimon12

@srimon12 srimon12 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR makes skill resolution configurable per agent, fixes a broken explicit
skill-loading path, and removes several failure-prone patterns from the core
agent runtime.

Skill resolution

Per-agent project skill directory

Adds:

MicroAgentBuilder::new(context)
    .skills_path("/workspace/project/.agents/skills")

The configured directory replaces the default project-local
.agents/skills lookup path for that agent only. It remains higher priority
than the global ~/.agents/skills directory.

The configured path is now used consistently by:

  • MicroAgentBuilder::add_skill
  • MicroAgentBuilder::find_skills
  • slash-prompt skill resolution (/skill-name)
  • the built-in skills tool

Existing public path constants remain available. agent::SKILLS_PATH now
uses the canonical definition from skills.

Traversal protection

Skill names are now validated before being joined to a skills directory.

The resolver rejects:

  • empty names
  • .
  • ..
  • names containing /
  • names containing \
  • non-normal path components

This prevents model-provided tool input such as ../outside-skill from
escaping a configured project skills directory.

Invalid names are handled according to their caller:

  • add_skill("../outside") returns
    MicroAgentBuilderError::InvalidSkillName
  • the built-in skills tool returns an error ToolResult
  • low-level lookup returns None

Explicit skill loading bug fix

MicroAgentBuilder::add_skill previously passed the skill directory to
parse_skill, even though the parser expects the SKILL.md file. It now
parses:

<skills-path>/<skill-name>/SKILL.md

Agent runtime cleanup

  • Extracts repeated storage.update_session error mapping into
    persist_event.
  • Makes session persistence errors consistent across all streamed events.
  • Replaces expect on model-provided tool-call JSON with a streamed
    AgentError::RunError.
  • Replaces unreachable!() branches for non-exhaustive message/result enums
    with explicit run errors.
  • Uses Option::take() for completed assistant messages, avoiding stale
    message reuse between tool-call iterations.
  • Enforces the existing ToolAlreadyDefined builder error instead of
    silently overwriting a tool with the same name.

Test reliability

Skill tests no longer change the process current working directory or depend
on the developer's global skills directory. Tests now inject explicit local
and global paths, making them safe to run in parallel and independent of
local machine state.

Validation

Passed:

cargo fmt --check
cargo check --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo test -p microagents-core

Test results:

  • 72 unit tests passed
  • 2 integration tests passed
  • 1 doctest passed

Summary by CodeRabbit

  • New Features

    • Added support for project-local and global skills, with project skills taking priority.
    • Added validation to prevent invalid skill names and path traversal.
  • Bug Fixes

    • Tool argument and result errors now appear as structured errors instead of causing crashes.
    • Duplicate tool names are rejected during configuration.
    • Improved handling of assistant messages containing text, thinking, and tool calls.
  • Reliability

    • Improved persistence of agent and session events, including clearer storage error handling.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@srimon12, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 89f66d8b-6c6c-4cfd-b5a6-74fe1d15f6d1

📥 Commits

Reviewing files that changed from the base of the PR and between 89c6c70 and 8107610.

📒 Files selected for processing (2)
  • microagents-core/src/agent.rs
  • microagents-core/src/common.rs
📝 Walkthrough

Walkthrough

The agent core centralizes skill resolution, validates skill names, detects duplicate tools, converts assistant messages, persists events through one helper, and returns structured errors for invalid tool input, unsupported results, and storage failures.

Changes

Agent core changes

Layer / File(s) Summary
Validated skill resolution and tool wiring
microagents-core/src/agent.rs, microagents-core/src/skills.rs
Skill names reject empty values, separators, and traversal components. Local skills take precedence over global skills. Skill loading, schemas, duplicate-tool checks, and related tests use the shared behavior.
Assistant message conversion
microagents-core/src/common.rs, microagents-core/src/agent.rs
SDK text, thinking, and tool-call parts convert into persisted assistant parts. Unsupported parts return AgentError::RunError.
Event persistence and structured run errors
microagents-core/src/agent.rs
Prompt, stream, tool, assistant, and stop events use persist_event. Invalid JSON arguments and unsupported tool results return structured errors. Persistence failures preserve RunError handling.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: astrabert

Poem

A rabbit checks each skill path twice,
Rejects unsafe hops and dice.
Events leave a steady trail,
Bad tool calls return detail.
Parts convert, duplicates flee—
A tidier agent tree!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: hardened skill loading and session persistence in the core runtime.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
microagents-core/src/agent.rs (1)

45-48: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rename or drop agent::GLOBAL_SKILLS_PATH

agent::GLOBAL_SKILLS_PATH is a static "~/.agents/skills" string while skills::GLOBAL_SKILLS_PATH is the runtime-resolved OnceLock<PathBuf>. Keeping both symbols with the same name and different types/semantics is confusing; use global_skills_path() where a real path is needed or rename the display literal.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@microagents-core/src/agent.rs` around lines 45 - 48, Remove or rename the
static string constant agent::GLOBAL_SKILLS_PATH to avoid conflicting with
skills::GLOBAL_SKILLS_PATH; update any references to use global_skills_path()
when a resolved filesystem path is required, and retain a differently named
display alias only if the literal is still needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@microagents-core/src/agent.rs`:
- Around line 322-366: Document the required builder call order in the method
comments for add_skill and find_skills: skills_path must be configured before
registering or discovering skills, because changing it afterward can make
existing registrations unresolvable. Keep the current implementation behavior
unchanged and clearly describe this ordering requirement near the affected
method documentation.

---

Nitpick comments:
In `@microagents-core/src/agent.rs`:
- Around line 45-48: Remove or rename the static string constant
agent::GLOBAL_SKILLS_PATH to avoid conflicting with skills::GLOBAL_SKILLS_PATH;
update any references to use global_skills_path() when a resolved filesystem
path is required, and retain a differently named display alias only if the
literal is still needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f84a87b-4674-4f5b-b678-c83b1b1cfdf8

📥 Commits

Reviewing files that changed from the base of the PR and between 7dd6294 and fc5e74f.

📒 Files selected for processing (2)
  • microagents-core/src/agent.rs
  • microagents-core/src/skills.rs

Comment thread microagents-core/src/agent.rs Outdated

@AstraBert AstraBert left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @srimon12 , i like the DRY for event persistence and assistant message parts, I don't really like the complexity introduced by allowing users to specify a custom skills path.
In general, I would advise you to open an issue before creating a PR, so that I can give you an idea of what I would include and what I would not, before you spend too much time on it 🙏

Comment thread microagents-core/src/agent.rs Outdated
Comment on lines +591 to +594
struct ConfiguredSkillsTool {
skills_path: PathBuf,
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is introducing more complexity than I would like: I don't see too much value in specifying a different path for the skill apart from the conventional .agents/skills (local and global), and, if anyone wants to expose skills that are under .claude/ or other paths I would simply suggest them to symlink

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i keep that deliberately because there was a API exposed but not connected internally.. removing it would be the wiser choice..

Comment thread microagents-core/src/agent.rs Outdated
Comment on lines +92 to +133
async fn persist_event(
storage: &dyn AgentStorage,
event: &AgentEventAny,
) -> Result<(), AgentError> {
storage
.update_session(event.clone())
.await
.map_err(|error| {
AgentError::RunError(format!(
"An error occurred while updating the session in the storage: {error}"
))
})
}

fn assistant_message_parts(message: &Message) -> Result<Vec<AssistantMessagePart>, AgentError> {
message
.content
.iter()
.map(|part| match part {
MessagePart::Text(text) => Ok(AssistantMessagePart::Text(AssistantTextPart {
text: text.text.clone(),
})),
MessagePart::Thinking(thinking) => {
Ok(AssistantMessagePart::Thinking(AssistantThinkingPart {
thinking: thinking.thinking.clone(),
signature: thinking.signature.clone(),
}))
}
MessagePart::ToolCall(tool_call) => {
Ok(AssistantMessagePart::ToolCall(AssistantToolCallPart {
id: tool_call.id.clone(),
name: tool_call.name.clone(),
arguments: tool_call.arguments.clone(),
}))
}
_ => Err(AgentError::RunError(
"Assistant response contains an unsupported message part".to_string(),
)),
})
.collect()
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would move this to common.rs

@AstraBert

Copy link
Copy Markdown
Owner

Also, I will push a release with my previous changes before merging this, jfyi 👍

@srimon12

Copy link
Copy Markdown
Contributor Author

Hey @srimon12 , i like the DRY for event persistence and assistant message parts, I don't really like the complexity introduced by allowing users to specify a custom skills path. In general, I would advise you to open an issue before creating a PR, so that I can give you an idea of what I would include and what I would not, before you spend too much time on it 🙏

yea thats on me..

will push the changes tommorow..

@AstraBert

Copy link
Copy Markdown
Owner

No problem! I really appreciate the contributions by the way, thank you so much!!! 🙏

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
microagents-core/src/agent.rs (1)

873-878: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Persist a failed SessionStopEvent before returning these run errors.

These branches run after session initialization and can run after turn events are persisted. They yield an error and return without a terminal event. A resumed session then has no recorded failure state for the turn.

  • microagents-core/src/agent.rs#L873-L878: Persist SessionStopEvent { success: false, ... } before returning an assistant-message conversion error.
  • microagents-core/src/agent.rs#L924-L933: Persist the failed terminal event before returning an invalid tool-arguments error.
  • microagents-core/src/agent.rs#L1048-L1053: Persist the failed terminal event before returning an unsupported tool-result error.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@microagents-core/src/agent.rs` around lines 873 - 878, Before each early
return for a run error, persist a failed SessionStopEvent with success: false
and the relevant error details: update the assistant-message conversion branch
around microagents-core/src/agent.rs lines 873-878, the invalid tool-arguments
branch at lines 924-933, and the unsupported tool-result branch at lines
1048-1053, then yield the error and return as before.
🧹 Nitpick comments (1)
microagents-core/src/common.rs (1)

274-291: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the remaining conversion branches.

This test covers only MessagePart::Text. Add tests for MessagePart::Thinking and MessagePart::ToolCall, including signature, id, name, and arguments. This protects the persisted AssistantMessagePart contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@microagents-core/src/common.rs` around lines 274 - 291, Add tests alongside
test_convert_message_to_assistant_parts_converts_text for the
MessagePart::Thinking and MessagePart::ToolCall conversion branches. Assert the
resulting AssistantMessagePart preserves Thinking.signature and ToolCall.id,
name, and arguments exactly, covering the persisted conversion contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@microagents-core/src/agent.rs`:
- Around line 873-878: Before each early return for a run error, persist a
failed SessionStopEvent with success: false and the relevant error details:
update the assistant-message conversion branch around
microagents-core/src/agent.rs lines 873-878, the invalid tool-arguments branch
at lines 924-933, and the unsupported tool-result branch at lines 1048-1053,
then yield the error and return as before.

---

Nitpick comments:
In `@microagents-core/src/common.rs`:
- Around line 274-291: Add tests alongside
test_convert_message_to_assistant_parts_converts_text for the
MessagePart::Thinking and MessagePart::ToolCall conversion branches. Assert the
resulting AssistantMessagePart preserves Thinking.signature and ToolCall.id,
name, and arguments exactly, covering the persisted conversion contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0e87b68d-eede-4405-89da-893cd5533af3

📥 Commits

Reviewing files that changed from the base of the PR and between fc5e74f and 89c6c70.

📒 Files selected for processing (3)
  • microagents-core/src/agent.rs
  • microagents-core/src/common.rs
  • microagents-core/src/skills.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • microagents-core/src/skills.rs

@srimon12

srimon12 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@AstraBert i know you are busy with the transition.. still once you get time check the changes

@AstraBert

Copy link
Copy Markdown
Owner

Hey, I decided to move this project to lightagent as I feel like microagents is pretty bloated and lost the original focus, and I really want to build an agent that I can use and scale with my codebase and not be blocked at the vector store stage forever. Thanks for your contributions, really appreciated 🙏

@AstraBert AstraBert closed this Aug 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants