Zum Inhalt springen

TTSR-Injektionslebenszyklus

Dieses Dokument behandelt den aktuellen Time Traveling Stream Rules (TTSR) Laufzeitpfad von der Regelerkennung über Stream-Unterbrechung, Retry-Injektion, Extension-Benachrichtigungen bis hin zur Sitzungszustandsverwaltung.

Bei der Sitzungserstellung lädt createAgentSession() alle erkannten Regeln und erstellt einen TtsrManager:

const ttsrSettings = settings.getGroup("ttsr");
const ttsrManager = new TtsrManager(ttsrSettings);
const rulesResult = await loadCapability<Rule>(ruleCapability.id, { cwd });
for (const rule of rulesResult.items) {
if (rule.ttsrTrigger) ttsrManager.addRule(rule);
}

loadCapability("rules") dedupliziert nach rule.name mit First-Wins-Semantik (höhere Provider-Priorität zuerst). Überdeckte Duplikate werden vor der TTSR-Registrierung entfernt.

Die Registrierung wird übersprungen, wenn:

  • rule.ttsrTrigger nicht vorhanden ist
  • eine Regel mit demselben rule.name bereits in diesem Manager registriert wurde
  • der reguläre Ausdruck nicht kompiliert werden kann (new RegExp(rule.ttsrTrigger) wirft einen Fehler)

Ungültige Regex-Trigger werden als Warnungen protokolliert und ignoriert; der Sitzungsstart wird fortgesetzt.

TtsrSettings.enabled wird in den Manager geladen, wird aber derzeit nicht bei der Laufzeitsteuerung geprüft. Wenn Regeln existieren, wird der Abgleich trotzdem durchgeführt.

Die TTSR-Erkennung läuft innerhalb von AgentSession.#handleAgentEvent.

Bei turn_start wird der Stream-Buffer zurückgesetzt:

  • ttsrManager.resetBuffer()

Wenn Assistenten-Updates eintreffen und Regeln existieren:

  • text_delta und toolcall_delta überwachen
  • Delta in den Manager-Buffer anhängen
  • check(buffer) aufrufen

