Session-Binding kanalunabhängiger Plan
Überblick
Dieses Dokument definiert das langfristige kanalunabhängige Session-Binding-Modell und den konkreten Umfang für die nächste Implementierungsiteration.
Ziel:
- Subagent-gebundenes Session-Routing zu einer Kernfähigkeit machen
- Kanalspezifisches Verhalten in Adaptern halten
- Regressionen im normalen Discord-Verhalten vermeiden
Warum das existiert
Das aktuelle Verhalten vermischt:
- Completion-Content-Policy
- Ziel-Routing-Policy
- Discord-spezifische Details
Dies verursachte Randfälle wie:
- Doppelte Main- und Thread-Zustellung bei gleichzeitigen Runs
- Veraltete Token-Nutzung bei wiederverwendeten Binding-Managern
- Fehlende Activity-Accounting für Webhook-Sends
Iteration 1 Umfang
Diese Iteration ist bewusst begrenzt.
1. Kanalunabhängige Core-Interfaces hinzufügen
Core-Typen und Service-Interfaces für Bindings und Routing hinzufügen.
Vorgeschlagene Core-Typen:
export type BindingTargetKind = "subagent" | "session";
export type BindingStatus = "active" | "ending" | "ended";
export type ConversationRef = {
channel: string;
accountId: string;
conversationId: string;
parentConversationId?: string;
};
export type SessionBindingRecord = {
bindingId: string;
targetSessionKey: string;
targetKind: BindingTargetKind;
conversation: ConversationRef;
status: BindingStatus;
boundAt: number;
expiresAt?: number;
metadata?: Record<string, unknown>;
};
Core-Service-Vertrag:
export interface SessionBindingService {
bind(input: {
targetSessionKey: string;
targetKind: BindingTargetKind;
conversation: ConversationRef;
metadata?: Record<string, unknown>;
ttlMs?: number;
}): Promise<SessionBindingRecord>;
listBySession(targetSessionKey: string): SessionBindingRecord[];
resolveByConversation(ref: ConversationRef): SessionBindingRecord | null;
touch(bindingId: string, at?: number): void;
unbind(input: {
bindingId?: string;
targetSessionKey?: string;
reason: string;
}): Promise<SessionBindingRecord[]>;
}
2. Einen Core-Delivery-Router für Subagent-Completions hinzufügen
Einen einzelnen Ziel-Auflösungspfad für Completion-Events hinzufügen.
Router-Vertrag:
export interface BoundDeliveryRouter {
resolveDestination(input: {
eventKind: "task_completion";
targetSessionKey: string;
requester?: ConversationRef;
failClosed: boolean;
}): {
binding: SessionBindingRecord | null;
mode: "bound" | "fallback";
reason: string;
};
}
Für diese Iteration:
- Nur
task_completionwird über diesen neuen Pfad geroutet - Bestehende Pfade für andere Event-Arten bleiben unverändert
3. Discord als Adapter beibehalten
Discord bleibt die erste Adapter-Implementierung.
Adapter-Verantwortlichkeiten:
- Thread-Konversationen erstellen/wiederverwenden
- Gebundene Nachrichten per Webhook oder Channel-Send senden
- Thread-Status validieren (archiviert/gelöscht)
- Adapter-Metadaten zuordnen (Webhook-Identität, Thread-IDs)
4. Aktuell bekannte Korrektheitsprobleme beheben
In dieser Iteration erforderlich:
- Token-Nutzung bei Wiederverwendung eines bestehenden Thread-Binding-Managers auffrischen
- Ausgehende Aktivität für Webhook-basierte Discord-Sends aufzeichnen
- Impliziten Main-Channel-Fallback stoppen wenn ein gebundenes Thread-Ziel für die Session-Modus-Completion ausgewählt ist
5. Aktuelle Runtime-Sicherheitsstandards beibehalten
Kein Verhaltensänderung für Benutzer mit deaktiviertem threadgebundenen Spawn.
Standards bleiben:
channels.discord.threadBindings.spawnSubagentSessions = false
Ergebnis:
- Normale Discord-Benutzer bleiben beim aktuellen Verhalten
- Der neue Core-Pfad betrifft nur gebundenes Session-Completion-Routing wo aktiviert
Nicht in Iteration 1
Explizit aufgeschoben:
- ACP-Binding-Ziele (
targetKind: "acp") - Neue Kanaladapter jenseits von Discord
- Globaler Ersatz aller Zustellungspfade (
spawn_ack, zukünftigesubagent_message) - Protokollebenen-Änderungen
- Store-Migration/Versioning-Redesign für alle Binding-Persistenz
Hinweise zu ACP:
- Interface-Design behält Platz für ACP
- ACP-Implementierung wird in dieser Iteration nicht begonnen
Routing-Invarianten
Diese Invarianten sind für Iteration 1 verbindlich.
- Zielauswahl und Content-Generierung sind separate Schritte
- Wenn Session-Modus-Completion zu einem aktiven gebundenen Ziel auflöst, muss die Zustellung dieses Ziel ansteuern
- Kein verstecktes Umrouten von gebundenem Ziel zum Main-Channel
- Fallback-Verhalten muss explizit und beobachtbar sein
Kompatibilität und Rollout
Kompatibilitätsziel:
- Keine Regression für Benutzer mit deaktiviertem threadgebundenen Spawning
- Keine Änderung für Nicht-Discord-Kanäle in dieser Iteration
Rollout:
- Interfaces und Router hinter aktuellen Feature-Gates landen.
- Discord-Completion-Modus-gebundene Zustellungen über Router routen.
- Legacy-Pfad für ungebundene Flows beibehalten.
- Mit gezielten Tests und Canary-Runtime-Logs verifizieren.
In Iteration 1 erforderliche Tests
Unit- und Integrations-Coverage erforderlich:
- Manager-Token-Rotation nutzt neuesten Token nach Manager-Wiederverwendung
- Webhook-Sends aktualisieren Channel-Activity-Timestamps
- Zwei aktive gebundene Sessions im selben Requester-Channel duplizieren nicht zum Main-Channel
- Completion für gebundenen Session-Modus-Run löst nur zum Thread-Ziel auf
- Deaktiviertes Spawn-Flag behält Legacy-Verhalten unverändert bei
Vorgeschlagene Implementierungsdateien
Core:
src/infra/outbound/session-binding-service.ts(neu)src/infra/outbound/bound-delivery-router.ts(neu)src/agents/subagent-announce.ts(Completion-Zielauflösungs-Integration)
Discord-Adapter und Runtime:
src/discord/monitor/thread-bindings.manager.tssrc/discord/monitor/reply-delivery.tssrc/discord/send.outbound.ts
Tests:
src/discord/monitor/provider*.test.tssrc/discord/monitor/reply-delivery.test.tssrc/agents/subagent-announce.format.test.ts
Done-Kriterien für Iteration 1
- Core-Interfaces existieren und sind für Completion-Routing verdrahtet
- Oben genannte Korrektheitsfixes sind mit Tests gemergt
- Keine Main- und Thread-doppelte-Completion-Zustellung in Session-Modus-gebundenen Runs
- Kein Verhaltensänderung für Deployments mit deaktiviertem Bound-Spawn
- ACP bleibt explizit aufgeschoben