v2: channel isolation model, manage-channels skill, refactored channel skills

- Add three-level isolation model (shared session, same agent, separate agent)
  with agent-shared session mode for cross-channel shared sessions
- Create /manage-channels skill for wiring channels to agent groups
- Refactor all 12 v2 channel skills: lean SKILL.md + VERIFY.md + REMOVE.md
  with structured Channel Info section for platform-specific metadata
- Create /add-discord-v2 skill (was missing)
- Add step 5a to setup SKILL.md invoking /manage-channels after channel install
- Update setup/verify.ts to check all 12 channel token types
- Add docs/v2-isolation-model.md explaining the isolation model
- Update v2-checklist.md and v2-setup-wiring.md to reflect completed work

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-09 13:19:19 +03:00
parent ed76d51e0b
commit 57a6491c7e
46 changed files with 677 additions and 449 deletions

View File

@@ -11,7 +11,7 @@ export interface ConversationConfig {
agentGroupId: string;
triggerPattern?: string; // regex string (for native channels)
requiresTrigger: boolean;
sessionMode: 'shared' | 'per-thread';
sessionMode: 'shared' | 'per-thread' | 'agent-shared';
}
/** Passed to the adapter at setup time. */

View File

@@ -27,6 +27,13 @@ export function findSession(messagingGroupId: string, threadId: string | null):
.get(messagingGroupId, 'active') as Session | undefined;
}
/** Find an active session scoped to an agent group (ignoring messaging group). */
export function findSessionByAgentGroup(agentGroupId: string): Session | undefined {
return getDb()
.prepare("SELECT * FROM sessions WHERE agent_group_id = ? AND status = 'active' ORDER BY created_at DESC LIMIT 1")
.get(agentGroupId) as Session | undefined;
}
export function getSessionsByAgentGroup(agentGroupId: string): Session[] {
return getDb().prepare('SELECT * FROM sessions WHERE agent_group_id = ?').all(agentGroupId) as Session[];
}

View File

@@ -11,7 +11,7 @@ import fs from 'fs';
import path from 'path';
import { DATA_DIR } from './config.js';
import { createSession, findSession, getSession, updateSession } from './db/sessions.js';
import { createSession, findSession, findSessionByAgentGroup, getSession, updateSession } from './db/sessions.js';
import { log } from './log.js';
import { INBOUND_SCHEMA, OUTBOUND_SCHEMA } from './db/schema.js';
import type { Session } from './types.js';
@@ -55,22 +55,35 @@ function generateId(): string {
/**
* Find or create a session for a messaging group + thread.
* Returns the session and whether it was newly created.
*
* Session modes:
* - 'shared': one session per messaging group (ignores threadId)
* - 'per-thread': one session per (messaging group, thread)
* - 'agent-shared': one session per agent group — all messaging groups
* wired with this mode share a single session (e.g. GitHub + Slack)
*/
export function resolveSession(
agentGroupId: string,
messagingGroupId: string,
threadId: string | null,
sessionMode: 'shared' | 'per-thread',
sessionMode: 'shared' | 'per-thread' | 'agent-shared',
): { session: Session; created: boolean } {
const lookupThreadId = sessionMode === 'shared' ? null : threadId;
const existing = findSession(messagingGroupId, lookupThreadId);
if (existing) {
return { session: existing, created: false };
// agent-shared: single session per agent group, regardless of messaging group
if (sessionMode === 'agent-shared') {
const existing = findSessionByAgentGroup(agentGroupId);
if (existing) {
return { session: existing, created: false };
}
} else {
const lookupThreadId = sessionMode === 'shared' ? null : threadId;
const existing = findSession(messagingGroupId, lookupThreadId);
if (existing) {
return { session: existing, created: false };
}
}
const id = generateId();
const lookupThreadId = sessionMode === 'per-thread' ? threadId : null;
const session: Session = {
id,
agent_group_id: agentGroupId,
@@ -85,7 +98,7 @@ export function resolveSession(
createSession(session);
initSessionFolder(agentGroupId, id);
log.info('Session created', { id, agentGroupId, messagingGroupId, threadId: lookupThreadId });
log.info('Session created', { id, agentGroupId, messagingGroupId, threadId: lookupThreadId, sessionMode });
return { session, created: true };
}

View File

@@ -26,7 +26,7 @@ export interface MessagingGroupAgent {
agent_group_id: string;
trigger_rules: string | null; // JSON: { pattern, mentionOnly, excludeSenders, includeSenders }
response_scope: 'all' | 'triggered' | 'allowlisted';
session_mode: 'shared' | 'per-thread';
session_mode: 'shared' | 'per-thread' | 'agent-shared';
priority: number;
created_at: string;
}