Skip to Content
EntwicklerhandbuchDaemon-Modus (Deep Dive für Entwickler)Session-Lifecycle & Identität

Session-Lifecycle & Identität

Übersicht

Eine Daemon-Session ist eine logische Konversation, die an eine ACP-sessionId gebunden ist. Die Bridge verwaltet pro Session einen SessionEntry (siehe 03-acp-bridge.md), der die ACP-Child-Verbindung mit dem HTTP-seitigen Bookkeeping koppelt: Prompt-FIFO, Model-Change-FIFO, Event Bus, ausstehende Permissions, angehängte Clients, Heartbeats, Restore-State, Terminal-Frame-Tombstones.

Ein Daemon-Client wird durch X-Qwen-Client-Id identifiziert – ein opaker, vom Daemon validierter String, den der HTTP-Caller seinen Requests hinzufügt. Die Bridge verfolgt, welche Clients mit welchen Sessions verbunden sind, und verwendet die Originator-Client-ID, um die designated Permission-Policy, Audit-Trails und Event-Attribution zu steuern.

Dieses Dokument erläutert jeden Session-Lifecycle-Übergang (create / attach / load / resume / close / die / evict) und jede Identitätsoberfläche, die der Daemon bereitstellt.

Zuständigkeiten

  • Sessions minten, anhängen, wiederherstellen und bereinigen.
  • X-Qwen-Client-Id validieren und fehlerhafte IDs ablehnen.
  • Mehrere angehängte Clients pro Session verfolgen (clientIds: Map<string, count>, attachCount).
  • originatorClientId auf ausgehende Events stempeln.
  • Heartbeats ausführen, damit Dashboards wissen, welche Clients noch verbunden sind.
  • Session-Metadaten (displayName) bereitstellen, die Operatoren über PATCH /session/:id/metadata setzen.
  • Ausgabe von Terminal-Frames steuern (session_died, session_closed, client_evicted, stream_error).

Architektur

BereichQuelleHinweise
SessionEntrypackages/acp-bridge/src/bridge.tsPro-Session-Struct; siehe 03-acp-bridge.md für die vollständige Feldauflistung.
BridgeSession (public)packages/acp-bridge/src/bridgeTypes.ts{ sessionId, workspaceCwd, attached, clientId?, createdAt? }, das an HTTP-Handler zurückgegeben wird.
BridgeSessionStatepackages/acp-bridge/src/bridgeTypes.tsLoadSessionResponse | ResumeSessionResponse, das im Entry als restoreState zwischengespeichert wird.
DaemonSession (SDK)packages/sdk-typescript/src/daemon/types.ts{ sessionId, workspaceCwd, attached, clientId?, createdAt? }.
Client-id validationpackages/acp-bridge/src/bridge.ts (around spawnOrAttach)Pattern [A-Za-z0-9._:-]{1,128}; InvalidClientIdError bei fehlerhafter Formatierung.
Session disconnect-reaperpackages/cli/src/serve/server.tsVerfolgt Spawn-Owner-Disconnects mit attachCount + spawnOwnerWantedKill.

State machine

Attach vs spawn

Unter sessionScope: 'single' (Standard) wird der defaultEntry der Bridge von jedem verbindenden Client geteilt. Ein POST /session, das eintrifft, während defaultEntry bereits existiert, gibt attached: true zurück, ohne ein neues ACP-Child zu spawnen. Die Bridge erhöht synchron attachCount und registriert die X-Qwen-Client-Id des Callers in clientIds.

Unter sessionScope: 'thread' kann jeder Thread eine eigene Session minten. Der Caller beachtet dabei weiterhin maxSessions.

Identität

X-Qwen-Client-Id ist optional, wird aber dringend empfohlen. Der Daemon generiert keine im Namen des Callers – Clients wählen ihre eigene und verwenden sie über Requests hinweg wieder, damit der Daemon Votes zuordnen, Events auditieren und Reconnects erkennen kann.

