@excitedjs/dreamux 0.26.0-beta.271 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +393 -0
- package/CHANGELOG.md +71 -1
- package/package.json +7 -7
package/CHANGELOG.json
CHANGED
|
@@ -1,6 +1,399 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@excitedjs/dreamux",
|
|
3
3
|
"entries": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.26.0",
|
|
6
|
+
"tag": "@excitedjs/dreamux_v0.26.0",
|
|
7
|
+
"date": "Fri, 09 Oct 2026 03:05:18 GMT",
|
|
8
|
+
"comments": {
|
|
9
|
+
"minor": [
|
|
10
|
+
{
|
|
11
|
+
"comment": "The Dispatcher Agent has its own Commands. `dispatcher.submit` submits one turn to the addressed Dispatcher's own Agent and `dispatcher.interrupt` interrupts it; both address that Dispatcher through the caller context, like every other Command in the namespace, and both are reachable over `admin.sock` and a Channel's in-process port. `team.submit` and `team.interrupt` now require `team_name`: a Team Command names exactly one Team, and a call that omits the name is answered `BAD_REQUEST` by schema validation before any Agent is reached, instead of silently reaching the Dispatcher Agent. A caller that relied on the omission calls the Dispatcher Command instead. The two submit Commands share one reader for the Channel-facing payload (`service/channel-submission.ts`), which owns the attribute, text, reminder, and `source_id` rules once and fixes the `channel` provenance for both, and `DispatcherService.interrupt(teamId | null)` splits into `interruptAgent()` and `interruptTeamLeader(teamId)` to match."
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"comment": "Support ultrathink through native Codex effort parameters, restore ordinary effort on the next ordinary submission, and pass submission text through unchanged. Busy input follows native Codex timing."
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"comment": "BREAKING: `dispatchers[].cwd` is now required and must be a non-empty string on every Dispatcher entry, enabled or not; a config without it fails to load with an error naming the Dispatcher id. Rebuild: add \"cwd\": \"<absolute dir>\" to every dispatchers[] entry that lacks it."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"comment": "Refuse new Workflow TeamMate construction after its Team or Dispatcher starts closing, before allocating a name, workspace, identity, or launch hooks; preserve already-admitted construction cleanup and lock finalization."
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"comment": "Default omitted dispatcher workspace policies and new onboarding to false; preserve explicit values. Repository-free work uses the dispatcher cwd unless isolation is enabled."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"comment": "Load plugins from an optional top-level `plugins[]` in `config.json` (`builtin:<id>` or `npm:<package>[#export]` refs, each a string or `{ ref, config }`); existing configs load unchanged. Plugins can contribute providers addressed as `builtin:<name>`, append instructions and skill roots to Dispatcher and TeamLeader launches, and react to Team creation; a failing plugin hook is logged and skipped. `dreamux doctor` lists loaded plugins and plugin load failures. Ships the built-in, opt-in `builtin:bootstrap` plugin, which injects `.workspace/identity.md` and `user.md` from the Dispatcher cwd."
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"comment": "Redact tool call invocations and arguments in the conversation projection. Secret masking and workspace/home path renaming now apply to every displayed member of a tool call, and a call's structured payloads are redacted by walking the JSON value, so a secret written into a nested object or a multi-line .env is masked and the payload is still valid JSON. The redaction rules themselves moved to @excitedjs/dreamux-utils, where the host logger and config display now share one list of secret key names; the logger previously missed api_key, private_key, and client_secret."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"comment": "Code organization refactor stage 6d: scheduler/service.ts renamed to index.ts and scheduler/mcp-delegate.ts to mcp.ts (matching the agent/team/workflow-service per-domain template); a new requests.ts holds the request readers and result projections previously split across types.ts and store.ts, which is now persistence-only; the dead dispatcherId option/parameter was dropped from CronJobStore and detectLegacyCronJobStore; the fire path was consolidated onto one store read per fire plus a pure advanceJob/impure rearm pair (a one-shot job whose dispatch throws is now disabled immediately instead of staying enabled with a stale next_run_at until the next start()); and SchedulerService now implements SchedulerCommands directly, with TeamService composing its own closing fence directly into the admit closure it passes at construction instead of wrapping the public command surface in a second, Team-scoped object."
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"comment": "Codex and Claude Code now load exactly like the built-in Feishu plugin: both are always-loaded plugins that contribute their Agent Runtime provider, instead of a core-side static builtin-provider registry pre-registering their descriptors ahead of the provider loader importing their package. No behavior change for a default install: both still register under builtin:codex / builtin:claude-code before agents[] is resolved, and a config naming an unmapped builtin:<ref> still fails loud with the same UnknownBuiltinProviderPackageError it always has. `dreamux uninstall`'s directory-safety guard also no longer hard-codes ~/.codex and ~/.claude: it now builds the same always-loaded plugin registry and asks each registered Agent Runtime provider for its own operatorStateRoot, so a provider added or removed by future plugin work is protected (or not) automatically. The protected paths for a default install are unchanged."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"comment": "BREAKING: code organization refactor stage 8c, item 1: `DREAMUX_CONFIG_DIR` is deleted, and so is `dreamux onboard`'s `--config-dir` option; `dreamux uninstall --config-dir` is deleted too, by inference — it has nothing left to relocate once `config.json` is root-derived. `config.json` now always lives at `<DREAMUX_ROOT>/config.json` (default `~/.dreamux/config.json`, unchanged only for an install that set neither `DREAMUX_CONFIG_DIR` nor `DREAMUX_ROOT`); the managed-service unit now writes `DREAMUX_ROOT` instead of `DREAMUX_CONFIG_DIR`, taking the value from the environment of the process that runs `dreamux daemon install` or `dreamux onboard` (`~/.dreamux` when unset); `dreamux uninstall` now also removes the whole root directory itself, not just its contents. Before this change `DREAMUX_ROOT` relocated only state, run, cache and logs, `config.json` came from `DREAMUX_CONFIG_DIR` or `~/.dreamux`, and the managed-service unit that `dreamux daemon install` wrote carried no `DREAMUX_ROOT`, so a managed service ran with `.dreamux` under its `HOME` as its root unless its environment supplied a `DREAMUX_ROOT` by another route (a unit drop-in, an environment file, or the service manager's own environment). Scenario 1: an install that set `DREAMUX_CONFIG_DIR` to a path other than the root it ran with — after upgrade `DREAMUX_CONFIG_DIR` is silently ignored, so the daemon looks for `config.json` under the root instead of the operator's configured directory. If no `config.json` exists there, the daemon fails on an ordinary missing-file error that never mentions `DREAMUX_CONFIG_DIR` or what moved, and the unit's `Restart=on-failure` loops; if a `config.json` already exists there (for example from an earlier default-path `dreamux onboard`), the daemon starts silently against that unrelated file instead. Scenario 2: an operator who exports `DREAMUX_ROOT` in their shell (profile, direnv, a foreground `dreamux serve`) while `config.json` stayed in `DREAMUX_CONFIG_DIR` or `~/.dreamux` — `dreamux serve` and `dreamux daemon install` run from that shell now look for `config.json` under the exported root and fail on the same missing-file error (`dreamux doctor` reports it); and if the operator moves `config.json` there and re-runs `dreamux daemon install`, the shell's root is written into the unit, so the service moves to a root it never used: its durable state (dispatcher identities, Teams, Workflow runs, `access.json`) stays behind in the old root and the service comes up on a root that holds none of it. Rebuild: keep the root the old service actually used — do not assume it is the `DREAMUX_ROOT` your shell exports. (1) Find that root from the environment the old daemon actually ran with. For a managed service, read it from the running process (on Linux `tr '\\0' '\\n' < /proc/$(systemctl --user show -p MainPID --value dreamux)/environ | grep -E '^(DREAMUX_ROOT|HOME)='`) or, when it is not running, from everything that defines that environment: the installed unit and any drop-ins (`systemctl --user cat dreamux`), any `EnvironmentFile` they name, and the user manager's own environment (`systemctl --user show-environment`); on macOS, the plist `~/Library/LaunchAgents/dev.excited.dreamux.plist` and `launchctl print gui/$(id -u)/dev.excited.dreamux`. A unit written by an earlier build has no `DREAMUX_ROOT` entry of its own, so unless one of those sources supplies it the root is `.dreamux` under the service's `HOME` (normally `~/.dreamux`). A foreground `dreamux serve` used the `DREAMUX_ROOT` of the shell that started it, else `~/.dreamux`. Confirm the root by checking that `<root>/state/` holds your dispatcher directories. (2) Put `config.json` at `<that root>/config.json`: move it from the old service's `DREAMUX_CONFIG_DIR` path (found the same way) when that names another directory (keep mode 0600, create parent directories as needed); there is nothing to move when it already sits in that root. (3) Re-run `dreamux daemon install` (or `dreamux onboard` again) from a shell whose `DREAMUX_ROOT` is that same root — or unset, when the root is `~/.dreamux` — so the unit is rewritten with the right `DREAMUX_ROOT`, then run `dreamux daemon restart`. For a foreground `dreamux serve`, export the same root in the shell that starts it. Only a deliberate change of root moves durable state: with the service stopped, move the old root's `state/` to `<new root>/state/` (`run/` is volatile, `cache/` is rebuildable, and `logs/` is diagnostics, so none of them needs to move) and `config.json` to `<new root>/config.json`, then do step (3) with `DREAMUX_ROOT` set to the new root; without the `state/` move the service starts on empty state."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"comment": "PR #455 review round: `plugin/loader.ts`'s `constructPlugin` now validates a factory-returned plugin `name` against a safe single-segment pattern (1-64 ASCII letters/digits/dot/underscore/dash, starting with a letter or digit) and fails loading with `PluginLoadError` otherwise. `pluginStateDir` now uses `name` verbatim as the `state/plugins/<name>` directory segment instead of running it through the lossy teammate-name sanitizer, which mapped both `@acme/tool` and the unrelated `_acme_tool` to `state/plugins/_acme_tool` and passed `.`/`..` through unchanged (resolving up out of `state/plugins/`). Every built-in plugin name already conforms; a third-party `npm:` plugin whose factory returns a name outside this pattern now fails `dreamux serve` at load instead of silently sharing or escaping another plugin's state directory."
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"comment": "PR #455 review round, two scheduler fixes. `CronJobStore.parseCronJob` no longer fails loud on a leftover `deliver` field on an old cron-jobs.json job row: like the existing `dispatcher_id` tolerance, it is now an ordinary unknown field under the persisted-shape policy, ignored on load and dropped the next time that job is rewritten; `dreamux doctor` no longer names a store file for carrying it. Separately, a missed cron fire's rearm write no longer derives its `{ enabled, next_run_at }` write from the in-memory job snapshot `SchedulerService` captured before the fire; `CronJobStore.applyMissed` now derives that write from the row as it stands at write time, inside the store's own serialized update, so a `cron.update` committed while a fire's submission was in flight can no longer be silently overwritten by a stale missed-outcome write."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"comment": "Core no longer rejects an Agent Runtime activity record whose `occurredAt` timestamp string is longer than 64 characters. This was the last provider-output length cap left over from R28 (\"Core no longer re-validates an Agent Runtime provider's activity page against a byte/char/cursor-length budget\", refactor-code-org-s2a-04); the provider's own bounds are the only bound now."
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"comment": "A `plugins[]` object entry (`{ ref, config }`) now follows the same persisted-shape policy (R21) as the rest of config.json: an unrecognized key alongside `ref`/`config` is tolerated and ignored instead of refusing `dreamux serve`. A missing `ref` still fails loading."
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"comment": "`dreamux uninstall` follows the existing next behavior: dry-run checks existence and reports planned removal without enumerating root contents or writing files; actual uninstall recursively removes the entire DREAMUX_ROOT, including other files and directories inside it. The foreign-retention predictor is removed. Results report removed or missing; permission and other filesystem errors still propagate, including after a successful preview. Normalized removal paths inside or containing a provider-derived operator runtime home are refused before any service mutation. Physical symlink aliases are not resolved. Current root addressing and service removal order remain unchanged."
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"comment": "`workflow_status` and `workflow_list` no longer serve a finished run's record from a stale in-memory cache for the rest of the dispatcher's process lifetime. The scope's `WorkflowRunStore` now releases a run's cached store once its owning run settles (or once a crash-recovery pass finds it already terminal), and reads finished records one-shot from disk afterward, so a hand edit to a finished run's record.json is now reflected."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"comment": "A provider ref listed under the wrong kind in config.json (for example a `builtin:codex` agent-runtime provider referenced from a `channels[]` entry) now fails config validation with a `RuleViolation` naming the actual and expected kind, instead of the provider package loader's own contract error. This follows from built-in providers now registering their descriptor and implementation together as always-loaded plugins (R9): the mismatch is caught once, in config validation, for both built-in and `npm:` provider refs. No behavior change for a correctly configured install."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"comment": "Code organization refactor stage 2a, item 3: cron jobs are no longer capped at 128 per dispatcher/Team owner. The cap had no named failure scenario. Old cron stores are unaffected."
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"comment": "Code organization refactor stage 2a, item 4: Core no longer re-validates an Agent Runtime provider's activity page against a byte/char/cursor-length budget, or a provider's declared capabilities against a tag-count/length or publicConfig size bound. The provider's own bounds are the only bound now."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"comment": "Code organization refactor stage 2a, item 5: the `dispatcher.start` Command and the `dispatcher start` server-ctl CLI verb are removed. Every configured, enabled Dispatcher already starts automatically when the daemon starts; there was no supported way to stop one independently and restart it in-process to begin with."
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"comment": "Code organization refactor stage 2a, item 12 (R47): `dreamux serve` no longer runs its startup pre-flight that aggregated every dispatcher's pre-#233 leftover state and removed cron stores and aborted the whole process before the admin socket opened, and `dreamux doctor` no longer reports a dispatcher's leftover state as a row. A stale `channel-bindings.json`/`collaboration-spaces.json` at the dispatcher root, a pre-#233 flat `teammate/`/`team/` leaf (`records`, `turns`, `identities`, `history`, `sessions.jsonl`, `ledger`), or an agent identity record still carrying a removed field (`checkpoint`, `checkpoint_kind`, `session_ref`, `display_name`, `close_status`) are now silently ignored rather than triggering a fail-loud rebuild instruction. This is not `BREAKING:`: nothing an operator relied on to start stops working — the opposite failure mode (refusing to start) is what's removed. A cron store with an unrecognized version and an identity still keyed by the pre-#148 `provider_ref` format still raise a named error when actually read (a dispatcher's scheduler start, `dreamux doctor`'s unchanged cron rows), but that failure is no longer aggregated into a whole-process startup refusal — it now fails only the one dispatcher whose store or identity is affected, the same as any other per-dispatcher start failure."
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"comment": "Code organization refactor stage 4b item 1: moved AgentIdentityStore and AgentEntityCollectionStore onto TransactionalStore, deleted AgentRuntimeStateStore's own mutation tail in favor of the store's serialized update, and fixed four second-authority duplicate-reader sites (dispatcher-root identity shared between Dispatchers and DispatcherService, TeammateCollection.mustIdentity, reprepareDeletedManagedWorktree's nested-update pattern, and TeammateCollection.buildEntity minting a second, never-loaded AgentIdentityStore instead of reusing the one that created or read the entity). AgentIdentityStoreBinding no longer carries onPersisted; every create/update/upsert call now threads it explicitly. Named behavior change: a name lookup for a live TeamMate (mustIdentity) now answers from that TeamMate's own live identity instead of a fresh file read, so it can no longer race a runtime that just committed a status transition; record.json's shape is unchanged."
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"comment": "Code organization refactor stage 4b item 2: moved TeamStore onto a per-Team TransactionalStore, replacing its exclusive-write-plus-readback create path with a load-decide-write inside the store's own serialized queue and its manual write queue with the store's tail. TeamService now holds its Team's TransactionalStore handle directly instead of a caller-refreshed record field, and the Team read model answers leader_state from a live TeamService's own in-memory identity before falling back to a one-shot file read. record.json's shape, TeamStore's/TeamService's public methods, and Command/MCP-visible behavior are unchanged; only the anti-resurrection mechanism moved from 'merge against disk' to 'one in-memory owner per Team, merging against its committed value' (already an accepted operator ruling, 2026-09-19)."
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"comment": "Code organization refactor stage 4b item 3: moved CronJobStore onto a single TransactionalStore<CronJobFile>, inlining the version-mismatch/missing-file-default check that JsonDocumentStore used to run. Dropped the operator-only nested `action` Command field from scheduler.cron.create/update. Its former `action.prompt` was consumed as the scheduled submission text; the Commands now take the same text as top-level `prompt`, while persisted jobs keep their internal `action.prompt` and stopped writing `dispatcher_id` in scheduler.cron.list/create/update/delete results, the cron_* MCP tool results, and newly written cron-jobs.json job rows; a leftover dispatcher_id on an old file now loads as an ordinary unknown field under the persisted-shape policy (tolerate unknown fields, reject only wrong types and missing fields) instead of being required and cross-checked against the store's own dispatcher id. Old cron-jobs.json files carrying either field still load. Named behavior change: deleting a Team's cron store file on dissolve now loads the file first (TransactionalStore.remove()'s own contract), so a cron-jobs.json that fails its version/shape check at that exact moment now fails the delete too, instead of always succeeding via a direct unlink(); this is one more of dissolve's ordinary collected cleanup-step failures, handled the same way as any other resource that would not close."
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"comment": "Code organization refactor stage 4b item 4: moved WorkflowRunStore onto a per-run TransactionalStore<WorkflowRunRecord | null>, inlining the version-mismatch/missing-file-default check that JsonDocumentStore used to run, and moved WorkflowJournal.create onto dreamux-utils' publishFileExclusive. run.ts keeps mutating its working draft before each persistence point; the queue that used to serialize those writes is gone (each run's own TransactionalStore now serializes them), but the reclassification of a failed journal/store write into WorkflowPersistenceError (which executeAgent's catch depends on to tell a storage failure apart from an ordinary agent failure) is preserved under a renamed helper. Named behavior change: WorkflowRun.snapshot(), which backs workflow_status/workflow_list for a live run, now returns the run's last committed (written) value instead of the in-progress in-memory draft between a mutation and the write that persists it. Freshness here was already documented as unspecified, so this narrows rather than changes the contract, but it is a real, observable difference from today's snapshot timing. record.json's shape, the journal's shape, and every public method signature are unchanged. `this.record.x = …` in-place mutation and `AgentCall.record` as a live alias into `this.record.agents[]` were both kept as they were, rather than becoming a pure builder passed to `store.update()` and an index respectively; traced for data loss (each write clones at commit time and every store now serializes on its own `TransactionalStore` queue) and found none."
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"comment": "Code organization refactor stage 4b item 5: config.json now tolerates unknown top-level, dispatchers[], channels[], agents[], and dispatchers[].workspace keys instead of rejecting them; wrong types and missing required fields are still rejected. Per R21's tolerate-unknown-fields policy, this also removes every remaining named rejection of a retired config.json key: the top-level codex block, dispatchers[].runtime, the dispatchers[].feishu/.codex legacy-shape pre-check, and channels[].collaborationSpace are no longer special-cased and now load as ordinary tolerated unknown fields like any other retired key."
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"comment": "Code organization refactor stage 4c: added two Commands, config.agents.get and config.agents.replace, reachable through admin.sock and any Channel's in-process invoke port like every other Command. config.agents.get returns the current agents[] in file shape with every secret-named value emptied; config.agents.replace validates and writes a whole new agents[] array, matched to the existing one by id (submitting an empty secret-named value keeps the stored value). A replace takes effect for the next runtime launch only — a runtime already running keeps what it launched with. The first replace re-serializes config.json with two-space indentation and the submitted agent order; other values, and dispatchers[], are written unchanged. Neither Command is dispatcher-scoped, and dispatchers[] still has no Command. No persisted-format change."
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"comment": "team.history's grep filter now ignores leading/trailing whitespace in the search term, matching teammate.history's existing behavior."
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"comment": "Code organization refactor stage 6a item 2a: renamed the class service/agent/service.ts's TeammateService -> AgentService and service/agent/runtime-generation.ts's TeammateRuntimeOwner -> RuntimeGeneration (propagated to every consumer's type-only imports across the package, e.g. dispatcher-service/ and team-service/; service/agent/factory.ts is left on the old name, since converting it into the per-dispatcher AgentServiceFactory is stage 6a item 2b). This one class now serves three roles (the Dispatcher's own Agent, a Team's leader, and an ordinary TeamMate) but hardcoded 'TeamMate' in every thrown-error message regardless of which role it was closing, locking, or starting; a Team's leader.close() (dissolve) or a Dispatcher agent's runtime-generation failure produced a wrong 'TeamMate ...' message. Added agentRoleNoun(role, name) (service/agent/service-types.ts, to avoid a service.ts <-> runtime-generation.ts import cycle) and used it at every such error message in these two files, so a Dispatcher agent now reads 'the Dispatcher agent ...' and a Team's leader reads 'Team leader \"<name>\" ...'. Most of an ordinary TeamMate's own wording is unchanged (byte-identical, same JSON.stringify(name) quoting); four messages that previously named 'TeamMate' without the entity's name now include it for every role, including teammate, as a byproduct of routing through the one shared helper: the close-note validation message (this is also a real fix, not just wording -- close() runs on a Team's leader too on dissolve, per team-service/closing.ts, so the pre-existing text was already wrong there), the two closed-with-no-closed_at invariant-violation messages, and the stale-lock-token invariant-violation message. Also deleted two dead-by-construction fields: ownsWorktreeOnClose (its condition, restricted to the three real construction sites, was already exactly implied by identity.worktree.mode === 'managed' && identity.worktree.cleanup === 'delete-on-close' alone -- the Dispatcher agent and every Team leader are always worktree.mode 'reuse-cwd', and a Team-scoped TeamMate's spawn path always forces a reuse-cwd shared workspace, so the flag never disagreed with the identity's own facts since it was introduced) and assertIdentityScope plus runtime-id.ts's three assert*Scope helper functions (the identity was already read by a store scoped to the correct directory by the owner that materialized it, so re-checking scope on every ensureStarted() call had no failure scenario construction-time scoping doesn't already rule out). No persisted format, public API, or MCP/Command contract changed."
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"comment": "Code organization refactor stage 6a item 3: the completion notice the Dispatcher's own agent receives after one of its Team's leaders settles a team.create-with-prompt turn (R16, deliverCompletionToDispatcher) always read 'TeamMate <name> has finished its task,' misdescribing a Team leader as a TeamMate. Added role: TeammateRole to TeammateCompletionFact (service/completion-router/index.ts) and threaded the producing AgentService's own role (fixed for its lifetime, passed once into EntityTurnCoordinatorOptions at construction) through service/agent/turn.ts's fact construction. service/agent/completion-renderer.ts's teammateStatusLine now words the sentence by the producer's actual role: a Team leader completion reads 'Team leader <name> has finished its task.' (and failed/stopped variants); an ordinary TeamMate completion, the only other role a delivered completion is produced by today, is unchanged. The Dispatcher-role branch is exhaustive but currently unreached -- the Dispatcher's own agent never attaches a completion-delivery closure to its own turns today, so 'The Dispatcher agent has finished its task.' is not yet observable, but is now correctly worded for when it is. workflowStatusLine is untouched. No persisted format, public API, or MCP/Command contract changed."
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"comment": "Code organization refactor stage 6a item 4: deleted TeammateRuntimeOwnerCallbacks, the function-valued dependency bag RuntimeGeneration used to reach back into AgentService's private phase field via isActive()/markClosing(). RuntimeGeneration.ensureStarted() now takes no phase callback and simply resolves once the runtime is live or throws; it no longer self-aborts mid-launch. AgentService now owns the race check itself through one new private method, ensureRuntimeStarted(), used by every one of its three callers of runtimeGeneration.ensureStarted() (admitToRuntime, activate(), the locked Workflow submit path): it checks this.phase itself before calling ensureStarted() (replacing the old pre-lease isActive() check) and again immediately after ensureStarted() resolves, stopping the runtime and reporting not-active itself when a close raced and won the awaited start (replacing the old post-start isActive() self-abort). The one case that used to call markClosing() — startFromRecord's rollback branch where the rollback's own runtime.stop() also fails after a native start failure — now has RuntimeGeneration throw a new named error, RuntimeTerminationUnproven (an AggregateError subclass carrying the same start/stop failure pair), which ensureRuntimeStarted() catches to set AgentService's own phase to 'closing' before rethrowing; this is the mutation every caller previously got for free via the callback, so centralizing it is what keeps a runtime whose termination could not be proven from being handed out by a later ensureStarted() call, which treats any non-null runtime handle as already live and never re-examines it. Internal control-flow refactor moving which class owns a phase decision. One caller-visible admission-outcome shape changed for the narrow start-races-close window: a submission that used to reject with an 'is closing' Error now resolves as an ordinary {status:'stopped'} TurnAdmission (the same shape a submission to an already-stopped recipient always gets), so its display reason becomes 'the agent runtime is not running' instead of the old message text. The issue #63 non-blocking submit ordering (runtime.submit fires synchronously before being handed to the admission continuation queue) is untouched."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"comment": "Code organization refactor stage 6b item 2 (R48): deleted the team.hooks.created plugin hook end-to-end, together with its background scheduling (fireCreated), its InFlightWork-backed in-flight tracking, and the Dispatcher shutdown drain that awaited it. A plugin that tapped this hook loses it; nothing in this tree taps it today, and Dispatcher shutdown no longer waits on it."
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"comment": "Fixed a bug: team.create with a prompt, called through admin.sock or a Channel's team.create Command, no longer pushes the leader's first-turn completion back to the Dispatcher Agent -- only the Dispatcher's own team MCP create tool call does now, matching the deliverCompletionToDispatcher contract team.submit has always had. DispatcherService.createTeam, TeamCollection.createFromRequest/createAtCandidate, and TeamService.createNew all take an explicit deliverCompletionToDispatcher boolean threaded the same way teamId/payloadHash already thread through; service/team/commands.ts's create.execute() passes false, service/team/mcp.ts's create() (the Dispatcher's own MCP call) passes true. initial-prompt.ts's submitInitialLeaderPrompt is folded into TeamService.createNew, which now calls submitToLeader(...) directly and checks the returned TurnAdmission itself, so the initial turn runs through the same admit-fenced submission path (and the same completion-initiator wiring) as every later turn; resolveTeamLeaderCompletionDelivery (completion-targets.ts) is deleted as the second initiator-to-delivery mapping it duplicated. TeamLeaderCompletionTargets itself, used for a Team's own members' completions reporting to the leader, is unrelated and unchanged."
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"comment": "Code organization refactor stage 6b item 6 (R10): rewrote TeamClosing's dissolve/close sequence. TeamClosingDeps drops the leader()/closeLeaderForDissolve() callback pair; dissolve(input, leader), abandonCreation(input, leader), and stopForHost(leader) now all take the TeamLeader as a plain AgentService | null value, read once by TeamService (its own leader_ field) at each call site instead of asked for through a callback. stopChildRuntimesForDissolve and the child-stopping half of stopRuntimesForDissolve are unified as stopChildrenForDissolve() and stopAllForDissolve(leader), both still built on the one shared, non-throwing stopChildRuntimes(failures) helper so a member-stop failure and a leader-stop failure still aggregate into one thrown error exactly as today; closeResources (now closeChildren) drops its own redundant repeat of workflows.closeAdmission()/stopAll() and scheduler.stop() -- both were already covered, idempotently, by stopAllForDissolve immediately before it, so the repeat inside closeResources answered nothing new. abandonCreation's adoptDurableLeader callback is removed: TeamService.createNew's own catch adopts an orphaned durable leader itself, before calling abandonCreation, and passes the result as the leader argument; a failure during that adoption attempt is now logged rather than folded into the thrown AggregateError alongside the original creation-failure cause. The number, order, and gating of every worktree assessment, the dissolve refusal contract, the dissolve receipt shape, and team.closed timing are unchanged. TeamService forgets this.leader_ (checked once, after closing.dissolve() settles) whenever the new AgentService.isSafeToForget() is true, not only once AgentService.isRetired() confirms the close's own durable write also landed: a leader whose close() fully released runtime authority (stopRuntime() succeeded) but whose identity write then failed is still forgotten and rebuilt fresh from disk on next use, matching the pre-refactor unconditional pre-close null. isSafeToForget() stays false for the different case of an unproven runtime termination (a failed start whose own rollback stop could not prove the native runtime dead), because that runtime might still be alive and must not be silently forgotten. A leader whose close was never reached at all (an earlier stop or assessment refused first) is left live and reusable exactly as before."
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"comment": "workflow.run and the workflow_run MCP tool now report BAD_REQUEST instead of INTERNAL for an empty resolved script, a scriptPath that is not a regular file, or a scriptPath over the read-size cap -- each is the caller's own mistake, not a server failure. Missing script/scriptPath and an out-of-range max_concurrency already reported BAD_REQUEST and are unaffected."
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"comment": "dispatcher.submit and team.submit now report SUBMIT_FAILED / SUBMIT_AMBIGUOUS instead of TEAM_SUBMIT_FAILED / TEAM_SUBMIT_AMBIGUOUS for a failed or ambiguous submission. The shared submit receipt covers both callers, and a Dispatcher's own Agent submission has no Team involved, so the wire code no longer names Team."
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"comment": "A dispatcher's own admission gate and its workflow-run admission check now report SERVER_SHUTTING_DOWN instead of INTERNAL when they refuse a request, matching the code the process-wide shutdown fence and a dispatcher's Channel port already reported for their own closed-scope refusals."
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"comment": "ChannelService.closeAll() now collects and reports every channel session's close failure instead of logging and swallowing it. A Dispatcher's own close already wraps this call in its aggregate shutdown-failure collector, so a channel provider's close() throwing during a dispatcher stop or the daemon's graceful shutdown now surfaces as a shutdown failure where it was previously silent."
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"comment": "dispatcher.status's status field and each dispatcher.list entry's status field now always report the runtime vocabulary (declared/starting/ready/degraded/stopping/stopped). Previously dispatcher.list reported the identity vocabulary (starting/running/degraded/closed/stopped) for both a live and a cold dispatcher, and dispatcher.status mixed the two: the runtime vocabulary while live, the identity vocabulary while cold. A caller branching on 'running' or 'closed' needs to branch on 'ready' or 'stopped' instead. Two cases that previously reported 'stopped' now report 'declared': a dispatcher with no persisted identity yet, and a materialized dispatcher whose agent is built but has never activated its runtime in this process (previously this fell back to the persisted identity's status, which could be stale after a crash)."
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"comment": "team.status already refused during a dispatcher stop; team.history and team.list now refuse the same way (SERVER_SHUTTING_DOWN) instead of answering from a Team's stale records. All three are per-Team operational reads and are now gated uniformly; dispatcher.list, dispatcher.status, and channel.list are unaffected and remain observable through a stop."
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"comment": "TeammateCollection now takes its admission fence at construction and gates every TeammateOps verb (spawn/send/close/list/status/history/last/getCapabilities) internally, uniformly, instead of wrapping only the three mutating verbs at the dispatcher-service boundary. A dispatcher-root teammate.list/.status/.history/.last call now refuses with SERVER_SHUTTING_DOWN during a Dispatcher stop instead of answering from stale records; a Team-scoped one now refuses with TEAM_CLOSED during that Team's dissolve, and with SERVER_SHUTTING_DOWN during a Dispatcher stop, instead of always answering. dispatcher-service/teammate-ops.ts is deleted; DispatcherService.teammates returns the collection directly."
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"comment": "The `dreamux dispatcher start` CLI verb is removed (R11: no in-process restart after a dispatcher has closed). It never reached a working code path (no `'start'` case existed on the server-side dispatcher-control method resolver), but its removal is a CLI surface change an operator's script could still reference. `dreamux dispatcher status`/`list` are unaffected."
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"comment": "A dispatcher's shutdown stops Workflows, each Team, each materialized TeamMate runtime, and the dispatcher's own Agent runtime, joins every already-admitted operation, then repeats that same runtime sweep once more before closing Channels last. The sweep runs before the admitted-work join as well as after: an admitted call can itself be starting a runtime with no timeout of its own, so joining first would wait on exactly the runtime the sweep exists to stop, and stopping it first is what makes that runtime's own teardown unblock the join. The second, post-join pass catches a pre-fence admission that only starts - or, since a runtime stop fences an entity's admission for its own convergence only and never durably closes it, revives - a runtime after the first pass already read past that entity. Separately, TeammateCollection's and TeamCollection's own entity-construction paths check the dispatcher's closing fact the instant they register a brand-new entity and self-close its runtime immediately, preempting a spawned TeamMate's first submission outright (a Team's leader can already have taken its first prompt by the time the collection notices, so it only stops that runtime as soon as it can) - a different race, from a new entity's own construction, than the two-pass sweep covers."
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"comment": "Code organization refactor stage 6f, item 2 (R48): wired the new Dispatcher.hooks.teammateLaunch hook through TeammateCollection.buildEntity (the one construction path behind spawn, createLocked, and reopen), the dispatcher-root and Team-scoped TeammateCollections, and the TeamMate system prompt, so a plugin can now append instructions and skill sources to every ordinary TeamMate's launch the same way it already can for the Dispatcher and a TeamLeader."
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"comment": "Code organization refactor stage 6f, item 3 (R48): wired the new Dispatcher.hooks.createTeam hook into TeamCollection.createFromRequest, which now runs it on a non-replay team.create request's own wire-shaped params before doing the repo/skill-source translation admin.sock and the Team MCP delegate used to each do for themselves; both callers now build one TeamCreateCommand and forward it unchanged."
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"comment": "Code organization refactor stage 6f, item 4 (R50): added platform/paths.ts's pluginStateDir(name), a host-owned per-plugin durable state directory under state/plugins/<name>/, and wired it into every plugin's server(host) call as ServerHost.stateDir. Purely additive; no existing path moves or changes shape."
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"comment": "Code organization refactor stage 6f, item 1 (R52): renamed the Dispatcher hook object's beforeLaunch key to launch and the Team hook object's beforeTeamLeaderLaunch key to leaderLaunch, following the matching dreamux-types rename. Internal wiring (DispatcherAgentOptions, TeamLeaderForTeamDeps, tapable debug-name labels) renamed to match; no ownership or firing-site change."
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"comment": "Code organization refactor stage 7, item 9: `dreamux serve` now runs `cli/server.ts`'s `runServe()` in-process instead of spawning `dist/cli/server.js` as a child process, and `dreamux status` / `dreamux dispatcher list` / `dreamux dispatcher status` now call the admin socket in-process through `admin/client.ts`'s `adminJsonInvoker` instead of spawning the deleted `server-ctl` entry; `cli/server-ctl.ts` is removed. Two small, verified output differences on failure paths: (1) when the admin socket is absent or refuses the connection, the status/dispatcher commands now report `TRANSPORT_ERROR` carrying Node's own socket error (e.g. `dreamux: admin socket connection to <path> failed - connect ENOENT <path>`) instead of the old bespoke `dreamux: cannot reach admin socket at <path> — is the server running?` sentence, matching the TRANSPORT_ERROR behavior the 0.24.0 CHANGELOG already documents for every other admin caller; a Command-level failure (the server ran and refused the request) still prints the unchanged `error: [code] message` line, and the exit code (1) is unchanged in both cases. These two commands are also now subject to the shared 10-second admin-socket request timeout instead of waiting indefinitely. (2) A `dreamux serve` startup failure now prints `dreamux: <message>` through the CLI's own top-level error handler instead of the old child process's `[server] fatal: <error with stack>` line, no stack included; verified for a missing/invalid config.json (rejects with `Error`/`RuleViolation`) and a failing plugin `server` hook (wrapped as `PluginLoadError`, also an `Error`), both of which keep exit code 1 unchanged, matching every other Dreamux failure's `Error`-subclass contract. `dreamux serve --help`'s old usage text (config path, `DREAMUX_CONFIG_DIR`, run/state/logs roots, plugin ref grammar) is dropped rather than carried into the new implementation: it was already unreachable through the `dreamux` CLI, which answers `--help` before ever invoking a serve handler, and that content already lives in `.agents/domains/state-config-and-files.md` and the `dreamux-maintenance` skill's `config-envelope.md` reference."
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"comment": "Code organization refactor stage 7, item 7 (R14): the teammate.spawn and teammate.submit Commands now reject an empty prompt, a blank agent_runtime, or a blank intent with BAD_REQUEST, matching what the equivalent TeamMate MCP tools already reject. The MCP tools' own behavior is unchanged."
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"comment": "Code organization refactor stage 7, item 5 (R20): the teammate.spawn and team.create Commands no longer accept repo.slug (already true of the equivalent MCP tools since 0.25.0). The worktree directory and default branch already derive from the allocated entity name; existing worktrees and persisted state are unchanged."
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"comment": "Move identity notifications, completion recipients, admission, role tools and command addressing to their actual owners. Remove dormant loader import, chmod, executable-directory probe, name/id, clock and Workflow runner construction overrides. Preserve real CLI logger/sweep extensions, channel-log creation timing and leader-tool refusal order; persisted formats and config require no migration."
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"comment": "Plugin hook review fixes: a plugin's own interceptor added with `hook.intercept` — on `call`, `tap`, `loop`, `result`, `done`, `error`, or `register` — is guarded the same way a tap is, attributed to the plugin that registered it: on `dispatcher`, `team`, and a launch hook a throw is logged and skipped, and on `hooks.plugin.for(name)` a throw fails plugin loading with a clear error, so neither crashes `dreamux serve` or `dreamux doctor` — including a `done`/`error` interceptor on an async hook, which tapable invokes from a continuation an ordinary try/catch around the hook cannot see. Interceptor methods must be synchronous: an `async` one's rejection is logged at runtime and fails plugin loading on `hooks.plugin.for(name)` with an error naming the interceptor, and an `async` `register` leaves the tap unchanged. A launch-hook interceptor sees only an opaque, disconnected handle, never the internal launch draft, so it cannot add a skill root that skips the skill fence. An async plugin tap on the sync `dispatcher`/`team` hooks is logged and skipped, with no unhandled rejection risk. An async `server()` or `contribute()` fails loading with a clear error naming the plugin, so a tap registered after its first `await` is never silently lost. A `plugins[]` entry's `config` block for a plugin with no `config.read` is ignored, so `dreamux serve` starts."
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"comment": "Revert #408 and restore the previous workspace and Team creation behavior."
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"comment": "Project RuntimeActivity without reshaping, redacting text and structured JSON payloads while retaining native ids, timestamps, result and error separately. Publish and validate the four Core events with camelCase members and remove unused redacted flags."
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"comment": "Project the new token.usage activity through the conversation projection as numeric counters with nothing to redact."
|
|
174
|
+
}
|
|
175
|
+
],
|
|
176
|
+
"none": [
|
|
177
|
+
{
|
|
178
|
+
"comment": "Document in the maintenance reference that a Feishu chat carries either a whole-chat binding or a collaboration space, never both, and which tool refuses each side."
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"comment": "Remove the repo-root bin/dreamux source-checkout shim; the package launcher at packages/dreamux/bin/dreamux is the only one. No runtime change: the shim was never published."
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"comment": "Document creation-time Feishu reply guidance in the maintenance reference and cover identity persistence, restore, and provider prompt composition without changing Core runtime or stored formats."
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"comment": "Document the Feishu document-comment subscriptions in the maintenance reference: the routing document's third section, the three tools that own it, and what happens to a comment nobody subscribed to."
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"comment": "Document the document comment's anchor attributes in the channel knowledge page, and pin the transport's new exported anchor type in the package boundary guard."
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"comment": "Update inbound event test fixtures and the transport export inventory for the one-body inbound contract; no runtime change."
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"comment": "Code organization refactor stage 8c, item 2: daemon/ now owns the managed-service lifecycle end to end. New daemon/unit.ts (unit render + parse: LAUNCHD_LABEL, SYSTEMD_UNIT, serviceUnitPath, renderLaunchdPlist, renderSystemdUnit, launchdTarget, and every parseLaunchd*/parseSystemd* helper), daemon/environment.ts (service-Node selection verbatim from onboard/service-node.ts, plus managedServiceEnvironment/resolveServiceExecutable/withUserLocalBinPath/validateManagedServiceLaunch moved from onboard/service.ts), and daemon/host.ts (new ServiceHost type + createServiceHost, the one {runner, platform, homeDir, uid} default every entry point now builds once instead of re-deriving). daemon/install.ts absorbs installUserService/registerLaunchd/registerSystemd/enableSystemdLinger/removeUserService from onboard/service.ts; installUserService/removeUserService now take a required host: ServiceHost field instead of separate optional runner/platform/homeDir/uid fields (the seven CLI-facing entry points — runDaemonInstall, runDaemonUninstall, controlUserService, getServiceStatus, runOnboard, runUninstall, runDreamuxDoctor — keep their existing flat optional shape unchanged). daemon/service-control.ts is renamed daemon/control.ts. New daemon/status.ts extracts ServiceStatus/getServiceStatus/launchdStatus/systemdStatus out of cli/doctor.ts. onboard/service.ts, onboard/service-node.ts, and cli/service-status-parse.ts (including its no-restricted-syntax re-export shim) are deleted; every caller now imports from the new daemon/ modules. The four independent launchd uid-undefined checks (in registerLaunchd, removeUserService, controlUserService, and the old standalone launchdTarget) collapse into one check inside daemon/unit.ts's launchdTarget, changing the exact wording of that already-unreachable-on-supported-platforms error for three of the four call sites. This is a code-location, internal-signature, and internal-error-wording move only: every public CLI command, config file shape, and service-unit content is unchanged."
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"comment": "Code organization refactor stage 8c, item 3: daemon install and onboard now share one install pipeline. New daemon/environment.ts function resolveManagedServiceAnswers(config, catalogs, dreamuxBin, startService, dryRun, host, env, nodeProbe?, execDirProbe?) replaces the fallback-exec-dirs / provider-binary-resolution / stable-Node-selection block each of runDaemonInstall (daemon/install.ts) and runOnboard (onboard/run.ts) used to hand-roll separately. config/catalogs are nullable to reproduce onboard's existing dry-run-has-no-real-config gate; daemon install always passes non-null values. runOnboard calls the shared resolver only when registerService is true, unchanged from today's gate; when false it keeps today's literal {nodeBin: process.execPath, providerBinChecks: [], fallbackDirs: []} defaults. Every branch (dry-run, no-config, registerService false) was traced against both current function bodies and reproduces the exact existing behavior. One call-order change falls out of sharing a single function: runOnboard used to select the stable service Node before writing config/state files and before provider-binary resolution; it now selects Node inside the shared pipeline, after those writes and after provider-binary resolution (daemon install's existing order). Node selection is side-effect-free and never throws (its own probes/capture calls are wrapped in try/catch), so this has no observable production effect; it only changes call order for a test double that records call sequence. ServiceInstallAnswers.providerBinChecks also becomes a required field (was optional): both producers always set it now, so the optional-with-fallback reader in daemon/environment.ts's serviceProviderBinChecks is simplified accordingly. This is a pure internal extraction with no output, CLI, or config-shape change."
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"comment": "Final pass (R53): repointed service/agent/agent-config.ts's AgentEntityRuntimeCapability import from types.ts to identity.ts, where an earlier stage had already left the type, and dropped DispatcherService's dead `log` field (every sub-component already reads `opts.log` directly). Both fixes make an already-uncompiled stage edit build; no behavior, shape, or published-API change."
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"comment": "The built-in bootstrap plugin is not published with this release: `@excitedjs/dreamux-plugin-bootstrap` is no longer a runtime dependency of `@excitedjs/dreamux`, so listing `builtin:bootstrap` in `plugins[]` fails plugin loading on an installed Dreamux. No released build ever shipped the plugin."
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"comment": "Stopping the daemon while a Team created with a prompt is still starting its TeamLeader no longer hangs: the Team is held, and stopped, before its first runtime start, and a create that arrives as the dispatcher closes is refused. A retried Team dissolve closes the TeamLeader identity an earlier failed attempt released. A recurring cron job whose fire was missed keeps its timer even if the wall clock stepped backward meanwhile."
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"comment": "Code organization refactor stage 1: the shared max-lines lint gate now counts code lines only (blank lines and comments excluded), so trimming comments or whitespace can no longer be used to duck the 700-line cap. Lint config and test-case deletion only; no runtime or published-API change."
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"comment": "Code organization refactor stage 1: added a warn-only dependency-cruiser gate (no-circular plus a layer-order rule over today's src/ directories) to npm run lint, as a monitoring baseline for the multi-stage refactor. Dev-tooling only; no runtime or published-API change."
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"comment": "Code organization refactor stage 1: the shared package-wide re-export ban now covers this package outside src/service/index.ts (previously service/** only), and a warn-only filename-convention lint rule flags *-helpers.ts/*-support.ts/*-ops.ts-style files repo-wide. Lint config and inline eslint-disable comments only; no runtime or published-API change."
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"comment": "Code organization refactor stage 1: added a warn-only Knip unused-export report (project scope driven by tsconfig.tests.json's src/+tests/ file list, with src/server.ts and src/service/index.ts allowlisted as entry points) to npm run lint, as a monitoring baseline for the multi-stage refactor. Dev-tooling only; no runtime or published-API change."
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"comment": "Code organization refactor stage 1: deleted the package-boundary-guards.test.ts case pinning dreamux-types index.ts's exact export-name set, since it regex-matches brace-block exports and dreamux-types's root barrel no longer has any (it now re-exports via export type * per module). Test-only; no runtime or published-API change."
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"comment": "Code organization refactor stage 2a, item 1: updated to the tightened dreamux-types context contract (activity/logger/child now required); deleted the dead HOST_INJECT_ENV process-env injection seam and its two call sites. Internal-only; no runtime or published-API change."
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"comment": "Code organization refactor stage 2a, item 2: made the always-supplied dependency-injection fields in the team-collection/team-service/teammate-collection/teammate-service/dispatcher-core-events layer required, deleting their dead optional-fallback handling. Internal-only; no runtime or published-API change."
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"comment": "Code organization refactor stage 2a, item 6: deleted the precomputed DispatcherConfig.runtime projection (and DispatcherRuntimeConfig), replaced by an on-demand dispatcherAgent(config, dispatcherId) accessor over agents[]; migrated server.ts, provider-diagnostics.ts, and onboard/config-files.ts to it and dropped config-files.ts's now-redundant per-dispatcher agents[] re-derivation. Deleted DispatcherStore's dead create()/upsert()/remove() and DispatcherCreateInput (zero callers). Internal-only; no persisted or published-API change."
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"comment": "Code organization refactor stage 2a, item 13: deleted deduplicate.ts's unused 'active' mode (both call sites already use once-semantics, so the decorator now always retains the first success and takes no options) and collapsed its now-redundant dual-WeakMap tracking into one. Deleted KeyedAsyncQueue.runBefore() (zero production callers). Deleted worktree/manager.ts's deadlineAt assessment-deadline mechanism (zero callers ever passed a deadline), including the now-dead WorktreeAssessmentOptions carrier and its assertAssessmentDeadline/withinAssessmentDeadline helpers. Deleted workflow-service/json-args.ts, which re-implemented platform/json-value's JSON-round-trip validation; its one call site now uses canonicalJsonValue directly. Internal-only; no persisted or published-API change."
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"comment": "Code organization refactor stage 2a, item 14: deleted platform/paths.ts's module-level currentConfig global and setRuntimeConfig()/resetRuntimeConfig()/getRuntimeConfig(). getRuntimeConfig() had zero production callers; the five setRuntimeConfig(...) call sites (server.ts, cli/doctor.ts, onboard/run.ts x2, daemon/install.ts) wrote a value nothing in production ever read. Internal-only; no persisted or published-API change."
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"comment": "Code organization refactor stage 2b, item 8: enabled exactOptionalPropertyTypes and fixed the 19 src + 1 test compile errors it surfaced, widening internal option types to accept explicit undefined and fixing one real behavioral gap (ObservableTransport.hasPerRequestStream now always returns a boolean, matching the MCP SDK's own `=== true` check). App package with no published .d.ts; no external-facing change."
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"comment": "Code organization refactor stage 3: added prettier --check to the package's lint script and reformatted src/tests/ to the shared repo-root config (R2). Whitespace/quote-style only; no runtime or published-API change."
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"comment": "Code organization refactor stage 4a: updated the dispatcher maintenance skill's builtin-feishu.md and feishu-access-v3.md references (paths under packages/dreamux/) to match the access.json/chat-bots.json/routing document changes in @excitedjs/feishu-channel. No @excitedjs/dreamux package source changed."
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"comment": "Code organization refactor stage 4b item 6: deleted platform/atomic-write.ts and platform/json-document-store.ts. Both were fully superseded by the identity/Team/cron/Workflow stores' move onto TransactionalStore (stage 4b items 1-4); nothing outside packages/dreamux imports either module. No externally-visible behavior changed."
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"comment": "Code organization refactor stage 4c item 1: extracted resolveConfig (the provider-loading + mergeWithDefaults tail of config.ts's readConfigFile, deliberately excluding plugins[] loading) as an exported function, and readConfigFile itself, for the Config Service to reuse on open and on config.agents.replace instead of duplicating the file-open sequence; readConfigFile's return now also carries the parsed raw object. Folded loadOrInitConfig's local atomicWriteIfAbsent onto dreamux-utils's existing publishFileExclusive. loadConfig/loadOrInitConfig keep their exact external behavior and return shapes."
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"comment": "Code organization refactor stage 5 item 1: moved state/dispatcher-id.ts to platform/dispatcher-id.ts since it is a dependency-free id-format validator with no state-tier dependency; logic unchanged, only its own relative import shortened (../platform/errors.js -> ./errors.js, now same-directory), and fixed its 8 importers. state/ now holds only dispatcher-store.ts, untouched by this item. No behavior change."
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"comment": "Code organization refactor stage 5 item 2: moved the dependency-free service-root primitives (closed-fact.ts, deduplicate.ts, in-flight-work.ts, serial-queue.ts, shutdown-errors.ts) from service/ to platform/ unchanged, and folded frozen-snapshot.ts's deepFreeze into platform/json-value.ts as a generic exported helper, replacing that module's private JsonValue-only duplicate. Repointed the 25 production import sites for the five moved files plus the 2 for deepFreeze; logic and behavior unchanged."
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"comment": "Code organization refactor stage 5 item 3: extracted the domain-neutral history-page paging primitives (history limit clamp, cursor encode/decode, free-text preview truncation, and a new generic matchesGrepText substring matcher) out of service/agent-entity/read-helpers.ts and service/team-collection/read-helpers.ts into new platform/history-page.ts. Each domain's read-helpers.ts keeps only its own row shape and field list and now calls the shared primitives; repointed the 4 production import sites (team-collection/read-model.ts, team-collection/types.ts, teammate-collection/index.ts, agent-entity/history-query.ts). No behavior change beyond the separately-recorded team.history grep-trim fix."
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"comment": "Code organization refactor stage 5 item 4: extracted the admin socket's pidfile instance-lock primitives (acquire/release, plus the shared readPidFile/defaultIsPidAlive helpers used by both the lock and the legacy-admin-server probe) from admin/socket.ts into platform/instance-lock.ts, renamed to acquireInstanceLock/releaseInstanceLock. admin/socket.ts keeps calling them from the same constructor site; its public API, AdminSocketOptions knobs, and chmodFn handling are unchanged."
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"comment": "Code organization refactor stage 5 item 5: moved the CommandRunner interface (onboard/types.ts) and its ExecaCommandRunner implementation (onboard/commands.ts, deleted) into platform/command-runner.ts, a generic host-shell-command abstraction with no onboarding-specific knowledge. Updated the 9 production importers (provider-diagnostics.ts, cli/doctor.ts, cli/commands/daemon.ts, onboard/{run,service,service-node,uninstall}.ts, daemon/{install,service-control}.ts) to import from the new path. No behavior change."
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"comment": "Code organization refactor stage 5 item 6: moved TeamMateWorktreeRequest from teammate-collection/types.ts into new worktree/types.ts, closing the request-type half of the named inversion where worktree/manager.ts and worktree/repo-request.ts imported their own request type back from teammate-collection/. teammate-collection/{types,commands,mcp-delegate}.ts and team-collection/types.ts now import it from worktree/types.ts. TeamWorkspaceLoan stays in teammate-collection/types.ts (it has no worktree/-side consumer). worktree/manager.ts's TeamMateSharedWorkspace import and worktree/workspaces.ts's SpawnTeamMateRequest/TeamMateSharedWorkspace imports still cross upward into teammate-collection/ (the result-type half of the inversion); out of this item's scope, left for a later item to decide. No behavior change."
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"comment": "Code organization refactor stage 5 item 7: moved channel/conversation-projection.ts to service/dispatcher-core-events/conversation-projection.ts and channel/core-port.ts to service/channel-service/core-port.ts. The first move closes a named inversion (channel/, the foundation tier, importing service-tier types DispatcherCoreEventPublisher and AgentEntityIdentity); the second is an ownership move onto channel-service/'s existing dependency-cruiser tier, with its one consumer unchanged. channel/ now holds only catalog.ts and external-channel-provider.ts. Updated all nine service/ importers plus input-source-lifecycle.ts's core-port.js import to the new paths; content unchanged beyond each file's own relative imports."
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"comment": "Code organization refactor stage 5 item 8: moved daemon/restart-intent.ts to service/dispatcher-service/restart-intent.ts, closing the inversion where service/dispatcher-service/{index,restart-notice,input-source-lifecycle}.ts and service/dispatchers/index.ts (core) imported it from daemon/ (an operator-surface directory). daemon/ and cli/commands/daemon.ts now import from service/dispatcher-service/, the correct direction. The persisted restart marker JSON shape (RestartIntentFile: version, created_at_ms, ttl_ms, announce, targets) is completely unchanged; this is a code-location move only. Also corrected a stale doc-comment sentence referencing a deleted dispatcher.start admin command with the actual current lazy agent-runtime-activation scenario."
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"comment": "Code organization refactor stage 5 item 9: command/ becomes kernel-only. Moved command/catalog.ts to server/command-catalog.ts and split command/host.ts, keeping the pure mustDispatcherId in command/ and moving CoreCommandHost/mustDispatcher/mustDispatcherRow (which name concrete domain types) to the new server/command-host.ts. Each of the 8 domain commands.ts factories (config, channel-service, dispatchers, mcp, scheduler, team-collection, teammate-collection, workflow-service) now takes a narrow per-domain resolver instead of the full CoreCommandHost, removing the 15 upward imports command/catalog.ts and command/host.ts (both kernel-tier) previously carried into foundation/service/composition-tier modules; one new same-direction-as-existing edge is added (team-collection/commands.ts to dispatcher-service/index.ts, a type-only import matching the pattern team-collection/mcp-delegate.ts already carries), for a net reduction of 14. createCoreCommandRegistry's external signature, the array order it builds, every Command's name/schema/behavior, and every thrown error type are unchanged; this is a code-location and internal-signature move only."
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
"comment": "Code organization refactor stage 5 item 10: split config/load.ts out of config/config.ts and deleted config/config-helpers.ts. Moved loadOrInitConfig, loadConfig, readConfigFile, resolveConfig, assertNoLegacyTomlOnly, the private providerRegistryFor, and the loader-only agentProviderRefs/channelProviderRefs/providerRefsFrom to load.ts; config.ts keeps the config data model, parse/validate (mergeWithDefaults, now exported for load.ts to call), stringifyConfig, redactConfigForDisplay, dispatcherAgent, the path builders, assertConfigFileMode, defaultWorkspaceEnabled, and the folded-in resolveConfigProvider/asChannelProvider/readOptionalBoolean/expandHome (expandHome's re-export shim is gone; it is defined directly in config.ts now). redactConfigSecrets (a bare alias of dreamux-utils's redactSecretKeyValues) is deleted; its one caller uses redactSecretKeyValues directly. Every moved function keeps its exact external behavior, signature, and return shape; this is a code-location move only."
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"comment": "Code organization refactor stage 5 item 11: ProviderRegistry.register(descriptor) and registerImplementation(id, implementation: unknown) collapse into one register(descriptor, implementation?: ProviderImplementation) call, typing the implementation argument as AgentRuntimeProvider<unknown> | ChannelProvider<unknown> instead of unknown. register() now recognizes completion of a pre-registered descriptor (the exact same object, not a duplicate) so a builtin descriptor registered before its package loads can still be completed with its implementation in one call; calling register() twice with two distinct descriptor objects for the same id still throws DuplicateProviderError as before. Updated the two call sites that paired the old two calls: registry/provider-loader.ts's loadOneProviderPackage and registry/builtins.ts's registerBuiltinProvider (and plugin/loader.ts's contribute(), which feeds the latter, is typed the same way instead of unknown). getImplementation() and createBuiltinProviderRegistry()'s pre-registration of builtin descriptors (no implementation yet) are unchanged; this is a write-side typing and call-site consolidation only, no change on any production path."
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"comment": "Code organization refactor stage 5 item 12: deleted the package-local platform/error-info.ts (errorMessage/errorInfo/ErrorInfo) and platform/errors.ts's duplicate errorMessage, repointing every importer at the canonical @excitedjs/dreamux-utils copy. Code-location move only; no behavior, wire shape, or persisted state changed."
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"comment": "Code organization refactor stage 5 item 13: TransactionalStore's write path now calls the new dreamux-utils writeFileAtomic instead of its own inline write-tmp-then-rename sequence. Same durability guarantee; no dreamux-owned behavior or file changed."
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"comment": "Code organization refactor stage 6a item 1: merged service/agent-entity/, service/teammate-service/, and service/teammate-collection/ into one service/agent/ directory, following the store -> service -> collection direction (identity/store/runtime-state/activity/records/requests/runtime-id, then runtime-generation/turn/admission/submission/completion-renderer/factory/service, then index/commands/mcp/mcp-tool-descriptors/system-prompt/errors/types), plus agent-config.ts and dissolve-members.ts moved as-is and teammate-service/types.ts kept as its own service-types.ts file (a plan gap this item resolved rather than folding it into service.ts, which would have created a service.ts <-> runtime-generation.ts import cycle). Folded activity-errors.ts into activity.ts and split turn-coordinator.ts + turn-recording.ts into turn.ts (EntityTurn/EntityTurnCoordinator/TurnOutcome/TurnCompletionDelivery, with one deduped asError) and admission.ts (AdmissionLedger/TurnAdmission/InboundDeliveryResult and their mapping functions). Dropped the dead CreateTeamLeaderInput type (zero importers). No class or exported type was renamed: TeammateService, TeammateCollection, TeammateRuntimeOwner, and every public type keep their current names this item. Updated every importer's module specifier across dispatcher-service/, team-collection/, team-service/, workflow-service/, scheduler/, mcp/, worktree/, dispatchers/, dispatcher-core-events/, channel-submission.ts, and server/command-catalog.ts to the new paths; this is a code-location move only; behavior, public names, and persisted formats are unchanged. Collapsed the three directories' .dependency-cruiser.cjs layer entries into one service-agent layer (positioned directly before the reduced service-collections layer, which still depends on it) and added a warn-only intra-directory direction rule for the same store/service/collection split."
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
"comment": "Code organization refactor stage 6a item 2b: turned service/agent/factory.ts's createTeammateService pure-forwarder function into an AgentServiceFactory class, constructed once per dispatcher as new AgentServiceFactory(dispatcherId, admissions) in dispatcher-service/index.ts where the AdmissionLedger used to be constructed alone. dispatcherId and the AdmissionLedger are now bound at construction instead of threaded through every call, so its create(input) method drops the now-redundant dispatcherId and admissions fields from CreateTeammateServiceInput (renamed CreateAgentServiceInput) -- every other field (identity, options, config, agentRuntimeProviders, identities, onPersisted, peers, worktrees, conversationProjection, log) is unchanged. Propagated the factory (replacing the raw AdmissionLedger) through DispatcherAgentDeps, TeamServiceDeps, TeamCollectionOptions, and TeammateCollectionOptions, and updated the three createTeammateService call sites (dispatcher-service/agent.ts, team-service/leader-agent.ts, service/agent/index.ts's TeammateCollection.buildEntity) to call factory.create(...) instead. Construction-path refactor only: the ledger is still exactly one per dispatcher, with the same window/eviction policy, so no observable behavior changed."
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"comment": "Code organization refactor stage 6a item 2c: moved resolveAgent and defaultAgentRuntime from the now-deleted service/agent/agent-config.ts into config/config.ts beside the existing dispatcherAgent() accessor, updating their three importers (service/agent/{activity,runtime-generation,index}.ts). agentRuntimeCapability, previously a free function in agent-config.ts used only by TeammateCollection.getCapabilities(), is now a private TeammateCollection method that reads its AgentRuntimeProviderCatalog off `this.opts` instead of taking it as a parameter. Also deleted the InboundDeliveryResult type and asInboundDeliveryResult converter from service/agent/admission.ts: they were a redundant narrower projection of TurnAdmission with no caller-visible difference (SchedulerServiceOptions.submitScheduled's only reader, scheduler/service.ts's submitDue, checks status !== 'submitted' && status !== 'ambiguous', which behaves identically against the wider TurnAdmission). scheduler/types.ts's submitScheduled now returns Promise<TurnAdmission> directly, and both callers (team-service/index.ts's and dispatcher-service/index.ts's submitScheduled wiring, the latter sharing the same SchedulerServiceOptions shape) drop the asInboundDeliveryResult(...) wrapper since submitToLeader/AgentService.submitInput already return Promise<TurnAdmission>. Internal type/import consolidation only: every scheduler cron fire's observable admission outcome is unchanged."
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"comment": "Code organization refactor stage 6b item 1: merged service/team-collection/ and service/team-service/ into one service/team/ directory, following the store -> service -> collection direction. Split team-collection/types.ts into types.ts (TeamRecord/TeamCollectionOptions/TeamServiceDeps and the rest of the pure facts, now also holding what was team-service/types.ts minus its submit-result projection) and requests.ts (the Command/MCP-reader functions plus teamSubmitResult/teamSubmitResultOutput, matching the requests.ts template every other domain uses). Moved TEAM_LEADER_REQUIRED_SKILL_SOURCES out of create-request.ts into leader.ts (renamed from leader-agent.ts), and folded read-helpers.ts's matchesTeamHistoryQuery/teamRowMatchesText as private functions into read-model.ts (its one importer), deleting read-helpers.ts. Renamed team-service/index.ts to service.ts, team-collection/mcp-delegate.ts to mcp.ts, and team-service/roster-projection.ts to roster.ts; every other file keeps its current name this item, including the five files (runtime-registry.ts, worktree-cleanup.ts, collaborators.ts, initial-prompt.ts, roster-reader.ts) this stage's later items fold away or delete. No class, type, or exported function was renamed. Updated every importer's module specifier across dispatcher-service/, dispatchers/, scheduler/, server/, cli/, and this directory's own internal relative imports; this is a code-location move only, so behavior, public names, and persisted formats are unchanged. Collapsed the team-collection and team-service .dependency-cruiser.cjs layer entries into one service-team layer, positioned where the two originals sat, and updated the channel-not-to-team-or-teammate and workflow-service-not-to-team named edge rules to the merged path; removed the now-superseded team-service-not-to-team-collection-owner rule (its cross-directory ownership check is intra-directory once the merge lands, and the new direction rule for that follows in a later item, once every file in service/team/ has its final name)."
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"comment": "Code organization refactor stage 6b item 3: folded TeamRuntimeRegistry and TeamWorktreeCleanup into TeamCollection (service/team/index.ts), and moved team.state event publishing ownership from TeamStore to TeamService. TeamCollection now holds one Map<teamId, {service, subscription}> (replacing the old cache/materialized/closedSubscriptions split) plus one constructing map, with create/get/rebuild/startSchedulers/startWorkflows/recoverWorkflows/closeWorkflowAdmissions/stopSchedulers/stopForHost as direct methods; the standalone TeamWorktreeCleanup class is gone, replaced by a private settleClosedWorktree(teamId) method exposed to each TeamService through a new settleWorktreeCleanup constructor-supplied callback on TeamServiceDeps (not a module import, matching the store<-service<-collection direction). TeamRosterProjection.publish() now calls DispatcherCoreEventPublisher.publish() directly instead of going through TeamStore.publishRosterState(); a new TeamRosterProjection.publishTeamState() lets TeamService's own updateRecord()/createNew() publish the team.state aggregate on every status transition, reading the roster it already holds live instead of asking a callback to resolve one from disk. TeamStore lost its coreEvents/roster constructor options and its four publish* methods; its persistence methods (teamRoot/handle/get/list/create/update) and TeamRecord's JSON shape/validation are unchanged. roster-reader.ts's readTeamRoster is deleted (no remaining caller once TeamService always has a live roster to publish from). The team.state event's shape, timing relative to the record write, and content are unchanged — only which class performs the publish moved."
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"comment": "Code organization refactor stage 6b item 4: folded collaborators.ts's buildTeamMembers/buildTeamWorkflows/buildTeamScheduler into TeamService's constructor as direct new TeammateCollection(...)/new WorkflowService(...)/new SchedulerService(...) calls, each a pure one-for-one inlining of the forwarder it replaces. Removed the one real duplicate this fold exposed: the Team-fence wrap buildTeamScheduler put around submitScheduled (deps.admit(() => deps.submitScheduled(input))) reentered the same Team admission fence that submitToLeader (the thing submitScheduled is wired to) already opens with internally; a cron fire now reaches this.submitToLeader(...) directly, with the dispatcher-gate admission (SchedulerService's own opts.admit) as the only wrap ahead of it. The commands: {list, create, update, delete} wrapping is unchanged, since it composes that same dispatcher gate with the Team's own fence on top -- two different fences, not the duplicate the scheduled-fire path had. Renamed TeamCollectionOptions.dispatcherCompletionInitiator to leaderCompletionInitiator so it and TeamServiceDeps share one field name for one fact, which let TeamServiceDeps become Omit<TeamCollectionOptions, 'root' | 'nameSuffixGenerator'> plus the three per-Team-instance fields (teamRoot, store, settleWorktreeCleanup) instead of a hand-written field-by-field type, and let TeamCollection.depsBase() become a spread of its own options plus those three overrides instead of a field-by-field remap. TeamService now exposes startScheduler()/stopScheduler() as public verbs beside its workflow-admission verbs; TeamCollection's own startSchedulers()/stopSchedulers() call them directly instead of going through a private schedulers Map and a TeamSchedulerLifecycle handle, both of which are deleted along with TeamServiceCreateOutput -- TeamService.createNew()/rebuild() now return TeamService directly instead of {service, schedulerLifecycle}. No externally observable behavior changes: the double-fence removal drops a redundant reentry of the same admission check, not a check itself."
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"comment": "Code organization refactor stage 6c, item 1: workflow-service's protocol.ts is now the one IPC codec -- it gains normalizeAgentOptions, the parent-side isWorkflowRunnerChildMessage decoder (moved from runner-process.ts), and the child-side parseParentMessage decoder (moved from runner.ts). WorkflowSemaphore moves to a new semaphore.ts; WorkflowPersistenceError moves to errors.ts; MAX_AGENTS, MAX_HELPER_ITEMS, and MAX_SCRIPT_BYTES move into limits.ts unchanged. run-support.ts and agent-policy.ts are deleted: WORKFLOW_AGENT_SYSTEM_PROMPT is inlined into run.ts (still exported), nonEmpty is inlined as a private run.ts helper, and every isRecord caller -- including runner.ts's own private errorMessage helper -- switches to dreamux-utils's isPlainObject, an identical typeof/Array.isArray check. Internal-only; no runtime or published-API change."
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"comment": "Code organization refactor stage 6c, item 3: journal.ts is now workflow-service's per-run durability owner. WorkflowJournal gains recover(record, now), moved verbatim from WorkflowService.recoverRunningRecords's reconciliation body (including the agentMatchesJournalResult helper); recoverRunningRecords shrinks to a loop calling it per running record. run.ts drops the redundant resultJournalCommitted/terminalJournalCommitted guard flags in favor of the journal's own idempotent ensureAgentResult/ensureTerminal commit-and-return, keeping resultCandidate/terminalCandidate unchanged. No persisted shape, log wording, or commit semantics change."
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"comment": "Code organization refactor stage 6c, item 4: fold WorkflowRunTerminal into WorkflowRun. Deleted the unreachable failAfterNotification/beforeFinalize/deliverWhileTerminal path (EntityTurn.settled never rejects, so the try/catch it guarded could never run) and run-terminal.ts's WorkflowRunTerminalDeps reverse-callback interface, moving its remaining state (terminalIntent, the memoized finalize task, the settled promise) and methods (reserveStop, requestTerminal, observeTerminal, ensureTerminalTask) onto WorkflowRun as private members. WorkflowRun.closeAdmission()/WorkflowService.closeAdmission() are renamed to requestStop()/requestStopAll() to name what they do; every internal call site is updated, and the redundant closeAdmission() call in TeamClosing.stopChildRuntimes is deleted since WorkflowService.stopAll() already calls requestStopAll() as its own first line. DispatcherWorkflows.closeAdmission() and TeamService.closeWorkflowAdmission() keep their own names (their own layer's naming is out of scope for this stage); only their bodies now call the renamed WorkflowService method. No persisted shape, wire contract, or observable run behavior changes."
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"comment": "Code organization refactor stage 6e item 3: ChannelService is now the single owner of the channel lifecycle. It absorbs the runnable-channel shape guard, the best-effort build-failure close helper, the Channel MCP delegate assembly, and the ChannelMetadata type (each from its own former file); the per-session initialize/start loops and the Core-port-lease tracking move here from DispatcherInputSourceLifecycle; the built/live instance maps and the port-lease array unify into one map keyed by channel id; and each channel's provider is now resolved from the catalog at most once instead of once per role. Internal ownership and file-location change only — same Commands, same MCP tool names, same channel.list output shape."
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
"comment": "Code organization refactor stage 6e item 4: the dispatcher's own agent (identity ensure, construction, mustAgent(), lazy activation with resume-notice injection, and runtime-status projection) now has one owner, DispatcherAgent, replacing three former files and a fourth cold-path status projection in the Dispatchers collection. Internal ownership and file-location change only."
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
"comment": "Code organization refactor stage 6e item 5: TeamCollection now implements a narrow TeamsPort interface (submitToLeader, interruptLeader, dissolve, leaderScope, scheduler, runForLeader, history, summary, list, createFromRequest), each gating itself on the dispatcher's admission internally instead of being wrapped by the caller. TeamLeaderHandle, TeamLeaderTeammateOps, and the teamLeaderHandle() factory move from dispatcher-service/ into team/leader-handle.ts, fixing a layering inversion where service/agent/mcp.ts imported a type from the dispatcher-service directory that itself depends on service/agent/. DispatcherService.team() now forwards to TeamCollection.leaderScope() instead of building the handle itself. Internal ownership and file-location change only; DispatcherService's other Team pass-through methods and public Command/MCP surface are unchanged."
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
"comment": "Code organization refactor stage 6e item 7a: DispatcherInputSourceLifecycle is renamed to DispatcherLifecycle (input-source-lifecycle.ts -> lifecycle.ts). input-source-start-rollback.ts, team-runtime-stop.ts, and inbound-task-drain.ts are deleted; DispatcherService's stop()/beginShutdown()/shutdown() methods and Dispatchers' beginShutdown()/shutdown() methods each collapse into one close()/shutdown() method; the shuttingDown/stoppingTask boolean-plus-promise pair is replaced by the single nullable closing-Promise fence the close operation itself publishes; DispatcherWorkflows.rollbackStart() (dead once the rollback path it served is gone) is deleted; RestartIntentConsumer moves from a post-construction setter to a plain constructor value threaded through DispatchersOptions/DispatcherServiceOptions. Internal ownership, file-location, and method-shape changes only: DispatcherService implements only the plugin-facing Dispatcher interface (id/cwd/hooks), which never declared any of the renamed/collapsed methods, so no plugin-visible or Command/MCP-visible surface changes."
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"comment": "Code organization refactor stage 6e item 7b: DispatcherService's ~15 Team/Channel per-verb pass-through methods (createTeam, submitToTeamLeader, interruptTeamLeader, listTeams, getTeamStatus, getTeamHistory, dissolveTeam, dissolveTeamForLeader, team(), teamScheduler(), runForTeamLeader, prepareChannels(), startInputSources(), listChannels()) are deleted; DispatcherService now exposes readonly teams (TeamsPort), teammates (TeammateOps), channels (ChannelService), and scheduler (SchedulerCommands) directly, and every caller (team/commands.ts, team/mcp.ts, channel-service/commands.ts, dispatcher-service/mcp-delegates.ts, scheduler/commands.ts, server/command-catalog.ts) reaches the owning port itself. Pure internal restructuring: same Commands, same MCP tool names, same external behavior. workspace() is the one surviving pass-through, still needed by the TeamMate Command/MCP surface."
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
"comment": "Stage 6e review correction: dispatcher-service/dispatcher-workflows.ts and its DispatcherWorkflows wrapper class are deleted. DispatcherService now constructs its dispatcher-scoped WorkflowService directly in its own constructor, the same way it already constructs SchedulerService; DispatcherLifecycle fans Workflow start/recover/close-admission out to the Team scope (teams.startWorkflows()/recoverWorkflows()/closeWorkflowAdmissions()) explicitly, beside the equivalent scheduler fan-out, instead of a wrapper class hiding that fan-out. Pure internal restructuring: the dispatcher.workflows Commands/MCP surface, its run/stop admission gating, and its status/list reads are unchanged."
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
"comment": "Code organization refactor stage 9, item 1: split service/agent/'s dependency-cruiser layer placement into a store tier (folded into service-primitives), a service tier (positioned before service-mid, since workflow-service/scheduler legitimately depend on it), and a collection tier (kept after service-mid); moved CreateLockedTeammateOptions to service-types.ts and service/channel-submission.ts into service/agent/. Internal reorg only; no runtime or published-API change."
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
"comment": "Code organization refactor stage 9, item 2: service/agent/mcp.ts and service/dispatcher-service/mcp-delegates.ts now type their DispatcherService/TeamLeaderHandle scope fields with narrow, locally-declared structural interfaces instead of importing the concrete classes, removing the resulting layer-order warnings and import cycles. Internal reorg only; no runtime or published-API change."
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"comment": "Code organization refactor stage 9, item 3: broke the daemon/ <-> onboard/ import-cycle cluster. ServiceInstallAnswers now lives in daemon/environment.ts (the file that builds it), so daemon/unit.ts and daemon/install.ts both depend on it one-directionally instead of on daemon/install.ts. The idempotent file-write ledger (FileLedger, TransparentFileLedger, ensureDirectory, ensureTextFile, writeTextFile) moved from onboard/ledger.ts to the neutral platform/file-ledger.ts, since daemon/install.ts's runDaemonInstall and onboard/run.ts's runOnboard both need the identical write tracking. onboard/ledger.ts's remaining exports (snapshotFiles, recordFileTreeChanges, FileSnapshot) had zero callers anywhere in the package, so the now-empty file is deleted rather than left as a dead stub. Internal reorg only; no runtime or published-API change."
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
"comment": "Code organization refactor stage 9, item 4: broke the five remaining two-file dependency-cruiser cycles. TeamServiceDeps (service/team) and SchedulerServiceOptions (service/scheduler) each named the concrete store class their own types.ts imported, so each moved to the file that actually constructs the service it configures (service.ts and index.ts respectively); service/team/leader.ts, which only reads a handful of TeamServiceDeps fields, now takes a small locally-declared structural type built from the same underlying field types instead of importing the wider bag, so it does not gain a new edge back into service.ts. service/agent/admission.ts's admissionWithoutTurn was a pure identity function with one call site; it is deleted and that call site now returns its input directly, and the TurnAdmission type it existed only to relabel moved to turn.ts (its actual producer) with admission.ts importing it back — every external importer of TurnAdmission now points at turn.ts. cli/doctor.ts and cli/doctor-plugins.ts each imported a type from the other; the shared DoctorCheck result row moved to a new cli/doctor-types.ts both import. admin/socket.ts took the whole concrete Server class just to call two of its members; createAdminSocketServer now takes a locally-declared AdminSocketHost naming only the admitted Command port and the logger. Internal reorg only; no runtime or published-API change."
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
"comment": "Code organization refactor stage 9, item 5: fixed the two remaining singleton dependency-cruiser violations. service/dispatcher-service/types.ts held DispatcherServiceOptions, a constructor deps bag referencing command/types.ts's CoreCommandRegistry (plus several other concrete classes), not a data-only types file; it moves to dispatcher-service/index.ts beside the DispatcherService constructor it configures, the same shape this stage already used for scheduler/index.ts's SchedulerServiceOptions and team/service.ts's TeamServiceDeps, leaving types.ts holding only DispatcherSummary/DispatcherRuntimeStatus and satisfying types-file-not-to-command naturally. service/scheduler/commands.ts's real, necessary need for service/team/'s TeamsPort and optionalTeamNameParam (a cron job can be Team-scoped) now has its own file-level layer placement in .dependency-cruiser.cjs positioned after service-team, mirroring the existing state/dispatcher-store.ts and src/mcp/(server|launch).ts file-level carve-outs; config-only, no code moved. Internal reorg only; no runtime or published-API change."
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
"comment": "Code organization refactor stage 9, item 6: flipped .dependency-cruiser.cjs's no-circular rule, every generated layer-order-* rule, and every namedEdgeRules entry from warn to error, now that items 1-5 closed all 36 baseline violations (verified with a clean 0-warning, 0-error depcruise run; no exception rules were needed). Rewrote the file's header comment, which used to explain why the gate stayed warn-only, to describe the now-enforced error gate instead. Reworded the two namedEdgeRules comments that used to cite tests/collection-ownership.test.ts and tests/workflow-service.test.ts as evidence to state each invariant directly, since those test files are retired separately by this stage's test-deletion item. Dev-tooling config only; no runtime or published-API change."
|
|
356
|
+
}
|
|
357
|
+
],
|
|
358
|
+
"patch": [
|
|
359
|
+
{
|
|
360
|
+
"comment": "Release fully retired Team stores and read historical records from disk while preserving live owners, queued writes and cleanup. Keep an admitted leader dissolve on that captured owner through its asynchronous precheck and detached cleanup, even when initial construction fails. Restore non-blocking Codex inbound and final-parent behavioral coverage with explicit native-model opt-in and separate auth-free protocol checks. Retain revocable event sources and skip display DTO production without an audience while preserving durable observations. Restore actual ESLint code-line limits and public package boundary assertions. Ignore non-Team inventory names without reading or altering their contents, and keep one construction record acquisition through workspace preparation and publication. Pure retained-record reads return the committed snapshot without waiting for unrelated queued writes; writer releases still drain their captured queues."
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
"comment": "`dreamux serve` now always exits on SIGTERM or SIGINT: a shutdown that fails is logged and the process exits with status 1, and a shutdown that has not settled by a fixed deadline is cut off and the process exits with status 1, where a failed shutdown used to leave the process running. Under systemd (`Restart=on-failure`), a failed or cut-off shutdown that was not requested through `systemctl stop` therefore restarts the service, and a runtime child process can outlive a daemon cut off this way, as after a manager kill. A value refused by config validation in an online config replace is now reported as a bad request rather than an internal failure. An unknown `builtin:` provider ref in a replace payload is classified the same way, and its message no longer reads as an external-provider load failure; `serve`, `doctor` and `onboard` print the same reworded message for such a ref. The `repo` input of Dispatcher `spawn` and Team `create` carries its field descriptions and length bounds, defined once beside the repo request reader; because the canonical `spawn` and `team.create` commands validate against that same schema, an empty or over-long repo path, base ref or branch is now rejected as a bad request over the admin socket and the Channel port as well. `last` on a running agent, including a TeamMate a `send` is still reopening, reads the provider, config and session of the runtime that is running instead of the current config. Two lifecycle fixes: a Workflow handle lock taken while an agent is created is released when that agent closes before the handle is handed over, and looking up an absent Team no longer leaves a store cached for that id."
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
"comment": "Preserve existing taps when a plugin register interceptor omits its result. Reject asynchronous plugin factories and config readers with attributed load errors while observing their rejections. Reject simultaneously loaded plugin names that differ only by ASCII case; preserve the spelling of individual names, API keys, and state paths."
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"comment": "Bind fixed runtime context once and return persistence and cleanup operations to their owning services."
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"comment": "Team dissolve now writes the record closed immediately after its worktree precheck passes, then destroys every child service (Workflows, scheduler, members, leader) and reclaims the worktree as best-effort steps behind that commit, instead of stopping and closing children first and writing closed last. A member that dirties the managed worktree after the precheck no longer refuses the dissolve: the Team still closes, and a non-forced cleanup then only keeps the directory instead of removing it. A process that dies after the closed record lands now leaves the Team durably closed with whatever children the destroy pass had not yet reached, rather than reopening it. Persisted record shapes are unchanged."
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"comment": "A workflow status/list read or a cron list made while its dispatcher is stopping or its Team is dissolving is now refused instead of answered: WorkflowService and SchedulerService fence those reads behind the same admission gate their mutations already crossed. A leader-scoped mutation racing both a dispatcher stop and a Team dissolve at the same time now reports TeamClosedError instead of ServerShuttingDownError, now that the TeamLeader handle is assembled from surfaces that already fence themselves instead of forwarding through 12 hand-written closures. Internally, several resources a parent used to build and hand down (the Workflow journal, the Agent admission ledger, the per-Team record handle) are now built by the module that owns them instead; persisted record and config shapes are unchanged."
|
|
376
|
+
}
|
|
377
|
+
],
|
|
378
|
+
"dependency": [
|
|
379
|
+
{
|
|
380
|
+
"comment": "Updating dependency \"@excitedjs/agent-runtime-claude-code\" to `0.8.0`"
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
"comment": "Updating dependency \"@excitedjs/agent-runtime-codex\" to `0.7.0`"
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
"comment": "Updating dependency \"@excitedjs/dreamux-types\" to `0.11.0`"
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
"comment": "Updating dependency \"@excitedjs/dreamux-utils\" to `0.6.0`"
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
"comment": "Updating dependency \"@excitedjs/feishu-channel\" to `6.3.0`"
|
|
393
|
+
}
|
|
394
|
+
]
|
|
395
|
+
}
|
|
396
|
+
},
|
|
4
397
|
{
|
|
5
398
|
"version": "0.25.0",
|
|
6
399
|
"tag": "@excitedjs/dreamux_v0.25.0",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,76 @@
|
|
|
1
1
|
# Change Log - @excitedjs/dreamux
|
|
2
2
|
|
|
3
|
-
This log was last generated on
|
|
3
|
+
This log was last generated on Fri, 09 Oct 2026 03:05:18 GMT and should not be manually modified.
|
|
4
|
+
|
|
5
|
+
## 0.26.0
|
|
6
|
+
Fri, 09 Oct 2026 03:05:18 GMT
|
|
7
|
+
|
|
8
|
+
### Minor changes
|
|
9
|
+
|
|
10
|
+
- The Dispatcher Agent has its own Commands. `dispatcher.submit` submits one turn to the addressed Dispatcher's own Agent and `dispatcher.interrupt` interrupts it; both address that Dispatcher through the caller context, like every other Command in the namespace, and both are reachable over `admin.sock` and a Channel's in-process port. `team.submit` and `team.interrupt` now require `team_name`: a Team Command names exactly one Team, and a call that omits the name is answered `BAD_REQUEST` by schema validation before any Agent is reached, instead of silently reaching the Dispatcher Agent. A caller that relied on the omission calls the Dispatcher Command instead. The two submit Commands share one reader for the Channel-facing payload (`service/channel-submission.ts`), which owns the attribute, text, reminder, and `source_id` rules once and fixes the `channel` provenance for both, and `DispatcherService.interrupt(teamId | null)` splits into `interruptAgent()` and `interruptTeamLeader(teamId)` to match.
|
|
11
|
+
- Support ultrathink through native Codex effort parameters, restore ordinary effort on the next ordinary submission, and pass submission text through unchanged. Busy input follows native Codex timing.
|
|
12
|
+
- BREAKING: `dispatchers[].cwd` is now required and must be a non-empty string on every Dispatcher entry, enabled or not; a config without it fails to load with an error naming the Dispatcher id. Rebuild: add "cwd": "<absolute dir>" to every dispatchers[] entry that lacks it.
|
|
13
|
+
- Refuse new Workflow TeamMate construction after its Team or Dispatcher starts closing, before allocating a name, workspace, identity, or launch hooks; preserve already-admitted construction cleanup and lock finalization.
|
|
14
|
+
- Default omitted dispatcher workspace policies and new onboarding to false; preserve explicit values. Repository-free work uses the dispatcher cwd unless isolation is enabled.
|
|
15
|
+
- Load plugins from an optional top-level `plugins[]` in `config.json` (`builtin:<id>` or `npm:<package>[#export]` refs, each a string or `{ ref, config }`); existing configs load unchanged. Plugins can contribute providers addressed as `builtin:<name>`, append instructions and skill roots to Dispatcher and TeamLeader launches, and react to Team creation; a failing plugin hook is logged and skipped. `dreamux doctor` lists loaded plugins and plugin load failures. Ships the built-in, opt-in `builtin:bootstrap` plugin, which injects `.workspace/identity.md` and `user.md` from the Dispatcher cwd.
|
|
16
|
+
- Redact tool call invocations and arguments in the conversation projection. Secret masking and workspace/home path renaming now apply to every displayed member of a tool call, and a call's structured payloads are redacted by walking the JSON value, so a secret written into a nested object or a multi-line .env is masked and the payload is still valid JSON. The redaction rules themselves moved to @excitedjs/dreamux-utils, where the host logger and config display now share one list of secret key names; the logger previously missed api_key, private_key, and client_secret.
|
|
17
|
+
- Code organization refactor stage 6d: scheduler/service.ts renamed to index.ts and scheduler/mcp-delegate.ts to mcp.ts (matching the agent/team/workflow-service per-domain template); a new requests.ts holds the request readers and result projections previously split across types.ts and store.ts, which is now persistence-only; the dead dispatcherId option/parameter was dropped from CronJobStore and detectLegacyCronJobStore; the fire path was consolidated onto one store read per fire plus a pure advanceJob/impure rearm pair (a one-shot job whose dispatch throws is now disabled immediately instead of staying enabled with a stale next_run_at until the next start()); and SchedulerService now implements SchedulerCommands directly, with TeamService composing its own closing fence directly into the admit closure it passes at construction instead of wrapping the public command surface in a second, Team-scoped object.
|
|
18
|
+
- Codex and Claude Code now load exactly like the built-in Feishu plugin: both are always-loaded plugins that contribute their Agent Runtime provider, instead of a core-side static builtin-provider registry pre-registering their descriptors ahead of the provider loader importing their package. No behavior change for a default install: both still register under builtin:codex / builtin:claude-code before agents[] is resolved, and a config naming an unmapped builtin:<ref> still fails loud with the same UnknownBuiltinProviderPackageError it always has. `dreamux uninstall`'s directory-safety guard also no longer hard-codes ~/.codex and ~/.claude: it now builds the same always-loaded plugin registry and asks each registered Agent Runtime provider for its own operatorStateRoot, so a provider added or removed by future plugin work is protected (or not) automatically. The protected paths for a default install are unchanged.
|
|
19
|
+
- BREAKING: code organization refactor stage 8c, item 1: `DREAMUX_CONFIG_DIR` is deleted, and so is `dreamux onboard`'s `--config-dir` option; `dreamux uninstall --config-dir` is deleted too, by inference — it has nothing left to relocate once `config.json` is root-derived. `config.json` now always lives at `<DREAMUX_ROOT>/config.json` (default `~/.dreamux/config.json`, unchanged only for an install that set neither `DREAMUX_CONFIG_DIR` nor `DREAMUX_ROOT`); the managed-service unit now writes `DREAMUX_ROOT` instead of `DREAMUX_CONFIG_DIR`, taking the value from the environment of the process that runs `dreamux daemon install` or `dreamux onboard` (`~/.dreamux` when unset); `dreamux uninstall` now also removes the whole root directory itself, not just its contents. Before this change `DREAMUX_ROOT` relocated only state, run, cache and logs, `config.json` came from `DREAMUX_CONFIG_DIR` or `~/.dreamux`, and the managed-service unit that `dreamux daemon install` wrote carried no `DREAMUX_ROOT`, so a managed service ran with `.dreamux` under its `HOME` as its root unless its environment supplied a `DREAMUX_ROOT` by another route (a unit drop-in, an environment file, or the service manager's own environment). Scenario 1: an install that set `DREAMUX_CONFIG_DIR` to a path other than the root it ran with — after upgrade `DREAMUX_CONFIG_DIR` is silently ignored, so the daemon looks for `config.json` under the root instead of the operator's configured directory. If no `config.json` exists there, the daemon fails on an ordinary missing-file error that never mentions `DREAMUX_CONFIG_DIR` or what moved, and the unit's `Restart=on-failure` loops; if a `config.json` already exists there (for example from an earlier default-path `dreamux onboard`), the daemon starts silently against that unrelated file instead. Scenario 2: an operator who exports `DREAMUX_ROOT` in their shell (profile, direnv, a foreground `dreamux serve`) while `config.json` stayed in `DREAMUX_CONFIG_DIR` or `~/.dreamux` — `dreamux serve` and `dreamux daemon install` run from that shell now look for `config.json` under the exported root and fail on the same missing-file error (`dreamux doctor` reports it); and if the operator moves `config.json` there and re-runs `dreamux daemon install`, the shell's root is written into the unit, so the service moves to a root it never used: its durable state (dispatcher identities, Teams, Workflow runs, `access.json`) stays behind in the old root and the service comes up on a root that holds none of it. Rebuild: keep the root the old service actually used — do not assume it is the `DREAMUX_ROOT` your shell exports. (1) Find that root from the environment the old daemon actually ran with. For a managed service, read it from the running process (on Linux `tr '\0' '\n' < /proc/$(systemctl --user show -p MainPID --value dreamux)/environ | grep -E '^(DREAMUX_ROOT|HOME)='`) or, when it is not running, from everything that defines that environment: the installed unit and any drop-ins (`systemctl --user cat dreamux`), any `EnvironmentFile` they name, and the user manager's own environment (`systemctl --user show-environment`); on macOS, the plist `~/Library/LaunchAgents/dev.excited.dreamux.plist` and `launchctl print gui/$(id -u)/dev.excited.dreamux`. A unit written by an earlier build has no `DREAMUX_ROOT` entry of its own, so unless one of those sources supplies it the root is `.dreamux` under the service's `HOME` (normally `~/.dreamux`). A foreground `dreamux serve` used the `DREAMUX_ROOT` of the shell that started it, else `~/.dreamux`. Confirm the root by checking that `<root>/state/` holds your dispatcher directories. (2) Put `config.json` at `<that root>/config.json`: move it from the old service's `DREAMUX_CONFIG_DIR` path (found the same way) when that names another directory (keep mode 0600, create parent directories as needed); there is nothing to move when it already sits in that root. (3) Re-run `dreamux daemon install` (or `dreamux onboard` again) from a shell whose `DREAMUX_ROOT` is that same root — or unset, when the root is `~/.dreamux` — so the unit is rewritten with the right `DREAMUX_ROOT`, then run `dreamux daemon restart`. For a foreground `dreamux serve`, export the same root in the shell that starts it. Only a deliberate change of root moves durable state: with the service stopped, move the old root's `state/` to `<new root>/state/` (`run/` is volatile, `cache/` is rebuildable, and `logs/` is diagnostics, so none of them needs to move) and `config.json` to `<new root>/config.json`, then do step (3) with `DREAMUX_ROOT` set to the new root; without the `state/` move the service starts on empty state.
|
|
20
|
+
- PR #455 review round: `plugin/loader.ts`'s `constructPlugin` now validates a factory-returned plugin `name` against a safe single-segment pattern (1-64 ASCII letters/digits/dot/underscore/dash, starting with a letter or digit) and fails loading with `PluginLoadError` otherwise. `pluginStateDir` now uses `name` verbatim as the `state/plugins/<name>` directory segment instead of running it through the lossy teammate-name sanitizer, which mapped both `@acme/tool` and the unrelated `_acme_tool` to `state/plugins/_acme_tool` and passed `.`/`..` through unchanged (resolving up out of `state/plugins/`). Every built-in plugin name already conforms; a third-party `npm:` plugin whose factory returns a name outside this pattern now fails `dreamux serve` at load instead of silently sharing or escaping another plugin's state directory.
|
|
21
|
+
- PR #455 review round, two scheduler fixes. `CronJobStore.parseCronJob` no longer fails loud on a leftover `deliver` field on an old cron-jobs.json job row: like the existing `dispatcher_id` tolerance, it is now an ordinary unknown field under the persisted-shape policy, ignored on load and dropped the next time that job is rewritten; `dreamux doctor` no longer names a store file for carrying it. Separately, a missed cron fire's rearm write no longer derives its `{ enabled, next_run_at }` write from the in-memory job snapshot `SchedulerService` captured before the fire; `CronJobStore.applyMissed` now derives that write from the row as it stands at write time, inside the store's own serialized update, so a `cron.update` committed while a fire's submission was in flight can no longer be silently overwritten by a stale missed-outcome write.
|
|
22
|
+
- Core no longer rejects an Agent Runtime activity record whose `occurredAt` timestamp string is longer than 64 characters. This was the last provider-output length cap left over from R28 ("Core no longer re-validates an Agent Runtime provider's activity page against a byte/char/cursor-length budget", refactor-code-org-s2a-04); the provider's own bounds are the only bound now.
|
|
23
|
+
- A `plugins[]` object entry (`{ ref, config }`) now follows the same persisted-shape policy (R21) as the rest of config.json: an unrecognized key alongside `ref`/`config` is tolerated and ignored instead of refusing `dreamux serve`. A missing `ref` still fails loading.
|
|
24
|
+
- `dreamux uninstall` follows the existing next behavior: dry-run checks existence and reports planned removal without enumerating root contents or writing files; actual uninstall recursively removes the entire DREAMUX_ROOT, including other files and directories inside it. The foreign-retention predictor is removed. Results report removed or missing; permission and other filesystem errors still propagate, including after a successful preview. Normalized removal paths inside or containing a provider-derived operator runtime home are refused before any service mutation. Physical symlink aliases are not resolved. Current root addressing and service removal order remain unchanged.
|
|
25
|
+
- `workflow_status` and `workflow_list` no longer serve a finished run's record from a stale in-memory cache for the rest of the dispatcher's process lifetime. The scope's `WorkflowRunStore` now releases a run's cached store once its owning run settles (or once a crash-recovery pass finds it already terminal), and reads finished records one-shot from disk afterward, so a hand edit to a finished run's record.json is now reflected.
|
|
26
|
+
- A provider ref listed under the wrong kind in config.json (for example a `builtin:codex` agent-runtime provider referenced from a `channels[]` entry) now fails config validation with a `RuleViolation` naming the actual and expected kind, instead of the provider package loader's own contract error. This follows from built-in providers now registering their descriptor and implementation together as always-loaded plugins (R9): the mismatch is caught once, in config validation, for both built-in and `npm:` provider refs. No behavior change for a correctly configured install.
|
|
27
|
+
- Code organization refactor stage 2a, item 3: cron jobs are no longer capped at 128 per dispatcher/Team owner. The cap had no named failure scenario. Old cron stores are unaffected.
|
|
28
|
+
- Code organization refactor stage 2a, item 4: Core no longer re-validates an Agent Runtime provider's activity page against a byte/char/cursor-length budget, or a provider's declared capabilities against a tag-count/length or publicConfig size bound. The provider's own bounds are the only bound now.
|
|
29
|
+
- Code organization refactor stage 2a, item 5: the `dispatcher.start` Command and the `dispatcher start` server-ctl CLI verb are removed. Every configured, enabled Dispatcher already starts automatically when the daemon starts; there was no supported way to stop one independently and restart it in-process to begin with.
|
|
30
|
+
- Code organization refactor stage 2a, item 12 (R47): `dreamux serve` no longer runs its startup pre-flight that aggregated every dispatcher's pre-#233 leftover state and removed cron stores and aborted the whole process before the admin socket opened, and `dreamux doctor` no longer reports a dispatcher's leftover state as a row. A stale `channel-bindings.json`/`collaboration-spaces.json` at the dispatcher root, a pre-#233 flat `teammate/`/`team/` leaf (`records`, `turns`, `identities`, `history`, `sessions.jsonl`, `ledger`), or an agent identity record still carrying a removed field (`checkpoint`, `checkpoint_kind`, `session_ref`, `display_name`, `close_status`) are now silently ignored rather than triggering a fail-loud rebuild instruction. This is not `BREAKING:`: nothing an operator relied on to start stops working — the opposite failure mode (refusing to start) is what's removed. A cron store with an unrecognized version and an identity still keyed by the pre-#148 `provider_ref` format still raise a named error when actually read (a dispatcher's scheduler start, `dreamux doctor`'s unchanged cron rows), but that failure is no longer aggregated into a whole-process startup refusal — it now fails only the one dispatcher whose store or identity is affected, the same as any other per-dispatcher start failure.
|
|
31
|
+
- Code organization refactor stage 4b item 1: moved AgentIdentityStore and AgentEntityCollectionStore onto TransactionalStore, deleted AgentRuntimeStateStore's own mutation tail in favor of the store's serialized update, and fixed four second-authority duplicate-reader sites (dispatcher-root identity shared between Dispatchers and DispatcherService, TeammateCollection.mustIdentity, reprepareDeletedManagedWorktree's nested-update pattern, and TeammateCollection.buildEntity minting a second, never-loaded AgentIdentityStore instead of reusing the one that created or read the entity). AgentIdentityStoreBinding no longer carries onPersisted; every create/update/upsert call now threads it explicitly. Named behavior change: a name lookup for a live TeamMate (mustIdentity) now answers from that TeamMate's own live identity instead of a fresh file read, so it can no longer race a runtime that just committed a status transition; record.json's shape is unchanged.
|
|
32
|
+
- Code organization refactor stage 4b item 2: moved TeamStore onto a per-Team TransactionalStore, replacing its exclusive-write-plus-readback create path with a load-decide-write inside the store's own serialized queue and its manual write queue with the store's tail. TeamService now holds its Team's TransactionalStore handle directly instead of a caller-refreshed record field, and the Team read model answers leader_state from a live TeamService's own in-memory identity before falling back to a one-shot file read. record.json's shape, TeamStore's/TeamService's public methods, and Command/MCP-visible behavior are unchanged; only the anti-resurrection mechanism moved from 'merge against disk' to 'one in-memory owner per Team, merging against its committed value' (already an accepted operator ruling, 2026-09-19).
|
|
33
|
+
- Code organization refactor stage 4b item 3: moved CronJobStore onto a single TransactionalStore<CronJobFile>, inlining the version-mismatch/missing-file-default check that JsonDocumentStore used to run. Dropped the operator-only nested `action` Command field from scheduler.cron.create/update. Its former `action.prompt` was consumed as the scheduled submission text; the Commands now take the same text as top-level `prompt`, while persisted jobs keep their internal `action.prompt` and stopped writing `dispatcher_id` in scheduler.cron.list/create/update/delete results, the cron_* MCP tool results, and newly written cron-jobs.json job rows; a leftover dispatcher_id on an old file now loads as an ordinary unknown field under the persisted-shape policy (tolerate unknown fields, reject only wrong types and missing fields) instead of being required and cross-checked against the store's own dispatcher id. Old cron-jobs.json files carrying either field still load. Named behavior change: deleting a Team's cron store file on dissolve now loads the file first (TransactionalStore.remove()'s own contract), so a cron-jobs.json that fails its version/shape check at that exact moment now fails the delete too, instead of always succeeding via a direct unlink(); this is one more of dissolve's ordinary collected cleanup-step failures, handled the same way as any other resource that would not close.
|
|
34
|
+
- Code organization refactor stage 4b item 4: moved WorkflowRunStore onto a per-run TransactionalStore<WorkflowRunRecord | null>, inlining the version-mismatch/missing-file-default check that JsonDocumentStore used to run, and moved WorkflowJournal.create onto dreamux-utils' publishFileExclusive. run.ts keeps mutating its working draft before each persistence point; the queue that used to serialize those writes is gone (each run's own TransactionalStore now serializes them), but the reclassification of a failed journal/store write into WorkflowPersistenceError (which executeAgent's catch depends on to tell a storage failure apart from an ordinary agent failure) is preserved under a renamed helper. Named behavior change: WorkflowRun.snapshot(), which backs workflow_status/workflow_list for a live run, now returns the run's last committed (written) value instead of the in-progress in-memory draft between a mutation and the write that persists it. Freshness here was already documented as unspecified, so this narrows rather than changes the contract, but it is a real, observable difference from today's snapshot timing. record.json's shape, the journal's shape, and every public method signature are unchanged. `this.record.x = …` in-place mutation and `AgentCall.record` as a live alias into `this.record.agents[]` were both kept as they were, rather than becoming a pure builder passed to `store.update()` and an index respectively; traced for data loss (each write clones at commit time and every store now serializes on its own `TransactionalStore` queue) and found none.
|
|
35
|
+
- Code organization refactor stage 4b item 5: config.json now tolerates unknown top-level, dispatchers[], channels[], agents[], and dispatchers[].workspace keys instead of rejecting them; wrong types and missing required fields are still rejected. Per R21's tolerate-unknown-fields policy, this also removes every remaining named rejection of a retired config.json key: the top-level codex block, dispatchers[].runtime, the dispatchers[].feishu/.codex legacy-shape pre-check, and channels[].collaborationSpace are no longer special-cased and now load as ordinary tolerated unknown fields like any other retired key.
|
|
36
|
+
- Code organization refactor stage 4c: added two Commands, config.agents.get and config.agents.replace, reachable through admin.sock and any Channel's in-process invoke port like every other Command. config.agents.get returns the current agents[] in file shape with every secret-named value emptied; config.agents.replace validates and writes a whole new agents[] array, matched to the existing one by id (submitting an empty secret-named value keeps the stored value). A replace takes effect for the next runtime launch only — a runtime already running keeps what it launched with. The first replace re-serializes config.json with two-space indentation and the submitted agent order; other values, and dispatchers[], are written unchanged. Neither Command is dispatcher-scoped, and dispatchers[] still has no Command. No persisted-format change.
|
|
37
|
+
- team.history's grep filter now ignores leading/trailing whitespace in the search term, matching teammate.history's existing behavior.
|
|
38
|
+
- Code organization refactor stage 6a item 2a: renamed the class service/agent/service.ts's TeammateService -> AgentService and service/agent/runtime-generation.ts's TeammateRuntimeOwner -> RuntimeGeneration (propagated to every consumer's type-only imports across the package, e.g. dispatcher-service/ and team-service/; service/agent/factory.ts is left on the old name, since converting it into the per-dispatcher AgentServiceFactory is stage 6a item 2b). This one class now serves three roles (the Dispatcher's own Agent, a Team's leader, and an ordinary TeamMate) but hardcoded 'TeamMate' in every thrown-error message regardless of which role it was closing, locking, or starting; a Team's leader.close() (dissolve) or a Dispatcher agent's runtime-generation failure produced a wrong 'TeamMate ...' message. Added agentRoleNoun(role, name) (service/agent/service-types.ts, to avoid a service.ts <-> runtime-generation.ts import cycle) and used it at every such error message in these two files, so a Dispatcher agent now reads 'the Dispatcher agent ...' and a Team's leader reads 'Team leader "<name>" ...'. Most of an ordinary TeamMate's own wording is unchanged (byte-identical, same JSON.stringify(name) quoting); four messages that previously named 'TeamMate' without the entity's name now include it for every role, including teammate, as a byproduct of routing through the one shared helper: the close-note validation message (this is also a real fix, not just wording -- close() runs on a Team's leader too on dissolve, per team-service/closing.ts, so the pre-existing text was already wrong there), the two closed-with-no-closed_at invariant-violation messages, and the stale-lock-token invariant-violation message. Also deleted two dead-by-construction fields: ownsWorktreeOnClose (its condition, restricted to the three real construction sites, was already exactly implied by identity.worktree.mode === 'managed' && identity.worktree.cleanup === 'delete-on-close' alone -- the Dispatcher agent and every Team leader are always worktree.mode 'reuse-cwd', and a Team-scoped TeamMate's spawn path always forces a reuse-cwd shared workspace, so the flag never disagreed with the identity's own facts since it was introduced) and assertIdentityScope plus runtime-id.ts's three assert*Scope helper functions (the identity was already read by a store scoped to the correct directory by the owner that materialized it, so re-checking scope on every ensureStarted() call had no failure scenario construction-time scoping doesn't already rule out). No persisted format, public API, or MCP/Command contract changed.
|
|
39
|
+
- Code organization refactor stage 6a item 3: the completion notice the Dispatcher's own agent receives after one of its Team's leaders settles a team.create-with-prompt turn (R16, deliverCompletionToDispatcher) always read 'TeamMate <name> has finished its task,' misdescribing a Team leader as a TeamMate. Added role: TeammateRole to TeammateCompletionFact (service/completion-router/index.ts) and threaded the producing AgentService's own role (fixed for its lifetime, passed once into EntityTurnCoordinatorOptions at construction) through service/agent/turn.ts's fact construction. service/agent/completion-renderer.ts's teammateStatusLine now words the sentence by the producer's actual role: a Team leader completion reads 'Team leader <name> has finished its task.' (and failed/stopped variants); an ordinary TeamMate completion, the only other role a delivered completion is produced by today, is unchanged. The Dispatcher-role branch is exhaustive but currently unreached -- the Dispatcher's own agent never attaches a completion-delivery closure to its own turns today, so 'The Dispatcher agent has finished its task.' is not yet observable, but is now correctly worded for when it is. workflowStatusLine is untouched. No persisted format, public API, or MCP/Command contract changed.
|
|
40
|
+
- Code organization refactor stage 6a item 4: deleted TeammateRuntimeOwnerCallbacks, the function-valued dependency bag RuntimeGeneration used to reach back into AgentService's private phase field via isActive()/markClosing(). RuntimeGeneration.ensureStarted() now takes no phase callback and simply resolves once the runtime is live or throws; it no longer self-aborts mid-launch. AgentService now owns the race check itself through one new private method, ensureRuntimeStarted(), used by every one of its three callers of runtimeGeneration.ensureStarted() (admitToRuntime, activate(), the locked Workflow submit path): it checks this.phase itself before calling ensureStarted() (replacing the old pre-lease isActive() check) and again immediately after ensureStarted() resolves, stopping the runtime and reporting not-active itself when a close raced and won the awaited start (replacing the old post-start isActive() self-abort). The one case that used to call markClosing() — startFromRecord's rollback branch where the rollback's own runtime.stop() also fails after a native start failure — now has RuntimeGeneration throw a new named error, RuntimeTerminationUnproven (an AggregateError subclass carrying the same start/stop failure pair), which ensureRuntimeStarted() catches to set AgentService's own phase to 'closing' before rethrowing; this is the mutation every caller previously got for free via the callback, so centralizing it is what keeps a runtime whose termination could not be proven from being handed out by a later ensureStarted() call, which treats any non-null runtime handle as already live and never re-examines it. Internal control-flow refactor moving which class owns a phase decision. One caller-visible admission-outcome shape changed for the narrow start-races-close window: a submission that used to reject with an 'is closing' Error now resolves as an ordinary {status:'stopped'} TurnAdmission (the same shape a submission to an already-stopped recipient always gets), so its display reason becomes 'the agent runtime is not running' instead of the old message text. The issue #63 non-blocking submit ordering (runtime.submit fires synchronously before being handed to the admission continuation queue) is untouched.
|
|
41
|
+
- Code organization refactor stage 6b item 2 (R48): deleted the team.hooks.created plugin hook end-to-end, together with its background scheduling (fireCreated), its InFlightWork-backed in-flight tracking, and the Dispatcher shutdown drain that awaited it. A plugin that tapped this hook loses it; nothing in this tree taps it today, and Dispatcher shutdown no longer waits on it.
|
|
42
|
+
- Fixed a bug: team.create with a prompt, called through admin.sock or a Channel's team.create Command, no longer pushes the leader's first-turn completion back to the Dispatcher Agent -- only the Dispatcher's own team MCP create tool call does now, matching the deliverCompletionToDispatcher contract team.submit has always had. DispatcherService.createTeam, TeamCollection.createFromRequest/createAtCandidate, and TeamService.createNew all take an explicit deliverCompletionToDispatcher boolean threaded the same way teamId/payloadHash already thread through; service/team/commands.ts's create.execute() passes false, service/team/mcp.ts's create() (the Dispatcher's own MCP call) passes true. initial-prompt.ts's submitInitialLeaderPrompt is folded into TeamService.createNew, which now calls submitToLeader(...) directly and checks the returned TurnAdmission itself, so the initial turn runs through the same admit-fenced submission path (and the same completion-initiator wiring) as every later turn; resolveTeamLeaderCompletionDelivery (completion-targets.ts) is deleted as the second initiator-to-delivery mapping it duplicated. TeamLeaderCompletionTargets itself, used for a Team's own members' completions reporting to the leader, is unrelated and unchanged.
|
|
43
|
+
- Code organization refactor stage 6b item 6 (R10): rewrote TeamClosing's dissolve/close sequence. TeamClosingDeps drops the leader()/closeLeaderForDissolve() callback pair; dissolve(input, leader), abandonCreation(input, leader), and stopForHost(leader) now all take the TeamLeader as a plain AgentService | null value, read once by TeamService (its own leader_ field) at each call site instead of asked for through a callback. stopChildRuntimesForDissolve and the child-stopping half of stopRuntimesForDissolve are unified as stopChildrenForDissolve() and stopAllForDissolve(leader), both still built on the one shared, non-throwing stopChildRuntimes(failures) helper so a member-stop failure and a leader-stop failure still aggregate into one thrown error exactly as today; closeResources (now closeChildren) drops its own redundant repeat of workflows.closeAdmission()/stopAll() and scheduler.stop() -- both were already covered, idempotently, by stopAllForDissolve immediately before it, so the repeat inside closeResources answered nothing new. abandonCreation's adoptDurableLeader callback is removed: TeamService.createNew's own catch adopts an orphaned durable leader itself, before calling abandonCreation, and passes the result as the leader argument; a failure during that adoption attempt is now logged rather than folded into the thrown AggregateError alongside the original creation-failure cause. The number, order, and gating of every worktree assessment, the dissolve refusal contract, the dissolve receipt shape, and team.closed timing are unchanged. TeamService forgets this.leader_ (checked once, after closing.dissolve() settles) whenever the new AgentService.isSafeToForget() is true, not only once AgentService.isRetired() confirms the close's own durable write also landed: a leader whose close() fully released runtime authority (stopRuntime() succeeded) but whose identity write then failed is still forgotten and rebuilt fresh from disk on next use, matching the pre-refactor unconditional pre-close null. isSafeToForget() stays false for the different case of an unproven runtime termination (a failed start whose own rollback stop could not prove the native runtime dead), because that runtime might still be alive and must not be silently forgotten. A leader whose close was never reached at all (an earlier stop or assessment refused first) is left live and reusable exactly as before.
|
|
44
|
+
- workflow.run and the workflow_run MCP tool now report BAD_REQUEST instead of INTERNAL for an empty resolved script, a scriptPath that is not a regular file, or a scriptPath over the read-size cap -- each is the caller's own mistake, not a server failure. Missing script/scriptPath and an out-of-range max_concurrency already reported BAD_REQUEST and are unaffected.
|
|
45
|
+
- dispatcher.submit and team.submit now report SUBMIT_FAILED / SUBMIT_AMBIGUOUS instead of TEAM_SUBMIT_FAILED / TEAM_SUBMIT_AMBIGUOUS for a failed or ambiguous submission. The shared submit receipt covers both callers, and a Dispatcher's own Agent submission has no Team involved, so the wire code no longer names Team.
|
|
46
|
+
- A dispatcher's own admission gate and its workflow-run admission check now report SERVER_SHUTTING_DOWN instead of INTERNAL when they refuse a request, matching the code the process-wide shutdown fence and a dispatcher's Channel port already reported for their own closed-scope refusals.
|
|
47
|
+
- ChannelService.closeAll() now collects and reports every channel session's close failure instead of logging and swallowing it. A Dispatcher's own close already wraps this call in its aggregate shutdown-failure collector, so a channel provider's close() throwing during a dispatcher stop or the daemon's graceful shutdown now surfaces as a shutdown failure where it was previously silent.
|
|
48
|
+
- dispatcher.status's status field and each dispatcher.list entry's status field now always report the runtime vocabulary (declared/starting/ready/degraded/stopping/stopped). Previously dispatcher.list reported the identity vocabulary (starting/running/degraded/closed/stopped) for both a live and a cold dispatcher, and dispatcher.status mixed the two: the runtime vocabulary while live, the identity vocabulary while cold. A caller branching on 'running' or 'closed' needs to branch on 'ready' or 'stopped' instead. Two cases that previously reported 'stopped' now report 'declared': a dispatcher with no persisted identity yet, and a materialized dispatcher whose agent is built but has never activated its runtime in this process (previously this fell back to the persisted identity's status, which could be stale after a crash).
|
|
49
|
+
- team.status already refused during a dispatcher stop; team.history and team.list now refuse the same way (SERVER_SHUTTING_DOWN) instead of answering from a Team's stale records. All three are per-Team operational reads and are now gated uniformly; dispatcher.list, dispatcher.status, and channel.list are unaffected and remain observable through a stop.
|
|
50
|
+
- TeammateCollection now takes its admission fence at construction and gates every TeammateOps verb (spawn/send/close/list/status/history/last/getCapabilities) internally, uniformly, instead of wrapping only the three mutating verbs at the dispatcher-service boundary. A dispatcher-root teammate.list/.status/.history/.last call now refuses with SERVER_SHUTTING_DOWN during a Dispatcher stop instead of answering from stale records; a Team-scoped one now refuses with TEAM_CLOSED during that Team's dissolve, and with SERVER_SHUTTING_DOWN during a Dispatcher stop, instead of always answering. dispatcher-service/teammate-ops.ts is deleted; DispatcherService.teammates returns the collection directly.
|
|
51
|
+
- The `dreamux dispatcher start` CLI verb is removed (R11: no in-process restart after a dispatcher has closed). It never reached a working code path (no `'start'` case existed on the server-side dispatcher-control method resolver), but its removal is a CLI surface change an operator's script could still reference. `dreamux dispatcher status`/`list` are unaffected.
|
|
52
|
+
- A dispatcher's shutdown stops Workflows, each Team, each materialized TeamMate runtime, and the dispatcher's own Agent runtime, joins every already-admitted operation, then repeats that same runtime sweep once more before closing Channels last. The sweep runs before the admitted-work join as well as after: an admitted call can itself be starting a runtime with no timeout of its own, so joining first would wait on exactly the runtime the sweep exists to stop, and stopping it first is what makes that runtime's own teardown unblock the join. The second, post-join pass catches a pre-fence admission that only starts - or, since a runtime stop fences an entity's admission for its own convergence only and never durably closes it, revives - a runtime after the first pass already read past that entity. Separately, TeammateCollection's and TeamCollection's own entity-construction paths check the dispatcher's closing fact the instant they register a brand-new entity and self-close its runtime immediately, preempting a spawned TeamMate's first submission outright (a Team's leader can already have taken its first prompt by the time the collection notices, so it only stops that runtime as soon as it can) - a different race, from a new entity's own construction, than the two-pass sweep covers.
|
|
53
|
+
- Code organization refactor stage 6f, item 2 (R48): wired the new Dispatcher.hooks.teammateLaunch hook through TeammateCollection.buildEntity (the one construction path behind spawn, createLocked, and reopen), the dispatcher-root and Team-scoped TeammateCollections, and the TeamMate system prompt, so a plugin can now append instructions and skill sources to every ordinary TeamMate's launch the same way it already can for the Dispatcher and a TeamLeader.
|
|
54
|
+
- Code organization refactor stage 6f, item 3 (R48): wired the new Dispatcher.hooks.createTeam hook into TeamCollection.createFromRequest, which now runs it on a non-replay team.create request's own wire-shaped params before doing the repo/skill-source translation admin.sock and the Team MCP delegate used to each do for themselves; both callers now build one TeamCreateCommand and forward it unchanged.
|
|
55
|
+
- Code organization refactor stage 6f, item 4 (R50): added platform/paths.ts's pluginStateDir(name), a host-owned per-plugin durable state directory under state/plugins/<name>/, and wired it into every plugin's server(host) call as ServerHost.stateDir. Purely additive; no existing path moves or changes shape.
|
|
56
|
+
- Code organization refactor stage 6f, item 1 (R52): renamed the Dispatcher hook object's beforeLaunch key to launch and the Team hook object's beforeTeamLeaderLaunch key to leaderLaunch, following the matching dreamux-types rename. Internal wiring (DispatcherAgentOptions, TeamLeaderForTeamDeps, tapable debug-name labels) renamed to match; no ownership or firing-site change.
|
|
57
|
+
- Code organization refactor stage 7, item 9: `dreamux serve` now runs `cli/server.ts`'s `runServe()` in-process instead of spawning `dist/cli/server.js` as a child process, and `dreamux status` / `dreamux dispatcher list` / `dreamux dispatcher status` now call the admin socket in-process through `admin/client.ts`'s `adminJsonInvoker` instead of spawning the deleted `server-ctl` entry; `cli/server-ctl.ts` is removed. Two small, verified output differences on failure paths: (1) when the admin socket is absent or refuses the connection, the status/dispatcher commands now report `TRANSPORT_ERROR` carrying Node's own socket error (e.g. `dreamux: admin socket connection to <path> failed - connect ENOENT <path>`) instead of the old bespoke `dreamux: cannot reach admin socket at <path> — is the server running?` sentence, matching the TRANSPORT_ERROR behavior the 0.24.0 CHANGELOG already documents for every other admin caller; a Command-level failure (the server ran and refused the request) still prints the unchanged `error: [code] message` line, and the exit code (1) is unchanged in both cases. These two commands are also now subject to the shared 10-second admin-socket request timeout instead of waiting indefinitely. (2) A `dreamux serve` startup failure now prints `dreamux: <message>` through the CLI's own top-level error handler instead of the old child process's `[server] fatal: <error with stack>` line, no stack included; verified for a missing/invalid config.json (rejects with `Error`/`RuleViolation`) and a failing plugin `server` hook (wrapped as `PluginLoadError`, also an `Error`), both of which keep exit code 1 unchanged, matching every other Dreamux failure's `Error`-subclass contract. `dreamux serve --help`'s old usage text (config path, `DREAMUX_CONFIG_DIR`, run/state/logs roots, plugin ref grammar) is dropped rather than carried into the new implementation: it was already unreachable through the `dreamux` CLI, which answers `--help` before ever invoking a serve handler, and that content already lives in `.agents/domains/state-config-and-files.md` and the `dreamux-maintenance` skill's `config-envelope.md` reference.
|
|
58
|
+
- Code organization refactor stage 7, item 7 (R14): the teammate.spawn and teammate.submit Commands now reject an empty prompt, a blank agent_runtime, or a blank intent with BAD_REQUEST, matching what the equivalent TeamMate MCP tools already reject. The MCP tools' own behavior is unchanged.
|
|
59
|
+
- Code organization refactor stage 7, item 5 (R20): the teammate.spawn and team.create Commands no longer accept repo.slug (already true of the equivalent MCP tools since 0.25.0). The worktree directory and default branch already derive from the allocated entity name; existing worktrees and persisted state are unchanged.
|
|
60
|
+
- Move identity notifications, completion recipients, admission, role tools and command addressing to their actual owners. Remove dormant loader import, chmod, executable-directory probe, name/id, clock and Workflow runner construction overrides. Preserve real CLI logger/sweep extensions, channel-log creation timing and leader-tool refusal order; persisted formats and config require no migration.
|
|
61
|
+
- Plugin hook review fixes: a plugin's own interceptor added with `hook.intercept` — on `call`, `tap`, `loop`, `result`, `done`, `error`, or `register` — is guarded the same way a tap is, attributed to the plugin that registered it: on `dispatcher`, `team`, and a launch hook a throw is logged and skipped, and on `hooks.plugin.for(name)` a throw fails plugin loading with a clear error, so neither crashes `dreamux serve` or `dreamux doctor` — including a `done`/`error` interceptor on an async hook, which tapable invokes from a continuation an ordinary try/catch around the hook cannot see. Interceptor methods must be synchronous: an `async` one's rejection is logged at runtime and fails plugin loading on `hooks.plugin.for(name)` with an error naming the interceptor, and an `async` `register` leaves the tap unchanged. A launch-hook interceptor sees only an opaque, disconnected handle, never the internal launch draft, so it cannot add a skill root that skips the skill fence. An async plugin tap on the sync `dispatcher`/`team` hooks is logged and skipped, with no unhandled rejection risk. An async `server()` or `contribute()` fails loading with a clear error naming the plugin, so a tap registered after its first `await` is never silently lost. A `plugins[]` entry's `config` block for a plugin with no `config.read` is ignored, so `dreamux serve` starts.
|
|
62
|
+
- Revert #408 and restore the previous workspace and Team creation behavior.
|
|
63
|
+
- Project RuntimeActivity without reshaping, redacting text and structured JSON payloads while retaining native ids, timestamps, result and error separately. Publish and validate the four Core events with camelCase members and remove unused redacted flags.
|
|
64
|
+
- Project the new token.usage activity through the conversation projection as numeric counters with nothing to redact.
|
|
65
|
+
|
|
66
|
+
### Patches
|
|
67
|
+
|
|
68
|
+
- Release fully retired Team stores and read historical records from disk while preserving live owners, queued writes and cleanup. Keep an admitted leader dissolve on that captured owner through its asynchronous precheck and detached cleanup, even when initial construction fails. Restore non-blocking Codex inbound and final-parent behavioral coverage with explicit native-model opt-in and separate auth-free protocol checks. Retain revocable event sources and skip display DTO production without an audience while preserving durable observations. Restore actual ESLint code-line limits and public package boundary assertions. Ignore non-Team inventory names without reading or altering their contents, and keep one construction record acquisition through workspace preparation and publication. Pure retained-record reads return the committed snapshot without waiting for unrelated queued writes; writer releases still drain their captured queues.
|
|
69
|
+
- `dreamux serve` now always exits on SIGTERM or SIGINT: a shutdown that fails is logged and the process exits with status 1, and a shutdown that has not settled by a fixed deadline is cut off and the process exits with status 1, where a failed shutdown used to leave the process running. Under systemd (`Restart=on-failure`), a failed or cut-off shutdown that was not requested through `systemctl stop` therefore restarts the service, and a runtime child process can outlive a daemon cut off this way, as after a manager kill. A value refused by config validation in an online config replace is now reported as a bad request rather than an internal failure. An unknown `builtin:` provider ref in a replace payload is classified the same way, and its message no longer reads as an external-provider load failure; `serve`, `doctor` and `onboard` print the same reworded message for such a ref. The `repo` input of Dispatcher `spawn` and Team `create` carries its field descriptions and length bounds, defined once beside the repo request reader; because the canonical `spawn` and `team.create` commands validate against that same schema, an empty or over-long repo path, base ref or branch is now rejected as a bad request over the admin socket and the Channel port as well. `last` on a running agent, including a TeamMate a `send` is still reopening, reads the provider, config and session of the runtime that is running instead of the current config. Two lifecycle fixes: a Workflow handle lock taken while an agent is created is released when that agent closes before the handle is handed over, and looking up an absent Team no longer leaves a store cached for that id.
|
|
70
|
+
- Preserve existing taps when a plugin register interceptor omits its result. Reject asynchronous plugin factories and config readers with attributed load errors while observing their rejections. Reject simultaneously loaded plugin names that differ only by ASCII case; preserve the spelling of individual names, API keys, and state paths.
|
|
71
|
+
- Bind fixed runtime context once and return persistence and cleanup operations to their owning services.
|
|
72
|
+
- Team dissolve now writes the record closed immediately after its worktree precheck passes, then destroys every child service (Workflows, scheduler, members, leader) and reclaims the worktree as best-effort steps behind that commit, instead of stopping and closing children first and writing closed last. A member that dirties the managed worktree after the precheck no longer refuses the dissolve: the Team still closes, and a non-forced cleanup then only keeps the directory instead of removing it. A process that dies after the closed record lands now leaves the Team durably closed with whatever children the destroy pass had not yet reached, rather than reopening it. Persisted record shapes are unchanged.
|
|
73
|
+
- A workflow status/list read or a cron list made while its dispatcher is stopping or its Team is dissolving is now refused instead of answered: WorkflowService and SchedulerService fence those reads behind the same admission gate their mutations already crossed. A leader-scoped mutation racing both a dispatcher stop and a Team dissolve at the same time now reports TeamClosedError instead of ServerShuttingDownError, now that the TeamLeader handle is assembled from surfaces that already fence themselves instead of forwarding through 12 hand-written closures. Internally, several resources a parent used to build and hand down (the Workflow journal, the Agent admission ledger, the per-Team record handle) are now built by the module that owns them instead; persisted record and config shapes are unchanged.
|
|
4
74
|
|
|
5
75
|
## 0.25.0
|
|
6
76
|
Thu, 10 Sep 2026 04:59:50 GMT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@excitedjs/dreamux",
|
|
3
|
-
"version": "0.26.0
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Dreamux core host for multi-dispatcher agent orchestration through provider interfaces.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -26,11 +26,6 @@
|
|
|
26
26
|
"CHANGELOG.json"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@excitedjs/agent-runtime-claude-code": "0.8.0-beta.271",
|
|
30
|
-
"@excitedjs/agent-runtime-codex": "0.7.0-beta.271",
|
|
31
|
-
"@excitedjs/dreamux-types": "0.11.0-beta.271",
|
|
32
|
-
"@excitedjs/dreamux-utils": "0.6.0-beta.271",
|
|
33
|
-
"@excitedjs/feishu-channel": "6.3.0-beta.271",
|
|
34
29
|
"@modelcontextprotocol/server": "2.0.0",
|
|
35
30
|
"acorn": "^8.16.0",
|
|
36
31
|
"croner": "^10.0.1",
|
|
@@ -40,7 +35,12 @@
|
|
|
40
35
|
"execa": "^9.6.1",
|
|
41
36
|
"pino": "^9.5.0",
|
|
42
37
|
"plist": "^5.0.0",
|
|
43
|
-
"tapable": "~2.3.3"
|
|
38
|
+
"tapable": "~2.3.3",
|
|
39
|
+
"@excitedjs/agent-runtime-claude-code": "0.8.0",
|
|
40
|
+
"@excitedjs/agent-runtime-codex": "0.7.0",
|
|
41
|
+
"@excitedjs/dreamux-utils": "0.6.0",
|
|
42
|
+
"@excitedjs/feishu-channel": "6.3.0",
|
|
43
|
+
"@excitedjs/dreamux-types": "0.11.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@modelcontextprotocol/client": "2.0.0",
|