* fix: confine gateway operators to specific agents via auth_scopes (fixes#6253)
A token's auth_scopes entry may now be {scopes: [...], agents: [...]}.
GatewayConfig.resolve_agents(token) returns the confinement (None =
every agent, so existing token -> [scopes] policies are unchanged). The
bot gateway enforces it where a WS client binds to an agent (hello/join,
including session resume via join) and withholds broadcast events
sourced from agents outside the grant.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: apply YAML operator policy at gateway startup; fail closed on unknown grant keys
- start_with_config now stamps gateway.auth_scopes / gateway.auth.tokens onto
self.config, so YAML agent confinement (and scopes) actually take effect.
- Grant mappings reject unknown keys (core parser + typed OperatorGrantSchema),
so a typo like 'agnts' cannot silently remove a confinement.
- Document that HTTP approval endpoints are not yet agent-filtered (#5151).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: expand ${VAR} auth_scopes keys at gateway startup; tolerate older core
- start_with_config(): load_gateway_config resolves ${VAR} in values only, so
auth_scopes keys stayed literal placeholders. Expand them, drop keys that
resolve to empty, and refuse to start if a declared policy ends up empty
(an empty policy means "no policy" and would fail open).
- Guard the private _parse_auth_scopes import and the resolve_agents call so
an older praisonaiagents keeps pre-#6253 behaviour instead of ImportError.
- Tests for placeholder expansion, all-unset fail-closed (both YAML forms)
and the older-core fallback.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: reject scalar 'agents' in gateway operator grants (#6253)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: refuse gateway startup when declared operator policy cannot be enforced by older core (#6253)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: fail closed on unknown auth.tokens[] keys; check join agent reach before session resume (#6253)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: refuse attaching a confined operator to another agent's shared session (#6253)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: guardrail without name=, OPENAI_TIMEOUT for plain-model path, visible rules notice (fixes#6257)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: show rules notice by default and list only active rules (#6257)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: keep tool turns intact across auto-save, compaction and history mutators (fixes#6217)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: save messages appended by history replacement and serialize it with auto-save (#6217)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: make doctor gateway security check respect unknown_user_policy (fixes#5092)
The gateway_security doctor check reported 'bot responds to EVERYONE!'
for any channel without an allowlist, even under the default
unknown_user_policy=deny where unknown DMs are dropped. This is the
same misleading text #5112 removed from the startup banner. Flag
open access only for policy=allow; warn that DMs are dropped for deny.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: doctor treats any non-pair unknown_user_policy as deny, matching runtime
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: recall stored facts on sqlite/chroma/mem0 memory backends (fixes#6040)
MemoryConfig.prefetch now defaults to None (auto): turn-start prefetch runs
for the full Memory class, whose system-prompt context is empty without a
query, and stays off for FileMemory. Explicit True/False still force it.
Memory.store_long_term/store_short_term stamp the store's user_id into
metadata so user-scoped recall finds facts saved via agent.store_memory().
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: share one sqlite store in cross-user recall isolation test
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: set project root in headless output_file test so contained writes land in tmp_path
The #5770 output_file containment rejects writes outside PRAISONAI_PROJECT_ROOT/cwd,
so this test failed on main too. The guard is unchanged; the test now points the
project root at tmp_path.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: resume goals in a fresh process, type the undo snapshot, stop promising boot resume (fixes#6028)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: resume docs require durable=True and the same journal
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: scope headless YAML output_file test to its tmp project root
#5770 contains task output_file writes in PRAISONAI_PROJECT_ROOT (or cwd),
so the test's tmp_path writes were skipped and failed on main too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: only forward style to dall-e-3 in ImageAgent OpenAI calls (fixes#6076)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: assert style key is absent for non-dall-e-3 models in ImageAgent test
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: add non-interactive per-channel gateway config lifecycle (fixes#6079)
Adds 'praisonai gateway channels add|update|disable|remove' and an
importable edit_gateway_channel() helper. Each edit is a surgical
read-modify-write of channels:, validated against GatewayConfigSchema
before an atomic write, so invalid mutations are never persisted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: honour parent --config, legacy platform formats, env-resolved validation and last-channel guard in gateway channel edits
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: lock gateway channel edits and resolve ${VAR} platform before legacy conversion
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: enforce max_budget and time caps on provider/model agents (fixes#6029)
- Count every call of the custom-LLM (provider/model) tool loop in the
Agent's cost/token totals through one accounting helper, so max_budget,
on_budget_exceeded and the autonomy spend caps fire mid-loop.
- Turn ExecutionConfig.max_execution_time and run_autonomous timeout_seconds
into a deadline on the existing per-turn cancel token; the loop guard's
per-turn limit follows max_execution_time (120s default kept).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: stop custom-LLM retries after budget exhaustion and report turn timeouts/budget stops in run_autonomous
- Refuse a provider/model turn before any request once max_budget is spent (stop mode).
- run_autonomous(_async): a turn cut by max_execution_time returns a failed timeout result.
- run_autonomous(_async): BudgetExceededError keeps the typed budget_exhausted result when an autonomy cap applies.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: stub the live litellm module in #6029 tests so evicted-module order cannot bypass the stub
tests/unit/embedding/test_embedding.py removes litellm from sys.modules.
LLM then imports a fresh module at call time, while these tests had patched
the module object bound at collection, so the real client ran and the
network guard failed them on CI. Resolve litellm inside the fixture instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: bill fallback_models spend to the agent's max_budget on the custom-LLM path
After rebasing onto main (#6053 fallback walk), the usage sink only matched
the primary LLM instance, so a turn answered by a fallback model escaped
cost tracking. Bind the sink per attempt to the LLM actually called.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: price fallback-model spend as the model that answered; isolate Agent default-model cache in #6029 tests
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: make inert GuardrailConfig.on_fail, ToolConfig line caps and rerank=True work or fail loud (fixes#6042)
- Agent() rejects GuardrailConfig.on_fail other than 'retry' (only policy
implemented); presets no longer set on_fail; docstring no longer claims
guardrails='strict' is a preset.
- ToolConfig.output_max_lines / output_direction now applied in the sync
tool-output truncation block (line cap, then char cap).
- Agent.rag uses SimpleReranker when rerank=True without rerank_model;
metadata['reranked'] reports whether a reranker actually ran.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: apply line cap to short lines, honour output_direction for dict results, rerank SearchResultItem hits
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: point headless YAML test's project root at tmp_path so contained output_file writes land
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: report backend (rerank_model) reranking in RAG metadata["reranked"]
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: make completion promise the only autonomy success signal (fixes#6027)
When a completion_promise is configured, the tool_completion, keyword,
direct-stage and no_tool_calls heuristics no longer end the run with
success=True in run_autonomous/run_autonomous_async. Promise-less runs are
unchanged. The async tool path now increments _autonomy_turn_tool_count for
sync/async parity, and `praisonai loop` sets mode="iterative" so
--completion-promise/--max-iterations take effect without --level full_auto.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: tell the model the completion promise and only count executed async tools
Addresses review on #6064: SDK autonomy loops now append the expected
<promise> tag to the prompt (unless already present in the prompt or
instructions), and the async per-turn tool count is incremented only after
the tool runs so BEFORE_TOOL-blocked calls cannot trigger tool_completion.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: require approval for GitHub write tools and scope git to repo_path (fixes#6023)
- Register github_create_branch/commit_and_push/create_pull_request as
high-risk and add them to the default deny preset, so unattended agents
need an explicit allow (same as execute_command).
- Add optional repo_path (cwd for every git/gh call) and paths args.
- Stage with 'git add -u' (+ explicit paths) after the branch-safety
checks, so untracked files are not swept in and a refusal leaves the
index untouched.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: scope github_commit_and_push staging to the working directory
git add -u with no pathspec stages tracked edits across the whole
repository, so a call from a subdirectory could publish unrelated sibling
changes. Use 'git add -u -- .' and scope the no-changes status check the
same way.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: run kanban dispatcher git work off the event loop (fixes#6039)
- dispatch_once / _spawn_worker / _shutdown hand _cleanup_completed_tasks
and _prepare_worktree to a worker thread, serialised by a class-level
lock so integrations never run git concurrently in the base checkout.
- Workers start in their own session; shutdown SIGTERMs every worker
group first, waits for all of them off the loop, then SIGKILLs groups
that are still alive.
- Sync helpers are unchanged, so the CLI one-shot dispatch path keeps
working.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: clean up cancelled worktree prep and always SIGKILL worker groups on shutdown
- Cancelling dispatch during worktree preparation now waits for the git
thread, discards the unused worktree/branch, closes the run and releases
the claim before re-raising.
- Shutdown SIGKILLs every worker's process group even when the worker
itself already exited, so SIGTERM-ignoring descendants cannot survive.
- Tests: new cancellation test; descendant test uses a marker file and a
SIGTERM-ignoring child instead of os.kill(pid, 0), which zombies pass.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: make loop-guard turn time limit configurable via ExecutionConfig.max_execution_time (fixes#6026)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: clarify that only ExecutionConfig.max_execution_time=0 disables the turn time limit
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: walk fallback_models on the custom-LLM chat/achat path (fixes#6030)
provider/model, dict, base_url and model-object agents call
LLM.get_response* directly and turned any primary-model outage into a
None turn without trying a single fallback model. Wrap that call so a
fallback-class error (per classify_llm_error) moves to the next entry in
fallback_models via a throwaway per-call LLM, emitting MODEL_FALLBACK.
self.llm_instance is never mutated, so concurrent turns keep the primary.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: keep base_url for bare-name fallbacks and skip fallback after a tool ran
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: kanban dispatcher heartbeats its live workers instead of re-running them (fixes#6025)
The dispatcher stamped a worker's heartbeat only at spawn, so any task
running longer than claim TTL + stale timeout (~30 min by default) was
reclaimed while healthy, re-dispatched, and the first worker orphaned.
- Renew lease/heartbeat for every live child before the reclaim sweep;
terminate a child whose claim was lost.
- Never overwrite a running worker's handle in running_tasks.
- Close a reclaimed task's open run as 'crashed' and count the failure.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: close reclaimed kanban run atomically and keep lost-claim worktree tracking
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: log run_closed when reclaim closes a run; scope headless output_file test to its project root
The reclaim sweep now records the usual run_closed event in the same
transaction that closes the abandoned run. test_output_file_and_metrics_json
writes under tmp_path, which the #5770 project-root guard rejects; point
PRAISONAI_PROJECT_ROOT at tmp_path so the test checks the written files
without loosening the guard. This test already fails the same way on main.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix: keep gateway scheduler tick alive on errors (fixes#6035)
A tick that raises (store lock I/O such as EMFILE, a malformed scheduler:
block) is now logged and retried with capped backoff instead of ending
_scheduler_task for good. _build_run_policy tolerates a non-mapping
scheduler: block by warning and using the fail-closed defaults.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: cap scheduler backoff exponent and scope headless output_file test to its project root
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix: whitespace in headless output_file test
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>