Jeder unabhängige Controller sollte eine eigene, stabile ID verwenden. Web Shell bewahrt das historische webui_-Präfix aus Kompatibilitätsgründen. Ein Host und eine eingebettete WebShell sollten sich eine ID nur teilen, wenn sie absichtlich als ein logischer Controller agieren; sobald sie geteilt wird, können Daemon-Logs nicht unterscheiden, welcher der beiden einen Request ausgelöst hat.

Validierungsregeln:

  • Zeichensatz: [A-Za-z0-9._:-].
  • Länge: 1–128.
  • Außerhalb dieses Sets: InvalidClientIdError (400).

Der Daemon stempelt originatorClientId auf ausgehende SSE-Events, wenn:

  1. der Request, der das Event ausgelöst hat, X-Qwen-Client-Id enthielt, UND
  2. die ID derzeit im clientIds-Set der Session registriert ist, UND
  3. die Session eine activePromptOriginatorClientId gesetzt hat (Inline-sessionUpdate und permission_request erben den Originator vom aktiven Prompt).

Anonyme Caller (ohne X-Qwen-Client-Id) funktionieren einwandfrei mit der first-responder-Policy; designated lehnt ihre Votes mit permission_forbidden{ reason: 'designated_mismatch' } ab; consensus lehnt mit demselben forbidden-Grund ab, da der Voter nicht im votersAtIssue-Snapshot zum Ausgabezeitpunkt ist; local-only ist die einzige Policy, die anonyme Loopback-Voter akzeptiert.

Workflow

Create or attach

Load / resume

POST /session/:id/load – stellt eine persistierte Session wieder her und gibt das aktuelle begrenzte Replay-Snapshot-Fenster zurück (session/load-Benachrichtigungen oder Response-Mode-Replay werden vor der Response-Ausgabe gespeist). POST /session/:id/resume – stellt wieder her, ohne abzuspielen (connection.unstable_resumeSession, verfügbar unter der stabilen session_resume-Daemon-Capability; unstable_session_resume bleibt ein deprecated Alias).

Beide:

  1. Verwenden ein session-spezifisches pendingRestoreIds-Set auf dem Channel, sodass gleichzeitige Restore-Aufrufe zusammengeführt werden (RestoreInProgressError).
  2. Zwischenspeichern von restoreState im Entry, sodass ein später hinzukommender Attacher dieselbe Payload erhält wie der ursprüngliche Restorer.

