feat: add monitor events by Eslamasabry · Pull Request #52569 · anomalyco/opencode · GitHub
Skip to content

feat: add monitor events - #52569

Open
Eslamasabry wants to merge 7 commits into
anomalyco:devfrom
Eslamasabry:monitor-events
Open

Eslamasabry wants to merge 7 commits into
anomalyco:devfrom
Eslamasabry:monitor-events

Conversation

@Eslamasabry

@Eslamasabry Eslamasabry commented Oct 1, 2026 •

Copy link
Copy Markdown

Issue for this PR

Related to #49842 and the closed, unmerged Monitor proposal #33806. This builds on v2’s existing shell jobs and synthetic inbox. Scheduled wakes in #52103 remain separate.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Problem: Agents poll and sleep to track changing work, wasting tokens and turns. Background shell already reports completion, but cannot report incremental progress.

What this adds:

The agent can call monitor({ command, description, timeoutMs?, delivery? }), receive a task ID, and end its turn. Stdout lines become clearly labelled events in the same session and wake the agent automatically. Stderr only goes to the shell log. monitor_stop({ id }) cancels a watch.

The TUI’s existing background task list shows monitor descriptions, event counts, start/deadline times, and terminal status. It includes a command-palette entry, output access, and stop progress/error feedback.

Typed monitor.started, monitor.event, and monitor.ended events and session-scoped list, output, and stop endpoints support reconnecting clients. Output stays accessible after completion and server restart without requiring live shell state.

Design:

  • Tools are registered through the existing Tool.Service.transform path in packages/core/src/tool/plugin/monitor.ts, wired by plugin/internal.ts.
  • packages/core/src/monitor.ts owns server-side jobs through Job.Service. A KV record stores monitor/session/shell IDs, saved PID, description, delivery mode, start/deadline/end times, event and byte counters, status, reason, and exit code.
  • packages/core/src/shell.ts streams stdout separately while retaining the existing combined log. Complete lines are batched over a 200 ms window, then admitted through Session.synthetic and the durable inbox.
  • Existing SessionRunCoordinator wake/busy handling delivers default steer events at safe step boundaries without cancelling tools. delivery: "queue" waits until the turn ends. Idle sessions wake automatically.
  • Stop, deadline, and limit paths use the existing process handle and saved process group. Monitors force group termination, including descendants that ignore SIGTERM. No process-name matching is used.
  • packages/server/src/routes.ts reconciles running records before serving requests after restart. They become ended: server_restarted; no process is resumed and no saved PID is signalled.
  • Canonical schemas in packages/schema/src/monitor.ts join event-manifest.ts and flow through the existing bus/SSE and generated clients. The TUI extends its existing shell task list.

Safety:

  • Shell and monitor share permission preparation: the same session directory, command scanning, external-directory checks, and approval prompt before spawn.
  • Every synthetic event is labelled [background event from monitor '<description>' — not a user message]. The system prompt treats event contents as untrusted data, never authority to change the user’s request or permissions.
  • Lifetime defaults to five minutes, with a thirty-minute maximum.
  • At most four monitors can be active in a session.
  • Retain the 25 most recently ended monitors per session. Pruning removes both KV records and owned logs; clients apply the same history cap. Session deletion cancels active monitors and removes retained records and logs.
  • Monitor logs have independent ownership, so eviction from shell history does not delete them. Output pages are capped at 64 KiB, and the TUI stops monitors through the session-scoped stop endpoint while retaining output.
  • Limits are 16 KiB of stdout per batch, 256 KiB cumulative stdout, and 1 MiB retained log.
  • Monitors stop above thirty events in a rolling thirty-second window after the initial window.
  • Tool guidance covers line-buffered pipes, matching failure states, remote API polling no faster than every thirty seconds, and choosing ordinary background shell for a single completion notification.
  • Guidance includes examples for log errors, CI job progress, and local process exit.

Follow-ups out of scope: WebSocket sources and keeping live monitors running across a server restart.

How did you verify your code works?

Latest review follow-up: Fixed all three review findings: bounded terminal history and owned-log cleanup, cancellation on session deletion (including deletion during approval), and reopening completed output independently of shell cache state. Added session-scoped output and stop APIs and regenerated both clients and all OpenAPI snapshots.

