Эксперименты

План независимого от каналов привязки сессий

Обзор

В этом документе определена долгосрочная модель привязки сессий, не зависящая от канала, и конкретный объем следующей итерации реализации. Цель:

  • сделать маршрутизацию сессий, привязанных к сабагенту, основной возможностью
  • оставить специфичное для канала поведение в адаптерах
  • избежать регрессий в обычном поведении Discord

Зачем это нужно

Текущее поведение смешивает:

  • политику контента завершений
  • политику маршрутизации назначения
  • специфичные для Discord детали

Это вызывало крайние случаи, такие как:

  • дублирование доставки в основной канал и тред при параллельных запусках
  • использование устаревших токенов при повторном использовании менеджеров привязки
  • отсутствие учета активности для отправок через вебхук

Объем итерации 1

Эта итерация намеренно ограничена.

1. Добавление основных интерфейсов, не зависящих от канала

Добавление основных типов и интерфейсов сервисов для привязок и маршрутизации. Предлагаемые основные типы:

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>;
};

Контракт основного сервиса:

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. Добавление одного основного маршрутизатора доставки для завершений сабагентов

Добавление единого пути разрешения назначения для событий завершения. Контракт маршрутизатора:

export interface BoundDeliveryRouter {
  resolveDestination(input: {
    eventKind: "task_completion";
    targetSessionKey: string;
    requester?: ConversationRef;
    failClosed: boolean;
  }): {
    binding: SessionBindingRecord | null;
    mode: "bound" | "fallback";
    reason: string;
  };
}

Для этой итерации:

  • только task_completion маршрутизируется через этот новый путь
  • существующие пути для других видов событий остаются без изменений

3. Оставить Discord в качестве адаптера

Discord остается первой реализацией адаптера. Обязанности адаптера:

  • создание/повторное использование тредов-бесед
  • отправка привязанных сообщений через вебхук или отправку в канал
  • проверка состояния треда (архивирован/удален)
  • сопоставление метаданных адаптера (идентификатор вебхука, id тредов)

4. Исправление известных на данный момент проблем корректности

Требуется в этой итерации:

  • обновление использования токена при повторном использовании существующего менеджера привязки треда
  • запись исходящей активности для отправок в Discord через вебхук
  • остановка неявного возврата на основной канал, когда для завершения в режиме сессии выбрано назначение в привязанном треде

5. Сохранение текущих настроек безопасности времени выполнения

Без изменения поведения для пользователей с отключенной привязкой порождения к треду. Настройки по умолчанию остаются:

  • channels.discord.threadBindings.spawnSubagentSessions = false

Результат:

  • обычные пользователи Discord остаются на текущем поведении
  • новый основной путь затрагивает только маршрутизацию завершений привязанных сессий, где она включена

Не входит в итерацию 1

Явно отложено:

  • цели привязки ACP (targetKind: "acp")
  • новые адаптеры каналов помимо Discord
  • глобальная замена всех путей доставки (spawn_ack, будущий subagent_message)
  • изменения на уровне протокола
  • редизайн миграции хранилища/версионирования для всего постоянного хранения привязок

Примечания по ACP:

  • дизайн интерфейса оставляет место для ACP
  • реализация ACP не начинается в этой итерации

Инварианты маршрутизации

Эти инварианты обязательны для итерации 1.

  • выбор назначения и генерация контента являются отдельными шагами
  • если завершение в режиме сессии разрешается в активное привязанное назначение, доставка должна быть нацелена на это назначение
  • никакой скрытой перемаршрутизации с привязанного назначения на основной канал
  • поведение возврата должно быть явным и наблюдаемым

Совместимость и внедрение

Цель совместимости:

  • отсутствие регрессии для пользователей с отключенной привязкой порождения к треду
  • отсутствие изменений для не-Discord каналов в этой итерации

Внедрение:

  1. Внедрение интерфейсов и маршрутизатора за текущими флаг-гейтами.
  2. Маршрутизация привязанных доставок завершений Discord через маршрутизатор.
  3. Сохранение старого пути для непривязанных потоков.
  4. Проверка с помощью целевых тестов и логов канареечного запуска.

Требуемые тесты в итерации 1

Требуется покрытие модульными и интеграционными тестами:

  • ротация токенов менеджера использует последний токен после повторного использования менеджера
  • отправки через вебхук обновляют временные метки активности канала
  • две активные привязанные сессии в одном канале инициатора не дублируются в основной канал
  • завершение для запуска в режиме привязанной сессии разрешается только в назначение треда
  • отключенный флаг порождения оставляет старое поведение неизменным

Предлагаемые файлы реализации

Основное:

  • src/infra/outbound/session-binding-service.ts (новый)
  • src/infra/outbound/bound-delivery-router.ts (новый)
  • src/agents/subagent-announce.ts (интеграция разрешения назначения завершений)

Адаптер Discord и среда выполнения:

  • src/discord/monitor/thread-bindings.manager.ts
  • src/discord/monitor/reply-delivery.ts
  • src/discord/send.outbound.ts

Тесты:

  • src/discord/monitor/provider*.test.ts
  • src/discord/monitor/reply-delivery.test.ts
  • src/agents/subagent-announce.format.test.ts

Критерии завершения итерации 1

  • основные интерфейсы существуют и подключены для маршрутизации завершений
  • указанные выше исправления корректности объединены с тестами
  • отсутствие дублирования доставки завершений в основной канал и тред для запусков в режиме привязанной сессии
  • отсутствие изменения поведения для развертываний с отключенной привязкой порождения
  • ACP остается явно отложенным

План PTY и супервизии процессовИсследование памяти рабочего пространства