Für eine persistierte Part-4A-Worktree-Session ist die Wiederherstellung eine integritätsgesicherte Erweiterung dieses Lifecycles. Der Sidecar identifiziert explizit das angeforderte Workspace-Root, der Checkout muss kanonisch unter dem entsprechenden .qwen/worktrees/-Verzeichnis enthalten sein, und sein Marker muss eine Single-Link-Regular-File sein, die die exakt wiederhergestellte Session-ID enthält. Der Daemon reloziert ein idle wiederhergestelltes Child erst nach diesen Prüfungen; ein aktives Child wird nur akzeptiert, wenn sein gemeldetes Cwd bereits dem Worktree entspricht, und ein aktives Child, dessen gemeldetes Cwd fehlt oder sich woanders befindet, schlägt fail-closed statt unter seinem Prompt reloziert zu werden – außer bei einem Cold Restore, der seinen Restore-Prompt nicht aufschieben konnte (suppressWorktreeContextRestore ausgeschaltet, sodass die Bridge die erneut eingehängte Frage abgefeuert hat, statt sie zu parken): diese Form behält das Pre-4B-Ergebnis bei, gibt kanonische Worktree-Metadaten ohne worktreeState zurück, nicht reloziert, wobei die Session überlebt. Die relozierten und akzeptierten Responses geben kanonische Worktree-Metadaten mit worktreeState: "persisted-v1" zurück. Ein Sidecar mit supersededBy wird niemals wiederhergestellt: die Route gibt 409 worktree_session_superseded mit der Ersatz-Session-ID zurück, eine Klassifizierung, die allein aus diesem Link vor jedem Marker-Read entschieden wird, sodass Caller umleiten und ihr Bookkeeping erst heilen, sobald ein Load dieser ID erfolgreich war – ein Pre-Commit-unterbrochener Transfer benennt einen Ersatz, der nicht der Marker-Owner ist, nicht selbst wiederhergestellt werden kann und durch den wiederholten Reset gereapt wird; ein wiederhergestellter Ersatz, dessen supersedes-Link mit dem alten Sidecar übereinstimmt, während der Marker sich nie bewegt hat – oder fehlt – gibt 409 worktree_reset_interrupted zurück, dessen Reparatur darin besteht, den Reset gegen die ersetzte Session zu wiederholen; und ein fehlender Marker ohne dieses übereinstimmende Link-Paar gibt 409 worktree_marker_missing zurück, dessen Reparatur darin besteht, den Task zurückzusetzen statt den Restore zu wiederholen, da kein Restore-Pfad einen Marker neu erstellt. Die unterbrochene Klassifizierung wird zuerst geprüft. Ungültiger Part-4A-State trennt ein bestehendes Attachment oder tötet einen Cold Restore mit requireZeroAttaches; ein fehlender Sidecar liefert ebenfalls keine Attestierung. Wann immer die effektive Restore-Quelle Channel-owned ist, unterdrückt die Route das Best-Effort-Cleanup des ACP-Agenten für Part-4A- oder nicht klassifizierbaren Sidecar-State, sodass ein Validierungsfehler die unsicheren Checkout-Beweise bewahrt. Persistierte Quell-Metadaten haben Vorrang; wenn sie fehlen, liefert der Load/Resume-Request die effektive Quelle. Ein strukturell gültiger Legacy-Sidecar ohne workspaceCwd behält stattdessen das bestehende Best-Effort-Agenten-Restore bei: er muss entweder das angeforderte Workspace-Root oder dessen Git-Repository-Top-Level identifizieren, wird ohne Marker-Attestierung auf Containment geprüft, kann vom Agenten bereinigt werden und kann worktree ohne worktreeState zurückgeben. Abgesehen von diesem expliziten Legacy-Kompatibilitätsfall behalten nur Sessions, deren effektive Restore-Quelle nicht Channel-owned ist, das bestehende Best-Effort-Cleanup vor der Route-Validierung.

Worktree-Ownership-Transfer (POST /session/:id/worktree-reset, beworben durch session_worktree_reset_v1) erweitert diesen Lifecycle für Channel-Task-Resets: der Daemon spawnt einen frischen Thread-scoped Ersatz im Root-Workspace, reloziert ihn in den verifizierten Checkout, verlinkt das Sidecar-Paar (supersededBy zuerst auf der alten Session, dann supersedes auf dem Ersatz), kippt den Marker auf den Ersatz unter einem Per-Checkout-Route-Lock und einer Admission-Barriere, die Prompt-Admission plus die sieben anderen Writer fence, die im Checkout Arbeit aufnehmen oder die Session-Cwd bewegen (Rewind, Cwd-Änderung, Branch, Fork, Shell, Goal-Control, Workflow-Task-Action), während die Release- und Stop-Pfade designbedingt nicht gefenced sind, und trennt dann die Client-Registrierungen und In-Memory-Worktree-Assoziation der ersetzten Session. Die Trennung berichtet, ob die ersetzte Session tatsächlich verschwunden ist: ein Überlebender, dessen Child noch Hintergrundarbeit hält, hält die Barriere aktiv, wird geloggt und wird dem Caller als supersededSessionLive: true gemeldet, statt von der Success-Response überdeckt zu werden. Ein gefenced Writer, der während des Transfers auf der ersetzten Session zugelassen wird, wird mit 409 worktree_reset_active abgelehnt; die vollständige Fehler-Taxonomie, einschließlich welche Crash-Windows ein Retry zurückrollt und welche fail-closed für Operator-Reparatur gehen, ist bei der Route in qwen-serve-protocol.md dokumentiert.

