Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

session.rpc.ui.elicitation() returns instant decline (1-2ms) without showing modal after sendAndWait

Abierto
#1,149 2 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
45/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Tranquilo
Stack tecnológico
javascript, node.js

Línea de trabajo

Comienza en el controlador del comando joinSession y sigue las rutas session.sendAndWait() y session.rpc.ui.elicitation(), incluido el fallback ask_user. Reproduce el rechazo instantáneo en Windows con el ejemplo mínimo e inspecciona los eventos de la sesión. Se considera terminado cuando la elicitation interactiva puede recopilar entradas después de sendAndWait, o la extensión recibe un resultado distinto y accionable de no disponible.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

documentation

Environment

  • Copilot CLI version: 1.0.36
  • Extension SDK: @github/copilot-sdk/extension (joinSession API)
  • OS: Windows 11 (Windows_NT)

Summary

When a Copilot CLI extension calls session.rpc.ui.elicitation() from inside a command handler that previously called session.sendAndWait(), the elicitation resolves with { action: "decline" } in 1-2ms without ever presenting a modal to the user. The session reports capabilities.ui.elicitation = true and mode = interactive, yet the user never sees any prompt.

The extension's agent-mediated fallback (sending a prompt asking the model to call ask_user) also fails because the ask_user tool auto-declines with "The user is not available to respond" for turns injected by sendAndWait.

Net result: The extension cannot collect any human input after delegating work via sendAndWait, making interactive gates (review finding selection, publish attestation) impossible.

Steps to reproduce

  1. Load a Copilot CLI extension that registers a command via joinSession({ commands: [...] })
  2. In the command handler, call session.sendAndWait() to delegate to another command
  3. After sendAndWait resolves, call session.rpc.ui.elicitation() with a valid schema
  4. Observe that it resolves instantly with { action: "decline" } --- no UI is shown
Minimal reproduction
import { joinSession } from "@github/copilot-sdk/extension";

const session = await joinSession({
  commands: [
    {
      name: "test-elicit",
      description: "Test elicitation after sendAndWait",
      handler: async (context) => {
        // Step 1: Delegate work to the model
        await session.sendAndWait({ prompt: "Say hello" }, 60_000);

        // Step 2: Try to elicit user input
        const start = Date.now();
        const result = await session.rpc.ui.elicitation({
          message: "Pick an option",
          requestedSchema: {
            type: "object",
            properties: {
              choice: {
                type: "string",
                title: "Your choice",
                enum: ["A", "B", "C"],
                default: "A",
              },
            },
            required: ["choice"],
          },
        });
        const elapsed = Date.now() - start;

        // Logs: "Elicitation returned decline in 1ms"
        await session.log(
          Elicitation returned {result.action} in {elapsed}ms
        );
      },
    },
  ],
});

Observed behavior

Timeline from session events
T+0s    session.info    "interactive elicitation: available; session mode: unknown"
        <- session.capabilities.ui.elicitation reports true

T+997s  sendAndWait("/review ...") called, completes successfully

T+997s  session.rpc.mode.set({ mode: "interactive" }) succeeds
        <- mode is now "interactive"

T+997s  session.rpc.ui.elicitation() called
        <- returns { action: "decline" } in 1ms
        <- NO MODAL IS SHOWN TO THE USER

T+997s  Agent-mediated fallback: sendAndWait() with prompt asking model to call ask_user
        <- model calls ask_user
        <- ask_user returns: "The user is not available to respond"
        <- model returns {"action":"decline"}

T+1024s Second elicitation attempt (text fallback)
        <- session.rpc.ui.elicitation() returns decline in 2ms (again, no modal)
        <- agent-mediated fallback also fails with "user not available"
Raw session event evidence
{
  "type": "session.warning",
  "data": {
    "message": "Direct elicitation returned decline in 1ms (mode: interactive); treating as synthetic decline."
  },
  "timestamp": "2026-04-27T03:00:07.856Z"
}

