Repository navigation
Conversation
… repeats in a step tail Refs #76. - Move the doom-loop detector from the processor into SessionTools.resolve. The tool table is rebuilt for every provider step, so the detector sees exactly one model response. The repeat that completes three equal blocks is not run and returns an error to the model, without a permission request; a doom_loop allow rule turns the refusal off. - Count, per run, how many steps in a row repeated the previous step's tool calls, and how many also got the same results. The count goes into a step tail: a user message appended at the end of each request and never stored. Cache breakpoints skip it, and the marker is removed before provider option keys are remapped. A step withdrawn for compaction before sending is not counted. - Rewrite the processor, CLI run and serve doom-loop tests for the refusal contract; add step tail unit and random tests, prompt-level regressions, tool-table refusal tests against the shared oracle, and cache placement tests. - Update the fix doc, mark the periodic doom-loop fix as superseded in its handling, and describe the new doom_loop behaviour in the English docs.
…he cache off the step tail Refs #76. Review findings after bfc07a0. - Refuse repeats on the step's final tool table. StructuredOutput was added after the guard, so identical StructuredOutput calls were never refused. - A step the provider rejects as too long never finished, so it no longer takes part in the repeat count; before, it counted as a step without tool calls and cleared the count. - With Anthropic automatic caching the server breakpoint lands on the last block, which is the step tail and changes every step, so nothing was read back. The last block before the tail now gets an explicit breakpoint with the same cache control, TTL included. - Add run-level random tests over step outcomes and over the final tool table, the three regressions, and an automatic-caching wire test; record the findings in the fix doc.
…unds and the stop hook Refs #75, #81, #73. - New subplan agentmgmt-2-run-state: AgentRun in session metadata, WakeSource, part metadata for agent messages, the experimental.session.stop hook (run end = loop exit and hook allows), the step tail's subagent section, report and stop-notice run attributes, agent_send wording, agent_status, and agent_list filters with a default limit of 50. Records the six decisions confirmed on 2026-10-23. - Step 0 expectations checklist for the subplan. - Architecture: five tools, G2 carried by agent_status, G7/G8, the new data structures, M3/M4/M5 ensures, tool schemas, correctness notes, gaps 12-14. - Detailed design: pointer notes on the touched functions and new sections for agent_status, the tail section and the stop gate. - Research §18: why §12's four-tool surface is reversed. - Fix doc: align woken_by, agent_send wording, run gaps and the doc list with the subplan.
Refs #75, #81, #73, #85, #90. - Run record keeps the current run plus the previous run's end; outcomes gain `lost`; re-arm is told from a new run by the work closure, not the record; leftover open records close as lost. - Step tail becomes a progress snapshot: clock and own position, counts, running list (oldest first) and finished-in-window list (newest first), ten rows each, window of ten minutes or since the previous step; the stored roster is retired with it. - Session.patch serialised per session (decision 1 over a separate table); the HTTP replace endpoint stays as a documented destructive path; the re-entrancy precondition was checked. - Stop hook: no timeout, last plugin wins, continuations per run, skipped at the step limit, permission-denied and length exits listed as not gated; termination argument restated honestly. - agent_status visibility through the Permission.disabled alias table; stop notice third branch; agent_send cases defined for every snapshot; no default step cap (three-way comparison in research §18.4). - Expectations rewritten: S-numbered steps, new fields and enums, INV-11 for the patch lock, performance budget per step.
Refs #75, #81, #73, #85, #90. - A continuation that finds its record already closed starts a new run, so the onExit read and the runner's read can never produce two reports for one number. - Session.patch lock acquisition dies after 30 s instead of hanging; H6c lists the five audited listeners and why none re-enters. - "last active" becomes "last step", taken from the in-memory status projection that every step already writes. - Runner.cancel returns the state it cancelled from; the stop notice's first branch requires the record to be closed as stopped. - The work sets busy before opening the run record; crashed children appear as cut-off rows regardless of the window; the view reuses Session.children; agent_status gains the running-but-closed form; continuations count per runLoop call; line and window edge cases fixed. - Residual roster and old-wording statements cleared from architecture, detailed design and the fix doc; audit-report.md records both rounds.
Refs #81. Subplan agentmgmt-2-run-state, commit 1. - New plugin hook `experimental.session.stop`, asked when the model gave a final answer with no tool calls, before the run settles and before any report reaches a parent. A block with a reason writes the reason as the next user message (synthetic, metadata kind `stop_hook`) and the same run continues. - Not asked for runs that end on an error, a refusal, a truncation, an interruption, structured output or the configured step limit. - `stop_hook_block_cap` (default 8) bounds consecutive blocks: at the cap the run ends without asking. A tool call resets the count, and so does any user message the hook did not write, whether a re-arm consumed it or it landed mid-run and the same loop read it (subplan §6, point 17). 0 never asks. A hook that throws, or blocks without a reason, allows the stop. Several plugins share the output; the last write wins. No timeout. - Tests: scripted plugin layer; reasons reach the model and the history; cap and a fresh attempt on the next run, zero cap, reset on tool call, reset on a message that lands mid-run, hook failure, ungated exits, step limit, last-writer; random runs under a random cap with tool steps, landed messages and streaks past the cap, checked against the definition, with no idle before an ask and a single report to the parent after the last one.
…ark agent messages Refs #75, #73 (server half). Subplan agentmgmt-2-run-state, commit 2. - `Session.patch` runs one at a time per session: it is read, merge, publish over the whole row, and concurrent patches used to write each other's stale fields back. Taking the permit is bounded (30 s, then a defect naming the session) so a listener patching the session it was told about cannot hang the process; `OPENCODE_SESSION_PATCH_LOCK_MS` shortens it for tests. - `SessionStatus.get`/`list` return the status with `at`, when it was set; the wire `Info` and the event are unchanged. - Run record `agentRun` in a subagent's session metadata: number, start, wake source, end and outcome of the current run plus the end of the one before. Opened by `SessionPrompt.loop` on the first execution of its work (a re-arm continues the open run; over a closed record it is the next run), closed in the same onExit that reports, from one read of the re-arm predicate. A leftover open record is closed as lost. Busy is set before the record opens. Pure functions in `agent-management/run.ts`. - Agent messages carry `{ kind: "agent_message", sender, sender_name?, sender_agent? }` in metadata, the TUI route's header `{ kind: "user_message" }`, and run-end reports gain the sender fields; the model-visible text is unchanged. - Tests: pure record functions; patch serialization under random concurrent writes (T9), the re-entry guard, lock duration; wake sources for all four rules; re-arm, stop, hook block, leftover, continuation window; random concurrent runs of several subagents checked against the definition (T3): mid-run messages, re-arms, continuation windows, an injected end-write failure (a wrapping Session layer), leftover records, busy before the record opens.
Refs #75. Subplan agentmgmt-2-run-state, commit 3. - Every step's tail now opens with the clock and where the session is (`Now: <ISO+tz>. You are in run 6, step 12.`; a main session has no run), and a parent gets a snapshot of its direct children: the counts, who is running (longest-running first, with the wake source, the last step and the previous run when it ended inside the window), who finished inside the window (newest first, with the outcome), and runs cut off by a crash whatever the window. Ten rows per list, the rest counted. The window is ten minutes or since the previous step's start, whichever is earlier. - Built each step from one query over the children and one copy of the status projection; never written to the history, so the request prefix the provider caches is untouched. Denying `agent_list` withholds it. - The roster that used to be written into the transcript at turn boundaries is retired; `AGENT_ROSTER_SENTINEL` stays so old parts are recognised. - Tests: the pure view (orders, caps, cut-off rows, headings, escaping), T7 random children and windows against the definition, the view in the parent's requests with the history identical across three steps and the repeat section after it, the deny gate, 600 children in one call under 50 ms, no roster written.
Refs #75. Subplan agentmgmt-2-run-state, commit 4. - A run-end report carries the run it closes: `<agent … run="5" ended="2026-10-20T03:41:18+08:00">`, and the summary the TUI shows ends in `(run 5, ended 03:41:18)`. Both come from the record closed in the same onExit; a missing or unclosed record claims nothing. - `Runner.cancel` and `SessionRunState.cancel` resolve with the state the cancel found. The stop notice uses it: `run: 5, stopped at …` when the target was running and its record now says stopped; `stop requested at …` when it was running but the record is open or ended otherwise; `had already ended at … (completed); nothing was running` when idle, or `was cut off … and will be recorded as lost` over a crash leftover. - `agent_send` reports what the message walked into, from a snapshot taken before the delivery: an idle target starts the next run (and how its last one ended, or that it was cut off), a running one is in run N or has no up-to-date record, a session that is nobody's subagent is just running or idle — then points at agent_status, then the accepted/one-way lines. - Tests: cancel's result for all four states; renderOutput attributes; the stop notice's five run lines; agent_send's wordings; the reports' run numbers checked in T3 (INV-6) and the predicted number against the next report through the real tool (INV-6b).
Refs #75. Subplan agentmgmt-2-run-state, commit 5. - New tool `agent_status(target)`: one neighbour in full — session_id, name, type, relation, status, its run (number, start and wake source while running; end and outcome when idle; cut off; none yet; or not updated yet), the run before, the last step while running, and the workdir. It reaches exactly what agent_list reaches and is refused where agent_list is denied; `Permission.disabled` aliases it to agent_list so the same deny hides both, and it is offered at every depth agent_list is. - `agent_list` takes `status`, `relation`, `query` (name or title, not the type) and `limit` (50 unless set); rows gain `run` and `last`; the order is yourself first, then the running, then the rest by latest activity; the last line counts the whole match (`showing 50 of 394 (10 running, 384 idle)`). The skeleton the tree builds carries the run record, so the rows cost no extra reads. - Tool descriptions state these facts; the skill doc and the agents page name the new tool and its permission alias. - Tests: agent_status's shapes, reach and gate; agent_list's columns, filters, order, cap and count; T4 random neighbourhoods and parameters against the definition; agent_status offered exactly when agent_list is, over permission shapes and depth (INV-9); the deny alias.
Subplan agentmgmt-2-run-state, Step 5 double verification. - T9's oracle now catches a lost update outright: no observed field (title, metadata.note, agent, model.id) may return to a value it already had, and every value written lands in some row. The old "at most one field changes between neighbours" let a stale touch revert the title unnoticed. - The exits the stop hook never sees are now covered for subagents: an error, a truncation and a rejected permission end the run unasked and the parent still hears; the step limit likewise. - Step markers S7, S17 and S24 added so the grep covers S1–S24. - Docs: audit-report §3.1 (paths A and B), decisions.md D1–D17, the expectations' Step 5 record and the wording the audit flagged (hook `agent`, schema invariants by construction, two view texts, agent_status failure texts, INV-11 and INV-13 restated, `ended` as a string, the run record's read outside the lock), the fix doc's parts 6–8 backfilled with the five commits.
…ubplan 2 Fixes from an external review of 9daaf4b and from the §5.2 five-dimension code review (audit-report §3.2, decisions D18–D40): - recordRunStart returns the run number it owns and recordRunEnd closes only that record; both read, decide and write under the session patch lock via the new Session.updateMetadata, so a cancel racing the next run can neither close the new record nor overwrite its open - a run whose open write failed owns nothing: no leftover is claimed and its report carries no run number; the next run closes the leftover as lost - the stop hook's continuation message keeps the turn's system text and tools - the subagent view's window floor moves only on steps that render the view - wake-source sender ids from part metadata are decoded, not trusted; source names, session ids and workdir paths are escaped in the view, agent_status and agent_list - agent_status checks the agent_list deny before resolving names and shows the last step for a running root session - escapeField moves to agent-management/schema (inbox re-exports it); the projection and the view share one status mapping; the record's open/closed test is the same everywhere - T3 gains start-write-failure and cancel-handoff modes, T7 hostile source names; new tests for ownership, locked metadata updates, continuation fields, gate order and workdir escaping - subplan, expectations, decisions and audit-report updated; the §5.2 checkbox now states that no per-commit self-review had been recorded
…or the response, stop notice names the run it hit Three P2 findings of the second external review of 05aba09 (audit-report §3.3, decisions D41–D46): - a re-arm continues only the record its previous execution owned: AgentRun.begin takes the owned number, so a run whose open write failed claims neither the leftover it was started over nor anything else when re-armed, and its report carries no run number - the subagent view's window floor moves only once a step's response is in: a step the overflow check withdrew before sending, or the provider rejected, no longer hides what ended during the step before it - agent_stop reads the run record before the cancel as well as after: the notice names the run that was open as the cancel went in, and says "run M has started since" when the next run opened during the stop instead of calling it the stopped one - T3 draws the late message and the write failure as independent dimensions (only impossible combinations excluded), so "leftover + failed open + re-arm" is generated; one deterministic test per finding, each failing before the fix
|
The following comment was made by an LLM, it may be inaccurate: |
This was referenced Nov 2, 2026
yixiao5428
added a commit
that referenced
this pull request
Nov 5, 2026
…e-74, refuseRepeats + projectMcpToolResult coexist - system.ts: issue-74 (#79) deleted prompt/codex.txt on dev (apply_patch artifact per its archaeology); our P1 had re-added the CODEX route on the upstream-re-added file. Drop the route + import; codex ids now route to PROMPT_GPT as dev's tests assert. String-returning provider() (B7 refactor) kept; dev's new routing tests adapted to it. - tools.ts: both sides inserted helpers after resolve — keep dev's refuseRepeats (#97) and our projectMcpToolResult side by side. - processor-side DoomLoop: dev's #97 removal wins (verified 0 refs). Co-Authored-By: Claude <noreply@anthropic.com>
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.
Issue for this PR
Closes #75, closes #76, closes #81.
Related to #73:包含消息来源 metadata,TUI 展示另行处理。
Type of change
What does this PR do?
让父 Agent 能区分子 Agent 的不同 run,并让插件在完成通知发出前决定是否允许停止;同时拦截单次响应内的周期重复工具调用,向模型展示跨步重复计数。
agent_status,为agent_list增加过滤、排序及默认 50 条上限。experimental.session.stop钩子。阻止停止时将理由作为后续输入,在同一 run 内继续;连续阻止上限可配置。doom_loop: allow,跨步重复仅计数。How did you verify your code works?
bun typecheck(packages/opencode)通过;推送钩子的 30 项 workspace typecheck 全部通过。修复与验证记录 · 完整子计划审查
CI 时间预算调整
后续提交
3eaa880741只调整测试:ACP 收包 15→30 秒并补 RPC 名诊断、transport 就绪等待 1→5 秒、活跃子 Agent 用例改为 5 秒上限与四次 2 秒工作。生产等待上限和运行逻辑不变。完整对照与验证
Screenshots / recordings
不适用,属于运行时、工具和插件接口改动。
Checklist