The panel stored notes, todos and plans inside one shared JSON file that
six unrelated domains also wrote to, synchronised itself through window
CustomEvents, and could only read plans. It is now Project knowledge:
server-owned storage with explicit routes, a store with rollback, a
section sidebar, plans that open and edit in place, and search across
all of it.
Notes and plans the user pins travel with every message sent in that
project. Pinning is project state, not an attachment to one message, so
it holds until unpinned and the work status panel names what is riding
along and can detach it.
Agent memory is added alongside, in two scopes: what is true about the
user, and what is true about this codebase. The split is not cosmetic —
a wrong project fact costs one project and is noticed, while a wrong
global fact quietly shapes every session everywhere and the user has no
code to check it against. It stays separate from notes so an agent
mistake cannot land in what the user wrote. Sessions receive an index of
titles only; bodies are read on demand, because an index carrying full
text grows until it crowds out the conversation.
Deciding what a session must be told, and whether it has been told, now
lives on the server. The client owned it before, which meant sessions
started without a UI — scheduled tasks, sessions the agent dispatches —
received nothing at all, and a tab's record of what it had sent outlived
the conversation: after compaction the agent no longer held the block
while the tab went on believing it did. What was delivered is recorded
in the session's own metadata, and compaction restores it through the
runtime that already restores pinned messages, in the same turn.
Agent memory ships dark behind OPENCHAMBER_MEMORY_ENABLE: unset, there
is no tool, no routes, no session index, no settings row and no panel
tab. Absent rather than switched off, so nothing invites turning on a
feature that has not been announced. Pinned notes and plans are
unaffected and ship as normal.
* fix(scheduled-tasks): claim schedule occurrences across server instances
Two OpenChamber servers sharing project config each armed timers and both
dispatched the same daily/weekly/cron/once slot (#2710). Claim the occurrence
in shared config under a cross-process write lock before creating a session.
Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
* fix(scheduled-tasks): harden occurrence claim failure and lock ownership
Address PR review blockers: release running-slot bookkeeping when claim
throws, avoid silently dropping an armed occurrence after a due-slack sync,
verify lock-file ownership on release, and cover real on-disk lock behavior.
Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
* fix(scheduled-tasks): always release running slot on state-write failures
Wrap runTask bookkeeping in finally so claim, manual-start, and completion
lock timeouts cannot stuck-run a task; drop the diskNext claim guard that
suppressed later occurrences; recover unparseable locks via mtime age.
Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
* fix(scheduled-tasks): stop re-arming past nextRunAt and clear stuck running
Only schedule future nextRunAt values so once-task losers and claim-failed
paths cannot spin delay-0 retries. Clear past once nextRunAt on claim, and on
completion-write failure retry terminal status so manual runNow still returns
the session instead of a hard 500 with lastStatus stuck running.
Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
* fix(scheduled-tasks): release write chain on lock acquire timeout
withProjectWriteLock left the in-process promise chain pending when
acquireProjectFileLock timed out, wedging every later project write and
stranding runTask before finally. Always release the chain; surface
persistError on run; record once claim failures in task state.
Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
---------
Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
Adds markdown-based scheduled-task definitions ("loops") discovered from
.agents/loops/*.md (project scope, ancestor directories up to the
worktree root) and ~/.agents/loops/*.md (user scope), mirroring the
skills discovery pattern.
File format: YAML frontmatter (name, schedule cron, enabled, model as
provider/model, optional agent/timezone) plus the markdown body as the
execution prompt. Discovery and parsing live in
scheduled-tasks/loops.js; project-config gains reconcileLoopTasks which
runs inside the project write lock on every syncProject:
- identity by task name; a loop takes over a matching task, preserving
its id and runtime state (markdown wins on conflict with JSON)
- tasks whose loopFile is gone are unscheduled; JSON tasks are never
removed
- new loops are created under deterministic loop:<scope>:<name> ids
- project scope shadows user scope on name collisions
- malformed files are skipped with a warning and never block valid ones
Runtime state stays in the project config/state store; it is never
written to the markdown files. Module documentation updated with the
file format and reconciliation rules.
Fixes#2583
Resolves armed slash-command objectives from authoritative templates before dispatch
Applies OpenCode argument expansion for goal metadata in UI and scheduled tasks
Falls back to the raw invocation when command details are unavailable
Add a shared OpenChamber control service with two thin adapters — a native
`openchamber` tool injected into managed OpenCode, and new CLI commands — so
users can manage parallel sessions, worktrees, and scheduled tasks
conversationally through agents or from the terminal.
Control plane:
- New openchamber-control service owning a fixed action contract:
projects.list, models.list, session list/create/send/fork/status/messages,
and schedule list/create/run/delete/toggle. Session and worktree deletion
and project registration are deliberately not exposed.
- New openchamber-sessions module owning create/worktree/prompt orchestration,
Goal Mode dispatch, wait semantics (initial idle never counts as completion;
timeout and cancellation are failures), and explicit partial-failure results.
- Scheduled-task logic extracted into a service shared by routes, CLI, and the
agent tool.
Agent tool:
- Managed OpenCode gets a materialized plugin registering one typed tool with
a loopback-only callback, per-child ephemeral bearer (timing-safe, never
persisted or logged), and abort propagation into the service.
- The ~1.5k-token schema applies progressive disclosure: short descriptions,
server-side validation returning actionable usage errors, and intent
guardrails — created sessions/tasks are user-facing work (not age
self-delegation); worktree/goal/agent/variant/wait are omit-by-default;
dispatches produce no completion notification, and later result r
to session.messages, which now returns the authoritative sessionStatus.
- session.create without a user-named model picks from favorites/re
send/fork omit the selection and the service reuses the target session's
last user-message model, agent, and variant before falling back t
- An "Agent control tool" setting (default on, Save + Reload to apply)
disables plugin injection entirely.
CLI:
- New `openchamber session`, `schedule`, `projects`, and `models` commands
with automatic instance targeting, --wait/--timeout/--last-assist
worktree flags, and Goal Mode, preserving interactive, non-TTY, --quiet,
and --json contracts. The control HTTP timeout derives from the w
instead of the 4-second default.
UI:
- New built-in "Schedule a Task" starter (/schedule-task) running a
dialogue that defines a task and offers to create it via the tool after
explicit confirmation; Craft a Goal and Feature Planning gain the
handoff offer, and guided starters reserve the question tool for concrete
option choices. Localized in all 10 locales, migrated into custom
starter lists, hidden on VS Code.
- Sidebar shows CLI/agent-created sessions live via the control eve
- openchamber tool calls render with per-action titles and metadata.
The distillation moved from PlanView into setSessionGoal — the shared
choke point for every goal source (composer, fork dialog, plans) — and
into the scheduled-tasks goal creation server-side. Any objective over
5000 chars is distilled into completion criteria by the small model; on a
transient failure a head+tail excerpt keeps the intent (top) and the
acceptance criteria (bottom) with a trim marker, plus a toast. The
working agent always received the full prompt in chat — only the auditor
is bound by the limit.
Compaction fixes (observed in a real long run):
- the summary message's zeroed tokens froze the goal counter at its
pre-compaction value; segments now close with the previously displayed
total as a continuity floor
- audits and continuations after a summary tail now take execution params
(provider/model/agent/variant) from the newest non-summary assistant
turn instead of inheriting agent 'compaction' and the summarize model
File-backed objectives:
- the objective text lives in <data-dir>/goals/<sessionId>.md, keyed by
session id (one goal per session, a new goal overwrites the file);
metadata carries only an objectiveFile flag so session.updated fanout
stays light, and never a path — ids are pattern-validated before any
filesystem access
- limit raised to 5000 chars, no snapshot field: the UI fetches content
via PUT/GET/DELETE /api/goals/objective/:sessionId (behind the blanket
/api auth gate), writes the file before stamping metadata, and falls
back to an inline objective when the write fails
- the loop reads the file fresh on every tick, so objectives are
live-editable mid-goal; a missing file falls back to the inline text
- scheduled goal tasks write the objective file server-side; VS Code
degrades to the audit note (route unavailable there by design)
Arm the target button in the composer and the next prompt becomes a goal:
the server keeps the session working toward it (idle tick -> small-model
audit -> continuation) until the objective is verifiably complete, blocked,
or out of budget — even with the UI closed.
Server (packages/web/server/lib/session-goal):
- event-driven loop on the global SSE hub; goal state lives in
session.metadata.openchamber.goal (merge-safe patches, stale-write guard
by goal id), so it survives restarts and syncs to every client for free
- the small-model audit (objective + last assistant turn only, language
pinned to the objective) is the sole termination authority; blocked needs
3 consecutive verdicts, audit outages tolerate one unaudited continuation
then stop the goal as resumable-blocked
- hard stops: optional token budget, auto-continuation cap (Resume grants a
fresh allowance), turn errors; user abort pauses the goal instead of
blocking it, and resuming over an aborted tail nudges immediately
- token accounting as a snapshot of the latest turn (input + cache.read +
output), goal-relative via a creation baseline and segmented across
compactions; a compaction summary skips the audit and continues
- continuations reuse the session's own provider/model/agent/variant
UI:
- three-mode target button (arm / disarm / manage dialog), informational
goal strip with inline pause/resume and an Evaluating indicator, sidebar
state glyph, objective length counter (2000-char server clamp),
read-only completed goals
- goal entry points: composer (sessions and drafts), start-new-session-
from-answer dialog, plan implement dialog (plan content becomes the
objective), scheduled tasks (Run as goal + budget)
- Settings -> Chat -> Goal: feature toggle + default token budget with
three-layer parity (web server, client persistence, VS Code bridge);
VS Code renders goal state but hides the entry points (the loop runs in
the web server only)
Notifications: per-turn "ready" notifications are suppressed while a goal
is active; settling sends one final notification (desktop, web-push, APNs
generic titles with the session name as body) honoring the completion
toggle. Error/question/permission notifications are untouched.
Docs: user guide (session-goals) in all 9 locales + sidebar entry,
scheduled-tasks cross-reference, server module DOCUMENTATION.md.
Replace the prompt-template workflow with snippet support that is compatible with opencode snippet conventions. Snippets are now stored and loaded from global and project snippet directories, including legacy pluralized paths, with frontmatter metadata for aliases and descriptions. Snippet expansion supports recursive references plus prepend and append sections, while inject sections are treated as unsupported no-ops so OpenChamber remains compatible without requiring an external plugin.
Add the snippets settings experience and remove the old prompt-template settings surface. The new settings page and sidebar support creating, editing, deleting, selecting, and describing snippets, with localized copy across every supported locale. The settings navigation now exposes Snippets with a dedicated icon and metadata.
Wire snippets into all prompt-entry surfaces that need them. Chat, multi-run groups, and scheduled task prompts now offer hash-trigger snippet autocomplete and expand snippets before sending work to OpenCode. Chat also uses an adaptive compact placeholder on mobile or narrow composer widths so helper trigger guidance stays readable in constrained layouts.
Keep multi-run aligned with grouped prompts. Multi-run sessions now use a shared title builder that handles both legacy titles and the newer g1, g2 prompt-group title format. Fusion parsing now recognizes grouped multi-run titles, scopes fusion sources to the same prompt group, and creates fusion sessions under the matching group so outputs from different prompts are not mixed accidentally.
Harden the icon sprite pipeline. The sprite generator now discovers icon names used through typed icon maps, JSX icon props, IconName returns, and generated-value flows without scanning unrelated string literals or the generated sprite itself. The generated sprite is strictly typed so invalid icon names are caught by type checking, and existing invalid or unsafe icon references were cleaned up across settings, provider, Git identity, scheduled task, voice, header, and sidebar surfaces.
Update backend configuration routes and documentation for snippets. The OpenCode config route layer now exposes snippet CRUD and expansion endpoints, accepts JSON bodies for snippet writes, and removes the old prompt-template provider. Scheduled task runtime expansion now uses snippets before dispatching messages.
Add regression coverage for snippet storage and expansion, config-route JSON handling, and multi-run title parsing. Validated with full type checking, full linting, targeted multi-run title tests, and targeted OpenCode snippet/config route tests.
* feat: keep desktop app running in background when closing last window
Closing last window hides it instead of quitting — sidecar keeps running
Cmd+Q now shows confirmation dialog warning about stopping background processes
Clicking dock icon reopens hidden window or creates a new one
* docs: add scheduled tasks impl plan
* feat: add scheduled tasks runtime, api, and ui
* feat: conditionally confirm desktop quit on risks
* chore: remove scheduled tasks plan doc
* feat: add scheduled tasks runtime and management UI
Add server-side scheduled task runtime with project-backed config persistence
Add task scheduling UI and API integration for creating and editing schedules
Add tests for runtime scheduling behavior and project config validation
* feat: add locale display preferences for scheduled tasks
Add Appearance settings for time format and week start with settings.json persistence
Apply preferences in scheduled task editor for time display and weekday ordering
Rename Thinking level control and disable it when model variants are unavailable
* feat: improve scheduled tasks editor and sidebar action order
Reorder session sidebar header actions to separate creation and management tools
Polish scheduled tasks dialog layout and controls for clearer editing flow
* feat: polish scheduled task editor usability
Improve scheduled task dialog layout for clearer scheduling controls
Refine time and weekday inputs for more intuitive task configuration
Update editor labels and control states for better model variant guidance
* feat: add prompt autocomplete and command-aware scheduled runs
Add @ and / autocomplete support to task, multi-run, and agent manager prompt fields
Fix agent mention selection so subagents can be inserted from @ suggestions
Run scheduled prompts as commands when they match slash commands, with message fallback