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:
@@ -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. */
|
||||
|
||||
@@ -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[];
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user