Skip to content

feat(core): reload agents from config change feed - #39167

Merged
kitlangton merged 1 commit into
v2from
agent-reload
Jul 27, 2026
Merged

kitlangton merged 1 commit into
v2from
agent-reload

Conversation

@kitlangton

Copy link
Copy Markdown
Contributor

What

Agent source invalidation via config.changes: editing, creating, renaming, or deleting Markdown files under a config root's {agent,agents} or legacy {mode,modes} directories now rebuilds agent state directly, without a config.updated event that descendant edits never trigger. Fifth slice of the config source reload series (#38983, #39131, #39157, #39160).

How

Same seam as the command plugin in #39160, with two refinements from review:

  • One merged trigger stream. Stream.merge(sourceChanges, configUpdates) feeds a single debounce and a single runForEach, so filesystem invalidations and structured config.updated reloads are serialized on one fiber — no duplicate load() scans on correlated bursts and no stale-scan overwrite of loaded.documents.
  • FSUtil.contains for path containment (Windows-robust, same helper Config's own reconcile uses), driven by one sourceDirectories constant colocated with legacySources instead of a second literal name list.

The predicate matches anything at or under <root>/{agent,agents,mode,modes} with no file-suffix check, so directory-level renames that carry no per-file paths still invalidate. The subscription forks before the initial scan.

New shared test helpers land in test/lib/clock.ts (advance, drain): both Stream.debounce (100ms) and State.reload (500ms batch) register TestClock sleeps from fiber hops a single TestClock.adjust flush can miss, so the helpers alternate real-macrotask settles with adjusts until the asserted condition holds.

Testing

Source-lifecycle matrix using the #39157 fixtures (Config.testLayer + Config.Test.emitChange):

  • created / updated / renamed / deleted, plus created in a legacy modes directory (asserts mode: "primary"), each verifying inside the agent.updated subscription so the event is asserted as a read barrier.
  • Debounce coalescing: three updates in one window produce exactly one rebuild; a later update produces a second.
  • Isolation: updates outside agent source directories (including a commands/ path) produce zero rebuilds, and the feed stays live afterwards.

Red first (all 7 fail without the fix), green with it; stable across 10 consecutive runs. bun test test/config/ passes except the pre-existing PluginSupervisor failure present on clean base; bun typecheck clean.

Follow-ups

  • Align the command plugin with the merged-stream + FSUtil.contains shape and migrate command.test.ts's inline clock helpers to test/lib/clock.ts (deferred from feat(core): reload commands from config change feed #39160 to avoid review churn).
  • Optional no-op rebuild suppression (deep-equality guard before reload, precedented by Config.reload) — needs its own red test, applies to both plugins.

@kitlangton
kitlangton merged commit 856c569 into v2 Jul 27, 2026
7 of 9 checks passed
@kitlangton
kitlangton deleted the agent-reload branch July 27, 2026 19:14
jinhuang712 pushed a commit to jinhuang712/opencode that referenced this pull request Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant