Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
36bb79b
CONFIG: add better-sqlite3 dep and ASAR unpack for dream daemon
johnhain-msft May 12, 2026
8c83bb7
ADD: TurnCompletionObserver shared type for chat turn observation
johnhain-msft May 12, 2026
fa06aac
ADD: workingMemory.consolidation schema for opt-in dream daemon
johnhain-msft May 12, 2026
fd673f5
ADD: mindMemory package with dream daemon background consolidation
johnhain-msft May 12, 2026
ca15812
ADD: TurnCompletionObserver wiring in ChatService
johnhain-msft May 12, 2026
40c75ff
ADD: WorkingMemoryComposer with sentinel-aware log composition
johnhain-msft May 12, 2026
da5feda
REFACTOR: convert MindManager.createSessionForMind call to named args
johnhain-msft May 12, 2026
3f62fa1
ADD: wire MindMemoryService into desktop composition root
johnhain-msft May 12, 2026
50fd7cc
TEST: integration coverage for dream daemon composition and multi-day…
johnhain-msft May 12, 2026
ec6b45c
CONFIG: gitignore .orchestrator/ and resources/copilot-runtime.new/
johnhain-msft May 12, 2026
0330a58
ADD: MindMemoryService.__debugGet for E2E access to per-mind daemon +…
johnhain-msft May 12, 2026
d254cec
ADD: dream daemon E2E hooks (mid-cycle sleep shim + globalThis servic…
johnhain-msft May 12, 2026
e8a8d00
ADD: expose DailyLogWriter on MindMemoryService.__debugGet for E2E ro…
johnhain-msft May 13, 2026
93cb04b
ADD: ensure-native-abi guard for better-sqlite3 across vitest and Pla…
johnhain-msft May 13, 2026
72e6463
FIX: prevent better-sqlite3 finalizer race in vitest by switching to …
johnhain-msft May 13, 2026
ec6cb56
REFACTOR: extract buildOneShotSession helper and add live-SDK integra…
johnhain-msft May 13, 2026
9e76aae
FIX: stop genesis-time errors that bricked fresh-mind console hygiene…
johnhain-msft May 13, 2026
3ce101a
FIX: pin better-sqlite3 ABI sentinel by NODE_MODULE_VERSION to catch …
johnhain-msft May 14, 2026
5fc9cdf
ADD: dream daemon opt-in UX with bidirectional log migration (v0.60.0)
johnhain-msft May 14, 2026
0ef4504
Merge master into feature/dream-daemon-memory-consolidation
johnhain-msft May 14, 2026
d307122
Merge origin/master into feature/dream-daemon-memory-consolidation
johnhain-msft May 26, 2026
c8bcfa3
feat(feature-flags): add dreamDaemon flag wiring (dev_only rollout)
johnhain-msft May 26, 2026
52582c3
feat(dream-daemon): gate runtime, IPC, services, and UI behind dreamD…
johnhain-msft May 26, 2026
645f8b3
docs(feature-flags): document dreamDaemon flag + fix AGENTS.md md-lint
johnhain-msft May 26, 2026
8f3a989
Merge upstream/master into feature/dream-daemon-memory-consolidation …
johnhain-msft May 29, 2026
d49ca00
Fix MindScaffold test mocks: rename destroy -> disconnect for SDK 0.3…
johnhain-msft May 29, 2026
2c7cee9
Merge upstream/master (v0.64.0) into feature/dream-daemon-memory-cons…
johnhain-msft Jun 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ resources/node/
resources/copilot-runtime/
resources/copilot-runtime.new/

# Orchestrator session forensics (winorch / tmux-orchestrator-win)
.orchestrator/

# Packaged Sharp runtime (generated by scripts/prepare-sharp-runtime.js)
resources/sharp-runtime/

Expand Down
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ Chamber is a desktop application where AI agents ("minds") operate as a Chief of
- **Handoff**: Agent-to-agent delegation
- **Magentic**: Manager-driven task ledger

### Memory Consolidation (Dream Daemon — experimental, opt-in)

- **Per-mind opt-in**: `workingMemory.consolidation.enabled` in each mind's `.chamber.json`. Default OFF.
- **Toggle surfaces**: Genesis wizard role screen (pre-genesis) and agent profile modal (post-genesis); never silent.
- **Bidirectional**: Disabling rolls the structured `log.md` (and any `log.legacy.md`) back to unstructured turn-by-turn markdown via `rollbackToUnstructured`. Single source of truth after rollback.
- **Failure semantics**: Toggle resolves even if rollback fails; config flip is the contract.

## Security Boundaries

### Credential Storage
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **Dream daemon ships as an opt-in toggle with bidirectional log migration** — The background memory consolidation daemon (introduced as scaffolding in 0.59.x) is now a first-class user-controlled feature. The genesis wizard exposes a single "Enable dream daemon (experimental)" switch on the role screen; the agent profile modal shows the same switch so existing minds can flip it post-genesis without touching `.chamber.json` by hand. Default is **OFF** — minds opted-in pre-genesis ship with `workingMemory.consolidation.enabled: true` written by `MindScaffold.create()`; minds opted-in post-genesis go through `MindManager.enableDreamDaemon(mindId)` which atomically patches the on-disk config (tmp+fsync+rename via the new `patchChamberMindConfig` helper that preserves passthrough fields and deep-merges only the `workingMemory.consolidation` subtree) and reloads the mind context so the new `MindMemoryService` daemon spins up against fresh providers. **Opt-out runs the migration in reverse**: `MindManager.disableDreamDaemon(mindId)` patches config → reloads (tearing down `DailyLogWriter` so the chat observer detaches and the daemon closes against a quiescent log) → calls the new `rollbackToUnstructured(mindPath)` which reads the structured `log.md`, parses every `chamber-structured-log/v1` frame, renders each as `## {ISO} — turn {turnId} ({model})\n\n**User**: …\n\n**Assistant**: …`, folds in any pre-existing `log.legacy.md` ahead of the rendered turns, atomically rewrites `log.md`, and removes `log.legacy.md` so the rolled-back mind has a single source of truth. Rollback is non-fatal (try/catch + `console.warn`): the user-visible toggle resolves successfully even if the rewrite fails — config is already flipped and the daemon is gone, so the worst case is a structured log that needs manual cleanup. Concurrent toggles for the same mindId are serialized through a `daemonToggling: Map<string, Promise<MindContext>>` in MindManager (same pattern as the existing `loading` map); rapid clicks return the same in-flight promise rather than racing the patch+reload+rollback pipeline. Three data-loss safety properties are now locked in by tests after the chamber-ui-tester E2E surfaced a real Flow 4 bug: (1) the structured-log parser at `StructuredLogFormat.ts::parseBlock` accepts an empty `model:` line (`/^model: (.*)$/` not `(.+)$`), so frames written by `ChatService` when no model was selected at turn time still round-trip cleanly through `parseLog`; (2) `ChatService.notifyTurnCompleted` coerces an empty `model` to the sentinel string `'unknown'` before serializing, so on-disk frames are always semantically complete; (3) `rollbackToUnstructured` adds a new `'no-op-malformed'` outcome — when the file has the sentinel and non-empty content but the parser produces zero turns AND `parsed.malformed > 0`, the rewrite is refused (file preserved byte-identical, warn logged) so unparseable history is never silently overwritten with an empty file. Tool surface for the renderer: `mind:setDreamDaemon` IPC channel (desktop adapter routes to `enable/disableDreamDaemon`, browser shim returns `unavailable`), `dreamDaemonEnabled: boolean` added to `AgentProfile` (populated from `loadChamberMindConfig`), `Switch` component in `AgentProfileModal` mirrors the role-screen ARIA pattern (`role="switch"` + `aria-checked` + sky-500/slate-700 colors). Validated by 11 `rollback.test.ts` scenarios (convert N frames, fold legacy + remove file, no-op variants for missing/empty/no-sentinel logs, idempotency, atomicity under synthetic rename failure, zero-frames sentinel-only + legacy → legacy preserved, zero-frames sentinel-only no-legacy → empty file, **Flow 4 regression: empty-model frame round-trips through rollback without data loss**, **all-malformed frames → preserved byte-identical**), 8 `MindManager` `enableDreamDaemon`/`disableDreamDaemon` tests (patch+reload happy paths, throw-on-missing, MindContext return, per-mindId serialization with promise identity, rollback ordering `['patch','reload','rollback']`, no-rollback-on-enable, disable-resolves-even-when-rollback-throws), and a live chamber-ui-tester Electron E2E driving the real Copilot SDK through all four flows (genesis OFF, genesis ON, post-genesis OFF→ON, post-genesis ON→OFF with rollback). The entire stack is also gated behind a new `dreamDaemon` app feature flag (stable/insiders both `false`, dev `true`) so stable and insiders builds do not construct `MindMemoryService`, accept `mind:setDreamDaemon` IPC mutation, write `workingMemory.consolidation.enabled: true` from genesis, or include `log.md` content in the system prompt — even for minds whose `.chamber.json` was opted-in under a dev build. Refs #288.

### Fixed

- **Load marketplace-installed skills in SDK sessions** — Mind sessions now pass each mind's `.github/skills` parent directory through `skillDirectories` when creating or resuming Copilot SDK sessions, so the SDK `skill` tool can discover marketplace-installed skills like `ttasks` and `automation`.
Expand Down Expand Up @@ -157,6 +161,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **Pin the packaged Copilot runtime to 1.0.45** — Updates the committed root and packaged `chamber-copilot-runtime` `@github/copilot` pins to match the CLI binary version expected by the package smoke check while keeping `@github/copilot-sdk` pinned at `0.3.0`.

## v0.59.8 (2026-05-13)

### Fixes

- **Stop the two genesis-time errors that bricked fresh-mind console hygiene** — Two production-blocking defects surfaced during manual testing of `feature/dream-daemon-memory-consolidation`: every fresh mind logged `[MindScaffold] Capability bootstrap failed (non-fatal): Error: Upgrade skill not found in genesis repo`, and `[WorkingMemoryComposer] log.md is unstructured` repeated on every system-prompt rebuild. Root causes: (1) Epic #67 moved the upgrade skill from `ianphil/genesis` to `ianphil/genesis-frontier` in commit `17580f5` but `MindScaffold.GENESIS_SOURCE` was never updated; (2) `MindScaffold.createStructure()` wrote a zero-byte `log.md` placeholder which violated the chamber-structured-log/v1 sentinel contract, so `WorkingMemoryComposer.readLog` warned and skipped on every prompt rebuild until the first `DailyLogWriter` rotation fired. `MindScaffold` now points at `ianphil/genesis-frontier@main`, the upgrade-skill error message names the repo and branch (`Upgrade skill not found in ianphil/genesis-frontier@main`) so operators can immediately see which coordinate was searched, and `createStructure()` pre-seeds `log.md` with `<!-- chamber-structured-log/v1 -->\n\n` (matching the byte-level format `DailyLogWriter.seedFreshLog` emits) before the `WORKING_MEMORY_FILES` placeholder loop runs — the loop's `existsSync` guard then skips the seeded file. The genesis prompt is also rewritten to drop log.md as a write target (the file is reserved for `DailyLogWriter` frames) and the SOUL.md "Continuity" section now reads "Your turn-by-turn history is preserved automatically; you do not write to it." `WorkingMemoryComposer.readLog` downgrades the unstructured-log message from `warn` to `info` so SRE dashboards don't alert on pre-existing minds whose `log.md` rotates lazily on the first turn. Validated end-to-end with a new tmpdir integration test (`tests/integration/mindScaffold.integration.test.ts`, 7 tests) that drives the full `MindScaffold.create()` against `mkdtempSync` with a fake `GitHubRegistryClient` and a fake `CopilotClientFactory`, asserting the sentinel-prefixed `log.md`, `registry.json.source === 'ianphil/genesis-frontier'`, the on-disk upgrade.js, no `log.legacy.md` after a `DailyLogWriter.write()`, and zero `WorkingMemoryComposer` warnings for a fresh mind. The 7th test locks the cross-cutting migration story for users upgrading into this release: an existing pre-fix mind (unstructured `log.md` on disk) → composer fires `info` (not `warn`) on the next system-prompt rebuild → `DailyLogWriter` rotates the legacy content to `log.legacy.md` and seeds a fresh sentinel-prefixed log on the first chat turn → composer is silent on subsequent rebuilds. Live Electron smoke (chamber-ui-tester driving the genesis wizard against an isolated `CHAMBER_E2E_GENESIS_BASE_PATH` tmpdir) confirmed both error patterns are gone and the on-disk contract holds end-to-end.

## v0.59.7 (2026-05-12)

### Fixes
Expand Down
47 changes: 45 additions & 2 deletions ai-docs/feature-flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,14 @@ Expected shape:
"stable": {
"switchboardRelay": false,
"byoLlm": false,
"chamberCopilot": false
"chamberCopilot": false,
"dreamDaemon": false
},
"insiders": {
"switchboardRelay": true,
"byoLlm": true,
"chamberCopilot": true
"chamberCopilot": true,
"dreamDaemon": false
}
}
}
Expand Down Expand Up @@ -108,6 +110,7 @@ normal app runs or release builds.
| `switchboardRelay` | remote | remote | Hides the activity-bar relay entry point and route. |
| `byoLlm` | remote | remote | Hides BYO model settings and disables desktop BYO runtime/IPC usage. |
| `chamberCopilot` | remote | remote | Wires the chamber-copilot ACP provider and `cli_*` tools. |
| `dreamDaemon` | remote `false` | remote `false` | Dev-only. Gates the working-memory consolidation daemon, the per-mind opt-in toggle, and the prompt-path use of consolidated memory. |

