Proposal: attribute recall and adoption to the agent session in every agent
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- typescript
- Domain
- analytics, cli, developer-experience, devtools, documentation
Research direction
Start with the adoption logic in src/transcript-parser.ts:303-340 and the existing hook handling around src/hook-handlers.ts:428-429, then inspect the bridge files src/opencode-hooks.ts, src/pi-hooks.ts, and src/omp-hooks.ts. Trace how session and run identifiers reach PostToolUse and how stats and the dashboard consume recall data. Done means the acceptance checks pass across the listed agents, privacy constraints hold, and the usage-guide documentation is updated.
Written by the indexing model from the issue text.
Description
Today. Recall feedback only half works, and only on Claude.
teamai recall → recalled_count++ per doc every agent, no session
adoption (upvotes) → Claude transcript parser ≈ 0 on the subagent path, none elsewhere (#883)
recall quality → joined by session id never joins (#883)
Proposal. Every recall knows its session, and adoption comes from the hook every agent already has.
teamai recall → local record {session, agent, docs} every agent
PostToolUse → sees the recall output and later reads every agent
adoption → a read of a recalled doc, same session every agent, subagents included
- Upvotes count again on Claude's recommended subagent path, and start counting on Codex, CodeBuddy, Copilot, Cursor, Pi, OpenCode and OMP.
statsand the dashboard can show recall and adoption per session.- The query is never stored, and nothing new goes to git.
This serves the roadmap in #647 (Team Context: "Improve Recall's recall and precision, and strengthen feedback mechanisms"). It fixes the second half of #883, whose first half is a small separate fix. It doesn't depend on OpenTelemetry. #877 can later export these records as-is.
Goal. For any supported agent, TeamAI knows which session ran each recall and whether that session then used what it got.
Terms. A recall run is one teamai recall process. Adoption is the same session opening a recalled doc after the run. A bridge is the plugin or extension TeamAI generates for agents without settings-file hooks (OpenCode, Pi, OMP).
flowchart LR
R["teamai recall<br/>stdout: … run=r_8f3a"] -->|"session env var"| L["recall log<br/>per scope"]
P["PostToolUse<br/>Bash: teamai recall"] -->|"session_id + run id"| L
P2["PostToolUse<br/>Read / cat of a recalled path"] -->|"same session, after the run"| A["adopted → upvote"]
L --> S["stats · dashboard · KB Health"]
L -.->|"later"| O["#877 export"]
Which session ran the recall. Two sources, so either can be missing:
at run time teamai recall reads the agent's session variable: agentSessionIdFromEnv() from #887
and prints a run id on the region's start line
at hook time the PostToolUse of that shell call carries session_id and the output;
the run id in the output joins them, and the hook's value wins
The hook wins because it can't be fooled by nesting: when Claude runs codex exec, the inner shell also sees CLAUDE_CODE_SESSION_ID, but only the inner agent's hook sees that command. The env value covers the cases with no hook call, such as a Codex command still running when its tool call returns.
A recall call counts wherever it runs, including inside the teamai-recall subagent: its PostToolUse carries the root session_id plus agent_id. agent_id only excludes adoption evidence (below), never the recall itself.
--- [teamai:recall:start] --- (3 results) run=r_8f3a
The recall log is local, per scope, bounded like the usage file:
{"ts":"2026-09-28T10:02:11Z","run":"r_8f3a","session":"5624be7c-…","agent":"claude","via":"hook",
"docs":[{"id":"redis-timeout","type":"learning","scope":"project","score":7.2}]}
No query, no prompt, no file content. votes keeps its per-doc counters. The team sees nothing new unless #877 exports it.
Adoption reuses the rule the transcript parser applies today (src/transcript-parser.ts:303-340, reviewed in #723), fed from PostToolUse instead:
evidence read-like tools: Read, Grep, Glob, Bash reader verbs on .md (cat, head, grep, …),
plus aliases: Cursor Shell, Copilot view; failed calls don't count
paths file_path / filePath / path / notebook_path, Bash operands, Grep/Glob result lines
match exact full path, or basename + ≥ 2 shared trailing segments; relative paths resolved
against the call's cwd; intersected with the session's recalled docs
window same session, after the run
excluded calls whose payload has agent_id (subagents, including the recall subagent itself)
The transcript parser stops deciding adoption. The opt-in judge (TEAMAI_UPVOTE_JUDGE) stays as it is.
Bridges forward what their hosts already give them. Today they send only cwd, tool_name and tool_input (src/opencode-hooks.ts, src/pi-hooks.ts, src/omp-hooks.ts), so their session id falls back to pid-<ppid>-<cwd>, which recall's pid-<ppid> never matches.
payload to teamai hook-dispatch
cwd, tool_name, tool_input
+ session_id OpenCode input.sessionID · Pi ctx.sessionManager.getSessionId() · OMP: see notes
+ tool output, is_error so PostToolUse sees the recall output and failed reads
OpenCode only
+ shell.env → TEAMAI_AGENT_SESSION_ID so the recall process knows its session
Evidence per agent.
| Agent | Session at run time | PostToolUse with input and output | Adoption |
|---|---|---|---|
| Claude Code | CLAUDE_CODE_SESSION_ID |
yes (seen live) | yes, subagent path included |
| Codex | CODEX_SESSION_ID |
yes (source); none for commands still running | yes |
| CodeBuddy, WorkBuddy | CODEBUDDY_SESSION_ID |
to check | yes if it does |
| Copilot CLI | COPILOT_AGENT_SESSION_ID |
yes, output in tool_result.text_result_for_llm (docs) |
yes |
| Cursor | CURSOR_CONVERSATION_ID |
yes, output in tool_output as a JSON string (docs) |
yes |
| Pi | PI_SESSION_ID |
after the bridge change | yes |
| OpenCode | after the bridge's shell.env |
after the bridge change | yes |
| OMP | none | after the bridge change | yes |
What I'd put in v1.
- The run id on the region's start line, and the per-scope recall log.
- Session from env at run time, confirmed or replaced by the
PostToolUseof the same call. - Adoption from
PostToolUsewith the rule above, replacing the transcript parser for adoption. - Bridges forwarding the session id, the tool output and
is_error; OpenCode'sshell.env. - Recall and adoption per session in
teamai statsand the dashboard. - Docs: the recall section of
docs/usage-guide.mdanddocs/usage-guide.zh-CN.md, including a per-agent adoption table and the per-agent notes that now say adoption is Claude-only (e.g. OpenCode,docs/usage-guide.md:1839); thecoreskill. The README support table has no recall column, so it doesn't change.
What could wait.
- Attributing each recall to a turn inside the session (the session's
prompt_submit→stopwindow does it once the session is known). - Exporting the recall log through #877.
- Adoption for tools without
PostToolUse(OpenClaw, Hermes, Kiro, JoyCode).
Acceptance check. Alice asks Claude Code a question, the teamai-recall subagent finds redis-timeout, and the main agent reads it. Her session upvotes it. Today the same session gets adoptedDocIds: []. Bob does the same in Codex, where cat learnings/redis-timeout.md counts. Carol's OpenCode session runs a recall and never opens the doc: the log has her session and the doc, and nothing is upvoted. Dan runs codex exec from inside Claude: the recall is attributed to the Codex session, not the Claude one. None of the log lines contain a query.
Design notes: proposed details and open questions
Proposed:
- Why not process ancestry. Walking up the parent processes to the agent's recorded pid breaks too often to be the key. On this machine a resumed Claude session kept running under a new pid while its recorded
monitorPidwas dead. Two Codex sessions share one app-server pid. No process start time is stored, so reused pids can't be told apart, and on Windows each lookup costs about 1.3 s of PowerShell. The run id makes the join exact without it. - Why not a transcript parser per agent. Codex rollouts, CodeBuddy's
index.json, Copilot'sevents.jsonland OpenCode's database each hold tool calls in a different shape, and Copilot's log is deliberately not read for privacy (src/dashboard-collector.ts:1234-1240).PostToolUsegives the same facts through one path that TeamAI already receives. - Phantom recalls. The parser trusts assistant text blocks (
src/transcript-parser.ts:228-241), so an agent that quotes therecalled-doc-idsmarker, for example while writing docs or an issue about recall, creates recalls that never ran. It happened in the session that wrote this proposal: the Stop hook asked it to confirma,bandredis-timeout, which were only examples in the text. A run id counts only when it appears in the output of ateamai recallcall seen byPostToolUse, so prose can't create one. - Nesting, seen live. In #887,
codex execstarted from a Claude Code shell recorded its recall under the Claude session, because the inner shell sees both agents' variables. Only Codex's own hook sees that command, so the hook's value fixes it. - Reading Claude's subagent transcript: considered, not pursued.
<session>/subagents/agent-*.jsonlholds the full recall region with itsFile:lines intoolUseResult.stdout, so reading it at Stop would restore adoption on Claude's subagent path alone. It covers one agent and adds parser code this proposal removes, so the fix for #883 part 2 is this proposal. - Run id placement. Older CLIs find the region by the
--- [teamai:recall:start] ---prefix (src/transcript-parser.ts:11), so text after(N results)doesn't break them. Therecalled-doc-idscomment has a strict pattern (src/transcript-parser.ts:429) and stays as it is. - Hook cost.
PostToolUsealready runs for every tool call, in the foreground, capped at 4.5 s by TeamAI and at 10 s by Cursor, Copilot and CodeBuddy. The new work is a string match on the output and a path match on the input, plus one append. - Subagents. Claude (docs) and Codex (source) give subagent tool calls the root session id, with
agent_idin the payload; Copilot passes the parent session id, and itsagent_idfield is to check. The recall subagent's own reads are excluded byagent_id, so the recall subagent doesn't adopt its own results. - OMP. It exports no session id. The spec checks whether its extension context has a session manager, as Pi's does. If not, the bridge and
recallboth usepid-<omp pid>without the cwd. They share that parent, which was checked with a probe. The docs then say that one omp process counts as one session,/newand subagents included. - Privacy. A recall query can contain user text, so it isn't logged. Tool output is read in the hook to find the run id and the recalled paths, and isn't stored.
- Older CLIs. A member on an older CLI keeps the transcript path and upvotes as today. Votes from both paths go through the same per-session ledger (
incrementUpvoted,src/hook-handlers.ts:428-429), so a doc isn't upvoted twice in one session.
Open questions:
- Should a Grep or Glob that only lists a recalled path count as adoption, as the parser does today, or only an actual read?
- Should adoption end when the session ends, or also after a number of turns?
- CodeBuddy's
PostToolUsepayload: does it carry the tool output? The spec checks this before the table is final.
Feedback from anyone who uses recall in more than one agent would help:
- Do the upvotes you see today match how often your agents actually use recalled docs?
- Is "opened in the same session" the right bar for adoption, or too loose?
- Is anything here more than a first version needs?
- Dominant language
- TypeScript
- Stars
- 5k
- Forks
- 376
- Avg merge
- 13h 21m
- Merged PRs (30d)
- 288
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Tencent/teamai-cli
-
bug help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
Tencent/teamai-cli#893 ·
Maintainers usually reply within 1 day
-
bug help wanted
Difficulty 4/5 3-5 days Newbie friendliness 65/100
Tencent/teamai-cli#894 · 1 comment ·
Maintainers usually reply within 1 day
-
bug help wanted
Difficulty 3/5 1-2 days Newbie friendliness 74/100
Tencent/teamai-cli#892 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 38/100
Tencent/teamai-cli#883 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 4/5 3-5 days Newbie friendliness 55/100
Tencent/teamai-cli#882 ·
Maintainers usually reply within 1 day
All issues in Tencent/teamai-cli
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
openedx/frontend-app-authoring#3274 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
area/documentation status/need-triage
Difficulty 1/5 Under an hour Newbie friendliness 95/100
google-gemini/gemini-cli#29548 ·
Maintainers usually reply within 1 day
-
sdk-typescript vector-store
Difficulty 2/5 Half a day Newbie friendliness 82/100
mem0ai/mem0#7495 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day