check() iteriert über registrierte Regeln und gibt alle übereinstimmenden Regeln zurück, die die Wiederholungsrichtlinie (#canTrigger) bestehen.

3. Trigger-Entscheidung und sofortiger Abbruchpfad

Abschnitt betitelt „3. Trigger-Entscheidung und sofortiger Abbruchpfad“

Wenn eine oder mehrere Regeln übereinstimmen:

  1. markInjected(matches) zeichnet Regelnamen im Injektionszustand des Managers auf.
  2. Übereinstimmende Regeln werden in #pendingTtsrInjections eingereiht.
  3. #ttsrAbortPending = true.
  4. agent.abort() wird sofort aufgerufen.
  5. Das ttsr_triggered-Event wird asynchron emittiert (fire-and-forget).
  6. Die Retry-Arbeit wird über setTimeout(..., 50) geplant.

Der Abbruch wird nicht durch Extension-Callbacks blockiert.

4. Retry-Planung, Kontextmodus und Erinnerungsinjektion

Abschnitt betitelt „4. Retry-Planung, Kontextmodus und Erinnerungsinjektion“

Nach dem 50ms-Timeout:

  1. #ttsrAbortPending = false
  2. ttsrManager.getSettings().contextMode auslesen
  3. Wenn contextMode === "discard", partielle Assistenten-Ausgabe mit agent.popMessage() verwerfen
  4. Injektionsinhalt aus ausstehenden Regeln mithilfe der ttsr-interrupt.md-Vorlage erstellen
  5. Eine synthetische Benutzernachricht anhängen, die einen <system-interrupt ...>-Block pro Regel enthält
  6. agent.continue() aufrufen, um die Generierung erneut zu starten

Die Vorlagen-Payload ist:

<system-interrupt reason="rule_violation" rule="{{name}}" path="{{path}}">
...
{{content}}
</system-interrupt>

Ausstehende Injektionen werden nach der Inhaltsgenerierung gelöscht.

  • discard: Die partielle/abgebrochene Assistentennachricht wird vor dem Retry entfernt.
  • keep: Die partielle Assistenten-Ausgabe verbleibt im Konversationszustand; die Erinnerung wird danach angehängt.

TtsrManager verfolgt #messageCount und pro Regel lastInjectedAt.

Eine Regel kann nur einmal ausgelöst werden, nachdem sie einen Injektionseintrag hat.

Eine Regel kann erneut ausgelöst werden, nur wenn:

  • messageCount - lastInjectedAt >= repeatGap

messageCount wird bei turn_end inkrementiert, daher wird die Lücke in abgeschlossenen Turns gemessen, nicht in Stream-Chunks.

6. Event-Emission und Extension-/Hook-Oberflächen

Abschnitt betitelt „6. Event-Emission und Extension-/Hook-Oberflächen“

AgentSessionEvent enthält:

{ type: "ttsr_triggered"; rules: Rule[] }

#emitSessionEvent() leitet das Event weiter an:

  • Extension-Listener (ExtensionRunner.emit({ type: "ttsr_triggered", rules }))
  • Lokale Sitzungsabonnenten
  • Die Extension-API stellt on("ttsr_triggered", ...) bereit
  • Die Hook-API stellt on("ttsr_triggered", ...) bereit
  • Custom Tools erhalten onSession({ reason: "ttsr_triggered", rules })

Unterschied bei der Darstellung im interaktiven Modus

Abschnitt betitelt „Unterschied bei der Darstellung im interaktiven Modus“

Der interaktive Modus verwendet session.isTtsrAbortPending, um die Anzeige des abgebrochenen Assistenten-Stoppgrunds als sichtbaren Fehler während der TTSR-Unterbrechung zu unterdrücken, und rendert eine TtsrNotificationComponent, wenn das Event eintrifft.

7. Persistenz und Wiederaufnahmezustand (aktuelle Implementierung)

Abschnitt betitelt „7. Persistenz und Wiederaufnahmezustand (aktuelle Implementierung)“

SessionManager hat volle Schemaunterstützung für die Persistenz injizierter Regeln:

  • Eintragstyp: ttsr_injection
  • Anhänge-API: appendTtsrInjection(ruleNames)
  • Abfrage-API: getInjectedTtsrRules()
  • Die Kontextrekonstruktion enthält SessionContext.injectedTtsrRules

TtsrManager unterstützt auch die Wiederherstellung über restoreInjected(ruleNames).

Im aktuellen Laufzeitpfad:

  • AgentSession fügt keine ttsr_injection-Einträge hinzu, wenn TTSR ausgelöst wird.
  • createAgentSession() stellt existingSession.injectedTtsrRules nicht in den ttsrManager zurück.

Nettoeffekt: Die Unterdrückung injizierter Regeln wird im Arbeitsspeicher für den laufenden Prozess durchgesetzt, wird aber derzeit über diesen Pfad nicht persistent gespeichert/wiederhergestellt bei Sitzungsneuladung/-wiederaufnahme.

8. Race-Condition-Grenzen und Reihenfolgegarantien

Abschnitt betitelt „8. Race-Condition-Grenzen und Reihenfolgegarantien“
  • Der Abbruch ist aus TTSR-Handler-Perspektive synchron (agent.abort() wird sofort aufgerufen)
  • Der Retry wird durch Timer verzögert (50ms)
  • Die Extension-Benachrichtigung ist asynchron und wird absichtlich nicht vor der Abbruch-/Retry-Planung abgewartet

Mehrere Übereinstimmungen im selben Stream-Fenster

Abschnitt betitelt „Mehrere Übereinstimmungen im selben Stream-Fenster“

check() gibt alle derzeit übereinstimmenden berechtigten Regeln zurück. Sie werden als Batch in der nächsten Retry-Nachricht injiziert.

Während des Timer-Fensters kann sich der Zustand ändern (Benutzerunterbrechung, Modusaktionen, zusätzliche Events). Der Retry-Aufruf erfolgt nach dem Best-Effort-Prinzip: agent.continue().catch(() => {}) schluckt Folgefehler.

  • Ungültiger ttsr_trigger-Regex: wird mit Warnung übersprungen; andere Regeln funktionieren weiter.
  • Doppelte Regelnamen auf Capability-Ebene: Duplikate mit niedrigerer Priorität werden vor der Registrierung überdeckt.
  • Doppelte Namen auf Manager-Ebene: Die zweite Registrierung wird ignoriert.
  • contextMode: "keep": Partielle verletzende Ausgabe kann vor dem Erinnerungs-Retry im Kontext verbleiben.
  • Repeat-after-gap hängt von Turn-Zähler-Inkrementen bei turn_end ab; Chunks innerhalb eines Turns erhöhen die Lückenzähler nicht.