Latest-head validation with Bun 1.4.2:

  • 56 focused tests passed: 19 monitor lifecycle tests, 13 batching/shell/permission tests, 6 client synchronization tests, 2 server API/restart tests, and 16 TUI composer/output tests.
  • bun run check passed: lint and all 36 typecheck tasks.
  • bun run lint:effect-simplifications passed.
  • Generated client and Protocol/website snapshots checked for consistency.
  • Website bun run build passed, including link validation.
  • git diff --check passed.

The complete test suite was not rerun for this follow-up. The full-suite results below describe the original submission.

Tests: Added coverage for line batching and UTF-8 boundaries, stderr exclusion, exit codes, process-group cleanup, deadlines, rate/size limits, busy queue/default steering, event labels, permission denial, startup cancellation, restart recovery, generated API access, client reconnect races, and narrow/wide TUI rendering.

Original submission validation with the repository-pinned Bun 1.4.2, without new dependencies or lockfile changes:

Command Result
bun install --frozen-lockfile Passed; 2,411 packages installed
bun run check Passed; lint and all 36 typecheck tasks
bun run lint:effect-simplifications Passed
TZ=UTC GITHUB_ACTIONS=false bun turbo test --concurrency=3 --continue=always Completed: 25/27 tasks passed; five existing sandbox-dependent tests failed
bun run check:generated in packages/client and services/www Passed
git diff origin/v2 --check Passed

Focused core monitor tests: 21 passed. Shell/process tests: 103 passed, 19 existing skips. Schema, protocol, server, client reconnect, and TUI rendering/stop tests also passed.

Batching, deadline, and rate tests use the existing Effect test clock. Subprocess tests use readiness handshakes and short deadlines.

Final package results:

  • Core: 5,602 passed, 34 skipped, 4 failed
  • CLI: 375 passed, 9 skipped, 1 failed
  • TUI: 1,437 passed, 2 skipped, 0 failed

Every other task passed. All Monitor tests passed.

Full-suite environment limitations:

  • The execution sandbox denies Unix-domain socket binding (EPERM), blocking four existing persistent-pty-daemon.test.ts tests.
  • Inner process IDs differ from /proc process IDs, blocking the existing ACP lifecycle test that enumerates children with pgrep -P.

No tests were skipped or modified to hide these restrictions. TZ=UTC matches CI and avoids an existing Date parity test’s timezone assumption.

OpenAPI snapshots were regenerated with the repository scripts. The generator also updates already-stale credential/pairing entries; no corresponding runtime behavior changed.

Screenshots / recordings

The TUI was checked at 100 and 48 columns, including running monitors, restart status, and stop-error feedback, in dark and light themes. Screenshot attachment is pending because the browser upload failed.

Illustrative demo—the agent writes and starts its own CI watcher:

Agent → monitor({command: "./watch-ci.sh 104", description: "CI 104"})
Tool  → Monitor started: mon_…
Agent → I'll watch the job results. [ends turn]

Wake 1 → [background event from monitor 'CI 104' — not a user message]: shard 1 success
Agent  → Shard 1 passed; I'll wait for the remaining results. [ends turn]

Wake 2 → [background event from monitor 'CI 104' — not a user message]: shard 2 failure
Agent  → I'll inspect shard 2's failure output.

Wake 3 → [background event from monitor 'CI 104' — not a user message]: run failure
         [background event from monitor 'CI 104' — not a user message]: exited with code 1, 3 events
Agent  → The run failed in shard 2. [reports the failure and next action]

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR
monitor-after-48(2)

Copilot AI balanced review requested due to automatic review settings October 1, 2026 18:56
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Hey! Your PR title Monitor events doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Active monitors outlive deleted sessions, retained records grow without bounds, and completed monitor output becomes inaccessible.

Review effort: Balanced
Findings: 3 Medium severity

Open (3)
What changed in this PR

Adds durable background monitors that stream shell stdout into sessions, expose lifecycle APIs/events, and appear in the TUI.