Heartbeat

POST /session/:id/heartbeat aktualisiert sessionLastSeenAt unabhängig von der clientId. Wenn der Request eine registrierte X-Qwen-Client-Id enthält, wird zusätzlich clientLastSeenAt.set(clientId, Date.now()) aktualisiert. Eine client-spezifische Eviction ist in v1 nicht implementiert; Revocation ist für F-Series Wave 5 geplant. Heute bieten Heartbeats Observability für Dashboards und für die kommende Revocation-Policy in PR 24.

Metadata

PATCH /session/:id/metadata akzeptiert {displayName?}. Validierung:

  • Maximale Länge: MAX_DISPLAY_NAME_LENGTH = 256.
  • Darf keine Steuerzeichen enthalten (hasControlCharacter lehnt Codepoints ≤ 0x1f oder == 0x7f ab).
  • InvalidSessionMetadataError (400) bei Verstößen.

Ein erfolgreiches Update verteilt session_metadata_updated an jeden Subscriber.

Termination

Terminal-FrameAuslöser
session_closedDELETE /session/:id (client_close) oder programmatisches Schließen.
session_diedchannel.exited wird aus irgendeinem Grund ausgelöst (Crash, Child-Kill). Enthält exitCode? + signalCode?, wenn der OS-Exit-Pfad verwendet wurde.
client_evictedQueue-Überlauf pro Subscriber auf dem EventBus (siehe 10-event-bus.md). KEINE Session-Level-Terminierung – nur dieser Subscriber wird geschlossen.
stream_errorSubscriberLimitExceededError oder anderer Route-Level-Stream-Fehler.

Ausstehende Permissions werden auf jedem Terminierungspfad über mediator.forgetSession(sessionId) als {kind:'cancelled', reason:'session_closed'} aufgelöst.

Disconnect-reaper guard

Wenn die HTTP-Response des Spawn-owning Clients nicht geschrieben werden kann (TCP-Reset mitten im Handshake), ruft die Route killSession({ requireZeroAttaches: true }) auf. Wenn bereits ein anderer Client angehängt ist (attachCount > 0), greift der Guard nicht und die Session bleibt bestehen. Das Setzen von spawnOwnerWantedKill = true merkt sich die Absicht, sodass ein späteres detachClient(), das attachCount wieder auf 0 bringt, das aufgeschobene Aufräumen (reap) abschließt. Ohne dies würde ein schnell trennender Spawn-Owner bei jedem zweiten Reconnect eine gesunde Session abbauen.

Zustand & Lebenszyklus

Für den Lifecycle kritische SessionEntry-Felder:

FeldTypBedeutung
clientIdsMap<string, number>Registrierte Client-IDs → Referenzzähler der Registrierung.
attachCountnumberWie oft spawnOrAttach für diesen Entry attached: true zurückgegeben hat.
activePromptOriginatorClientIdstring?Originator für den aktuell laufenden Prompt.
restoreStateBridgeSessionState?Zwischengespeicherte Load/Resume-Response, sodass spätere Attacher konsistente Payloads sehen.
spawnOwnerWantedKillbooleanTombstone für aufgeschobenes Aufräumen (siehe Disconnect-Reaper oben).
sessionLastSeenAtnumber?Letzter Heartbeat über alle Clients hinweg (Epoch-ms).
clientLastSeenAtMap<string, number>Heartbeat pro Client.
pendingPermissionIdsSet<string>Derzeit ausstehende ACP-Request-IDs – werden bei Cancel/Close verwendet, um sie als cancelled aufzulösen.

Abhängigkeiten

  • ACP-Layer: connection.newSession, connection.unstable_resumeSession, connection.loadSession.
  • 03-acp-bridge.md für die umgebende Bridge-Architektur.
  • 04-permission-mediation.md dafür, wie Originator + Identität Policy-Entscheidungen steuern.
  • 10-event-bus.md für die Zustellung von Terminal-Frames.

Zusätzliche Session-Endpoints

Diese Endpoints erweitern die Basis-Lifecycle-Oberfläche:

Non-blocking Prompt (non_blocking_prompt capability tag)

POST /session/:id/prompt gibt jetzt HTTP 202 mit { promptId, lastEventId } zurück, anstatt zu blockieren, bis der Prompt abgeschlossen ist. Das eigentliche Ergebnis trifft auf SSE als turn_complete / turn_error ein, und das promptId-Feld korreliert diese Events mit der 202-Response. DaemonSessionClient.prompt() verwendet automatisch den nicht-blockierenden Pfad, wenn es ein aktives Event-Abonnement hat, und gleicht das Ergebnis transparent aus dem SSE-Stream ab.

Session Recap (session_recap capability tag)

POST /session/:id/recap fragt das schnelle Modell nach einer einzeiligen Zusammenfassung im Stil von „Wo habe ich aufgehört“. Es gibt { sessionId, recap: string | null } zurück; null bedeutet, dass die Historie zu kurz war oder das Modell vorübergehend fehlgeschlagen ist. Dieser Endpoint ist Best-Effort.

Session BTW / Side Question (session_btw Capability-Tag)

POST /session/:id/btw stellt eine einmalige Frage im Session-Kontext, ohne den Hauptkonversationsfluss zu unterbrechen. Es verwendet runForkedAgent auf dem Cache-Pfad für einen Single-Turn-LLM-Aufruf ohne Tool-Nutzung und gibt { sessionId, answer: string | null } zurück. Die Implementierung erzwingt BTW_MAX_INPUT_LENGTH, Cross-Session-Leakage-Guards und Timeout-Handling.

Shell Command Execution

POST /session/:id/shell führt einen Shell-Befehl direkt auf dem Daemon-Host aus, ohne Routing über das LLM. Es streamt die Ausgabe über den Session-SSE-Bus via user_shell_command / user_shell_result Events und injiziert den Befehl sowie das Ergebnis in den LLM-Konversationsverlauf. Die Antwort ist { exitCode, output, aborted }. Für eine Live-Secondary-Workspace-Session löst die singuläre REST-Route den Session-Owner auf und führt auf der Bridge dieser Runtime aus, sodass der Befehl im Workspace-Cwd des Owners startet. Die Route stellt keine Pfad-Sandbox bereit. Workspace-qualifizierte ACP-Clients können weiterhin _qwen/session/shell auf der Owner-Workspace-Verbindung verwenden.

Session Rewind

GET /session/:id/rewind/snapshots und POST /session/:id/rewind lösen die besitzende Live-Workspace-Runtime auf. Persistierte Sessions müssen vor dem Rewind geladen oder fortgesetzt werden. Rewind kürzt den Konversationsverlauf und stellt optional Dateien wieder her, die von edit und write_file getrackt werden; er macht Shell-Befehle, Git, Skripte oder manuelle Änderungen nicht rückgängig. Die Dateiwiederherstellung ist Best-Effort, sodass eine Antwort rewound: false und filesFailed[] melden kann, nachdem der Konversationsverlauf bereits verschoben wurde. SDK-Rewind-Aufrufe verwenden immer Owner-aware-REST, auch wenn der Client ansonsten ACP-Transport nutzt, weil die Mutation eine strikte REST-Authentifizierung beibehalten muss.

Session Detach

POST /session/:id/detach trennt einen Client explizit von einer Session, indem attachCount dekrementiert wird; es schließt die Session nicht von selbst. Wenn kein weiterer Attach oder Subscriber verbleibt, wird die Session bereinigt. Der Endpoint gibt 204 zurück.

