Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
b97a28d
fix(gateway): drop WebChat frames for closed sockets
adriannoes Oct 16, 2026
0d5f881
fix(file): reject reads that do not fit the result buffer
adriannoes Oct 16, 2026
98b920e
fix(agent): include the compaction boundary message in the summary
adriannoes Oct 16, 2026
2360fe6
fix(agent): keep history that does not fit the summary
adriannoes Oct 16, 2026
16be49c
fix(gateway): serialize dashboard and cron SQLite access
adriannoes Oct 16, 2026
1d5e983
fix(cron): reject interval schedules that overflow next_run
adriannoes Oct 16, 2026
efaaadd
fix(cron): reject schedule numbers that stop early
adriannoes Oct 16, 2026
9bfd9c5
fix(channels): keep Telegram polling after a bad getUpdates batch
adriannoes Oct 16, 2026
d65c61e
fix(channels): deliver long Telegram and Discord replies
adriannoes Oct 16, 2026
139c9cb
fix(providers): apply live config reload to Anthropic
adriannoes Oct 16, 2026
ef965e0
fix(providers): send parallel Anthropic tool results in one user message
adriannoes Oct 16, 2026
be7e281
fix(providers): retry the local probe after a startup miss
adriannoes Oct 16, 2026
25b2fde
fix(sandbox): hide host secrets from sandboxed shell commands
adriannoes Oct 16, 2026
977cc5d
fix(shell): stop trickle output from bypassing the command timeout
adriannoes Oct 16, 2026
def64cf
test(sandbox): exercise drain_pipe deadlines and env scrub without na…
adriannoes Oct 16, 2026
983685b
build(sandbox): keep the sandbox test object under src for the covera…
adriannoes Oct 16, 2026
87c42da
build(sandbox): remove the sandbox test object in make clean
adriannoes Oct 16, 2026
0104890
docs(changelog): record read_file refuse-vs-truncate fix
adriannoes Oct 17, 2026
fd3a098
test(file): assert full oversize error and one-over boundary
adriannoes Oct 17, 2026
9347780
fix(gateway): serialize session deletion with the agent lock
adriannoes Oct 17, 2026
c91904d
fix(cron): log the job snoozed on a rejected stored schedule
adriannoes Oct 17, 2026
97a4a92
docs(changelog): record the cron schedule and SQLite serialization fixes
adriannoes Oct 17, 2026
2d87216
refactor(channels): extract shared chunked send helper
adriannoes Oct 17, 2026
82ea234
fix(channels): log failing chunk index and offset on partial send
adriannoes Oct 17, 2026
14fffcf
fix(channels): pace consecutive Telegram sendMessage chunks
adriannoes Oct 17, 2026
e741b7c
docs(changelog): record the closed-socket WebChat frames fix
adriannoes Oct 17, 2026
d9e7f45
docs(gateway): document ws_send_to drop on closed sockets
adriannoes Oct 17, 2026
592b5ce
docs(changelog): record consolidated channel delivery fixes
adriannoes Oct 17, 2026
3d048ed
fix(agent): keep history when compaction cannot finish the turn
adriannoes Oct 17, 2026
026eca8
refactor(sandbox,shell): share the wall-clock deadline helpers
adriannoes Oct 17, 2026
4d4d356
test(sandbox): strengthen the env-scrub regressions
adriannoes Oct 17, 2026
9fb01d0
test(shell,sandbox): assert trickle timeouts kill the command
adriannoes Oct 17, 2026
c37b031
docs(sandbox): record the env scrub and wall-clock timeout changes
adriannoes Oct 17, 2026
2f3b505
fix(agent): keep compaction progressing when one message cannot fit t…
adriannoes Oct 17, 2026
77fc20b
docs(changelog): record compaction history-loss, fold, and no-stall f…
adriannoes Oct 17, 2026
ccbfa38
fix(providers): throttle the local re-probe while unavailable
adriannoes Oct 17, 2026
77d2bfc
test(anthropic): cover tool-result grouping boundaries
adriannoes Oct 17, 2026
fa52090
docs(providers): document local re-probe throttle and consolidated fixes
adriannoes Oct 17, 2026
d708a8c
refactor(sandbox,shell): loop-scope the deadline elapsed counter
adriannoes Oct 17, 2026
5bcd79b
Merge pull request #120 from asap-protocol/fix/file-read-bounds
adriannoes Oct 17, 2026
d9eea29
chore: merge development (changelog sync after #120)
adriannoes Oct 17, 2026
32843fe
Merge pull request #119 from asap-protocol/fix/gateway-ws-frames
adriannoes Oct 17, 2026
53d4968
fix(gateway): wake WebChat writes on the lws service thread
adriannoes Oct 17, 2026
6bbb091
docs(changelog): record the WebChat service-thread wake fix
adriannoes Oct 17, 2026
44edb71
chore: merge development (changelog sync after #119)
adriannoes Oct 17, 2026
c2a93b6
Merge pull request #121 from asap-protocol/fix/agent-summary-compaction
adriannoes Oct 17, 2026
26ccd08
chore: merge development (changelog sync after #121)
adriannoes Oct 17, 2026
4adbcf7
Merge pull request #122 from asap-protocol/fix/cron-schedule-and-sqlite
adriannoes Oct 17, 2026
5e81668
fix(gateway): hold the agent lock when deleting a session
adriannoes Oct 17, 2026
49e237e
fix(skills): refuse a partial skill body on dashboard read
adriannoes Oct 17, 2026
22aa10b
chore: merge development (changelog sync after #122)
adriannoes Oct 17, 2026
b1a5867
Merge pull request #124 from asap-protocol/fix/providers-anthropic-local
adriannoes Oct 17, 2026
bd3b60b
fix(providers): map gateway model ids before cloud fallback
adriannoes Oct 17, 2026
a97d3aa
docs(changelog): record the model id mapping fix
adriannoes Oct 17, 2026
ba2dca9
style(providers): scope the OpenAI skip error message
adriannoes Oct 17, 2026
c560f5c
chore: merge development (changelog sync after #124)
adriannoes Oct 17, 2026
ea0ca54
Merge pull request #123 from asap-protocol/fix/channels-telegram-discord
adriannoes Oct 17, 2026
42ce366
chore: merge development (changelog sync after #123)
adriannoes Oct 17, 2026
026e2b9
Merge pull request #125 from asap-protocol/fix/sandbox-shell-hardening
adriannoes Oct 17, 2026
7a9b5ea
style(gateway): scope the skill limit description buffers
adriannoes Oct 17, 2026
a6483e6
chore: merge development (changelog sync)
adriannoes Oct 17, 2026
886d10a
Merge pull request #128 from asap-protocol/fix/gateway-ws-wake-writes
adriannoes Oct 17, 2026
fb42340
chore: merge development (changelog sync)
adriannoes Oct 17, 2026
5ba1f23
Merge pull request #129 from asap-protocol/fix/gateway-skills-consoli…
adriannoes Oct 17, 2026
1f1b0a2
chore: merge development (changelog sync)
adriannoes Oct 17, 2026
7df572d
Merge pull request #130 from asap-protocol/fix/providers-model-mapping
adriannoes Oct 17, 2026
fa39435
chore: merge main (v1.0.0 changelog date already applied via consolid…
adriannoes Oct 17, 2026
01242b3
docs(changelog): split the consolidated fixes under Unreleased
adriannoes Oct 17, 2026
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@ All notable changes to ShellClaw are documented here. Format follows [Keep a Cha

## [Unreleased]

### Fixed
- Provider model ids keep their vendor prefix in config but are stripped per backend before the request: `anthropic/claude-...` no longer reaches the Anthropic API (HTTP 4xx) or OpenAI after a 5xx, so the fallback chain runs through to local.
- WebChat writes are moved off the agent thread: frames are queued and the lws service thread is woken (`lws_cancel_service`), marking writable only from `EVENT_WAIT_CANCELLED`, which stops the poll-set race against a closing socket.
- Sandboxed shell commands no longer inherit the agent environment: the command process keeps only `PATH`, locale (`LANG`, `LC_ALL`, `LC_CTYPE`), `TERM`, `TZ`, and `HOME` (set to the workspace), so a prompt-injected `printenv` cannot read provider API keys or bot tokens. `LD_*`, `CUDA_*`, and proxy variables are dropped before `exec`; see the environment model in `docs/SECURITY.md` (#109).
- The shell command timeout is enforced against a monotonic wall clock on both the sandboxed and the unsandboxed paths, so a command that keeps stdout trickling can no longer outlive `shell_timeout_sec` and hold the agent lock until the output buffer fills (#116).
- Telegram `getUpdates` requests a single update per poll and skips a message without a numeric sender id, so a backlog larger than the 256 KiB read buffer (or a channel/anonymous post) no longer pins the poll offset and stalls the inbox.
- Telegram `sendMessage` delivery is verified from the HTTP status code and the `ok` body field, so an API refusal (HTTP 400/429) is no longer reported as a successful send and acknowledged as delivered.
- Long Telegram and Discord replies are chunked to the platform limits (4096/2000 bytes, UTF-8 boundary-safe) through the shared `channel_send_chunked` helper, so a reply over the cap is delivered as several messages instead of failing the whole send; chunk posts are paced for the ~1 msg/s Telegram chat rate, and a failed mid-reply post logs its chunk index and byte offset.
- Live config reload reaches the Anthropic provider (`provider_anthropic_set_live_config`, guarded like OpenAI), so dashboard/SIGHUP reloads update model and `max_tokens` instead of staying on the config captured at init.
- Parallel tool results are grouped into one `user` message (one `tool_result` block per call, per the Messages API contract), fixing Anthropic 400s on parallel tool calls.
- The local provider re-probes on the next request after an unreachable startup, at most once every 15 seconds, so a down `llama-server` no longer costs synchronous curl probes on every agent turn.
- Dashboard and cron SQLite access is serialized with `agent_lock()` (matching `POST /asap`), including `DELETE /api/sessions/{id}` session deletion, so the lws service thread cannot race `agent_run` / `cron_poll` on the shared `g_db` (`SQLITE_THREADSAFE=0`).
- `interval:` schedules reject intervals that do not fit `now + seconds` and numbers that stop parsing early (`interval:60abc`), instead of wrapping to a past `next_run` that stays due and re-fires forever.
- Cron schedules reject partial numbers instead of silently accepting the numeric prefix: `at:2026-10-08` (would become 2026), comma lists such as `0 9 1,15 * *`, and step syntax are now parse errors.
- Stored recurring jobs whose schedule the stricter parser now rejects no longer disappear silently: on ack they are snoozed to `now + 365d` and the job id and schedule are logged (`shellclaw: cron:`), so the year-long gap is diagnosable.
- Session compaction includes the boundary turn (index `msg_count - max_context_messages`) in the summary window, so the turn at the keep-tail boundary is not deleted without being summarized.
- Session compaction keeps a message that does not fit the 64 KiB summary source, and everything after it, instead of skipping the long line and deleting it anyway.
- Session compaction no longer persists the summarized transcript before the user turn finishes: a failed follow-up provider call leaves the raw stored session unchanged, and when the kept tail already starts with a user turn the summary is folded into that turn so consecutive user roles cannot break the next Anthropic call (supersedes #126).
- Session compaction summarizes a message larger than the summary source as a truncated `[truncated]` stub, so a single oversized line can no longer abort the pass with an empty summary source or keep the session above `max_context_messages` forever.
- WebChat `ws_send_to` drops a send whose session has no live `wsi` (still returning 0) and `ws_unregister_conn` drops frames still queued for the closing session, so ~64 abandoned replies cannot pin the shared outgoing queue at `MAX_QUEUE` (64) and stall every live client until restart.
- `read_file` reads that do not fit the result buffer now fail with a JSON error instead of returning a clipped prefix, so a later `write_file` cannot persist a silently truncated file.

## [1.0.0] - 2026-09-25

### Fixed
Expand Down
45 changes: 35 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,12 @@ ASAP_INVOKE_TEST_O := $(BINDIR)/asap_invoke_test.o
# Sandbox (Phase 3 §5)
SANDBOX_CORE_O := src/sandbox/sandbox.o
SANDBOX_LANDLOCK_O := src/sandbox/sandbox_landlock.o
SANDBOX_O := $(SANDBOX_CORE_O) $(SANDBOX_LANDLOCK_O)
SANDBOX_ENV_O := src/sandbox/sandbox_env.o
# Sandbox unit test object: exposes drain_pipe helpers (SHELLCLAW_SANDBOX_TEST)
# so coverage does not depend on namespace privileges the CI runners revoke.
# Lives under src/sandbox/ so the coverage.sh capture (--directory src) merges it.
SANDBOX_TEST_O := src/sandbox/sandbox_test.o
SANDBOX_O := $(SANDBOX_CORE_O) $(SANDBOX_LANDLOCK_O) $(SANDBOX_ENV_O)
ALLOWLIST_SCAN_O := src/sandbox/allowlist.o
ALLOWLIST_PATH_O := src/sandbox/allowlist_path.o
ALLOWLIST_O := $(ALLOWLIST_SCAN_O) $(ALLOWLIST_PATH_O)
Expand Down Expand Up @@ -372,7 +377,7 @@ $(HTTP_O): src/gateway/http.c src/gateway/http.h src/gateway/http_lws.h src/gate
$(HTTP_LWS_O): src/gateway/http_lws.c src/gateway/http_lws.h src/gateway/asap_http_body.h src/gateway/routes.h src/gateway/auth.h src/gateway/static.h src/gateway/ws.h
$(CC) $(CFLAGS) $(INC) $(GATEWAY_CFLAGS) -pthread -c -o $@ src/gateway/http_lws.c

$(ROUTES_O): src/gateway/routes.c src/gateway/routes.h src/gateway/routes_hardware.h src/gateway/http.h src/gateway/http_lws.h src/gateway/auth.h src/gateway/rate_limit.h src/tools/context.h src/asap/manifest.h src/asap/envelope.h src/asap/server.h src/asap/log.h src/core/bootstrap.h src/core/agent.h src/core/config.h src/core/config_patch.h src/core/reload.h src/core/memory.h src/core/skill.h src/providers/provider.h src/channels/channel.h src/tools/cron.h src/tools/tool.h
$(ROUTES_O): src/gateway/routes.c src/gateway/routes.h src/gateway/routes_hardware.h src/gateway/http.h src/gateway/http_lws.h src/gateway/auth.h src/gateway/rate_limit.h src/tools/context.h src/asap/manifest.h src/asap/envelope.h src/asap/server.h src/asap/log.h src/core/bootstrap.h src/core/agent.h src/core/config.h src/core/config_patch.h src/core/dispatch.h src/core/reload.h src/core/memory.h src/core/skill.h src/providers/provider.h src/channels/channel.h src/tools/cron.h src/tools/tool.h
$(CC) $(CFLAGS) $(INC) $(GATEWAY_CFLAGS) -pthread -c -o $@ src/gateway/routes.c

$(ROUTES_HARDWARE_O): src/gateway/routes_hardware.c src/gateway/routes_hardware.h src/gateway/routes.h src/gateway/http_lws.h src/gateway/uri_match.h src/hardware/hardware.h src/hardware/hardware_gpio_snapshot.h src/hardware/hardware_tegrastats.h src/hardware/board_detect.h src/core/config.h
Expand All @@ -385,9 +390,15 @@ $(SHELL_O): src/tools/shell.c src/tools/tool.h src/tools/shell.h src/core/config
src/sandbox/sandbox.h src/sandbox/allowlist.h
$(CC) $(CFLAGS) $(INC) -c -o $@ src/tools/shell.c

$(SANDBOX_CORE_O): src/sandbox/sandbox.c src/sandbox/sandbox.h src/sandbox/sandbox_landlock.h
$(SANDBOX_CORE_O): src/sandbox/sandbox.c src/sandbox/sandbox.h src/sandbox/sandbox_landlock.h src/sandbox/sandbox_env.h
$(CC) $(CFLAGS) $(INC) -c -o $@ src/sandbox/sandbox.c

$(SANDBOX_ENV_O): src/sandbox/sandbox_env.c src/sandbox/sandbox_env.h
$(CC) $(CFLAGS) $(INC) -c -o $@ src/sandbox/sandbox_env.c

$(SANDBOX_TEST_O): src/sandbox/sandbox.c src/sandbox/sandbox.h src/sandbox/sandbox_landlock.h src/sandbox/sandbox_env.h
$(CC) $(CFLAGS) $(INC) -DSHELLCLAW_SANDBOX_TEST -c -o $@ src/sandbox/sandbox.c

$(SANDBOX_LANDLOCK_O): src/sandbox/sandbox_landlock.c src/sandbox/sandbox_landlock.h
$(CC) $(CFLAGS) $(INC) -c -o $@ src/sandbox/sandbox_landlock.c

Expand Down Expand Up @@ -467,7 +478,7 @@ $(HARDWARE_I2C_O): src/hardware/hardware_i2c.c src/hardware/hardware_i2c.h
$(HARDWARE_CAMERA_O): src/hardware/hardware_camera.c src/hardware/hardware_camera.h src/hardware/board_detect.h
$(CC) $(CFLAGS) $(INC) -c -o $@ src/hardware/hardware_camera.c

$(CRON_O): src/tools/cron.c src/tools/cron.h src/crypto/crypto.h src/core/memory.h src/channels/channel.h src/core/config.h
$(CRON_O): src/tools/cron.c src/tools/cron.h src/crypto/crypto.h src/core/agent.h src/core/memory.h src/channels/channel.h src/core/config.h
$(CC) $(CFLAGS) $(INC) -c -o $@ src/tools/cron.c

$(ASAP_INVOKE_O): src/tools/asap_invoke.c src/tools/asap_invoke.h src/tools/tool.h src/asap/envelope.h src/asap/client.h src/asap/registry.h src/asap/ulid.h src/asap/asap_version.h src/core/config.h vendor/cJSON/cJSON.h
Expand Down Expand Up @@ -631,9 +642,10 @@ test_web_search: tests/test_web_search.c $(WEBSEARCH_O) $(CONFIG_O) $(TOML_O) $(
$(CC) $(CFLAGS) $(LDFLAGS) $(INC) -o $(BINDIR)/$@ tests/test_web_search.c $(WEBSEARCH_O) $(CONFIG_O) $(TOML_O) $(CJSON_O) $(LDLIBS)
$(DSYM_SCRIPT)

test_cron: tests/test_cron.c $(CRON_O) $(CRYPTO_LINK) $(MEMORY_O) $(SQLITE3_O) $(CHANNEL_COMMON_O) $(CJSON_O)
test_cron: tests/test_cron.c $(CRON_O) $(CRYPTO_LINK) $(MEMORY_O) $(SQLITE3_O) $(CHANNEL_COMMON_O) $(CJSON_O) $(AGENT_O) $(SKILL_O) $(CONFIG_O) $(TOML_O) $(PROVIDER_COMMON_O)
@mkdir -p $(BINDIR)
$(CC) $(CFLAGS) $(LDFLAGS) $(INC) -o $(BINDIR)/$@ tests/test_cron.c $(CRON_O) $(CRYPTO_LINK) $(MEMORY_O) $(SQLITE3_O) $(CHANNEL_COMMON_O) $(CJSON_O) $(LDLIBS)
$(CC) $(CFLAGS) $(LDFLAGS) $(INC) -o $(BINDIR)/$@ tests/test_cron.c $(CRON_O) $(CRYPTO_LINK) $(MEMORY_O) $(SQLITE3_O) $(CHANNEL_COMMON_O) $(CJSON_O) $(AGENT_O) $(SKILL_O) $(CONFIG_O) $(TOML_O) $(PROVIDER_COMMON_O) $(LDLIBS) -pthread
$(DSYM_SCRIPT)

test_ws: tests/test_ws.c $(WS_TEST_O)
@mkdir -p $(BINDIR)
Expand Down Expand Up @@ -735,6 +747,18 @@ test_gateway_http: shellclaw tests/test_gateway_http.c $(AUTH_O) $(CONFIG_O) $(T
$(CC) $(CFLAGS) $(LDFLAGS) $(INC) -DSHELLCLAW_GATEWAY -pthread -o $(BINDIR)/$@ tests/test_gateway_http.c $(AUTH_O) $(CRYPTO_LINK) $(CONFIG_O) $(TOML_O) $(CJSON_O) $(LDLIBS) -pthread
$(DSYM_SCRIPT)

GATEWAY_DB_LOCK_OBJS := $(filter-out src/core/main.o,$(SHELLCLAW_OBJS))

test_gateway_db_lock: tests/test_gateway_db_lock.c $(GATEWAY_DB_LOCK_OBJS)
@mkdir -p $(BINDIR)
@if [ "$(GATEWAY)" != "1" ]; then \
if [ "$(CI)" = "true" ]; then echo "test_gateway_db_lock: GATEWAY=0 in CI — install libwebsockets-dev"; exit 1; fi; \
echo "test_gateway_db_lock: skipped (GATEWAY=0)"; \
else \
$(CC) $(CFLAGS) $(LDFLAGS) $(INC) $(GATEWAY_CFLAGS) -DSHELLCLAW_GATEWAY -pthread -o $(BINDIR)/$@ tests/test_gateway_db_lock.c $(GATEWAY_DB_LOCK_OBJS) $(LDLIBS) $(GATEWAY_LDLIBS) $(LIBGPIOD_LDLIBS) -pthread; \
fi
$(DSYM_SCRIPT)

test_routes_hardware: tests/test_routes_hardware.c tests/test_routes_json_stub.c $(ROUTES_HARDWARE_O) $(HARDWARE_GPIO_SNAPSHOT_O) $(HARDWARE_TEGRASTATS_O) $(HARDWARE_INIT_O) $(HARDWARE_STUB_O) $(BOARD_DETECT_O) $(HARDWARE_I2C_O) $(HARDWARE_CAMERA_O) $(HARDWARE_LIBGPIOD_O) $(CONFIG_O) $(TOML_O) $(CJSON_O)
@if [ "$(GATEWAY)" != "1" ]; then \
if [ "$(CI)" = "true" ]; then echo "test_routes_hardware: GATEWAY=0 in CI — install libwebsockets-dev"; exit 1; fi; \
Expand All @@ -750,9 +774,9 @@ test_static: tests/test_static.c src/gateway/ui_assets.h src/gateway/static.o
$(CC) $(CFLAGS) $(LDFLAGS) $(INC) -o $(BINDIR)/$@ tests/test_static.c src/gateway/static.o $(LDLIBS)
$(DSYM_SCRIPT)

test_sandbox: tests/test_sandbox.c $(SANDBOX_O)
test_sandbox: tests/test_sandbox.c $(SANDBOX_TEST_O) $(SANDBOX_LANDLOCK_O) $(SANDBOX_ENV_O)
@mkdir -p $(BINDIR)
$(CC) $(CFLAGS) $(LDFLAGS) $(INC) -o $(BINDIR)/$@ tests/test_sandbox.c $(SANDBOX_O) $(LDLIBS)
$(CC) $(CFLAGS) $(LDFLAGS) $(INC) -o $(BINDIR)/$@ tests/test_sandbox.c $(SANDBOX_TEST_O) $(SANDBOX_LANDLOCK_O) $(SANDBOX_ENV_O) $(LDLIBS)
$(DSYM_SCRIPT)

test_allowlist: tests/test_allowlist.c $(ALLOWLIST_O)
Expand Down Expand Up @@ -889,6 +913,7 @@ test: test_config test_config_patch test_memory test_skill test_provider test_an
$(MAKE) test_static && $(BINDIR)/test_static
@if [ "$(GATEWAY)" = "1" ]; then \
$(BINDIR)/test_routes_hardware && \
$(MAKE) test_gateway_db_lock && $(BINDIR)/test_gateway_db_lock && \
$(MAKE) test_asap_http_body && $(BINDIR)/test_asap_http_body && \
$(MAKE) test_gateway_http && $(BINDIR)/test_gateway_http; \
elif [ "$(CI)" = "true" ]; then echo "GATEWAY=0 in CI — install libwebsockets-dev"; exit 1; fi
Expand All @@ -909,9 +934,9 @@ clean-root-dsym:
@rm -f shellclaw test_agent test_anthropic test_channel test_cli test_config test_file test_memory test_local_provider test_openai test_provider test_router test_shell test_skill test_telegram test_web_search test_ws

clean: clean-root-dsym
rm -f $(OBJS) $(PROVIDER_COMMON_O) $(STUB_O) $(ANTHROPIC_O) $(OPENAI_COMPAT_O) $(OPENAI_O) $(LOCAL_O) $(ROUTER_O) $(CJSON_O) $(TWEETNACL_O) $(ANTHROPIC_TEST_O) $(OPENAI_TEST_O) $(LOCAL_TEST_O) $(CONTEXT_TEST_OBJS) $(HEARTBEAT_TEST_O) $(CHANNEL_TG_TEST_O) $(CHANNEL_COMMON_O) $(CHANNEL_STUB_O) $(CHANNEL_CLI_O) $(CHANNEL_TG_O) $(CHANNEL_DISCORD_O) $(DISCORD_HELPERS_O) $(CHANNEL_HEARTBEAT_O) $(CHANNEL_WEBCHAT_O) $(AUTH_O) $(STATIC_O) $(HTTP_O) $(HTTP_LWS_O) $(ASAP_HTTP_BODY_O) $(ROUTES_O) $(ROUTES_HARDWARE_O) $(WS_O) $(MANIFEST_O) $(MANIFEST_PROFILES_O) $(MANIFEST_BUILD_O) $(MANIFEST_SIGN_O) $(MANIFEST_KEYS_O) $(ENVELOPE_O) $(ULID_O) $(CLIENT_O) $(ASAP_REGISTRY_O) $(SERVER_O) $(ASAP_LOG_O) $(RATE_LIMIT_O) $(SHELL_O) $(WEBSEARCH_O) $(FILE_O) $(REGISTRY_O) $(CONTEXT_O) $(CONTEXT_CACHE_O) $(CONTEXT_HTTP_O) $(CONTEXT_GEO_O) $(CRYPTO_O) $(JCS_O) $(HARDWARE_STUB_O) $(HARDWARE_INIT_O) $(HARDWARE_GPIO_SNAPSHOT_O) $(HARDWARE_TEGRASTATS_O) $(HARDWARE_TOOLS_O) $(BOARD_DETECT_O) src/hardware/hardware_libgpiod.o $(HARDWARE_I2C_O) $(HARDWARE_CAMERA_O) $(CRON_O) $(ASAP_INVOKE_O) $(SANDBOX_CORE_O) $(SANDBOX_LANDLOCK_O) $(ALLOWLIST_SCAN_O) $(ALLOWLIST_PATH_O)
rm -f $(OBJS) $(PROVIDER_COMMON_O) $(STUB_O) $(ANTHROPIC_O) $(OPENAI_COMPAT_O) $(OPENAI_O) $(LOCAL_O) $(ROUTER_O) $(CJSON_O) $(TWEETNACL_O) $(ANTHROPIC_TEST_O) $(OPENAI_TEST_O) $(LOCAL_TEST_O) $(CONTEXT_TEST_OBJS) $(HEARTBEAT_TEST_O) $(CHANNEL_TG_TEST_O) $(CHANNEL_COMMON_O) $(CHANNEL_STUB_O) $(CHANNEL_CLI_O) $(CHANNEL_TG_O) $(CHANNEL_DISCORD_O) $(DISCORD_HELPERS_O) $(CHANNEL_HEARTBEAT_O) $(CHANNEL_WEBCHAT_O) $(AUTH_O) $(STATIC_O) $(HTTP_O) $(HTTP_LWS_O) $(ASAP_HTTP_BODY_O) $(ROUTES_O) $(ROUTES_HARDWARE_O) $(WS_O) $(MANIFEST_O) $(MANIFEST_PROFILES_O) $(MANIFEST_BUILD_O) $(MANIFEST_SIGN_O) $(MANIFEST_KEYS_O) $(ENVELOPE_O) $(ULID_O) $(CLIENT_O) $(ASAP_REGISTRY_O) $(SERVER_O) $(ASAP_LOG_O) $(RATE_LIMIT_O) $(SHELL_O) $(WEBSEARCH_O) $(FILE_O) $(REGISTRY_O) $(CONTEXT_O) $(CONTEXT_CACHE_O) $(CONTEXT_HTTP_O) $(CONTEXT_GEO_O) $(CRYPTO_O) $(JCS_O) $(HARDWARE_STUB_O) $(HARDWARE_INIT_O) $(HARDWARE_GPIO_SNAPSHOT_O) $(HARDWARE_TEGRASTATS_O) $(HARDWARE_TOOLS_O) $(BOARD_DETECT_O) src/hardware/hardware_libgpiod.o $(HARDWARE_I2C_O) $(HARDWARE_CAMERA_O) $(CRON_O) $(ASAP_INVOKE_O) $(SANDBOX_CORE_O) $(SANDBOX_LANDLOCK_O) $(SANDBOX_ENV_O) $(ALLOWLIST_SCAN_O) $(ALLOWLIST_PATH_O)
rm -f src/gateway/ui_assets.h
find . -name '*.gcno' -o -name '*.gcda' -o -name '*.gcov' | xargs rm -f 2>/dev/null || true
rm -f $(WS_TEST_O) $(BINDIR)/asap_registry_test.o $(BINDIR)/asap_invoke_test.o $(CONTEXT_TEST_OBJS) $(HEARTBEAT_TEST_O) $(BINDIR)/shellclaw $(BINDIR)/test_tweetnacl_smoke $(BINDIR)/test_config $(BINDIR)/test_config_patch $(BINDIR)/test_memory $(BINDIR)/test_skill $(BINDIR)/test_provider $(BINDIR)/test_anthropic $(BINDIR)/test_openai $(BINDIR)/test_local_provider $(BINDIR)/test_router $(BINDIR)/test_heartbeat $(BINDIR)/test_crypto $(BINDIR)/test_hardware_stub $(BINDIR)/test_board_detect $(BINDIR)/test_hardware_libgpiod $(BINDIR)/test_hardware_i2c $(BINDIR)/test_hardware_camera $(BINDIR)/test_pin_tables $(BINDIR)/test_hardware_init $(BINDIR)/test_hardware_tools $(BINDIR)/test_registry $(BINDIR)/test_ws $(BINDIR)/test_agent $(BINDIR)/test_channel $(BINDIR)/test_cli $(BINDIR)/test_shell $(BINDIR)/test_file $(BINDIR)/test_telegram $(BINDIR)/test_discord_helpers $(BINDIR)/test_web_search $(BINDIR)/test_cron $(BINDIR)/test_context $(BINDIR)/test_manifest_build $(BINDIR)/test_manifest_keys $(BINDIR)/test_jcs $(BINDIR)/test_asap_envelope $(BINDIR)/test_asap_ulid $(BINDIR)/test_asap_client $(BINDIR)/test_asap_registry $(BINDIR)/test_asap_server $(BINDIR)/test_asap_invoke $(BINDIR)/test_asap_log $(BINDIR)/test_auth $(BINDIR)/test_gateway_http $(BINDIR)/test_static $(BINDIR)/test_sandbox $(BINDIR)/test_allowlist $(BINDIR)/test_rate_limit
rm -f $(WS_TEST_O) $(SANDBOX_TEST_O) $(BINDIR)/asap_registry_test.o $(BINDIR)/asap_invoke_test.o $(CONTEXT_TEST_OBJS) $(HEARTBEAT_TEST_O) $(BINDIR)/shellclaw $(BINDIR)/test_tweetnacl_smoke $(BINDIR)/test_config $(BINDIR)/test_config_patch $(BINDIR)/test_memory $(BINDIR)/test_skill $(BINDIR)/test_provider $(BINDIR)/test_anthropic $(BINDIR)/test_openai $(BINDIR)/test_local_provider $(BINDIR)/test_router $(BINDIR)/test_heartbeat $(BINDIR)/test_crypto $(BINDIR)/test_hardware_stub $(BINDIR)/test_board_detect $(BINDIR)/test_hardware_libgpiod $(BINDIR)/test_hardware_i2c $(BINDIR)/test_hardware_camera $(BINDIR)/test_pin_tables $(BINDIR)/test_hardware_init $(BINDIR)/test_hardware_tools $(BINDIR)/test_registry $(BINDIR)/test_ws $(BINDIR)/test_agent $(BINDIR)/test_channel $(BINDIR)/test_cli $(BINDIR)/test_shell $(BINDIR)/test_file $(BINDIR)/test_telegram $(BINDIR)/test_discord_helpers $(BINDIR)/test_web_search $(BINDIR)/test_cron $(BINDIR)/test_context $(BINDIR)/test_manifest_build $(BINDIR)/test_manifest_keys $(BINDIR)/test_jcs $(BINDIR)/test_asap_envelope $(BINDIR)/test_asap_ulid $(BINDIR)/test_asap_client $(BINDIR)/test_asap_registry $(BINDIR)/test_asap_server $(BINDIR)/test_asap_invoke $(BINDIR)/test_asap_log $(BINDIR)/test_auth $(BINDIR)/test_gateway_http $(BINDIR)/test_gateway_db_lock $(BINDIR)/test_static $(BINDIR)/test_sandbox $(BINDIR)/test_allowlist $(BINDIR)/test_rate_limit
rm -rf $(BINDIR)/*.dSYM $(DSYMDIR)
rm -f $(BOOTSTRAP_DISPATCH_STUB_O) $(TOOL_RELOAD_STUB_O) $(RELOAD_CHANNEL_STUB_O) $(HTTP_RELOAD_STUB_O)
2 changes: 2 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# Copy to ~/.shellclaw/config.toml and edit

[agent]
# Gateway-style vendor prefix. Anthropic receives the id after "anthropic/".
# A foreign cloud id is skipped without HTTP 4xx so local fallback still runs.
model = "anthropic/claude-sonnet-4-20250514"
max_tokens = 4096
temperature = 0.7
Expand Down
2 changes: 1 addition & 1 deletion docs/LOCAL_INFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ llama-server (127.0.0.1:8080)
GGUF model on disk (/var/lib/shellclaw/models/)
```

At startup, `local.c` probes `GET /health` and `GET /v1/models`. If unreachable, the local provider marks itself unavailable and the router falls through to the next entry in `providers.fallback_chain`.
At startup, `local.c` probes `GET /health` and `GET /v1/models`. If unreachable, the local provider marks itself unavailable and the router falls through to the next entry in `providers.fallback_chain`. After an unreachable startup the provider re-probes on the next request, at most once every 15 seconds, so a `llama-server` that comes up later can still serve.

---

Expand Down
Loading
Loading