* 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: send reasoning_effort as reasoning.effort on the Responses API path
OpenAI reasoning models go to the Responses API by default, but neither
Responses request builder applied the unified reasoning_effort (#4452):
LLM._build_responses_params never read self.reasoning_effort, and the
agent's non-streaming dispatch never forwarded it to the native OpenAI
client, whose _build_responses_input had no mapping for it. The request
went out without any effort.
Resolve the level with resolve_reasoning_params and send it as
reasoning: {"effort": ...}; forward the native level from the agent's
non-streaming dispatch to the OpenAI client. Unset, off and models without
a reasoning control stay no-ops.
* fix: read reasoning_effort and the dispatcher model defensively
Some callers and tests build LLM or Agent objects without the full __init__,
or with a dispatcher stub that has no model attribute. Use getattr with a
None default so the Responses API path does not raise there.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: swf1996120 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Addresses merge-gate findings on #6251:
- the manifest no longer writes the user prompt to disk in plaintext; it
keeps a SHA-256 digest (run_digest) used only to recognise retries
- revert SessionManagerMixin._save/_load_session_state to main: no callers,
so the new plaintext state write had no live consumer
- tests: prompt never reaches disk; absolute/backslash/'..' ids stay inside
the manifest directory
Co-Authored-By: Claude Opus 5.5 <[email protected]>
AgentFlow normalises an Agent step to Task(agent=...), so editing the
agent's instructions or model under the same name was served a stale
durable result. Include the wrapped agent's config and agent_config.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Encode set/frozenset constants (and set-valued variables) in sorted order so
a key no longer depends on PYTHONHASHSEED and durable resume hits.
- Step definition now includes immutable defaults/closure values, module-level
Python helpers the handler calls, and an agent's role/goal/instructions/llm/
tool names, so a same-name edit misses instead of serving a stale result.
- Docstring states what is not covered; tests edit steps under the same name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- manifest filenames use the conversation store's injective naming (a:b vs a_b)
- end_run(run_id=...) leaves another run's marker alone; read-modify-write is
guarded by the store's cross-process FileLock
- praisonai run scopes manifests under the project sessions dir
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Step keys include a fingerprint of the handler's code, so an edited
handler kept under the same name is not served its old result.
- set() rejects values that change in a JSON round trip (int dict keys,
tuples) instead of resuming with different data.
- set() uses the locked update_json read-modify-write so two caches on
one path no longer erase each other's entries.
- Docstring now states that agent steps are keyed by name only.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
GitGuardian reads ("praisonaiagents.gateway.protocols", "DeviceAuth") as an
'Authentication Tuple'. Every PR that merges main carries that line and fails
the GitGuardian check, which the merge gate treats as failed CI. Use a
module-path constant instead; DeviceAuth still resolves lazily.
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Adds a per-session manifest that stores references to each of a session's
storage areas, plus a write-ahead run-in-flight marker with a limit on retry
attempts. Replaces the session_manager.py placeholders with code that uses the
manifest, and makes `praisonai run --continue/--session` report a run that was
interrupted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Step 2 now says the model takes an exam made from real past work it never
studied, matching the website and docs.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- safe_join now percent-encodes names (bijective), so distinct names such as
'a/b' and 'a_b~c14cddc033f6' can no longer share one file; list_todos()
decodes filenames so listed names round-trip through load/delete_todo.
- Legacy <name>.json fallback skips names containing '%' to avoid aliasing.
- Tests restoring the approval backend snapshotted get_backend(), whose
ConsoleBackend fallback was then installed globally on restore and leaked
into tests/unit/skills/test_approval_scoping.py under xdist. Snapshot
_global_backend directly instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(readme): say what "your own AI" is in the handover section
The five handover steps never said where the own model comes from. Add one
line: an open model you run yourself, used as it is or trained on your own
past work with PraisonAI Train, and only after it passes the test.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
* fix: match handover wording to the Test step ("its tests")
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Co-authored-by: MervinPraison <[email protected]>
* test: read Python source using its declared encoding
* test: read Python source using its declared encoding
* test: read Python source using its declared encoding
- utils/atomic_io.safe_join: injective, separator-free filename stems
(same scheme as session store); legacy safe names are unchanged.
- PlanStorage save/load/delete_todo and save_plan use safe_join, so
names like '../../x' can no longer escape todos_dir/plans_dir.
- ContextAgent creates PRPs/ and writes PRPs atomically via safe_join.
- generate_title and crawl4ai_*_sync use run_coroutine_from_any_context,
so they work inside a running event loop instead of crashing or
silently returning the fallback.
Co-Authored-By: Claude Opus 5.5 <[email protected]>