Skip to content

efr 0.0.3

Choose a tag to compare

@github-actions github-actions released this 09 Oct 20:17
· 1325 commits to main since this release

Added

  • Model calls to OpenAI can go over a WebSocket, which cuts the time to the first
    token. Each conversation keeps one connection open between the calls of a turn and
    between turns, and closes it after 10 minutes without a call. Before a call on a
    connection that had no call for 30 seconds, efr sends a ping. When the connection
    died during a sleep of the computer or a change of network, the call goes over HTTP
    after at most 5 seconds. While the connection holds the previous answer, a call
    sends only its new input. A call that the server did not take on the WebSocket goes
    over HTTP. An error answer on the WebSocket, such as a rate limit or a context
    overflow, fails the call as it does over HTTP, without a second request. A call
    whose request went out and whose connection then broke or went silent fails as on
    HTTP and does not go out again, because the model may already run. When you
    interrupt a turn, efr stops the answer on the connection and keeps the connection
    for the next call. The new key [openai] websocket chooses the transport: auto
    (the default) uses a WebSocket for each model that the model catalog marks with
    prefer_websockets and HTTP for the other models, so a new catalog from the backend
    changes the choice from the next call. on uses it for every model, and off uses
    HTTP only. The key needs a restart of efrd. The debug log shows the time to the
    first event of each call with its transport (phase=provider_accepted and
    phase=provider_first_event), so you can compare the two.
  • The model edits files with a new apply_patch tool. One patch adds, changes,
    deletes or moves one or more files, and it changes all of them or none: when a part
    does not match, the model gets the nearest lines of the file and no file changes.
    When you interrupt the turn while the patch writes, efr puts every file back.
    The call shows the diff of each file. A change or a new file in the turn's project
    or in $SCRATCH needs no approval, as for write_file. A delete or a move always
    asks, also in auto, because undo cannot bring the file back yet. The question
    shows the diff of every file and marks each delete and move. The models that take
    freeform tools get the tool with its grammar. When you change to a model that does
    not take freeform tools, the earlier calls go to it in the form that it takes. The
    system prompt tells the model to use it, and not sed -i or a whole-file write,
    for an edit.
  • efr shows an apply_patch call by its files, such as apply_patch src/a.rs +3 −1, new notes.md +2, delete old.rs, and never the text of the patch. After the call,
    each file gets its own diff block with a row that names it, and
    render.diff_lines applies to each file. The approval of a patch shows the diff of
    every file, and marks a delete or a move in the warning colour. A file outside the
    project, $SCRATCH and your home keeps its absolute path, such as /etc/hosts.
    While the model writes the patch, the status row shows preparing apply_patch, 3.2 KB.
  • An input row below a running turn, in the terminal that follows it. Type there while
    the turn runs: Enter steers the turn, Tab queues the text as a prompt behind it, Esc
    interrupts it and Alt+Up takes back the newest prompt that you queued there. The row
    edits like a shell line (arrows, Home, End, Ctrl+A/E/U/W/K, Alt+B/F), Ctrl+J adds a
    newline, and a paste keeps its newlines. Keys that you type while the prompt goes
    out land in the row too. An approval takes only the keys that you type after it
    shows: keys typed before it and the rest of a paste stay in the row. While the
    output of a command ends with a password prompt, the row hides, and what you type
    never goes to the model as a steer. A complete output line that only names a
    password, as grep password prints, does not hide the row.
    render.turn_input = false turns the row off.
  • Steers that the model did not read yet show above the status row as ↳ steer:, and
    prompts that you queued as ↳ queued:. When a model call reads a steer, it moves
    into the reply as your message. A steer that comes too late for its turn waits in
    the queue instead.
  • efr follows each prompt that you queue from the input row after the turn before
    it, and ends after the last one. Esc sends the steers that the model did not read as
    a new prompt, and puts the prompts that you queued back into the row, so nothing
    runs that you did not see. Esc stops the turn also when the queue is full or when
    the settings of your terminal do not work now: the steers then go with the settings
    of the stopped turn. Ctrl+C clears the row; on an empty row it interrupts the
    turn and also takes back the prompts that you queued there and the steers that the
    model did not read. A prompt that still waits behind another turn is taken back
    instead, by Esc and by Ctrl+C. When efrd does not answer Ctrl+C in time, or the
    connection ends, all of that text goes back to your shell with a note, so no text
    is lost.
  • Text that is still in the input row when efr ends goes back to your zsh command
    line as , <text>, so you can edit it and send it again. Without the zsh plugin,
    efr shows it as one muted line. Each efr run gets a file of its own, so the
    texts of two runs before the next prompt both come back, also from a pipeline or
    after you source the plugin again. A new shell removes only the files of gone
    shells in its own pid namespace, so a shell in a container (distrobox, toolbox)
    keeps its text.
  • When efrd restarts while efr follows a turn with the input row, efr connects
    again. Prompts that you queued and steers that the model did not read come back to
    your command line when the restart cancelled them. A steer or a prompt whose answer
    the restart lost is not sent twice. When efrd does not come back, they come back
    too. SIGTERM and SIGHUP end efr at once also while it connects again.
  • The diff of each file write shows in its call block, in the diff colours: the first
    render.diff_lines lines (default 20), then a muted … N more lines. This works in
    every directory.
  • A shell call that changed files gets one muted row under its result, such as
    changed src/a.rs +3 −1 · deleted old.rs · new notes.md (+2 more). A turn that
    changed files gets a muted line such as 3 files changed, +24 −7 before its end.
    This works in registered projects (git or not) and in $SCRATCH. The row and the
    line show what changed while the call or the turn ran, also a file that another
    conversation or your editor saved at the same time.
  • efr diff prints what a turn changed: by default the last turn of this terminal's
    conversation, --turn <id> for another, --stat for the list of files. In a pipe it
    writes the plain diff. An ignored file such as .env shows in the list, but its
    content stays out of the diff.
  • efr's own snapshot store: one bare git repository per registered project or
    $SCRATCH below the data directory. It never writes the project's .git or index.
    The [snapshot] table sets it (snapshot.enabled, size limits, keep_turns,
    max_age_days).
  • efrd has the parts that the input row of a turn needs. A steer that comes too late
    for the running turn can become a queued prompt (turn.steer with if_late). You
    can take back a prompt that waits in the queue before it starts (prompt.withdraw).
    An interrupt can take back the queued prompts of one terminal and send its unread
    steers again as one new prompt, which runs next with the settings of that terminal.
    An interrupt can also take back unread steers, so that no model call reads them
    (steering_withdrawn). efrd records when a model call reads a steer
    (steering_delivered). efr history shows a prompt that you took back as
    withdrawn before it ran, and steers that you took back as took back a steer that the model did not read.
  • efrd sends the conversation id as the prompt cache key of every model request, and
    on the subscription also as the session-id header, as Codex does. The requests of
    one conversation then go to the same prompt cache.
  • turn_completed, turn_failed and turn_interrupted say how full the model's
    context is (context: the tokens, the point where efrd compacts and the window).
    Their usage also gives the cached input tokens, the reasoning tokens and the
    context of the last model call (context_tokens). A client that follows a turn gets
    a context draft before and after each model call.
  • An entry of [openai] models can be a table that gives the model's limits:
    { id = "gpt-next", context_window = 400000, max_output_tokens = 128000 }. An
    entry can also name a built-in model to change its limits. models.list shows the
    context window of each model that efr knows. On an API key, a model's own output
    limit wins over [model] max_output_tokens.
  • efr compacts the context of a conversation before it fills the model's window. When
    a request reaches [compaction] auto_at percent of the window (76 by default),
    efrd first gives old tool outputs a short stub, when that frees at least 20000
    tokens. When that is not enough, the conversation's own model writes a summary with
    fixed sections: the task and its state, the decisions, the important details, the
    files and places, the open work and the next step, the directories and the shell,
    and the open system tasks. The next requests send a fresh block that efrd reads from
    disk (your directory, the hidden shell's directory and its jobs, the git status and
    the AGENTS.md files from the project root down), the summary, and the newest
    messages word for word, about 20000 tokens. The turn goes on after the compaction.
    [compaction] auto = false turns it off. All events stay in the log. The history
    keeps every earlier turn that the model's window can hold, so the compaction makes
    room, not a limit of turns or bytes. A summary that the model cuts off at its output
    limit never replaces the history. When the history does not fit in the summary
    request, its oldest messages go, and the summary, the compaction's line and the log
    say how many.
  • efr shows how full the model's context is. The status row of a running turn, and
    the line of a running command, end with ctx 43%, and the end-of-turn line reads
    done in 42s, ctx 43% (89k/206k), 1.1k out. 100% is the point where efr compacts
    the context. The field is green below 50%, yellow from 50% and red from 90%, in the
    colours of your theme. While efr compacts the context, the status row says
    compacting context. Each compaction leaves one dim line, such as context compacted (auto): 231k -> 24k tokens, kept 3 turns, summary 3.2k, or a line that
    says that the context is full and what to do. efr history shows the same line at
    the place of each compaction, and --verbose adds the summary. A client that
    attaches while a turn runs shows the field at once.
  • efr compact [focus] and ,compact [focus] compact the context of a conversation
    now, between turns: efr writes a summary of the earlier turns and keeps the newest
    turns word for word. The focus says what the summary must keep. It starts no turn,
    and a prompt that you send meanwhile waits for it. While a turn runs, efr refuses
    it. A retry of the same command waits for the compaction that runs. The plugin
    hands the focus to efr in its environment, as it does a prompt.
  • efr knows the current OpenAI models (gpt-6.1-sol first) with their windows and
    efforts, from a table built into efr. efrd also asks the ChatGPT backend for its
    model list in the background, at start, after efr login openai and then every
    hour, with efr's own version. Today the backend lists its models only for Codex
    versions, so the built-in table stays in use. A prompt never waits for this
    request. When the backend sends a list that efr can use, it applies from the next
    turn on, and efrd keeps it in its state directory
    (~/.local/state/efr/model_catalog.json by default) for a start without network.
    Models that the backend hides, or whose tools efr cannot send, are not offered.
  • efr models shows the context window of each model and the largest window that
    [openai] models can set, such as gpt-6.1-sol 272k (up to 872k), and a last line
    that says where the list came from: models: from the backend, fetched 5m ago,
    from the cache, fetched 2h 5m ago or built into efr. efr status shows the same
    as its models line.
  • A context_window in [openai] models can raise or lower the window of a model of
    the list, up to its largest window. Above that, efrd uses the largest window and
    warns once in its log, and efr config check adds a note.
  • The token counts of a turn on the protocol now include the input tokens that the
    provider wrote to its prompt cache: cache_write_tokens, and
    cache_write_1h_tokens for the part that it keeps for an hour. efr reads OpenAI's
    count of cache writes. Like cached_input_tokens, both are parts of
    input_tokens.
  • The end-of-turn line shows the part of the turn's input that the provider's prompt
    cache served, such as done in 42s, ctx 43% (89k/207k), 1.1k out, cache 91%. A
    cache write counts as a miss. The line shows it only when the turn's input is 2048
    tokens or more, because a smaller input never makes a cache entry.
  • Claude models with an Anthropic API key, provider anthropic-api. Log in with
    efr login anthropic, set [model] provider = "anthropic-api" and restart efrd.
    efrd streams Anthropic's Messages API, sends the signed thinking of each answer back
    unchanged, and sends an effort to each model that takes one: medium unless you
    set another. A new [anthropic] table has base_url, models, cache_ttl and
    workspace_id (for a key that is not scoped to one workspace). cache_ttl = "auto", the default, keeps
    everything in the prompt cache for an hour, because an entry for one hour cannot
    build on an entry for five minutes; 5m keeps everything for five minutes.
  • The debug log of efrd gives each model call the time since the start of the
    conversation's call before, as gap_ms. Each request to Claude also writes one line
    with its prompt cache markers and their times, such as cache_ttl=auto markers=S1h,A1h,P1h,T1h. With the token counts of the turns, these lines show which
    [anthropic] cache_ttl costs less.
  • efr has no built-in list of Claude models. The models, their windows, their output
    limits and their efforts come from the API's model list, which efrd fetches at
    start, after a login and every hour, and keeps in anthropic_model_catalog.json in
    its state directory. Until efrd has a list, a prompt waits for one fetch. The
    default model is claude-opus-5-5. Without a key, a turn fails and says to log in.
  • efr models and efr status name the provider of the model list, such as
    models: anthropic-api, from the backend, fetched 5m ago, and say when efrd has no
    list yet. efr config check also notes a window of [anthropic] models above the
    largest window of its model.
  • A new edit tool for Claude models. It replaces the exact text old_string of one
    file with new_string, once, or at every match with replace_all. When the text
    occurs more than once without replace_all, or does not occur, no file changes and
    the model gets the count or the nearest lines of the file. In a file with Windows
    line ends, the model's line breaks match them. A request offers a model either
    apply_patch (OpenAI models) or edit (Claude models), never both, and a call of
    the other one does not run. An edit needs approval where a write_file of the same
    file does. The question shows the diff of the file, and efr shows the call as
    edit <path>.
  • Logins with an API key: efr login openai-api and efr login anthropic (also
    anthropic-api). efr reads the key at a prompt that does not show it when stdin is
    a terminal, else from stdin, or with --from-env from OPENAI_API_KEY or
    ANTHROPIC_API_KEY of your shell. It never takes the key as an argument. efrd
    checks the key with one request that runs no model, --no-check skips the check,
    and then efrd keeps the key in its secrets/ directory. A login does not change
    the provider of new conversations: efr says how to set [model] provider and that
    efrd needs a restart.
  • efr logout <provider> forgets the login of openai, openai-api or anthropic.
    A key stays valid at its provider; efr says where to revoke it.
  • efr status lists every provider (openai-subscription, openai-api and
    anthropic-api), shows the start and the last four characters of each stored key,
    such as sk-ant-...a1b2, and marks the provider of new conversations with
    (active). Its line about a missing login names the login of that provider.
  • efr logout of the provider of new conversations warns on stderr: the provider
    stays in [model] provider, so its turns fail until you log in again. A logout
    records logout_completed in the event log, with the provider and nothing of the
    key. The answer of admin.logout has a new active member.
  • efr status shows when a provider refused its stored API key, such as key sk-ant-...a1b2 refused at 2026-10-09T12:03:00Z (5m 0s ago), after a 401 on a model
    call or on the fetch of the model list. The mark goes away when a request of the
    same kind with the key works again, or after a new login or a logout. After a model
    call that worked, a refused fetch of the model list does not show, so a key whose
    scopes allow one endpoint and not the other does not flip the mark each hour. For
    the active provider, a line
    on stderr says that its turns fail and how to log in. A provider in the answer of
    admin.status has a new key_refused_at member.
  • After efr login openai-api and after efr config set model.provider openai-api,
    one line says that OpenAI bills the API key per token, apart from a ChatGPT plan.
  • A [model] name of another company than [model] provider, such as gpt-5.5 with
    anthropic-api: efr config check warns about it with the fix, efrd logs a
    warning at start and after each reload, and a prompt that names no model of its own
    fails at once with the cause and the fix (set [model] name to a model of the
    provider, or remove it). An id in the config's list of models of the provider's
    company is not of another company.
  • When efrd has no list of Claude models at the start of a turn, the prompt now fails
    with the cause instead of a turn that says that the provider does not serve the
    model. Without a stored key, it says log in with efr login anthropic. After a
    failed fetch, it says efr could not fetch Claude's model list and why, such as the
    API's message for a refused key. A model whose output limit the config gives still
    runs without a list.
  • New keys [openai] organization and [openai] project, sent as the
    OpenAI-Organization and OpenAI-Project headers of every request with an API
    key. Both need a restart of efrd.
  • A new page, docs/providers.md, tells how to log in with an API key, how to change
    the provider, which settings the Claude models have ([anthropic] cache_ttl and what
    auto does, the workspace id, a smaller window), and what makes the prompt cache
    miss.
  • Do not put an API key in the environment of the efrd service: efrd does not read
    it, and it gives its environment to the hidden shells, where a command of the agent
    can read it outside the auto sandbox. docs/providers.md says more.