Changes:

  • Adds monitor execution, limits, recovery, permissions, and agent tools.
  • Exposes monitor schemas, events, API routes, and generated clients.
  • Adds TUI task status, output/stop controls, and extensive tests.
File Description
services/​www/​public/​openapi.json Updates published API snapshot.
services/​www/​openapi.json Updates website API snapshot.
packages/​tui/​test/​fixture/​tui-client.ts Mocks monitor listing.
packages/​tui/​test/​component/​dialog-shell-output.test.tsx Updates empty-state assertion.
packages/​tui/​test/​cli/​tui/​composer-keymap.test.tsx Tests monitor rendering, stopping, and reconnects.
packages/​tui/​src/​routes/​session/​index.tsx Adds background-task palette command.
packages/​tui/​src/​routes/​session/​composer/​shell-tab.tsx Displays and controls monitor tasks.
packages/​tui/​src/​config/​keybind.ts Renames shell-task keybind descriptions.
packages/​server/​test/​monitor.test.ts Tests listing and restart recovery.
packages/​server/​src/​routes.ts Registers and recovers monitor service.
packages/​server/​src/​handlers/​monitor.ts Implements monitor list handler.
packages/​server/​src/​handlers.ts Registers monitor handler.
packages/​schema/​test/​monitor.test.ts Tests monitor contracts and events.
packages/​schema/​src/​monitor.ts Defines monitor schemas and events.
packages/​schema/​src/​index.ts Exports monitor schema.
packages/​schema/​src/​event-manifest.ts Registers monitor events.
packages/​protocol/​test/​event.test.ts Verifies public event classification.
packages/​protocol/​src/​groups/​monitor.ts Defines monitor HTTP API group.
packages/​protocol/​src/​client.ts Maps monitor client namespace.
packages/​protocol/​src/​api.ts Adds monitor protocol group.
packages/​protocol/​openapi.json Updates protocol API snapshot.
packages/​core/​test/​tool-monitor.test.ts Tests monitor permission handling.
packages/​core/​test/​shell-stream.test.ts Tests streaming and process cleanup.
packages/​core/​test/​monitor.test.ts Tests monitor lifecycle and limits.
packages/​core/​test/​monitor-output.test.ts Tests output decoding and bounds.
packages/​core/​test/​location-layer.test.ts Verifies monitor tool registration.
packages/​core/​src/​tool/​shell-permission.ts Shares shell authorization preparation.
packages/​core/​src/​tool/​plugin/​shell.ts Uses shared permission preparation.
packages/​core/​src/​tool/​plugin/​monitor.ts Adds monitor agent tools.
packages/​core/​src/​shell.ts Adds stdout capture and forced stopping.
packages/​core/​src/​session/​runner/​prompt/​system.txt Marks monitor output as untrusted.
packages/​core/​src/​plugin/​internal.ts Registers monitor service and plugin.
packages/​core/​src/​monitor/​output.ts Implements bounded line batching.
packages/​core/​src/​monitor.ts Implements monitor persistence and lifecycle.
packages/​client/​test/​solid-monitor.test.ts Tests monitor cache synchronization.
packages/​client/​test/​contract-identity.test.ts Verifies schema export identity.
packages/​client/​src/​solid/​data.ts Adds reactive monitor state.
packages/​client/​src/​promise/​index.ts Exports promise monitor API type.
packages/​client/​src/​promise/​generated/​types.ts Adds generated monitor types.
packages/​client/​src/​promise/​generated/​client.ts Adds generated promise endpoint.
packages/​client/​src/​promise/​api.ts Exposes promise monitor API.
packages/​client/​src/​effect/​index.ts Exports Effect monitor contracts.
packages/​client/​src/​effect/​generated/​client.ts Adds generated Effect endpoint.
packages/​client/​src/​effect/​api/​api.ts Defines Effect monitor API.
packages/​client/​script/​build.ts Includes monitor types in generation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/core/src/monitor.ts Outdated
Comment thread packages/core/src/monitor.ts
Comment thread packages/tui/src/routes/session/composer/shell-tab.tsx
@Eslamasabry Eslamasabry changed the title Monitor events feat: add monitor events Oct 1, 2026
@Eslamasabry

Copy link
Copy Markdown
Author

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants