feat: mount store rw for main agent and add requiresTrigger to register_group
- Mount store/ separately as read-write so the main agent can access the SQLite database directly. - Add requiresTrigger parameter to the register_group MCP tool (host IPC already supported it, but the tool never exposed it). Defaults to false (no trigger). - Update group registration instructions to ask user about trigger. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -460,6 +460,12 @@ Use available_groups.json to find the JID for a group. The folder name must be c
|
|||||||
'Channel-prefixed folder name (e.g., "whatsapp_family-chat", "telegram_dev-team")',
|
'Channel-prefixed folder name (e.g., "whatsapp_family-chat", "telegram_dev-team")',
|
||||||
),
|
),
|
||||||
trigger: z.string().describe('Trigger word (e.g., "@Andy")'),
|
trigger: z.string().describe('Trigger word (e.g., "@Andy")'),
|
||||||
|
requiresTrigger: z
|
||||||
|
.boolean()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
'Whether messages must start with the trigger word. Default: false (respond to all messages). Set to true for busy groups with many participants where you only want the agent to respond when explicitly mentioned.',
|
||||||
|
),
|
||||||
},
|
},
|
||||||
async (args) => {
|
async (args) => {
|
||||||
if (!isMain) {
|
if (!isMain) {
|
||||||
@@ -480,6 +486,7 @@ Use available_groups.json to find the JID for a group. The folder name must be c
|
|||||||
name: args.name,
|
name: args.name,
|
||||||
folder: args.folder,
|
folder: args.folder,
|
||||||
trigger: args.trigger,
|
trigger: args.trigger,
|
||||||
|
requiresTrigger: args.requiresTrigger ?? false,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ private_key, .secret
|
|||||||
|
|
||||||
**Read-Only Project Root:**
|
**Read-Only Project Root:**
|
||||||
|
|
||||||
The main group's project root is mounted read-only. Writable paths the agent needs (group folder, IPC, `.claude/`) are mounted separately. This prevents the agent from modifying host application code (`src/`, `dist/`, `package.json`, etc.) which would bypass the sandbox entirely on next restart.
|
The main group's project root is mounted read-only. Writable paths the agent needs (store, group folder, IPC, `.claude/`) are mounted separately. This prevents the agent from modifying host application code (`src/`, `dist/`, `package.json`, etc.) which would bypass the sandbox entirely on next restart. The `store/` directory is mounted read-write so the main agent can access the SQLite database directly.
|
||||||
|
|
||||||
### 3. Session Isolation
|
### 3. Session Isolation
|
||||||
|
|
||||||
@@ -88,6 +88,7 @@ Each NanoClaw group gets its own OneCLI agent identity. This allows different cr
|
|||||||
| Capability | Main Group | Non-Main Group |
|
| Capability | Main Group | Non-Main Group |
|
||||||
|------------|------------|----------------|
|
|------------|------------|----------------|
|
||||||
| Project root access | `/workspace/project` (ro) | None |
|
| Project root access | `/workspace/project` (ro) | None |
|
||||||
|
| Store (SQLite DB) | `/workspace/project/store` (rw) | None |
|
||||||
| Group folder | `/workspace/group` (rw) | `/workspace/group` (rw) |
|
| Group folder | `/workspace/group` (rw) | `/workspace/group` (rw) |
|
||||||
| Global memory | Implicit via project | `/workspace/global` (ro) |
|
| Global memory | Implicit via project | `/workspace/global` (ro) |
|
||||||
| Additional mounts | Configurable | Read-only unless allowed |
|
| Additional mounts | Configurable | Read-only unless allowed |
|
||||||
|
|||||||
@@ -83,15 +83,16 @@ Anthropic credentials must be either an API key from console.anthropic.com (`ANT
|
|||||||
|
|
||||||
## Container Mounts
|
## Container Mounts
|
||||||
|
|
||||||
Main has read-only access to the project and read-write access to its group folder:
|
Main has read-only access to the project, read-write access to the store (SQLite DB), and read-write access to its group folder:
|
||||||
|
|
||||||
| Container Path | Host Path | Access |
|
| Container Path | Host Path | Access |
|
||||||
|----------------|-----------|--------|
|
|----------------|-----------|--------|
|
||||||
| `/workspace/project` | Project root | read-only |
|
| `/workspace/project` | Project root | read-only |
|
||||||
|
| `/workspace/project/store` | `store/` | read-write |
|
||||||
| `/workspace/group` | `groups/main/` | read-write |
|
| `/workspace/group` | `groups/main/` | read-write |
|
||||||
|
|
||||||
Key paths inside the container:
|
Key paths inside the container:
|
||||||
- `/workspace/project/store/messages.db` - SQLite database
|
- `/workspace/project/store/messages.db` - SQLite database (read-write)
|
||||||
- `/workspace/project/store/messages.db` (registered_groups table) - Group config
|
- `/workspace/project/store/messages.db` (registered_groups table) - Group config
|
||||||
- `/workspace/project/groups/` - All group folders
|
- `/workspace/project/groups/` - All group folders
|
||||||
|
|
||||||
@@ -172,10 +173,11 @@ Fields:
|
|||||||
### Adding a Group
|
### Adding a Group
|
||||||
|
|
||||||
1. Query the database to find the group's JID
|
1. Query the database to find the group's JID
|
||||||
2. Use the `register_group` MCP tool with the JID, name, folder, and trigger
|
2. Ask the user whether the group should require a trigger word before registering
|
||||||
3. Optionally include `containerConfig` for additional mounts
|
3. Use the `register_group` MCP tool with the JID, name, folder, trigger, and the chosen `requiresTrigger` setting
|
||||||
4. The group folder is created automatically: `/workspace/project/groups/{folder-name}/`
|
4. Optionally include `containerConfig` for additional mounts
|
||||||
5. Optionally create an initial `CLAUDE.md` for the group
|
5. The group folder is created automatically: `/workspace/project/groups/{folder-name}/`
|
||||||
|
6. Optionally create an initial `CLAUDE.md` for the group
|
||||||
|
|
||||||
Folder naming convention — channel prefix with underscore separator:
|
Folder naming convention — channel prefix with underscore separator:
|
||||||
- WhatsApp "Family Chat" → `whatsapp_family-chat`
|
- WhatsApp "Family Chat" → `whatsapp_family-chat`
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ function buildVolumeMounts(
|
|||||||
|
|
||||||
if (isMain) {
|
if (isMain) {
|
||||||
// Main gets the project root read-only. Writable paths the agent needs
|
// Main gets the project root read-only. Writable paths the agent needs
|
||||||
// (group folder, IPC, .claude/) are mounted separately below.
|
// (store, group folder, IPC, .claude/) are mounted separately below.
|
||||||
// Read-only prevents the agent from modifying host application code
|
// Read-only prevents the agent from modifying host application code
|
||||||
// (src/, dist/, package.json, etc.) which would bypass the sandbox
|
// (src/, dist/, package.json, etc.) which would bypass the sandbox
|
||||||
// entirely on next restart.
|
// entirely on next restart.
|
||||||
@@ -89,6 +89,15 @@ function buildVolumeMounts(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Main gets writable access to the store (SQLite DB) so it can
|
||||||
|
// query and write to the database directly.
|
||||||
|
const storeDir = path.join(projectRoot, 'store');
|
||||||
|
mounts.push({
|
||||||
|
hostPath: storeDir,
|
||||||
|
containerPath: '/workspace/project/store',
|
||||||
|
readonly: false,
|
||||||
|
});
|
||||||
|
|
||||||
// Main also gets its group folder as the working directory
|
// Main also gets its group folder as the working directory
|
||||||
mounts.push({
|
mounts.push({
|
||||||
hostPath: groupDir,
|
hostPath: groupDir,
|
||||||
|
|||||||
Reference in New Issue
Block a user