Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,25 @@ Session events are emitted as:
```elixir
{:jido_shell_session, session_id, event}
```

## 10. Sprite Lifecycle Module Renamed

The lifecycle helper module was renamed and the old name was removed:

- `Jido.Shell.SpriteLifecycle` -> `Jido.Shell.Environment.Sprite`

### Example update

Before (removed):

```elixir
{:ok, result} = Jido.Shell.SpriteLifecycle.provision(workspace_id, sprite_config)
teardown = Jido.Shell.SpriteLifecycle.teardown(session_id, sprite_name: workspace_id)
```

After (canonical):

```elixir
{:ok, result} = Jido.Shell.Environment.Sprite.provision(workspace_id, sprite_config)
teardown = Jido.Shell.Environment.Sprite.teardown(session_id, sprite_name: workspace_id)
```
32 changes: 32 additions & 0 deletions lib/jido_shell/backend/output_limiter.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
defmodule Jido.Shell.Backend.OutputLimiter do
@moduledoc """
Shared output-limit logic for SSH and Sprite backends.

Tracks emitted bytes and aborts when the configured limit is exceeded.
"""

alias Jido.Shell.Error

@doc """
Check whether emitting `chunk_bytes` would exceed `output_limit`.

Returns `{:ok, updated_bytes}` when under the limit, or
`{:limit_exceeded, %Jido.Shell.Error{}}` when the limit is breached.
"""
@spec check(non_neg_integer(), non_neg_integer(), non_neg_integer() | nil) ::
{:ok, non_neg_integer()} | {:limit_exceeded, Error.t()}
def check(chunk_bytes, emitted_bytes, output_limit)
when is_integer(chunk_bytes) and is_integer(emitted_bytes) do
updated_total = emitted_bytes + chunk_bytes

if is_integer(output_limit) and output_limit > 0 and updated_total > output_limit do
{:limit_exceeded,
Error.command(:output_limit_exceeded, %{
emitted_bytes: updated_total,
max_output_bytes: output_limit
})}
else
{:ok, updated_total}
end
end
end
39 changes: 15 additions & 24 deletions lib/jido_shell/backend/sprite.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule Jido.Shell.Backend.Sprite do

@behaviour Jido.Shell.Backend

alias Jido.Shell.Backend.OutputLimiter
alias Jido.Shell.Error

@default_task_supervisor Jido.Shell.CommandTaskSupervisor
Expand Down Expand Up @@ -359,22 +360,15 @@ defmodule Jido.Shell.Backend.Sprite do

defp emit_stream_chunk(state, cmd_ref, data, output_limit, emitted_bytes) do
chunk = IO.iodata_to_binary(data)
chunk_bytes = byte_size(chunk)
updated_total = emitted_bytes + chunk_bytes

cond do
is_integer(output_limit) and output_limit > 0 and updated_total > output_limit ->
_ = close_remote_handle(state, cmd_ref)

{:error,
Error.command(:output_limit_exceeded, %{
emitted_bytes: updated_total,
max_output_bytes: output_limit
})}

true ->
case OutputLimiter.check(byte_size(chunk), emitted_bytes, output_limit) do
{:ok, updated_total} ->
send(state.session_pid, {:command_event, {:output, chunk}})
{:ok, updated_total}

{:limit_exceeded, error} ->
_ = close_remote_handle(state, cmd_ref)
{:error, error}
end
end

Expand All @@ -383,17 +377,14 @@ defmodule Jido.Shell.Backend.Sprite do

defp maybe_emit_output(session_pid, output, output_limit) do
chunk = IO.iodata_to_binary(output)
chunk_bytes = byte_size(chunk)

if is_integer(output_limit) and output_limit > 0 and chunk_bytes > output_limit do
{:error,
Error.command(:output_limit_exceeded, %{
emitted_bytes: chunk_bytes,
max_output_bytes: output_limit
})}
else
send(session_pid, {:command_event, {:output, chunk}})
:ok

case OutputLimiter.check(byte_size(chunk), 0, output_limit) do
{:ok, _} ->
send(session_pid, {:command_event, {:output, chunk}})
:ok

{:limit_exceeded, error} ->
{:error, error}
end
end

Expand Down
Loading