## Local development flags

Expand All @@ -126,6 +129,7 @@ export const DEV_FEATURE_FLAGS = {
switchboardRelay: true,
byoLlm: true,
chamberCopilot: true,
dreamDaemon: true,
};
```

Expand Down Expand Up @@ -156,6 +160,45 @@ to mind tool providers. Stable builds also ignore the legacy
`chamberCopilotEnabled` key in `~/.chamber/config.json`; users cannot turn this
surface on locally.

## Dream Daemon

`dreamDaemon` gates Chamber's working-memory consolidation surface (the
"dream daemon") and the prompt-time use of the consolidated memory it produces.
The flag rolls out as **dev-only**: both `channels.stable` and
`channels.insiders` are `false` in `docs/flags/v1/flags.json` so external
testers do not see the surface yet; `DEV_FEATURE_FLAGS.dreamDaemon = true`
keeps local development behavior unchanged.

When disabled:

- `apps/desktop/src/main.ts` does not call `buildMindMemoryService`, so the
daemon, scheduler, and SQLite-backed memory store are never constructed.
The `__chamberMindMemoryService` E2E global is also not exposed.
- `IPC.MIND.SET_DREAM_DAEMON` rejects with `"Dream Daemon is not available in
this build"` when the renderer asks to enable. Disable requests still pass
through so a stable build can clean up persisted opt-in state from an
insiders run.
- `genesis.create` coerces `enableDreamDaemon: false` server-side before
calling `MindScaffold.create`, regardless of the renderer payload. The
newly written `.chamber.json` always has
`workingMemory.consolidation.enabled: false`.
- `MindManager.enableDreamDaemon` throws before any mind lookup as a
defense-in-depth gate behind IPC.
- `IdentityLoader.resolveComposerConfig` forces `enabled: false` in the
composer config it returns, regardless of the per-mind `.chamber.json`.
Persisted caps (`lastKTurns`, `perTurnMaxBytes`, `memoryMaxBytes`) are kept
faithful so a future flip-on does not lose the user's settings.
- `MindProfileService.getProfile` reports `dreamDaemonEnabled: false` even
when `.chamber.json` says `true`, so the (now-hidden) UI never sees a stale
ON state.
- `RoleScreen`, `GenesisFlow`, and `AgentProfileModal` hide the
dream-daemon Switch / toggle row and coerce `enableDreamDaemon: false` at
every emit boundary in the renderer.

The asymmetry on the IPC and `MindManager` gates is deliberate: enable is
gated, disable is always allowed. A stable build must be able to clean up
opt-in state for minds that were enabled under an insiders build.

## Adding a new feature flag

Use this checklist when introducing a flag for a feature still under
Expand Down
Loading