Batch Session Delete

POST /sessions/delete akzeptiert { sessionIds: string[] } (bis zu 100 IDs), schließt Bridge-Sessions und löscht aktive oder archivierte Transcript-Dateien. Wenn für dieselbe ID sowohl aktive als auch archivierte JSONL-Dateien existieren, entfernt ein Hard Delete beide, damit Betreiber den Konflikt bereinigen können. Es bereinigt aktive und archivierte Worktree-Sidecars, lässt aber File-History-Snapshots, Subagent-Transcripts und Runtime-Sidecars intakt. Es verwendet Promise.allSettled für die Resilienz und gibt { removed, notFound, errors } zurück.

Session Archive

POST /sessions/archive verschiebt inaktive Session-JSONL-Dateien von chats/ nach chats/archive/. Wenn die Ziel-Session live ist, durchläuft der Daemon zunächst ein sessionspezifisches Archive-Gate und führt ein striktes Schließen durch, das erfordert, dass das ACP-Child den ChatRecordingService flush; der Archivierungsvorgang belässt die JSONL an ihrem Platz, wenn das Schließen oder Flush fehlschlägt.

POST /sessions/unarchive verschiebt archivierte JSONL-Dateien zurück nach chats/. Dies ist nur eine Storage-State-Transition; Clients müssen danach session/load oder session/resume aufrufen. Archivierte Sessions geben 409 session_archived für Load/Resume zurück, und Mutationen, die mit einem Archivierungsübergang konkurrieren, geben 409 session_archiving zurück.

Leere, beschädigte und verwaiste reguläre Transkriptdateien bleiben für diese Lifecycle-Operationen qualifiziert, auch wenn sie nicht als Konversationen geladen werden können. Ownership-Sicherheitsprüfungen können absichtlich fail-closed ausfallen und einen Operator-Eingriff erfordern. Eine Datei, die geändert wurde, nachdem ein Writer seinen zertifizierten Handoff-Nachweis versiegelt hat, schlägt mit SessionTranscriptChangedError fehl, bis der Operator das versiegelte Lock und die geänderten Bytes auflöst. Ein JSON-förmiger erster physischer Datensatz, der das begrenzte Ownership-Lesefenster überschreitet, schlägt mit SessionTranscriptIdentityUnavailableError fehl, bis der Datensatz repariert oder verkleinert wird; übergroße beschädigte Datensätze mit einem Nicht-Objekt-Präfix bleiben qualifiziert. Ein parsbarer wiederhergestellter Datensatz muss String-sessionId- und cwd-Ownership-Felder enthalten, und gemischte lokale/fremde Archiv-Zustände fallen ebenfalls fail-closed aus. Wenn session_storage_conflict_repair beworben wird, akzeptieren Archive und Unarchive resolveConflicts: true: Archive behält die archivierte Kopie, während Unarchive die aktive Kopie behält. Ohne diese Option verschieben, entfernen oder überschreiben Active/Archive-Konflikte keine der persistenten Kopien und werden im Batch-errors-Array zurückgegeben. Archive schließt weiterhin strikt eine Live-Session, bevor es den Konflikt klassifiziert, was Warteschlangen-Datensätze in das aktive Transkript flushen kann. Workspace-qualifizierte Lifecycle-Routen verwenden jetzt dieses HTTP-200-Batch-Envelope statt ihrer früheren HTTP-409 session_conflict-Response.

Context Usage (session_context_usage Capability-Tag)

GET /session/:id/context-usage gibt die strukturierte Context-Window-Nutzung zurück. ?detail=true schließt eine feiner granulierte Nutzung ein, gruppiert nach Tool, Memory und Skill.

Session Stats (session_stats Capability-Tag)

