You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* fix(agent): parallel write batch must pass the safety gate
The parallel write fast path executed tools directly via the ToolExecutor,
bypassing ToolSafetyGate entirely — so with multiple write calls in one turn,
permission checks and skill restrictions were not enforced.
Make can_run_parallel_write_batch consult the gate itself (single source of
truth): only fast-path when, for every call, no active skill restriction forbids
the tool AND the permission checker explicitly Allows it. A missing checker
resolves to Ask (a Deny without a confirmation manager), so it correctly refuses.
This preserves the optimization for the explicit-allow case while closing the
bypass. Exposes ToolSafetyGate::{check_skill_restrictions,permission_decision}
as pub(crate).
* feat(orchestration): programmable Workflow facade, execute_loop, shared budget
Compose the existing combinators into a runtime-driven, Claude-Code-style
dynamic workflow mechanism, reusing the AgentExecutor / SessionStore /
WorkflowCheckpoint / BudgetGuard seams rather than inventing parallel ones.
- Workflow facade (orchestration/workflow.rs): a cheaply-clonable handle whose
verbs agent/parallel/phase/pipeline each delegate to one combinator; phase is
a named, resumable barrier ({root_id}/{index}:{name}) emitting WorkflowEvent
milestones. Control flow lives in the host language.
- execute_loop + LoopDecision (combinators.rs): bounded loop-until-dry with a
mandatory max_iterations hard cap.
- WorkflowBudget (orchestration/workflow_budget.rs): an aggregating BudgetGuard
that sums token spend from every step into one shared ledger (soft cap).
- Wire BudgetGuard through ChildRunContext so a single guard spans a fan-out
(closes a gap: budget was per-session_id only).
- AgentSession::workflow()/workflow_with_token_budget(): pre-wire executor,
store, per-step events, session-derived root id, and the shared budget. Also
fix agent_executor() to install parent_context (security/skill/workspace), so
orchestrated steps are neither more nor less privileged than delegated ones.
- README: document the Workflow facade, loop, and shared budget.
Tests: full lib suite green; new unit + e2e coverage for every verb, resume,
loop caps, budget aggregation, and a real child-agent workflow step.
* feat(sdk): budgeted workflow fan-out (workflowParallel / workflow_parallel)
Expose the shared workflow budget through both SDKs via a session method built
on AgentSession::workflow_with_token_budget(): run a fan-out where every child
agent feeds ONE token ledger and, once the cap is reached, further child LLM
calls are denied (a soft cap; the in-flight fan-out is never force-killed).
Returns the per-step outcomes plus the ledger snapshot.
- Node (napi): session.workflowParallel(specs, budgetTokens?) -> { outcomes, budget }
- Python (pyo3): session.workflow_parallel(specs, budget_tokens=None) -> dict
- Regenerate node generated.d.ts; document both in the README SDK examples.
Both native modules build (napi build / maturin) and pass an offline smoke test
(empty fan-out takes no LLM path; correct ledger snapshot). Full orchestration
behavior is covered by the core crate's test suite.
* test: integration coverage for the Workflow facade, loop & shared budget
- core/tests/test_workflow_facade_real_llm.rs (#[ignore], real-LLM gated like
test_orchestration_real_llm.rs): the Workflow facade phase + milestones,
execute_loop's max_iterations hard cap, session.workflow() running a real
child agent with the shared ledger accumulating spend, and sequential budget
enforcement (a step started after the cap is denied).
- sdk/node/test.mjs: offline workflowParallel shape check (empty fan-out, ledger
snapshot) in the existing smoke run.
- sdk/python/tests/test_workflow_parallel.py: the same offline check for Python.
Verified: core suite green; the integration target compiles and registers its 4
ignored cases; node 'npm test' and the python smoke both pass offline.
* refactor(sdk): fold the budgeted fan-out into parallel(specs, budgetTokens?)
Drop the awkwardly-named workflowParallel / workflow_parallel methods. The only
difference from parallel was a shared token budget, so make it an optional
argument on parallel itself instead of leaking the internal 'workflow' facade
name into the public API.
- Node: parallel(specs, budgetTokens?) -> Array<StepOutcomeObject> |
WorkflowParallelResult (napi Either). No budget => the plain outcomes array
(unchanged, non-breaking); a budget => { outcomes, budget } (ledger snapshot).
- Python: parallel(specs, budget_tokens=None) -> list | dict, same split.
- Update README SDK examples; node test.mjs and the renamed
test_parallel_budget.py cover both the array and budgeted shapes offline.
Rebuilt both native modules (napi build / maturin); npm test and the python
smoke pass; clippy/fmt clean.
---------
Co-authored-by: Claude <claude@anthropic.com>
0 commit comments