Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ node_modules/
dist/
.env
*.log
log
.DS_Store
*.tsbuildinfo
config/
soul/
memory/
memory/
.hermes/
tmp/
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Mercury has been in rapid development through 0.x releases. The Second Brain fea
### Permission Modes

- **Ask Me** — confirm before file writes, shell commands that need approval, and scope changes (default on both CLI and Telegram)
- **Allow All** — auto-approve everything for the session (scopes, commands, loop continuation). Resets on restart.
- **Allow All** — auto-approve everything in the current session/channel while keeping the shell blocklist and filesystem scoping in force. Resets on restart.
- CLI: arrow-key menu at session start. Telegram: inline keyboard on first message, `/permissions` to change.

### Step-by-Step Tool Feedback
Expand All @@ -54,7 +54,7 @@ Mercury has been in rapid development through 0.x releases. The Second Brain fea
- **Model selection during onboarding** — after validating an API key, Mercury fetches available models and lets you choose
- **Telegram editable status messages** — streaming updates use `editMessageText` for live response editing
- **Scheduled task notifications** — Mercury notifies the originating channel when a scheduled task runs
- **Full temporary scope for scheduled tasks** — tasks run in Allow All mode with auto-approved scopes
- **Scheduled tasks follow the restricted auto-approval model** — tasks run with `Allow All` behavior inside their originating session/channel, without any extra root filesystem scope

### Breaking Changes

Expand Down
4 changes: 2 additions & 2 deletions DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
## ADR-008: Scheduler with YAML persistence

- **Context**: Mercury needs to set reminders, run periodic tasks, and trigger skills on a schedule.
- **Decision**: Expose `schedule_task`, `list_scheduled_tasks`, `cancel_scheduled_task` as AI-callable tools. Persist scheduled tasks to `~/.mercury/schedules.yaml`. Restore on startup. Tasks fire as internal (non-channel) messages through the agent loop.
- **Consequence**: Mercury can autonomously schedule work. Tasks survive restarts. Internal execution keeps scheduled tasks invisible to channels unless the agent explicitly sends output.
- **Decision**: Expose `schedule_task`, `list_scheduled_tasks`, `cancel_scheduled_task` as AI-callable tools. Persist scheduled tasks to `~/.mercury/schedules.yaml`. Restore on startup. Tasks run as system messages through the agent loop, preserving `sourceChannelId` / `sourceChannelType` when available instead of forcing a separate non-channel execution path.
- **Consequence**: Mercury can autonomously schedule work. Tasks survive restarts. Scheduled runs inherit channel context for delivery and session isolation when that context exists, while still using system-message auto-approval. This auto-approval does not grant root filesystem scope.

## ADR-009: Daemonization via Custom Hybrid Approach

Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ mercury doctor

Every AI agent can read files, run commands, and fetch URLs. Most do it silently. **Mercury asks first — and remembers what matters.**

- **Permission-hardened** — Shell blocklist (`sudo`, `rm -rf /`, etc. never execute). Folder-level read/write scoping. Pending approval flow. Ask Me or Allow All per session. No surprises.
- **Permission-hardened** — Shell blocklist (`sudo`, `rm -rf /`, etc. never execute). Folder-level read/write scoping. Pending approval flow. Ask Me or Allow All per session. `Allow All` auto-approves within the current session/channel, but does not grant root filesystem access.
- **Second Brain** — Persistent, structured memory with SQLite + FTS5 full-text search. 10 memory types, auto-extraction, conflict resolution, auto-consolidation. Mercury learns your preferences, goals, and habits without manual entry.
- **Soul-driven** — Personality defined by markdown files you own (`soul.md`, `persona.md`, `taste.md`, `heartbeat.md`). No corporate wrapper.
- **Token-aware** — Daily budget enforcement. Auto-concise when over 70%. `/budget` command to check, reset, or override.
Expand Down Expand Up @@ -149,6 +149,14 @@ Type these during a conversation — they don't consume API tokens. Work on both
| `/memory` | View and manage second brain memory |
| `/unpair` | Telegram: reset all access |

### Permission modes

- **Ask Me** — prompts before risky shell commands, writes, and permission escalations.
- **Allow All** — auto-approves within the current session/channel, but keeps the shell blocklist and filesystem scoping in force.
- Permission state is isolated by session/channel, so one Telegram chat or CLI session does not silently unlock another.
- System-triggered runs (internal flows and scheduled tasks) are auto-approved under the same restricted model — no extra root filesystem scope is added.
- See `docs/permissions-model.md` for the live permission model and implementation notes.

## Built-in Tools

| Category | Tools |
Expand Down
54 changes: 54 additions & 0 deletions docs/mercury-sandbox-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Mercury sandbox smoke test

Smoke test reproducible para validar Mercury dentro del sandbox con `glm-5.1`, sin tocar la lógica del agente.

Comment on lines +1 to +4
## Qué automatiza

- carga manualmente `sandbox/mercury-home/.env`
- exporta `MERCURY_HOME`
- fija el `cwd` al workspace del sandbox
- arranca Mercury en foreground con PTY usando `node dist/index.js start --foreground`
- selecciona `Ask Me` enviando `\r`
- manda `Di solo OK y nada más.`
- verifica que la respuesta útil del asistente sea únicamente `OK`
- guarda transcript raw y transcript limpio sin ANSI

## Requisitos

- `dist/index.js` debe existir en el repo (`npm run build` si hace falta)
- `python3` con `pexpect` disponible
- sandbox existente en:
- `MERCURY_SANDBOX_HOME=/home/raul/dev/mercury-test/sandbox/mercury-home`
- `MERCURY_SANDBOX_WORKSPACE=/home/raul/dev/mercury-test/sandbox/workspace`

## Uso

Desde el repo:

```bash
./scripts/run_mercury_sandbox_smoke.sh
```

Opcionalmente puedes sobreescribir rutas o prompt:

```bash
MERCURY_SANDBOX_HOME=/ruta/mercury-home \
MERCURY_SANDBOX_WORKSPACE=/ruta/workspace \
MERCURY_SMOKE_PROMPT='Di solo OK y nada más.' \
./scripts/run_mercury_sandbox_smoke.sh
```

## Salida

Los transcripts se guardan en `tmp/mercury-smoke/`:

- `*.log`: salida raw de terminal
- `*.clean.txt`: salida limpiada, sin secuencias ANSI

El script falla si:

- no encuentra `.env`
- no encuentra `dist/index.js`
- el arranque no muestra `glm-5.1`
- no logra pasar el menú de permisos
- la respuesta del asistente no es exactamente `OK`
123 changes: 123 additions & 0 deletions docs/permissions-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Mercury permission model

This document reflects the live permission wiring in the local repo, not the older scheduler/security notes.

## Summary

- Mercury keeps permission state per session/channel.
- `Ask Me` and `Allow All` are session modes, not global switches.
- `Allow All` enables auto-approval for the current session/channel only.
- `Allow All` does **not** add root filesystem scope.
- Internal flows and scheduled tasks also use auto-approval, but under that same restricted model.

## Session and channel isolation

Permission state lives inside `PermissionManager` session state keyed by channel. That state includes:

- `autoApproveAll`
- pending approvals
- temporary scopes
- channel type metadata

Practical consequence: enabling `Allow All` in one Telegram chat or one CLI session does not silently affect another channel.

Relevant files:

- `src/capabilities/permissions.ts`
- `src/capabilities/permissions.test.ts`

## Interactive modes

Interactive channels expose two modes:

- **Ask Me** — Mercury asks before risky shell commands, file writes, and permission escalations.
- **Allow All** — Mercury auto-approves those interactive prompts inside the current session/channel.

The current wiring is in `src/core/permission-mode.ts`:

```ts
permissions.setCurrentChannel(channelId, channelType);
permissions.setAutoApproveAll(mode === 'allow-all');
```

That function does **not** add `addTempScope('/')` or any equivalent root filesystem grant.

Relevant files:

- `src/core/permission-mode.ts`
- `src/core/permission-mode.test.ts`
- `src/index.ts`

## System messages: internal and scheduled

Mercury uses one permission model for user sessions and system-triggered runs.

`getMessagePermissionPolicy(...)` currently returns only:

- `autoApproveAll: true`

for:

- internal messages (`channelType === 'internal'`)
- scheduled/system messages (`senderId === 'system'` outside the internal channel)

This means scheduled runs are auto-approved, but they do not get a different unrestricted filesystem mode.

Relevant files:

- `src/core/agent.ts`
- `src/core/agent-permissions.test.ts`

## Scheduled task context

Scheduled tasks are not strictly “non-channel” anymore. When a task is created from a live channel, Mercury persists the origin context and replays the job as a system message tied to that source channel.

That preserves:

- correct delivery context
- session/channel isolation
- the same restricted auto-approval model

Relevant files:

- `src/capabilities/scheduler/schedule-task.ts`
- `src/capabilities/registry.ts`
- `src/core/agent.ts`

## What Allow All does not do

`Allow All` should not be documented as unrestricted filesystem access.

What still applies:

- shell blocklist
- filesystem scoping
- per-session/channel isolation

So the correct mental model is:

> `Allow All` removes confirmation prompts inside the current session. It does not remove Mercury's filesystem boundaries.

## Local patch consolidation status

The local repo now reflects a fully consolidated permission hardening pass with three practical outcomes:

1. `Allow All` is wired through `applySessionPermissionMode(...)` without root temp scope.
2. Internal and scheduled runs stay auto-approved without `addTempScope('/')`.
3. The message permission contract now models only the active behavior (`autoApproveAll`), with session state isolation covered by dedicated tests.

## Evidence and reproducibility

Useful verification points in this repo:

- `src/core/permission-mode.test.ts`
- `src/core/agent-permissions.test.ts`
- `src/capabilities/permissions.test.ts`
- `src/channels/telegram.test.ts`

For sandbox validation, use:

- `docs/mercury-sandbox-smoke.md`
- `scripts/run_mercury_sandbox_smoke.sh`

The smoke test validates the sandbox startup path with `glm-5.1`, manual `.env` loading, workspace `cwd`, interactive permission selection, and a minimal `OK` roundtrip.
Loading
Loading