GET /session/:id/stats gibt Nutzungsstatistiken zurück: Modell-Metriken (Input/Output-Tokens, Cache-Reads/Writes, Gesamtkosten), Anzahl der Tool-Calls und Latenzen pro Tool, Anzahl der File-Edits und Anzahl der Skill-Invocations pro Skill für die Live-Session. Der skills-Block spiegelt nur Skill-Body-Loads und Skill-Slash-Commands innerhalb dieser Session wider; er ist kein sessionübergreifendes Aktivitätsaggregat.

Session Tasks (session_tasks Capability-Tag)

GET /session/:id/tasks gibt einen Background-Task-Snapshot für Agent-Tasks, Shell-Tasks, Monitor-Tasks und deren Lifecycle-States zurück. Agent-Einträge, die von einem anderen Sub-Agenten erzeugt wurden, tragen optionale Lineage-Felder (parentAgentId, parentName, depth), sodass Clients verschachtelte Sub-Agenten als Baum rendern können; siehe das Payload-Beispiel in qwen-serve-protocol.md.

Die session_monitor_tool_correlation-Capability garantiert zusätzlich, dass Monitor-Einträge toolUseId tragen, sodass Clients einen Transkript-Tool-Call mit seinen Task-Details korrelieren können.

Session LSP Status (session_lsp Capability-Tag)

GET /session/:id/lsp gibt den bereinigten, sessionspezifischen LSP-Status für Daemon-Clients zurück: Enablement-Status, aggregierte Server-Anzahlen, Unavailable/Initialization-Status sowie pro-Server name, status, languages, transport, command und error. Deaktivierter oder nicht verfügbarer LSP wird als HTTP-200-Statusdaten dargestellt, nicht als Transport-Fehler.

Compacted Replay

POST /session/:id/load gibt nun eine BridgeRestoredSession zurück, die compactedReplay?: BridgeEvent[], liveJournal?: BridgeEvent[] und lastEventId?: number enthalten kann. Diese Felder sind das begrenzte In-Memory-Replay-Fenster des Daemons für eine Live-Session, keine vollständige Transkript-API. Der Standard-Fenster-Cap beträgt 4 MiB pro Live-Session (--compacted-replay-max-bytes), und der Boot weist ungültige Caps zurück; die harte Obergrenze liegt bei 256 MiB. compactedReplay wird von der TurnBoundaryCompactionEngine erzeugt: An Turn-Grenzen faltet sie aufeinanderfolgende Text-/Thought-Blöcke, komprimiert Tool-Call-Sequenzen auf ihren Endzustand, verwirft transiente Signale und erzeugt O(turns) Replay-Logs anstelle von O(tokens) Logs (typischerweise eine 25-30-fache Reduzierung). Wenn ältere Replay-Einträge aus diesem Byte-Fenster entfernt wurden, ist compactedReplay[0] ein synthetischer ID-loser history_truncated-Marker mit {reason: 'replay_window_exceeded', truncatedEvents, retainedEvents, maxBytes, truncatedTurns?, fullTranscriptAvailable: boolean}. fullTranscriptAvailable ist ein Capability-Flag: true bedeutet, dass der Client das vollständige persistierte Transkript mit GET /session/:id/transcript seitenweise abrufen kann, während false bedeutet, dass nur das begrenzte Replay verfügbar ist. Clients sollten es als Status darstellen und das behaltene Replay normal anwenden; es darf keinen Resync-Loop auslösen.

ACP Child Preheat