Changed

  • Each request to the model now starts with the request before it and its answer, so
    the provider's prompt cache reads the earlier turns again and a long conversation
    costs less. The prompts of earlier turns keep their <live_state> block, and the
    system prompt tells the model that only the newest block is current. efr keeps the
    exact messages of every turn since the last summary, not of the newest 50 turns
    only, and a conversation of more than 4096 events no longer leaves out its oldest
    turns. Before, each turn changed the prompt of the turn before it, and from the 51st
    turn on each turn also changed the oldest turn.

  • When the history would leave out a turn, such as after a change to a model with a
    smaller window, the turn compacts first, also below [compaction] auto_at. Before,
    the history left out one more turn at each turn.

  • The fresh context block of a compaction (your directories, the git status and the
    AGENTS.md files) is now part of its record, and conversation_compacted carries
    it as fresh. After a restart of efrd, a request sends the same block. Before, the
    next turn read the files again.

  • The default system prompt tells the model to change files with the edit tool that
    it has, apply_patch or edit, not with apply_patch only.

  • With an OpenAI API key, efrd asks the API for the ids of the models that the key
    can use, at start, after a login and every hour, and offers only those models of
    its built-in list. The efforts are the API's: no model offers ultra, and
    medium is the default effort of every model.

  • An OpenAI answer of 429 for a used-up quota or a spend limit, such as
    insufficient_quota, now fails the turn at once. Before, efr sent the same request
    up to three more times.

  • With an OpenAI API key (openai-api), a key that the server refuses now fails the
    turn at once, and the error shows the server's reason, such as an expired key or a
    missing scope. Before, efr sent the same key a second time and showed no reason.

  • The default model is now the first model of the backend's list (gpt-6.1-sol today),
    not gpt-5.5, which the backend lists as its legacy model. [model] name still wins.
    The first id of [openai] models no longer picks the default model. The window,
    the efforts, the default effort and the tool form of each model come from the
    backend's list too. With an API key, the built-in list stands in, so a model that
    it does not hold needs an entry in [openai] models.

  • When efrd cannot fetch the model list, it tries again after 15 seconds, then after
    30 seconds, 1 minute and 2 minutes, then every 5 minutes. A good fetch starts the
    waits over. Before, it waited 5 minutes each time, so after a start before the
    network was up, the list came late.

  • A long command no longer leaves a short first word alone in its row (cp \). A row
    breaks at a space only when it is at least half full; else it breaks inside the word
    with ↩.

  • The hint of an empty input row starts one column after the cursor. Before, a block
    cursor stood on the first letter of the hint and seemed to cover it. efr does not
    change the cursor shape.

  • The note interrupted to send your message now comes after the call that Esc
    stopped and after the end of the interrupted turn. Before, it came above the
    stopped call.

  • efr history shows the lines of each turn together, and the turns in the order
    that they started. Before, it showed the events in the order that they came, so a
    prompt that you queued or sent again with Esc showed in the middle of the turn
    before it. A prompt that you took back shows where it was in the queue, with the
    note withdrawn before it ran.

  • A call in auto costs less in a project with nested git repositories. The surface
    guard runs git only for a git config that changed since the last call, reads no
    *.sample hook, and lists directories faster. In a copy of efr's own repository, the
    guard went from 15 ms to 6 ms per call.

  • When the model refuses a request as too large for its context window, efrd never
    sends that request again as a retry. It compacts the context once and sends the
    request again. Before each model call, efrd estimates the size of the request, and
    it never sends a request above 95% of the window. When the model refuses the request
    again, when auto compaction is off, or when the request stays above the cap, the turn
    fails with a message that names the cause, the size and the way out, such as the context is still full after a compaction: the model refused about 281k of 272k tokens; run ,compact or start a new conversation. When the compaction fails, the
    message also says why, such as the compaction failed (the provider is rate limiting requests). After two compactions in a row that leave the context too full, efr
    stops compacting on its own and says so, also in the next turns, until the context
    is below the trigger again or you run ,compact. Before, the turn failed with the
    provider's own message.

  • When the history of a long conversation leaves out earlier turns, the model reads
    3 earlier turns are omitted. first, and efrd logs a warning. Before, the turns
    went without a trace.