{
  "type": "tool.execution_complete",
  "data": {
    "toolTelemetry": {
      "properties": {
        "elicitation_action": "decline",
        "elicitation_field_types": "[\"multi-enum\",\"string\"]"
      }
    },
    "result": {
      "content": "The user is not available to respond and will review your work later."
    }
  },
  "timestamp": "2026-04-27T03:00:31.300Z"
}

Expected behavior

session.rpc.ui.elicitation() should present an interactive modal to the user and block until the user accepts, declines, or cancels. If the host cannot present a modal in the current execution context (e.g., inside a sendAndWait callback), it should either:

  1. Queue the elicitation and present it after the current turn completes, or
  2. Throw an error with a clear message like "Elicitation not supported during sendAndWait callbacks", or
  3. Return a distinct result (e.g., { action: "unavailable" }) so extensions can distinguish "host can't show UI" from "user clicked decline"

Additionally, ask_user should work for model turns injected by extensions via sendAndWait. The user IS present --- they typed a command and are waiting. The sendAndWait turn is extension-initiated, but the session is still interactive.

Call context where elicitation fails

User types: /submit-pr
  -> handleSubmitPr()
    -> runPreflight()                          <- works
    -> sendAndWait("/review ...")              <- works (30min timeout)
    -> recordAndLogReviewOutput()              <- works
    -> askForReviewDecisions()                 <- FAILS HERE
      -> elicit(message, schema)
        -> session.rpc.ui.elicitation()        <- returns decline in 1ms
        -> agentMediatedElicitation()
          -> sendAndWait(prompt)               <- model calls ask_user
            -> ask_user                        <- "user not available"

Extension workaround code (currently failing)

async function elicit(message, requestedSchema) {
  if (!session.capabilities?.ui?.elicitation) {
    return agentMediatedElicitation(message, requestedSchema, "unavailable");
  }

  const mode = await ensureInteractiveMode(message);
  if (mode === "autopilot") {
    return agentMediatedElicitation(message, requestedSchema, "autopilot");
  }

  const elicitStart = Date.now();
  const result = await session.rpc.ui.elicitation({ message, requestedSchema });
  const elicitMs = Date.now() - elicitStart;

  // Detect synthetic decline (returned too fast for human interaction)
  const isSyntheticDecline =
    result.action === "decline" &&
    (["autopilot", "unknown"].includes(await refreshSessionMode())
     || elicitMs < 2000);

  if (isSyntheticDecline) {
    return agentMediatedElicitation(message, requestedSchema,
      licitation-returned-in-{elicitMs}ms);
  }

  return result;
}

async function agentMediatedElicitation(message, requestedSchema, reason) {
  // Asks the model to call ask_user on behalf of the extension
  // FAILS: ask_user auto-declines for sendAndWait-injected turns
  const response = await sendAndCaptureContent(
    agentElicitationPrompt(message, requestedSchema, reason),
    15 * 60 * 1000
  );
  // ...parse JSON response...
}

Both layers fail:

  • Layer 1 fails because session.rpc.ui.elicitation() returns decline in 1ms
  • Layer 2 fails because ask_user auto-declines for turns injected by sendAndWait

Impact

  • Extensions cannot implement interactive multi-step workflows
  • session.capabilities.ui.elicitation capability check is misleading --- reports true but doesn't work in practice
  • Extensions requiring human-in-the-loop gates (security attestation, publish confirmation) cannot enforce them
  • The sendAndWait -> elicit pattern is the most natural way to build multi-step extension workflows, and it's completely broken
Lenguaje dominante
TypeScript
Estrellas
10.5k
Forks
1.5k
Merge medio
1 d 6 h
PR fusionados (30 d)
106

Preparar el entorno

Abrir en Codespaces

Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de github/copilot-sdk

Todos los issues de github/copilot-sdk

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.