Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,4 @@ examples/VPS_SETUP.md
examples/VPS_SETUP.md
.commandcode/settings.json
.commandcode/taste/taste.md
ecc-pre-bypass.md
108 changes: 104 additions & 4 deletions AGENTS.md

Large diffs are not rendered by default.

24 changes: 22 additions & 2 deletions docs/chat-archiving.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,26 @@ full on-disk transcript all survive.
surface. The **Archive page** (App menu → Archive) is the one place to see
them: a single cross-workspace list with search, a workspace filter, per-row
Restore/Delete, and the retention control.
- Every archive row shows the chat's **last-used calendar date** as
`MM/DD/YYYY`. Archiving does not change that value: a chat last used on
`08/28/2026` and archived on `08/30/2026` still displays `08/28/2026`.

## Chat timestamps

Each newly created `Session` records two independent wall-clock values:

- `createdAt` is stamped once when the chat is created.
- `lastUsedAt` starts at creation and advances only when work is sent through
that chat (including normal prompts, follow-ups, voice sends, delegated
prompts, browser-grab sends, handoffs, and `/compact`). Opening, renaming,
pinning, restoring, or archiving a chat does not advance it.

The Archive page displays and sorts by `lastUsedAt`; `archivedAt` remains
separate and continues to drive retention. Sessions saved before these fields
existed are backfilled once from transcript modification times, which are the
best available evidence of actual activity because archive/restore does not
rewrite transcripts. If no transcript metadata exists, CrewCode uses the
archive timestamp or first-launch-after-upgrade time as a non-zero fallback.

## Retention

Expand Down Expand Up @@ -71,8 +91,8 @@ of the session in `crewcode:sessionsByTab`.

| File | Role |
| --- | --- |
| `src/renderer/src/types/index.ts` | `Session.archived`, `Session.archivedAt`, `archive` tab kind |
| `src/renderer/src/hooks/useChatSessions.ts` | `setArchived`, `backfillArchivedAt`, live-only accessors, id allocation |
| `src/renderer/src/types/index.ts` | `Session.createdAt`, `Session.lastUsedAt`, `Session.archived`, `Session.archivedAt`, `archive` tab kind |
| `src/renderer/src/hooks/useChatSessions.ts` | session timestamp creation/backfill/touch, `setArchived`, `backfillArchivedAt`, live-only accessors, id allocation |
| `src/renderer/src/hooks/archive-retention.ts` | pure expiry/age rules |
| `src/renderer/src/hooks/useSettings.tsx` | `archiveRetentionDays` + its fail-safe normalization |
| `src/renderer/src/App.tsx` | `archiveSession` / `restoreSession` / `renameSession`, live vs archived grouping, page wiring |
Expand Down
11 changes: 10 additions & 1 deletion docs/conversation-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ If a sharded file is missing or unreadable, CrewCode can lazily recover that ses

### Browser/Brain conversation scopes

Remote browser replay history remains authoritative on the Brain, not in browser `localStorage`. The shared renderer supplies an opaque chat session id; the remote boundary namespaces it as `web:<session>` before `AgentBridgeService` reads or writes the same per-session conversation shards described above. This keeps desktop `thread:` keys and browser keys from aliasing each other.
Remote browser and Brain-attached desktop replay history remains authoritative on the Brain, not in renderer `localStorage`. The shared renderer supplies an opaque chat session id; the remote boundary namespaces it as `web:<session>` before `AgentBridgeService` reads or writes the same per-session conversation shards described above. First-time desktop attachment copies missing state and creates non-destructive `web:` aliases for existing `thread:` shards. Provider-native resume IDs continue to use the desktop-compatible `<session>:<provider>` key, so switching clients does not lose resume state and switching providers cannot consume another provider's native id.

The Brain also serializes prompt entry per conversation. Desktop and web may submit concurrently, but one conversation receives one provider turn at a time in FIFO order; different conversations remain concurrent. Stable bridge starts are coalesced so simultaneous first attachment does not create competing provider processes.

Cross-thread browser handoff is a bounded Brain-side operation. The browser names a source chat and an already-owned destination bridge, but never downloads the source replay shard. The Brain summarizes the source with a disposable destination-provider bridge, appends only the resulting handoff packet to the destination shard, clears the destination's native resume id, and replays the combined destination history once on its next native-provider prompt. Stateless HTTP providers consume the updated shard directly. Missing source history, summary failure, a running destination, or lost destination ownership is an explicit failure and is never inferred as success.

