Python skill scripts using zoneinfo.ZoneInfo (and Go time.LoadLocation with
named zones) fail on the published images because alpine:3.23 ships without
tzdata: ZoneInfoNotFoundError: 'No time zone found with key Europe/Paris'.
Add tzdata to the base runtime apk install so every image variant (base,
latest, full, claude-cli) has the system zoneinfo database.
Current Claude CLI releases (>= 2.x) no longer register TodoRead and
NotebookRead. Passing them via --disallowedTools makes the CLI print
'Permission deny rule "..." matches no known tool' on every invocation,
including background episodic summarization, burying real errors.
Keep TodoWrite/NotebookEdit blocked (still registered, no GoClaw
equivalent). Add a regression test guarding both directions.
Fixes#1374
The bridge exposed a static BridgeToolNames subset that drifted from the
tool registry: use_skill, datetime, knowledge_graph_search and skill_manage
were never added, while the system prompt's skill-loading protocol requires
agents to call use_skill. claude_cli agents following the protocol hit a
nonexistent tool and could fabricate results.
Implement the structural fix recommended in #1373 triage:
- register the full bridge-capable surface (registry minus hard exclusions
spawn/create_forum_topic) instead of the static list
- gate BOTH tools/list (new WithToolFilter) and tools/call through one
shared predicate bridgeToolAllowed:
* callers WITH a verified agent policy get exactly the policy-filtered
surface (same WouldAllow check the call path always enforced)
* callers WITHOUT one (anonymous, or agent without tools_config) keep the
legacy conservative BridgeToolNames set - no exposure widening
- downgrade the per-call denial log Warn->Info; list filtering makes probes
of denied tools rare and the call gate is the intended enforcement point
Fixes#1373
Cron job execution set the tenant ID on its context but not the tenant
slug. Tenant-scoped filesystem paths (skills-store, workspace, media via
config.TenantScopedDir) key off the slug and fall back to an id-based
path when it is absent — a different directory than where HTTP/WS skill
upload materialized the files (which sets the slug). As a result a cron
agent turn in a non-master tenant saw NONE of its tenant's managed
skills: skill_search returned 0 results and the agent, unable to run the
skill, produced an ungrounded answer.
Add cronTenantContext() which resolves the tenant slug via TenantStore
and sets both WithTenantID and WithTenantSlug. Master tenant and
nil-store/lookup-failure paths fall back to id-only (prior behavior).
Thread TenantStore into makeCronJobHandler and runCommandCronJob.
Tested: added unit tests for cronTenantContext (slug injected for
non-master; master skips lookup; nil store and lookup error fall back to
id-only). Verified end-to-end on a live tenant: before, a daily-agenda
cron guessed an empty day; after, it read the real event from the DB.
Note: other background executors that build a context from a tenant ID
(e.g. heartbeat) likely share this gap and are worth an audit.