feat(agent-core): add Monitor tool for real-time per-line stdout streaming#987
feat(agent-core): add Monitor tool for real-time per-line stdout streaming#987Nitjsefnie wants to merge 9 commits into
Conversation
Co-Authored-By: Kimi K2.7 Code <noreply@kimi.com>
…lume cap Co-Authored-By: Kimi K2.7 Code <noreply@kimi.com>
…anager Co-Authored-By: Kimi K2.7 Code <noreply@kimi.com>
Co-Authored-By: Kimi K2.7 Code <noreply@kimi.com>
Co-Authored-By: Kimi K2.7 Code <noreply@kimi.com>
Co-Authored-By: Kimi K2.7 Code <noreply@kimi.com>
… param Widen BackgroundTaskNotification.source_kind to 'background_task' | 'monitor' so monitorNotify builds a properly-typed notification and fires the hook without an 'as unknown as' cast. Also drop the now-unused parameter on MonitorBackgroundTask.checkVolume. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agent-core and protocol are private (ignored) packages per .changeset/README.md; the changeset must declare the bump on the publishable CLI package that ships the feature. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: acc7571 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1e6cbfd1a8
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const taskId = this.background.registerMonitorTask(proc, command, args.description, { | ||
| detached: true, | ||
| timeoutMs, | ||
| }); |
There was a problem hiding this comment.
Clean up the spawned monitor when registration fails
When a monitor command is spawned successfully but registerMonitorTask() throws, for example because background.maxRunningTasks has already been reached, this path lets the exception escape without killing or disposing proc. That leaves a long-running command such as tail -F ... orphaned outside the background manager, unlike the Bash path which cleans up after registration failures.
Useful? React with 👍 / 👎.
| case 'monitor': | ||
| // Monitors are tool-spawned persistent background tasks. | ||
| return 'tool'; |
There was a problem hiding this comment.
Return monitor kind from the task adapter
For Monitor tasks exposed through the server task API, toProtocolTask() still maps the new agent-core monitor kind to tool, even though packages/protocol/src/task.ts now adds monitor as a valid wire kind. As a result, REST clients listing or fetching tasks cannot distinguish Monitor tasks from generic tool/question tasks and the newly added protocol literal is never emitted.
Useful? React with 👍 / 👎.
…emit 'monitor' wire kind Address Codex review on MoonshotAI#987: - MonitorTool now kills+disposes the spawned process if registerMonitorTask throws (e.g. maxRunningTasks reached), mirroring the Bash path, so a long-running command isn't orphaned outside the manager. - mapKind returns the first-class 'monitor' wire kind (added to the protocol enum) instead of collapsing to 'tool', so REST clients can distinguish Monitor tasks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks @chatgpt-codex-connector — both addressed in acc7571:
|
|
To use Codex here, create a Codex account and connect to github. |
Related Issue
Relates to #301 (which requested Monitor, Loop/Cron, and
/goal). That issue was closed as completed — Cron and/goalexist, and the close note pointed to the existing background-task //tasksflow for Monitor. But that flow is poll-based and notifies once on terminal state; the per-line streaming primitive #301 asked for ("启动后台进程持续监控日志/文件变化,实时推送到对话中" / "monitortail -f deploy.log | grep ERROR") was never built. This PR adds it. Happy to reopen #301 or file a fresh tracking issue if maintainers prefer.Problem
There is no way for the agent to watch a long-running command and react to its output as it happens.
Bash+run_in_backgroundplusTaskOutputis poll-based: the agent only learns about output when it explicitly checks, and the background subsystem only pushes a notification once, on terminal state. For tailing logs, watching a dev server, or polling job state, you want one notification per matching stdout line, in real time — Claude Code'sMonitortool. kimi-code had no equivalent.What changed
A new
monitorbackground-task kind that streams stdout incrementally and emits batched, volume-capped per-line notifications — reusing kimi-code's existing machinery rather than bolting on a parallel system:MonitorBackgroundTask(agent/background/monitor-task.ts) observes the process streams exactly likeProcessBackgroundTask(tees every chunk to the sink soTaskOutput/persistence keep working) and additionally runs a line buffer: split on\n, hold a trailing partial line until its newline, flush the partial when the task ends (so a crash mid-line orprintfwith no trailing newline isn't dropped), batch lines within a 200 ms window, and auto-stop with awarningif it emits > 200 lines / 5 s (sinceturn.steerisn't rate-limited).BackgroundManager.registerMonitorTask/monitorNotifyrender a<notification type="monitor_line">via the existingrenderNotificationXmland inject it withagent.turn.steer, reusing the same origin/hook path as terminal notifications. The terminal notification still fires on exit, so a crash/non-zero/timeout is always reported even if the filter matched nothing.MonitorTool(tools/background/monitor.ts) mirrorsBashTool(spawn, approval rule, non-interactive env, Windows path handling). Params:command,description,timeout_ms(default 5 min, ignored when persistent),persistent. Root/default profile only; goes through the normal approval + plan-mode policy. Stop via the existingTaskStop.monitorkind is wired through the closedBackgroundTaskInfounion, theprotocoldiscriminated-union schema + event types, and the exhaustivemapKindswitch — sotscenforces completeness.Why this approach fits kimi-code: it adds one task kind and one tool, reuses the worker/sink/notification/approval infrastructure verbatim, and the command self-filters (
grep --line-buffered) so the existing output path is the event stream. Design doc (architecture, data flow, risks): https://docs.nitjsefni.eu/#/d/kimi-code/monitor-tool-designTests:
monitor-task.test.ts(batching, partial-line hold, terminal flush of both a trailing partial and a single unterminated line, volume-cap auto-stop),monitor-manager.test.ts(per-linesteer, terminal notification still fires,TaskOutputreturns tee'd output),monitor-tool.test.ts(registration).pnpm typecheck,pnpm lint,pnpm testall green.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.🤖 Generated with Claude Code