bridge.preheat() steht expliziten Embeddern weiterhin zur Verfügung, aber qwen serve versucht auch nach dem Startup, das vertrauenswürdige primäre Child aus Kompatibilitätsgründen vorzuwärmen. Ein fehlgeschlagenes Preheat ist nicht fatal; der nächste Runtime-Befehl oder die nächste Session löst einen Retry aus. Vertrauenswürdige sekundäre Children starten bei erster Benutzung. Die Workspace Runtime besitzt das Child, solange Arbeit aktiv ist. Nachdem alle Session- und Management-Leases gedrainet sind, bringt ein weggelassenes oder null channelIdleTimeoutMs das Child sofort zum Reap; ein bloßes Preheat selbst bleibt für die erste Benutzung erhalten und aktiviert diesen Reaper nicht. Ein positiv konfigurierter Delay oder ein aktiver Keepalive hält das Child für das längere verbleibende Fenster wiederverwendbar. Der öffentliche Workspace-Runtime-ensure-Befehl fügt eine erneuerbare zehnminütige Workspace-Lease hinzu; jeder erfolgreiche Aufruf setzt dieses Fenster zurück, auch wenn der Channel bereits live war.

Konfiguration

  • BridgeOptions.maxSessions (Standard 32) — Obergrenze.
  • BridgeOptions.sessionScope (Standard 'single'; optional 'thread').
  • BridgeOptions.initializeTimeoutMs (Standard 10s) — Deadline für den ACP-Child-Startup (Channel-Factory + initialize-Handshake) und Standard-Request-Timeout.
  • BridgeOptions.sessionRestoreTimeoutMs (Standard 60s) — ACP-loadSession / unstable_resumeSession-Deadline. Standardmäßig 60s; eine explizit konfigurierte Initialize-Timeout kann sie erhöhen, aber niemals senken.
  • BridgeOptions.channelIdleTimeoutMs (nicht gesetzt oder 0 reappt nach dem Drainen der Runtime-Arbeit, außer dass ein bloßes Preheat für die erste Benutzung erhalten bleibt; ein positiver Wert oder ein aktiver Keepalive verzögert das Reapen, und die längere Verzögerung gewinnt).
  • Capability-Tags: session_create, session_id_override, session_scope_override, session_load, session_resume, unstable_session_resume (deprecated alias), session_list, session_info, session_close, session_metadata, session_set_model, client_identity, client_heartbeat, session_recap, session_generation, session_btw, session_context_usage, session_tasks, session_monitor_tool_correlation, session_stats, session_lsp, session_resources, session_status, non_blocking_prompt.

Zustandslose Generierung (session_generation Capability-Tag)

POST /session/:id/generate akzeptiert { "prompt": string } und gibt einen request-scoped SSE-Stream mit started, optionalem thinking, delta, done oder error Events zurück. Der Request liest keinen Konversationsverlauf, zeichnet keinen Turn auf und stellt keine Tools bereit. Das ACP-Child verwendet ein gültiges konfiguriertes schnelles Modell, falls verfügbar, und andernfalls das Hauptmodell der Session.

Hinweise & bekannte Grenzen

  • connection.unstable_resumeSession kann auf der ACP-Ebene noch instabil sein, aber der Daemon bietet den festgeschriebenen v1-Route-Contract mit session_resume an. unstable_session_resume wird nur als deprecated Kompatibilitäts-Alias beibehalten.
  • v1 hat keine client-spezifische Eviction; nur session-spezifische und subscriber-spezifische Terminierung. Die Revocation-Policy ist F-Series Wave 5 / PR 24.
  • client_evicted ist subscriber-spezifisch, nicht session-spezifisch. Ein Client, dessen SSE-Subscriber evicted wurde, kann sich erneut verbinden.
  • Anonyme Clients (ohne X-Qwen-Client-Id) können unter designated- oder consensus-Policies nicht voten.

Referenzen

  • packages/acp-bridge/src/bridge.ts (SessionEntry-Definition)
  • packages/acp-bridge/src/bridgeTypes.ts (HttpAcpBridge, BridgeSession, BridgeSessionState)
  • packages/sdk-typescript/src/daemon/types.ts (DaemonSession)
  • packages/sdk-typescript/src/daemon/DaemonSessionClient.ts
  • Wire-Referenz: ../qwen-serve-protocol.md (Route-Katalog).
Last updated on