Conversation
…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
|
Changed-file classification: backend/runtime. Author-reported runtime / visual state
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. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_toolsetsonly 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 persistedmetadata.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 thefilesdomain, and_KEYWORD_HINTSgains a shell entry so the retrieval fallback paths (e.g. ChromaDB-less installs, cf. #4048) never dropbashon "run this in the terminal". Schema visibility only — every existing permission gate still applies (details below).Target branch
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
Checklist
_classify_agent_requestregion — rebase order matters) and feat(agent): add PowerShell command interface #6238 (PowerShell tool should join the files-domain seed once merged).devdocker compose uporuvicorn app:app) and verified the change works end-to-end. Type-checks and unit tests are not enough.How to Test
list the files here with ls. On the next turn saynow 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: [...].do it via the command line/run this in the CLI— bash is selected without needing the words "bash"/"terminal".requires_toolsets: [bash], ask the agent to follow it, then follow up withcontinue with step 2— the skill's shell access persists even though the follow-up matches nothing.disabled_tools); same for a non-admin user in multi-user mode and for API-token runs.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.pySafety notes for reviewers: carried tools are schema-visibility only. Owner/token/plan-mode/toggle disables and the external-context gate merge into
disabled_toolsbefore the carry runs, andrun_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 toknown_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.