Skip to content

fix(agent): keep recently used tools and viewed-skill toolsets available across turns - #6275

Draft
dan0v wants to merge 1 commit into
odysseus-dev:devfrom
dan0v:dev
Draft

dan0v wants to merge 1 commit into
odysseus-dev:devfrom
dan0v:dev

Conversation

@dan0v

@dan0v dan0v commented Sep 11, 2026

Copy link
Copy Markdown

Summary

Per-turn tool selection is keyed off the latest user message only (embedding retrieval + keyword intent), so tools the agent used in the previous turn silently vanish on a follow-up that doesn't re-name them — the model then reports "bash is not available this turn" mid-task (#4365). Skills were worst-hit: a fetched skill's frontmatter requires_toolsets only rode along while the Jaccard matcher kept re-matching the new query, and that path is skipped entirely on low-signal turns. This PR carries tools forward: tools actually executed in the last two real turns (from persisted metadata.tool_events) and toolsets declared by skills the model recently viewed are re-attached into the current turn's selection. It also closes two first-turn gaps: explicit CLI phrasings (cli, command line, cmd, console, powershell, zsh) now classify as the files domain, and _KEYWORD_HINTS gains a shell entry so the retrieval fallback paths (e.g. ChromaDB-less installs, cf. #4048) never drop bash on "run this in the terminal". Schema visibility only — every existing permission gate still applies (details below).

Target branch

  • This PR targets dev, not main. All PRs land in dev; main is curated by the maintainer at each release. If your PR is on main by accident, click "Edit" on this PR and change the base.

Linked Issue

Fixes #4365
Part of #6103 (implements the "additive tool selection" behaviour for the consecutive-turn case)
Related: #3934 (low-signal gate, first-message case not addressed here), #4048 (no-ChromaDB fallback helped by the keyword-hint entry)

Type of Change

  • Bug fix (non-breaking — fixes a confirmed issue)

Checklist

How to Test

  1. Consecutive-request carry (Agent drops core tools (bash/web/file) on low-signal follow-ups #4365 repro): in agent mode, ask list the files here with ls. On the next turn say now show the git log — previously the model replied that bash is unavailable this turn; now bash/python stay in the toolset. The server log shows [tool-rag] Carried forward recent tools: [...].
  2. First-turn explicit CLI: in a fresh chat ask do it via the command line / run this in the CLI — bash is selected without needing the words "bash"/"terminal".
  3. Skills: create a skill whose frontmatter declares requires_toolsets: [bash], ask the agent to follow it, then follow up with continue with step 2 — the skill's shell access persists even though the follow-up matches nothing.
  4. Permission regression check: turn the shell off (agent settings toggle), then send a follow-up in the same chat — bash stays disabled (carried tools are filtered through disabled_tools); same for a non-admin user in multi-user mode and for API-token runs.
  5. Unit tests: python -m pytest tests/test_agent_recent_tool_carryover.py tests/test_agent_loop.py tests/test_chat_route_tool_policy.py tests/test_embedding_lanes_tool_index.py

Gap: I ran the unit suites (110 passing across the touched areas) but did not run the full app end-to-end; steps 1–4 are the runtime validation for the reviewer.

Safety notes for reviewers: carried tools are schema-visibility only. Owner/token/plan-mode/toggle disables and the external-context gate merge into disabled_tools before the carry runs, and run_security.decision_for() enforces capability gates at execution time independent of selection — no execution rights are granted. Skill names parsed from history are owner-scoped on lookup (SkillsManager.load(owner=…)), restricted to known_tool_names(), and control characters are stripped (log forgery). Injected-context rows (metadata.trusted=False) don't consume the turn window.

Visual / UI changes

None — server-side agent-loop/tool-selection changes plus tests only.

…ble across turns

Co-authored by GLM 5.3

Per-turn tool selection keys only off the latest user message, so a
follow-up that doesn't re-name the shell ("now show the diff", "do
step 3 now") silently dropped bash/python and the model reported them
"not available this turn" mid-task. Skills were worst-hit: a fetched
skill's requires_toolsets only rode along while the Jaccard matcher
kept re-matching the new query, and that path is skipped entirely on
low-signal turns.

- _recently_used_tools: re-attach tools executed in the last two real
  turns from persisted metadata.tool_events (16-tool cap, resolves
  mcp__ names, filters disabled_tools)
- _recently_viewed_skill_names: re-attach viewed skills' declared
  requires_toolsets, parsed from JSON / key=value / bare "view name=..."
  manage_skills events (control chars stripped; "view" inside a search
  query is deliberately not a view action)
- injected-context rows (metadata.trusted=False) don't consume the
  turn window — uploads/research context are not real turns
- classify cli / command line / cmd / console / powershell / zsh as the
  files domain so explicit shell requests seed bash deterministically
- add a shell entry to _KEYWORD_HINTS so the retrieval fallback paths
  (e.g. no-ChromaDB installs) never drop bash on "run this in the
  terminal"

Carried tools stay subject to every existing gate: owner, token,
plan-mode, and toggle disables are merged into disabled_tools before
the carry; the schema filter and runtime tool_policy / run_security
checks still deny execution, and the external-context gate is
unaffected (it is enforced at execution time, not selection).

Fixes odysseus-dev#4365
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown

⚠️ PR description is complete; validation evidence is still outstanding

Changed-file classification: backend/runtime.

Author-reported runtime / visual state

  • The author explicitly reports that app/runtime validation was not performed.

Checkboxes are author attestations. GitHub Actions results remain the execution evidence for CI; this check does not prove that a local command ran.


This comment updates automatically when the description or changed files change.

@github-actions github-actions Bot added needs work PR description incomplete — please update before review needs runtime validation Runtime validation not attested — tick the app-run box after running it, or state the gap and removed needs work PR description incomplete — please update before review labels Sep 11, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs runtime validation Runtime validation not attested — tick the app-run box after running it, or state the gap

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent drops core tools (bash/web/file) on low-signal follow-ups

1 participant