Fixed

  • efrd no longer loses a steer at the end of a turn. Before, efrd accepted a steer
    that came after the last model call of the turn or after an interrupt request, and
    no model call read it. Now efrd never records such a steer. It refuses a steer from
    ,! with a conflict, so you can send the text as a new prompt. A steer from the
    input row becomes a queued prompt.
  • efrd now ends a turn before it tells the clients that the turn ended. Before, a
    steer or an interrupt that you sent just after you saw the end could go to the
    ended turn, and a prompt could wait behind it. Now efrd refuses the steer and the
    interrupt, and the prompt starts at once.
  • Ctrl+C while the auto sandbox starts a call now stops the call, with
    status 130. Before, the call could fail with status 125 and the message "the sandbox
    could not start", and efrd checked the sandbox again for no reason. A Ctrl+C that
    came just before bwrap started was lost, and the command ran to its end.
  • The sandbox launcher no longer hangs when Ctrl+C ends bwrap while bwrap starts the
    sandbox.
  • Ctrl+\ (SIGQUIT) now stops a shell call of the auto mode, as Ctrl+C does. Before,
    the child shell of the call ignored it. A Ctrl+\ during the line stopped only the
    running command, and the rest of the line ran. A Ctrl+\ just before the line was
    lost, and the line ran.
  • A TRAPINT or TRAPQUIT function no longer lets a line of the auto mode go on after
    Ctrl+C or Ctrl+. Before, such a function of your shell, or one that an earlier call
    made, could catch the signal in the child shell of the call.
  • A client that resumes pty.attach with since_seq now gets each new size that the
    PTY took while the client was away, as resized at its place in the output. Before,
    the recording did not keep sizes, so the client got the output only. The recording
    keeps the sizes now, and older recordings still read.
  • The time that a new hidden shell takes to show its first prompt no longer counts
    against the timeout_seconds of a shell call. A slow .zshrc made short calls fail
    with "an earlier command is still running", although no command ran. The startup
    timeout (10 s) limits that wait now. When a new shell never shows a ready prompt,
    the model reads that no command ran in it yet.
  • The hidden shell now follows you when you change directories between two prompts.
    Before, it stayed where the model left it, so the model worked in the old directory
    or used long paths. Now efrd moves the shell to your new directory before the turn
    starts. The shell keeps its variables. When you did not move, the shell stays where
    the model put it. While a command still runs in the shell, the shell stays, and the
    model reads where it is.
  • The model now reads that you changed directories since the previous prompt, and
    whether its hidden shell moved with you. Before, a short follow-up such as and here? could go to the old directory.
  • After a restart, the model reads each steer of an earlier turn at the place where
    it read the steer during that turn. Before, a turn without saved messages showed a
    steer where you typed it, and also showed steers that the model never read, such as
    a steer of a failed turn or a steer that you took back.
  • A conversation that grew on a model with a large window now goes on with a model of
    a smaller window, such as a move from a Claude model (1M tokens) to an OpenAI model
    (272k tokens). The first turn on the new model compacts before its first call, and
    the summary request fits the new window: it first leaves out the newest messages,
    which stay after the summary word for word, and then the oldest messages. The
    summary says when it left out old messages. Before, efr sent the whole history in the
    summary request, and the provider refused it once as too large.
  • When you change to a model with another edit tool, such as from a Claude model with
    edit to an OpenAI model with apply_patch, the new model reads the earlier calls of
    the other tool, and their results, as text. A call of a tool that efr no longer has
    shows as text too. Before, efr sent them as calls of a tool that the request did not
    offer, which a provider can refuse. The text is the same in every request, so the
    prompt cache still reads it. When you change back, the calls go in their own form
    again.
  • After a switch of [model] provider, the first turn on the new model estimates its
    request. Before, it started from the context that the old model reported last, and
    when that count was above the cap of the new window, the turn failed as full.
  • ,compact right after a switch of [model] provider now uses the default model of
    the new provider. Before, it sent the summary request with the model of the newest
    turn, which the new provider does not serve, and the compaction failed.
  • When the newest messages alone do not fit in the context, the error now says to
    start a new conversation. Before, it said to run ,compact, which then answered
    that there was nothing to compact.
  • On Claude, when the model calls a tool that the request does not offer next to a
    tool that it offers, in one answer, the next request of the turn keeps the thinking
    of that answer. Before, efr sent that answer without its thinking, and the Messages
    API can refuse such a request, so the turn failed.
  • On Claude, a prompt after a turn that ended early now goes as a message of its own,
    as after an interrupt while a tool ran or after a model call that failed, and the
    new turn now gets its one-hour cache entry. Before, efr joined the prompt to the
    last message of that turn, and the new turn had no one-hour cache entry.
  • Each <live_state> block now starts with the rule that only the newest block is
    current. Before, only the default system prompt said so, and a [model] system_prompt of your own lost the rule, so the model could read an old directory
    as the current one. Blocks that efr sent before keep their bytes.
  • The first key that you type after a question or a password prompt appears no longer
    goes to the input row. Before, when efr read a key from the terminal at the moment
    the question appeared, efr could count that key twice, and then the next key, such
    as the first letter of a password, went to the row.

Security

  • The <live_state> block shows your last command with its secrets redacted: the
    value of an assignment to a name that looks like a secret, such as
    export GITHUB_TOKEN=... or --api-key=..., also inside the value of an option,
    such as --env=API_TOKEN=... or --from-literal=password=... (a count such as
    --max-tokens=4096 or TOKEN_COUNT=5 stays); each word in the form of a known key
    (sk-ant-, sk-proj-, sk-, ghp_ and the other GitHub token forms); the password
    of a URL, such as https://user:...@host; the value of an Authorization,
    Cookie or other secret header, such as -H 'Authorization: Bearer ...', without
    its scheme, where a header stands (a search pattern such as
    grep "Authorization: Bearer" stays); and the password option of the programs that
    take one, such as mysql -p..., sshpass -p ..., docker login -p ... and
    curl -u user:..., also among other short options in one word
    (curl -su user:...). efr now saves the block with the turn and sends it again with
    every later request, so a secret there would reach the disk and every request after
    it. The list of patterns cannot find every secret: a secret in another form stays in
    the command.