Expand Down Expand Up @@ -96,6 +98,13 @@ The rich UI thread (the full renderer `Message[]` — user/agent/thinking/toolca

Messages are stored opaquely — the main process never inspects their shape, so the renderer `Message` type stays renderer-only. IPC surface: `transcripts:loadAll`, `transcripts:save`, `transcripts:remove`, and a **synchronous** `transcripts:saveSyncBatch` used only on window teardown (an async `invoke` can be dropped before the renderer dies, so the last turn is written synchronously).

In a Brain-attached runtime, `src/main/transcript-service.ts` owns the equivalent
Brain-side shards. Since desktop and browser can save full arrays based on different
snapshots, it merges by stable message identity (ignoring client-local display time
where no durable id exists) before writing. New divergent rows are appended in Brain
receipt order, known activity/tool/turn rows are replaced, and explicit
`transcripts.remove` remains the only whole-thread deletion path.

### L1 — `crewcode:messagesByTab` localStorage (bounded fast-paint cache)

`src/renderer/src/stores/chat-messages-store.ts` keeps a synchronous localStorage copy so the transcript paints instantly on launch. localStorage has a hard ~5MB per-origin quota; the cache therefore caps each scope's tail (`MAX_PERSISTED_MESSAGES_PER_SCOPE`) and, on `QuotaExceededError`, evicts the least-recently-touched scopes so the newest conversation always wins the remaining space.
Expand Down
37 changes: 29 additions & 8 deletions docs/crewcoder-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,39 @@ a turn is running so authority cannot change underneath live execution. It
stays absent for unavailable or inactive providers and from the phone layout,
where the desktop model-row reveal itself is intentionally hidden.

When the concrete **CrewCoder** profile is selected, the row also shows a
session-scoped approval picker with all native policies:

- **Review** (`review`) lets safe calls proceed and asks for mutations and dangerous calls.
- **Always** (`always`) asks for every non-safe call.
- **Never** (`never`) shows no prompts while continuing to block dangerous calls.
- **Full access** (`full-access`) accepts calls without prompts.
- **Sandboxed** (`sandboxed`) shows no prompts and runs non-dangerous calls through the sandbox policy where supported.

Older or invalid persisted values fail closed to Review. Changing the policy
drops only the idle CrewCoder bridge and native-resumes it on the next prompt;
both controls are disabled during a running turn. Full access is an explicit
authority escalation: CrewCoder stops emitting approval requests and permits
dangerous commands, so CrewCode's permission overlay and dangerous-command
tripwire cannot interpose on those provider-native calls.

A concrete CrewCoder profile also owns the agent's behavioral mode, so CrewCode
locks its separate execution policy to **Build** and disables the
Ask/Plan/Build/Full control. Build remains active underneath as the approval
gate: writes still require CrewCode's permission overlay instead of becoming
implicitly Full Access. The phone model menu disables its Mode row for the same
session. Returning to **Configured default** re-enables the execution-mode
control; the session remains on Build until the user chooses another policy.
Ask/Plan/Build/Full control. Under the default Review policy, Build remains the
CrewCode permission gate. Explicit CrewCoder Full access bypasses that native
request path and must not be described as Build-protected. The phone model menu
disables its Mode row for the same session. Returning to **Configured default**
re-enables the execution-mode control; the session remains on Build until the
user chooses another policy.

The `crewcoder` profile adds a runtime inspect → clarify → plan → approve
sequence inside CrewCoder. CrewCode does not enforce that gate; it projects
`crewcoder_clarify` and `crewcoder_propose_plan` into the agent activity overlay
and sends `/approve-plan` as a user prompt when the user clicks **approve plan**
or picks the CrewCoder slash command. That prompt is not `/approve` and does
not settle a `session/request_permission` card. After plan approval, CrewCode
Build permission prompts still apply to mutating tools. Revising a proposed
handles any permission requests emitted by the selected native approval policy;
Never, Full access, and Sandboxed may deliberately emit none. Revising a proposed
plan is a normal composer message; CrewCoder treats that as a new
`awaiting_plan` cycle rather than approval.

Expand Down Expand Up @@ -182,8 +200,11 @@ CrewCoder's separate agent-profile `--mode` option (`general`, `crewcoder`,
`plugin`, `extension`). The selected `Session.crewcoderMode` is the only value
allowed onto that launch flag. It is process-scoped, whereas CrewCode execution
mode remains the permission policy described above. A concrete CrewCoder
profile fixes that policy to Build; it must never inherit a hidden prior Ask,
Plan, or Full Access value.
profile fixes that CrewCode policy to Build; it must never inherit a hidden
prior Ask, Plan, or Full Access value. `Session.crewcoderApprovalMode` is a
separate native authority value. CrewCode passes only `review`, `always`,
`never`, `full-access`, or `sandboxed` to `--approval`, defaults
missing/invalid values to `review`, and records the value in execution custody.

## Filesystem and SSH behavior

Expand Down
Loading
Loading