Skip to content

Session extension: pass the prompt language to the context section and make it async #526

Description

@smart-ssh-planner

Goal

The session extension's context section can follow the session's prompt language and can read the extension's own (async) data store. Today the hook gets neither, so an edition that keeps per-server, per-group or per-tag data in its extension tables cannot build a language-correct context section without blocking or duplicating core logic.

Context

  • SessionExtension::context_section(&self, _scope: &ServerScope) -> Option<String> is synchronous and receives only the scope (crates/app-logic/src/session_extension.rs:32).
  • Its only caller, append_extension_section (crates/app-logic/src/session_extension.rs:140, call at :149), already has the prompt language in parts.language (used for the heading via extension_heading, :128, :156) but does not pass it on.
  • append_extension_section is called from two async contexts:
    • crates/app-shell/src/commands/connect.rs:500 (session connect, inside an async fn that already awaits extension_scope),
    • crates/app-shell/src/commands/chat.rs:276 (rebuilt on every user message, inside an async loop that already awaits extension_scope).
  • The language type is PromptLanguage (crates/app-logic/src/system_prompt.rs:135: De, En, Other(ResponseLanguage)).
  • Extension-owned tables (Spec 0038, section 2a) are reached through an async database handle, so a synchronous hook can only read them by blocking the runtime or keeping a separate cache.
  • Precedent for an async edition hook: OutgoingTransform::prepare_outgoing uses #[async_trait] (crates/core/src/outgoing.rs:84-93); async-trait is already a dependency of app-logic (crates/app-logic/Cargo.toml:27).
  • Governing docs: Spec 0038, section 2 "Sitzungs-Erweiterung" (docs/specs/0038-repository-app-shell-ci.md:38-68), ADR 0136 (docs/adr/0136-session-extension-point.md).

Requirements

  • SessionExtension::context_section receives the prompt language of the session as a PromptLanguage argument, the same value append_extension_section uses for the section heading.
  • context_section becomes async (via #[async_trait] on SessionExtension, like OutgoingTransform), so an extension can await reads from its own data store. No block_in_place or other blocking bridge in the core.
  • The default implementation still returns None.
  • append_extension_section becomes async; both call sites (connect.rs:500, chat.rs:276) await it. The order stays: core paragraphs, then the extension heading, then the redacted and fenced section (redact, then fence; ADR 0034).
  • known_secrets, server_removed and group_removed keep their current signatures and behaviour.
  • Without a registered extension the system prompt and the redactor stay byte-identical to before.

Acceptance Criteria

  • context_section is an async trait method that takes &ServerScope and PromptLanguage and returns Option<String>; its default returns None.
  • append_extension_section passes parts.language to context_section; a test extension that returns a different text per language yields the German text for PromptLanguage::De and the English text for PromptLanguage::En (counter-proof: the test fails if the language is not forwarded).
  • A test extension whose context_section awaits (e.g. a tokio::sync read) works through append_extension_section without blocking the runtime.
  • The existing session_extension tests (fence after core text in both languages, empty/whitespace section adds nothing, no extension adds nothing, section is redacted before fencing) pass in their async form.
  • Both call sites in connect.rs and chat.rs await the async section; behaviour for a session without an extension is unchanged.
  • Spec 0038 (section 2, "Sitzungs-Erweiterung") states that the context section receives the prompt language and may be answered asynchronously; ADR 0136 gets a short note on the async hook and the language argument.
  • Relevant automated tests pass

Technical Notes

  • Affected: crates/app-logic/src/session_extension.rs (trait, append_extension_section, tests incl. TestExtension at :248 and RecordingExtension), crates/app-shell/src/commands/connect.rs, crates/app-shell/src/commands/chat.rs, and every other impl SessionExtension in tests (crates/app-logic/src/mcp_lookup.rs:252, crates/app-logic/src/session.rs:1869); they only implement known_secrets but the trait gains #[async_trait], so check they still compile.
  • This changes an internal extension-point API; editions that implement SessionExtension have to adapt their context_section to the new signature.
  • Security: the section remains untrusted content. Redact-then-fence, the server_note fence, and setting untrusted_content_ingested must stay exactly as today. Filter, risk classification and redaction rules are not touched.
  • No data model or migration change.

Spec impact: update 0038

Related Work

Activity

  1. added this to the 0.7.0 milestone on Oct 14, 2026
  2. smart-ssh-coder commented on Oct 14, 2026

    @smart-ssh-coder
    Contributor

    Implementation update

    PR: #527
    Implemented:

    • SessionExtension::context_section is async and takes &ServerScope + PromptLanguage; append_extension_section is async; connect.rs and chat.rs await it.
    • Tests: language forwarding (fails if not forwarded) and an awaiting extension; existing tests are async.
    • Spec 0038 and ADR 0136 updated.
      Remaining / open:
    • Windows CI job was still running at handoff; macOS and Ubuntu are green.
      Decisions:
    • No changelog fragment (internal extension API).
  3. smart-ssh-reviewer commented on Oct 14, 2026

    @smart-ssh-reviewer

    Review result

    Approved — see review on #527.

    Findings:

    • No blocking findings; CI green on all jobs.

    Recommended next step: merge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Fields

    Priority

    P1 - High

    Stage

    Done

    Role

    None

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions