deepstrike 0.2.30__tar.gz → 0.2.32__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {deepstrike-0.2.30 → deepstrike-0.2.32}/Cargo.lock +5 -5
- {deepstrike-0.2.30 → deepstrike-0.2.32}/Cargo.toml +2 -2
- {deepstrike-0.2.30 → deepstrike-0.2.32}/PKG-INFO +1 -1
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/governance/quota.rs +8 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/runtime/kernel.rs +129 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/state_machine/gate.rs +26 -2
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/state_machine/mod.rs +36 -1
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/signals/queue.rs +28 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/signals/router.rs +49 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/signal.rs +20 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-py/src/lib.rs +32 -1
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/__init__.py +46 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/harness/harness.py +14 -34
- deepstrike-0.2.32/deepstrike/harness/judge.py +98 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/providers/anthropic.py +34 -18
- deepstrike-0.2.32/deepstrike/providers/anthropic_compatible.py +38 -0
- deepstrike-0.2.32/deepstrike/providers/deepseek.py +132 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/providers/factories.py +14 -4
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/providers/gemini.py +48 -4
- deepstrike-0.2.32/deepstrike/providers/glm.py +62 -0
- deepstrike-0.2.32/deepstrike/providers/kimi.py +140 -0
- deepstrike-0.2.32/deepstrike/providers/minimax.py +115 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/providers/openai.py +135 -28
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/providers/openai_responses.py +25 -5
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/providers/qwen.py +147 -31
- deepstrike-0.2.32/deepstrike/providers/vendor_profiles.py +133 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/__init__.py +56 -0
- deepstrike-0.2.32/deepstrike/runtime/event_stream.py +80 -0
- deepstrike-0.2.32/deepstrike/runtime/reactive_session.py +187 -0
- deepstrike-0.2.32/deepstrike/runtime/run_group.py +134 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/runner.py +88 -5
- deepstrike-0.2.32/deepstrike/runtime/turn_policy.py +103 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/signals/gateway.py +14 -3
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/signals/types.py +13 -2
- {deepstrike-0.2.30 → deepstrike-0.2.32}/pyproject.toml +1 -1
- deepstrike-0.2.30/deepstrike/providers/deepseek.py +0 -238
- deepstrike-0.2.30/deepstrike/providers/glm.py +0 -50
- deepstrike-0.2.30/deepstrike/providers/kimi.py +0 -49
- deepstrike-0.2.30/deepstrike/providers/minimax.py +0 -260
- {deepstrike-0.2.30 → deepstrike-0.2.32}/README.md +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/Cargo.toml +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/compression.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/config.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/dashboard.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/manager.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/partitions.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/pressure.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/renderer.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/renewal.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/sections.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/skill_catalog.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/snapshot.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/summarizer.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/task_state.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/text.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/context/token_engine.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/governance/audit.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/governance/constraint.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/governance/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/governance/permission.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/governance/pipeline.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/governance/rate_limit.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/governance/sandbox.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/governance/tool_decision.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/governance/veto.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/harness/eval.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/harness/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/lib.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/memory/curator.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/memory/durable.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/memory/extractor.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/memory/idle_pipeline.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/memory/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/memory/runtime.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/memory/semantic.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/memory/session.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/memory/synthesis.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/memory/trace_analyzer.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/memory/working.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/mm/handle.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/mm/memory.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/mm/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/orchestration/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/orchestration/task_graph.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/orchestration/tournament.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/orchestration/workflow/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/orchestration/workflow/run.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/proc/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/runtime/event_log.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/runtime/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/runtime/repair.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/runtime/replay.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/runtime/session.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/runtime/snapshot.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/milestone.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/policy.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/rollback.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/state_machine/capability.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/state_machine/eviction.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/state_machine/milestone_exec.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/state_machine/process.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/state_machine/signal.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/state_machine/tests.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/state_machine/workflow.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/tcb.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/signals/attention.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/signals/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/syscall/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/agent.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/capability.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/contract.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/error.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/message.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/milestone.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/mod.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/model.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/policy.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/result.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/skill.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/types/task.rs +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-py/Cargo.toml +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/collaboration/__init__.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/collaboration/contract.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/collaboration/handoff.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/collaboration/harness.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/collaboration/modes.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/collaboration/pool.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/governance.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/harness/__init__.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/kernel/__init__.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/knowledge/__init__.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/knowledge/source.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/memory/__init__.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/memory/agent.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/memory/in_memory_store.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/memory/protocols.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/memory/working.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/providers/__init__.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/providers/base.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/providers/ollama.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/providers/replay.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/providers/replay_validator.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/providers/stream.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/archive.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/credential_vault.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/eval.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/execution_plane.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/facade.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/filtered_plane.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/kernel_event_log.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/kernel_step.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/large_result_spool.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/mcp_proxy_plane.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/os_profile.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/os_snapshot.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/output_schema.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/process_sandbox_plane.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/provider_replay.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/reducers.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/remote_vpc_plane.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/replay_fixture.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/replay_provider.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/replay_sanitize.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/session_log.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/session_repair.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/sub_agent_orchestrator.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/workflow_control_flow.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/workflow_store.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/runtime/worktree_plane.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/safety/__init__.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/safety/permissions.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/signals/__init__.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/signals/scheduled.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/skills/__init__.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/skills/registry.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/skills/watcher.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/tools/__init__.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/tools/builtin/__init__.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/tools/builtin/read_file.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/tools/errors.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/tools/execution.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/tools/registry.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/types/__init__.py +0 -0
- {deepstrike-0.2.30 → deepstrike-0.2.32}/deepstrike/types/agent.py +0 -0
|
@@ -194,7 +194,7 @@ dependencies = [
|
|
|
194
194
|
|
|
195
195
|
[[package]]
|
|
196
196
|
name = "deepstrike-core"
|
|
197
|
-
version = "0.2.
|
|
197
|
+
version = "0.2.32"
|
|
198
198
|
dependencies = [
|
|
199
199
|
"compact_str",
|
|
200
200
|
"pretty_assertions",
|
|
@@ -206,7 +206,7 @@ dependencies = [
|
|
|
206
206
|
|
|
207
207
|
[[package]]
|
|
208
208
|
name = "deepstrike-node"
|
|
209
|
-
version = "0.2.
|
|
209
|
+
version = "0.2.32"
|
|
210
210
|
dependencies = [
|
|
211
211
|
"compact_str",
|
|
212
212
|
"deepstrike-core",
|
|
@@ -218,7 +218,7 @@ dependencies = [
|
|
|
218
218
|
|
|
219
219
|
[[package]]
|
|
220
220
|
name = "deepstrike-py"
|
|
221
|
-
version = "0.2.
|
|
221
|
+
version = "0.2.32"
|
|
222
222
|
dependencies = [
|
|
223
223
|
"compact_str",
|
|
224
224
|
"deepstrike-core",
|
|
@@ -229,7 +229,7 @@ dependencies = [
|
|
|
229
229
|
|
|
230
230
|
[[package]]
|
|
231
231
|
name = "deepstrike-sdk"
|
|
232
|
-
version = "0.2.
|
|
232
|
+
version = "0.2.32"
|
|
233
233
|
dependencies = [
|
|
234
234
|
"async-stream",
|
|
235
235
|
"async-trait",
|
|
@@ -263,7 +263,7 @@ dependencies = [
|
|
|
263
263
|
|
|
264
264
|
[[package]]
|
|
265
265
|
name = "deepstrike-wasm"
|
|
266
|
-
version = "0.2.
|
|
266
|
+
version = "0.2.32"
|
|
267
267
|
dependencies = [
|
|
268
268
|
"compact_str",
|
|
269
269
|
"deepstrike-core",
|
|
@@ -3,13 +3,13 @@ resolver = "2"
|
|
|
3
3
|
members = ["crates/deepstrike-core", "crates/deepstrike-py"]
|
|
4
4
|
|
|
5
5
|
[workspace.package]
|
|
6
|
-
version = "0.2.
|
|
6
|
+
version = "0.2.32"
|
|
7
7
|
edition = "2024"
|
|
8
8
|
license = "MIT"
|
|
9
9
|
repository = "https://github.com/kongusen/deepstrike"
|
|
10
10
|
|
|
11
11
|
[workspace.dependencies]
|
|
12
|
-
deepstrike-core = { path = "crates/deepstrike-core", version = "0.2.
|
|
12
|
+
deepstrike-core = { path = "crates/deepstrike-core", version = "0.2.32" }
|
|
13
13
|
notify = "6"
|
|
14
14
|
serde = { version = "1", features = ["derive"] }
|
|
15
15
|
serde_json = "1"
|
|
@@ -17,8 +17,15 @@ use serde::{Deserialize, Serialize};
|
|
|
17
17
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
18
18
|
pub struct ResourceQuota {
|
|
19
19
|
/// Max sub-agents in the `Running` state at once. Further `Spawn`s are denied while at cap.
|
|
20
|
+
/// *Instantaneous* — vehicle-scoped (cannot span stateless replicas; spec §2.5).
|
|
20
21
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
21
22
|
pub max_concurrent_subagents: Option<u32>,
|
|
23
|
+
/// L1 (RunGroup): max sub-agents spawned *cumulatively* over the whole governance domain. Unlike
|
|
24
|
+
/// `max_concurrent_subagents` this counts every spawn ever (running + completed), seeded across
|
|
25
|
+
/// members via `group_spawns_base`, so it spans N stateless top-level runs. A hard `Deny` at cap
|
|
26
|
+
/// (a completed sibling never frees a cumulative slot).
|
|
27
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
28
|
+
pub max_total_subagents: Option<u32>,
|
|
22
29
|
/// Max sub-agent nesting depth (direct children of the root loop are depth 1).
|
|
23
30
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
24
31
|
pub max_spawn_depth: Option<u32>,
|
|
@@ -37,6 +44,7 @@ impl ResourceQuota {
|
|
|
37
44
|
/// Whether any limit is actually set (used to short-circuit the gate when fully open).
|
|
38
45
|
pub fn is_open(&self) -> bool {
|
|
39
46
|
self.max_concurrent_subagents.is_none()
|
|
47
|
+
&& self.max_total_subagents.is_none()
|
|
40
48
|
&& self.max_spawn_depth.is_none()
|
|
41
49
|
&& self.memory_writes_per_window.is_none()
|
|
42
50
|
&& self.max_workflow_nodes.is_none()
|
|
@@ -150,6 +150,16 @@ pub struct RunConfig {
|
|
|
150
150
|
pub scheduler_max_wall_ms: Option<u64>,
|
|
151
151
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
152
152
|
pub resource_quota: Option<crate::governance::quota::ResourceQuota>,
|
|
153
|
+
/// L1 (RunGroup): cumulative tokens already spent by *other* members of this run's governance
|
|
154
|
+
/// domain, seeded at boot so the run-level token cap (`max_total_tokens`) is enforced across the
|
|
155
|
+
/// whole group, not per-vehicle. `None`/0 ⇒ no group (N=1) ⇒ pre-L1 per-kernel behavior.
|
|
156
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
157
|
+
pub group_tokens_base: Option<u64>,
|
|
158
|
+
/// L1 (RunGroup): sub-agents already spawned by *other* members of this run's governance domain,
|
|
159
|
+
/// seeded at boot so `ResourceQuota::max_total_subagents` is enforced across the whole group.
|
|
160
|
+
/// `None`/0 ⇒ no group (N=1) ⇒ pre-L1 per-vehicle behavior.
|
|
161
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
162
|
+
pub group_spawns_base: Option<u32>,
|
|
153
163
|
}
|
|
154
164
|
|
|
155
165
|
/// Build a [`GovernancePipeline`](crate::governance::pipeline::GovernancePipeline) from the ABI policy
|
|
@@ -766,6 +776,12 @@ impl KernelRuntime {
|
|
|
766
776
|
self.sm.is_terminal()
|
|
767
777
|
}
|
|
768
778
|
|
|
779
|
+
/// L1 (RunGroup): this vehicle's cumulative sub-agent spawns this run, read back by the SDK at run
|
|
780
|
+
/// end to charge the group ledger (so the next member's cumulative spawn cap is seeded correctly).
|
|
781
|
+
pub fn local_subagents_spawned(&self) -> u32 {
|
|
782
|
+
self.sm.local_subagents_spawned()
|
|
783
|
+
}
|
|
784
|
+
|
|
769
785
|
pub fn step(&mut self, input: KernelInput) -> KernelStep {
|
|
770
786
|
let action = match input.event {
|
|
771
787
|
KernelInputEvent::SetTools { tools } => {
|
|
@@ -884,6 +900,8 @@ impl KernelRuntime {
|
|
|
884
900
|
attention_max_queue_size,
|
|
885
901
|
scheduler_max_wall_ms,
|
|
886
902
|
resource_quota,
|
|
903
|
+
group_tokens_base,
|
|
904
|
+
group_spawns_base,
|
|
887
905
|
} = config;
|
|
888
906
|
if let Some(tools) = tools {
|
|
889
907
|
self.sm.tools = tools;
|
|
@@ -924,6 +942,12 @@ impl KernelRuntime {
|
|
|
924
942
|
if let Some(quota) = resource_quota {
|
|
925
943
|
self.sm.set_resource_quota(quota);
|
|
926
944
|
}
|
|
945
|
+
if let Some(base) = group_tokens_base {
|
|
946
|
+
self.sm.seed_group_budget(base);
|
|
947
|
+
}
|
|
948
|
+
if let Some(base) = group_spawns_base {
|
|
949
|
+
self.sm.seed_group_spawns(base);
|
|
950
|
+
}
|
|
927
951
|
return KernelStep::empty(self.sm.take_observations());
|
|
928
952
|
}
|
|
929
953
|
KernelInputEvent::SetAttentionPolicy { max_queue_size } => {
|
|
@@ -1396,6 +1420,111 @@ mod tests {
|
|
|
1396
1420
|
assert!(!runtime.state_machine().is_suspended());
|
|
1397
1421
|
}
|
|
1398
1422
|
|
|
1423
|
+
#[test]
|
|
1424
|
+
fn group_budget_base_enforces_shared_token_cap() {
|
|
1425
|
+
use crate::types::message::{Content, Message, ToolCall, ToolResult};
|
|
1426
|
+
|
|
1427
|
+
// Drive one tool-calling turn under a 100-token run cap, with `group_base` already spent by
|
|
1428
|
+
// other members of the governance domain. The token-budget axis is checked after the tool
|
|
1429
|
+
// results, against `group_base + local`.
|
|
1430
|
+
fn run_one_turn(group_base: Option<u64>) -> KernelStep {
|
|
1431
|
+
let mut runtime = KernelRuntime::new(LoopPolicy {
|
|
1432
|
+
max_total_tokens: 100,
|
|
1433
|
+
..LoopPolicy::default()
|
|
1434
|
+
});
|
|
1435
|
+
runtime.step(KernelInput::new(KernelInputEvent::ConfigureRun {
|
|
1436
|
+
config: RunConfig { group_tokens_base: group_base, ..RunConfig::default() },
|
|
1437
|
+
}));
|
|
1438
|
+
runtime.step(KernelInput::new(KernelInputEvent::StartRun {
|
|
1439
|
+
task: RuntimeTask::new("task"),
|
|
1440
|
+
run_spec: None,
|
|
1441
|
+
}));
|
|
1442
|
+
let mut msg = Message::assistant("");
|
|
1443
|
+
msg.token_count = Some(10); // this vehicle's local spend this turn
|
|
1444
|
+
msg.tool_calls.push(ToolCall {
|
|
1445
|
+
id: "c1".into(),
|
|
1446
|
+
name: "echo".into(),
|
|
1447
|
+
arguments: serde_json::json!({}),
|
|
1448
|
+
});
|
|
1449
|
+
runtime.step(KernelInput::new(KernelInputEvent::ProviderResult {
|
|
1450
|
+
message: msg,
|
|
1451
|
+
observed_input_tokens: None,
|
|
1452
|
+
observed_output_tokens: None,
|
|
1453
|
+
now_ms: None,
|
|
1454
|
+
}));
|
|
1455
|
+
runtime.step(KernelInput::new(KernelInputEvent::ToolResults {
|
|
1456
|
+
results: vec![ToolResult {
|
|
1457
|
+
call_id: "c1".into(),
|
|
1458
|
+
output: Content::Text("ok".into()),
|
|
1459
|
+
is_error: false,
|
|
1460
|
+
is_fatal: false,
|
|
1461
|
+
error_kind: None,
|
|
1462
|
+
token_count: None,
|
|
1463
|
+
}],
|
|
1464
|
+
}))
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
let exceeded = |step: &KernelStep| {
|
|
1468
|
+
step.observations.iter().any(|o| {
|
|
1469
|
+
matches!(o, KernelObservation::BudgetExceeded { budget, .. } if budget == "token_budget")
|
|
1470
|
+
})
|
|
1471
|
+
};
|
|
1472
|
+
|
|
1473
|
+
// Group already spent 95; this vehicle's 10 pushes the domain to 105 > 100 → shared cap fires.
|
|
1474
|
+
assert!(
|
|
1475
|
+
exceeded(&run_one_turn(Some(95))),
|
|
1476
|
+
"group token budget must span the whole domain"
|
|
1477
|
+
);
|
|
1478
|
+
// N=1 / no group (base 0): local 10 is far under the cap → pre-L1 behavior unchanged.
|
|
1479
|
+
assert!(
|
|
1480
|
+
!exceeded(&run_one_turn(None)),
|
|
1481
|
+
"no group seed ⇒ per-vehicle budget, well under cap"
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
#[test]
|
|
1486
|
+
fn group_spawns_base_enforces_cumulative_spawn_cap() {
|
|
1487
|
+
use crate::governance::quota::ResourceQuota;
|
|
1488
|
+
use crate::types::agent::{AgentIdentity, AgentRole, AgentRunSpec};
|
|
1489
|
+
|
|
1490
|
+
// Cumulative cap of 2 sub-agents across the domain. Other members already spawned 2 (seeded),
|
|
1491
|
+
// so this vehicle's very first spawn is denied — the cap spans the whole group.
|
|
1492
|
+
let mut runtime = KernelRuntime::new(LoopPolicy::default());
|
|
1493
|
+
runtime.step(KernelInput::new(KernelInputEvent::ConfigureRun {
|
|
1494
|
+
config: RunConfig {
|
|
1495
|
+
resource_quota: Some(ResourceQuota {
|
|
1496
|
+
max_total_subagents: Some(2),
|
|
1497
|
+
..ResourceQuota::default()
|
|
1498
|
+
}),
|
|
1499
|
+
group_spawns_base: Some(2),
|
|
1500
|
+
..RunConfig::default()
|
|
1501
|
+
},
|
|
1502
|
+
}));
|
|
1503
|
+
runtime.step(KernelInput::new(KernelInputEvent::StartRun {
|
|
1504
|
+
task: RuntimeTask::new("task"),
|
|
1505
|
+
run_spec: None,
|
|
1506
|
+
}));
|
|
1507
|
+
runtime.state_machine_mut().take_observations();
|
|
1508
|
+
|
|
1509
|
+
let spec = AgentRunSpec::new(
|
|
1510
|
+
AgentIdentity::sub_agent("worker", "worker-session"),
|
|
1511
|
+
AgentRole::Implement,
|
|
1512
|
+
"do work",
|
|
1513
|
+
);
|
|
1514
|
+
let step = runtime.step(KernelInput::new(KernelInputEvent::SpawnSubAgent {
|
|
1515
|
+
spec,
|
|
1516
|
+
parent_session_id: "parent-session".to_string(),
|
|
1517
|
+
}));
|
|
1518
|
+
|
|
1519
|
+
// Denied: domain already at the cumulative cap → rolled back, no process registered.
|
|
1520
|
+
assert!(matches!(
|
|
1521
|
+
step.actions.as_slice(),
|
|
1522
|
+
[KernelAction::CallProvider { .. }]
|
|
1523
|
+
));
|
|
1524
|
+
assert!(runtime.state_machine().agent_process("worker").is_none());
|
|
1525
|
+
assert_eq!(runtime.local_subagents_spawned(), 0);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1399
1528
|
#[test]
|
|
1400
1529
|
fn default_runtime_leaves_spawn_unquota_ed() {
|
|
1401
1530
|
use crate::types::agent::{AgentIdentity, AgentRole, AgentRunSpec};
|
{deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/state_machine/gate.rs
RENAMED
|
@@ -85,6 +85,9 @@ impl LoopStateMachine {
|
|
|
85
85
|
// M4/G5 token headroom: the run-level cumulative token cap is always set on the scheduler
|
|
86
86
|
// budget, so a coordinator always sees how many tokens remain (the "use 10k tokens" signal).
|
|
87
87
|
let tokens_max = self.policy.max_total_tokens;
|
|
88
|
+
// L1: report the governance domain's cumulative token spend (this vehicle + other members'
|
|
89
|
+
// seeded base) so a coordinator scales submissions to the group's remaining headroom.
|
|
90
|
+
let tokens_used = self.total_tokens.saturating_add(self.group_tokens_base);
|
|
88
91
|
Some(crate::orchestration::workflow::WorkflowBudget {
|
|
89
92
|
nodes_used,
|
|
90
93
|
nodes_max,
|
|
@@ -93,9 +96,9 @@ impl LoopStateMachine {
|
|
|
93
96
|
max_concurrent_subagents,
|
|
94
97
|
concurrency_remaining: max_concurrent_subagents
|
|
95
98
|
.map(|m| m.saturating_sub(running_subagents)),
|
|
96
|
-
tokens_used
|
|
99
|
+
tokens_used,
|
|
97
100
|
tokens_max: Some(tokens_max),
|
|
98
|
-
tokens_remaining: Some(tokens_max.saturating_sub(
|
|
101
|
+
tokens_remaining: Some(tokens_max.saturating_sub(tokens_used)),
|
|
99
102
|
})
|
|
100
103
|
}
|
|
101
104
|
|
|
@@ -122,6 +125,17 @@ impl LoopStateMachine {
|
|
|
122
125
|
};
|
|
123
126
|
}
|
|
124
127
|
}
|
|
128
|
+
if let Some(max) = quota.max_total_subagents {
|
|
129
|
+
// L1: cumulative across the whole governance domain (other members' seeded base + this
|
|
130
|
+
// vehicle's spawns ever). A hard Deny — a completed sibling never frees a cumulative slot.
|
|
131
|
+
let total = self.group_spawns_base + self.local_subagents_spawned();
|
|
132
|
+
if total >= max {
|
|
133
|
+
return Disposition::Deny {
|
|
134
|
+
stage: "quota",
|
|
135
|
+
reason: format!("max_total_subagents={max} reached ({total} spawned in domain)"),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
125
139
|
if let Some(max) = quota.max_spawn_depth {
|
|
126
140
|
// Sub-agents currently parent to the root task (depth 1). Nested spawning would
|
|
127
141
|
// generalize this to the spawning task's lineage depth.
|
|
@@ -161,6 +175,16 @@ impl LoopStateMachine {
|
|
|
161
175
|
return Disposition::Defer { slot: running };
|
|
162
176
|
}
|
|
163
177
|
}
|
|
178
|
+
if let Some(max) = quota.max_total_subagents {
|
|
179
|
+
// Cumulative cap is permanent (no sibling completion frees a slot) → hard Deny, not Defer.
|
|
180
|
+
let total = self.group_spawns_base + self.local_subagents_spawned();
|
|
181
|
+
if total >= max {
|
|
182
|
+
return Disposition::Deny {
|
|
183
|
+
stage: "quota",
|
|
184
|
+
reason: format!("max_total_subagents={max} reached ({total} spawned in domain)"),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
164
188
|
if let Some(max) = quota.max_spawn_depth {
|
|
165
189
|
let depth = 1u32;
|
|
166
190
|
if depth > max {
|
{deepstrike-0.2.30 → deepstrike-0.2.32}/crates/deepstrike-core/src/scheduler/state_machine/mod.rs
RENAMED
|
@@ -157,6 +157,14 @@ pub struct LoopStateMachine {
|
|
|
157
157
|
pub observations: Vec<KernelObservation>,
|
|
158
158
|
pub(super) policy: LoopPolicy,
|
|
159
159
|
pub(super) total_tokens: u64,
|
|
160
|
+
/// L1 (RunGroup): cumulative tokens spent by *other* members of this run's governance domain,
|
|
161
|
+
/// seeded at boot via `seed_group_budget`. The run-level token cap is enforced against
|
|
162
|
+
/// `group_tokens_base + total_tokens` so the budget spans the whole group, not one vehicle.
|
|
163
|
+
/// 0 (default) ⇒ no group (N=1) ⇒ pre-L1 per-kernel behavior (byte-identical).
|
|
164
|
+
pub(super) group_tokens_base: u64,
|
|
165
|
+
/// L1 (RunGroup): sub-agents spawned by *other* members of this run's governance domain, seeded
|
|
166
|
+
/// at boot. `max_total_subagents` is enforced against `group_spawns_base + local spawns`. 0 ⇒ N=1.
|
|
167
|
+
pub(super) group_spawns_base: u32,
|
|
160
168
|
/// When set, the next LLM call strips tools to force a text response,
|
|
161
169
|
/// then terminates with this reason once the response arrives.
|
|
162
170
|
pub(super) pending_termination: Option<TerminationReason>,
|
|
@@ -234,6 +242,8 @@ impl LoopStateMachine {
|
|
|
234
242
|
observations: Vec::new(),
|
|
235
243
|
policy,
|
|
236
244
|
total_tokens: 0,
|
|
245
|
+
group_tokens_base: 0,
|
|
246
|
+
group_spawns_base: 0,
|
|
237
247
|
pending_termination: None,
|
|
238
248
|
session_history_baseline: 0,
|
|
239
249
|
checkpoint: TurnCheckpoint::default(),
|
|
@@ -293,7 +303,9 @@ impl LoopStateMachine {
|
|
|
293
303
|
fn root_tcb(&self) -> Tcb {
|
|
294
304
|
let mut tcb = Tcb::root("root", self.policy.clone());
|
|
295
305
|
tcb.budget.turns = self.turn;
|
|
296
|
-
|
|
306
|
+
// L1: the token-budget axis is evaluated against the whole governance domain's cumulative
|
|
307
|
+
// spend (this vehicle's `total_tokens` plus other members' `group_tokens_base`).
|
|
308
|
+
tcb.budget.total_tokens = self.total_tokens.saturating_add(self.group_tokens_base);
|
|
297
309
|
tcb.budget.started_at_ms = self.started_at_ms;
|
|
298
310
|
tcb.state = self.lifecycle();
|
|
299
311
|
tcb
|
|
@@ -318,6 +330,26 @@ impl LoopStateMachine {
|
|
|
318
330
|
self.resource_quota = Some(quota);
|
|
319
331
|
}
|
|
320
332
|
|
|
333
|
+
/// L1 (RunGroup): seed the cumulative tokens already spent by other members of this run's
|
|
334
|
+
/// governance domain. The run-level token cap is then enforced against the group total. Seeding
|
|
335
|
+
/// 0 (the default) preserves pre-L1 per-vehicle behavior.
|
|
336
|
+
pub fn seed_group_budget(&mut self, tokens_spent: u64) {
|
|
337
|
+
self.group_tokens_base = tokens_spent;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/// L1 (RunGroup): seed the sub-agents already spawned by other members of this run's governance
|
|
341
|
+
/// domain. `max_total_subagents` is then enforced against the group total. 0 ⇒ pre-L1 behavior.
|
|
342
|
+
pub fn seed_group_spawns(&mut self, subagents_spawned: u32) {
|
|
343
|
+
self.group_spawns_base = subagents_spawned;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/// L1: this vehicle's cumulative sub-agent spawns this run — every child task ever registered in
|
|
347
|
+
/// the `TaskTable` (running + completed), distinct from the *instantaneous* running count. Used
|
|
348
|
+
/// for the cumulative spawn quota and read back by the SDK to charge the group ledger at run end.
|
|
349
|
+
pub fn local_subagents_spawned(&self) -> u32 {
|
|
350
|
+
self.tasks.all().iter().filter(|t| t.proc.is_some()).count() as u32
|
|
351
|
+
}
|
|
352
|
+
|
|
321
353
|
/// Install the long-term memory policy (`set_memory_policy`). Once set it gates `write_memory`
|
|
322
354
|
/// validation and bounds `query_memory` retrieval breadth. Not setting it (the default)
|
|
323
355
|
/// preserves pre-policy behavior.
|
|
@@ -723,6 +755,9 @@ impl LoopStateMachine {
|
|
|
723
755
|
observations: Vec::new(),
|
|
724
756
|
policy,
|
|
725
757
|
total_tokens: snap.total_tokens,
|
|
758
|
+
// Re-seeded from the replayed `ConfigureRun` (strategy is data, not serialized state).
|
|
759
|
+
group_tokens_base: 0,
|
|
760
|
+
group_spawns_base: 0,
|
|
726
761
|
pending_termination: None,
|
|
727
762
|
session_history_baseline: 0,
|
|
728
763
|
checkpoint: TurnCheckpoint::default(),
|
|
@@ -65,6 +65,34 @@ impl SignalQueue {
|
|
|
65
65
|
self.heap.pop().map(|ps| ps.signal)
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/// Pop the highest-priority signal visible to `recipient`: broadcasts
|
|
69
|
+
/// (`signal.recipient == None`) plus those addressed to `recipient`.
|
|
70
|
+
/// Non-matching entries are retained (re-pushed) so heap ordering is preserved.
|
|
71
|
+
/// `recipient == None` ⇒ no filtering (drains anything; back-compat with `pop`).
|
|
72
|
+
pub(super) fn pop_for(&mut self, recipient: Option<&str>) -> Option<RuntimeSignal> {
|
|
73
|
+
let Some(recipient) = recipient else {
|
|
74
|
+
return self.pop();
|
|
75
|
+
};
|
|
76
|
+
let mut skipped: Vec<PrioritizedSignal> = Vec::new();
|
|
77
|
+
let mut found = None;
|
|
78
|
+
while let Some(ps) = self.heap.pop() {
|
|
79
|
+
let visible = ps
|
|
80
|
+
.signal
|
|
81
|
+
.recipient
|
|
82
|
+
.as_deref()
|
|
83
|
+
.is_none_or(|r| r == recipient);
|
|
84
|
+
if visible {
|
|
85
|
+
found = Some(ps.signal);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
skipped.push(ps);
|
|
89
|
+
}
|
|
90
|
+
for ps in skipped {
|
|
91
|
+
self.heap.push(ps);
|
|
92
|
+
}
|
|
93
|
+
found
|
|
94
|
+
}
|
|
95
|
+
|
|
68
96
|
pub(super) fn len(&self) -> usize {
|
|
69
97
|
self.heap.len()
|
|
70
98
|
}
|
|
@@ -54,6 +54,12 @@ impl SignalRouter {
|
|
|
54
54
|
self.queue.pop()
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/// Pull the next queued signal visible to `recipient` (broadcasts plus signals
|
|
58
|
+
/// addressed to it); other recipients' signals stay queued. `None` ⇒ no filter.
|
|
59
|
+
pub fn next_for(&mut self, recipient: Option<&str>) -> Option<RuntimeSignal> {
|
|
60
|
+
self.queue.pop_for(recipient)
|
|
61
|
+
}
|
|
62
|
+
|
|
57
63
|
/// Number of queued signals.
|
|
58
64
|
pub fn depth(&self) -> usize {
|
|
59
65
|
self.queue.len()
|
|
@@ -139,6 +145,49 @@ mod tests {
|
|
|
139
145
|
assert_eq!(router.ingest(s2, false), SignalDisposition::Dropped);
|
|
140
146
|
}
|
|
141
147
|
|
|
148
|
+
#[test]
|
|
149
|
+
fn next_for_drains_recipient_plus_broadcast_only() {
|
|
150
|
+
let mut router = SignalRouter::new(100);
|
|
151
|
+
let to_a = RuntimeSignal::new(SignalSource::Gateway, SignalType::Event, Urgency::Normal, "a")
|
|
152
|
+
.with_recipient("sess-a")
|
|
153
|
+
.with_timestamp(1);
|
|
154
|
+
let to_b = RuntimeSignal::new(SignalSource::Gateway, SignalType::Event, Urgency::Normal, "b")
|
|
155
|
+
.with_recipient("sess-b")
|
|
156
|
+
.with_timestamp(2);
|
|
157
|
+
let broadcast =
|
|
158
|
+
RuntimeSignal::new(SignalSource::Cron, SignalType::Job, Urgency::Normal, "all")
|
|
159
|
+
.with_timestamp(3);
|
|
160
|
+
router.ingest(to_a, false);
|
|
161
|
+
router.ingest(to_b, false);
|
|
162
|
+
router.ingest(broadcast, false);
|
|
163
|
+
|
|
164
|
+
// Puller "sess-a" sees only its own + the broadcast, never sess-b's.
|
|
165
|
+
let first = router.next_for(Some("sess-a")).unwrap();
|
|
166
|
+
let second = router.next_for(Some("sess-a")).unwrap();
|
|
167
|
+
let mut got: Vec<_> = [first.summary.as_str(), second.summary.as_str()]
|
|
168
|
+
.iter()
|
|
169
|
+
.map(|s| s.to_string())
|
|
170
|
+
.collect();
|
|
171
|
+
got.sort();
|
|
172
|
+
assert_eq!(got, vec!["a".to_string(), "all".to_string()]);
|
|
173
|
+
assert!(router.next_for(Some("sess-a")).is_none());
|
|
174
|
+
|
|
175
|
+
// sess-b's signal is still queued for its own puller.
|
|
176
|
+
assert_eq!(router.next_for(Some("sess-b")).unwrap().summary.as_str(), "b");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
#[test]
|
|
180
|
+
fn next_for_none_drains_anything() {
|
|
181
|
+
let mut router = SignalRouter::new(100);
|
|
182
|
+
router.ingest(
|
|
183
|
+
RuntimeSignal::new(SignalSource::Gateway, SignalType::Event, Urgency::Normal, "x")
|
|
184
|
+
.with_recipient("sess-a"),
|
|
185
|
+
false,
|
|
186
|
+
);
|
|
187
|
+
// No recipient filter ⇒ back-compat: drains the addressed signal too.
|
|
188
|
+
assert_eq!(router.next_for(None).unwrap().summary.as_str(), "x");
|
|
189
|
+
}
|
|
190
|
+
|
|
142
191
|
#[test]
|
|
143
192
|
fn clear_dedup_allows_reingest() {
|
|
144
193
|
let mut router = SignalRouter::new(100);
|
|
@@ -12,6 +12,14 @@ pub struct RuntimeSignal {
|
|
|
12
12
|
pub payload: serde_json::Value,
|
|
13
13
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
14
14
|
pub dedupe_key: Option<CompactString>,
|
|
15
|
+
/// Target a specific agent/session loop. `None` ⇒ broadcast (drained by any puller).
|
|
16
|
+
/// The canonical key is the recipient's `sessionId` (see R1 / L0).
|
|
17
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
18
|
+
pub recipient: Option<CompactString>,
|
|
19
|
+
/// Optional topic for pub/sub-style fan-out. Carried through for subscribers;
|
|
20
|
+
/// multi-subscriber routing is deferred (field-only this phase).
|
|
21
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
22
|
+
pub topic: Option<CompactString>,
|
|
15
23
|
pub timestamp_ms: u64,
|
|
16
24
|
}
|
|
17
25
|
|
|
@@ -56,6 +64,8 @@ impl RuntimeSignal {
|
|
|
56
64
|
summary: summary.into(),
|
|
57
65
|
payload: serde_json::Value::Null,
|
|
58
66
|
dedupe_key: None,
|
|
67
|
+
recipient: None,
|
|
68
|
+
topic: None,
|
|
59
69
|
timestamp_ms: 0,
|
|
60
70
|
}
|
|
61
71
|
}
|
|
@@ -65,6 +75,16 @@ impl RuntimeSignal {
|
|
|
65
75
|
self
|
|
66
76
|
}
|
|
67
77
|
|
|
78
|
+
pub fn with_recipient(mut self, recipient: impl Into<CompactString>) -> Self {
|
|
79
|
+
self.recipient = Some(recipient.into());
|
|
80
|
+
self
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pub fn with_topic(mut self, topic: impl Into<CompactString>) -> Self {
|
|
84
|
+
self.topic = Some(topic.into());
|
|
85
|
+
self
|
|
86
|
+
}
|
|
87
|
+
|
|
68
88
|
pub fn with_payload(mut self, payload: serde_json::Value) -> Self {
|
|
69
89
|
self.payload = payload;
|
|
70
90
|
self
|
|
@@ -91,13 +91,17 @@ struct RuntimeSignal {
|
|
|
91
91
|
#[pyo3(get, set)]
|
|
92
92
|
dedupe_key: Option<String>,
|
|
93
93
|
#[pyo3(get, set)]
|
|
94
|
+
recipient: Option<String>,
|
|
95
|
+
#[pyo3(get, set)]
|
|
96
|
+
topic: Option<String>,
|
|
97
|
+
#[pyo3(get, set)]
|
|
94
98
|
timestamp_ms: f64,
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
#[pymethods]
|
|
98
102
|
impl RuntimeSignal {
|
|
99
103
|
#[new]
|
|
100
|
-
#[pyo3(signature = (source, urgency, summary, signal_type="event", payload="null", dedupe_key=None, timestamp_ms=0.0))]
|
|
104
|
+
#[pyo3(signature = (source, urgency, summary, signal_type="event", payload="null", dedupe_key=None, timestamp_ms=0.0, recipient=None, topic=None))]
|
|
101
105
|
fn new(
|
|
102
106
|
source: String,
|
|
103
107
|
urgency: String,
|
|
@@ -106,6 +110,8 @@ impl RuntimeSignal {
|
|
|
106
110
|
payload: &str,
|
|
107
111
|
dedupe_key: Option<String>,
|
|
108
112
|
timestamp_ms: f64,
|
|
113
|
+
recipient: Option<String>,
|
|
114
|
+
topic: Option<String>,
|
|
109
115
|
) -> Self {
|
|
110
116
|
Self {
|
|
111
117
|
id: uuid::Uuid::new_v4().to_string(),
|
|
@@ -115,6 +121,8 @@ impl RuntimeSignal {
|
|
|
115
121
|
signal_type: signal_type.into(),
|
|
116
122
|
payload: payload.into(),
|
|
117
123
|
dedupe_key,
|
|
124
|
+
recipient,
|
|
125
|
+
topic,
|
|
118
126
|
timestamp_ms,
|
|
119
127
|
}
|
|
120
128
|
}
|
|
@@ -154,6 +162,12 @@ impl RuntimeSignal {
|
|
|
154
162
|
if let Some(ref key) = self.dedupe_key {
|
|
155
163
|
sig = sig.with_dedupe(key.as_str());
|
|
156
164
|
}
|
|
165
|
+
if let Some(ref recipient) = self.recipient {
|
|
166
|
+
sig = sig.with_recipient(recipient.as_str());
|
|
167
|
+
}
|
|
168
|
+
if let Some(ref topic) = self.topic {
|
|
169
|
+
sig = sig.with_topic(topic.as_str());
|
|
170
|
+
}
|
|
157
171
|
sig
|
|
158
172
|
}
|
|
159
173
|
|
|
@@ -183,6 +197,8 @@ impl RuntimeSignal {
|
|
|
183
197
|
summary: s.summary.to_string(),
|
|
184
198
|
payload: serde_json::to_string(&s.payload).unwrap_or_else(|_| "null".into()),
|
|
185
199
|
dedupe_key: s.dedupe_key.as_ref().map(|k| k.to_string()),
|
|
200
|
+
recipient: s.recipient.as_ref().map(|r| r.to_string()),
|
|
201
|
+
topic: s.topic.as_ref().map(|t| t.to_string()),
|
|
186
202
|
timestamp_ms: s.timestamp_ms as f64,
|
|
187
203
|
}
|
|
188
204
|
}
|
|
@@ -865,6 +881,11 @@ impl KernelRuntime {
|
|
|
865
881
|
self.inner.state_machine().turn
|
|
866
882
|
}
|
|
867
883
|
|
|
884
|
+
/// L1 (RunGroup): cumulative sub-agent spawns this run, for charging the group ledger at run end.
|
|
885
|
+
fn local_subagents_spawned(&self) -> u32 {
|
|
886
|
+
self.inner.local_subagents_spawned()
|
|
887
|
+
}
|
|
888
|
+
|
|
868
889
|
fn recovery_content_bytes(&self) -> u32 {
|
|
869
890
|
let sm = self.inner.state_machine();
|
|
870
891
|
let tokens = sm.ctx.config.recovery_content_tokens(sm.ctx.max_tokens);
|
|
@@ -922,6 +943,16 @@ impl SignalRouter {
|
|
|
922
943
|
self.inner.next().as_ref().map(RuntimeSignal::from_rust)
|
|
923
944
|
}
|
|
924
945
|
|
|
946
|
+
/// Pull the next queued signal visible to `recipient` (broadcasts plus signals
|
|
947
|
+
/// addressed to it); other recipients' signals stay queued. None ⇒ no filter.
|
|
948
|
+
#[pyo3(signature = (recipient=None))]
|
|
949
|
+
fn next_for(&mut self, recipient: Option<String>) -> Option<RuntimeSignal> {
|
|
950
|
+
self.inner
|
|
951
|
+
.next_for(recipient.as_deref())
|
|
952
|
+
.as_ref()
|
|
953
|
+
.map(RuntimeSignal::from_rust)
|
|
954
|
+
}
|
|
955
|
+
|
|
925
956
|
fn depth(&self) -> usize {
|
|
926
957
|
self.inner.depth()
|
|
927
958
|
}
|
|
@@ -38,6 +38,29 @@ from deepstrike.runtime import (
|
|
|
38
38
|
InMemorySessionLog,
|
|
39
39
|
FileSessionLog,
|
|
40
40
|
SessionLog,
|
|
41
|
+
RunGroup,
|
|
42
|
+
GroupBudgetStore,
|
|
43
|
+
GroupLedger,
|
|
44
|
+
GroupMember,
|
|
45
|
+
InMemoryGroupBudgetStore,
|
|
46
|
+
SessionLogGroupBudgetStore,
|
|
47
|
+
BlackboardEvent,
|
|
48
|
+
EventStream,
|
|
49
|
+
EventViewer,
|
|
50
|
+
InMemoryEventStream,
|
|
51
|
+
is_visible_to,
|
|
52
|
+
PeerView,
|
|
53
|
+
react_by_mention,
|
|
54
|
+
director_driven,
|
|
55
|
+
round_robin,
|
|
56
|
+
first_non_empty,
|
|
57
|
+
union,
|
|
58
|
+
ReactiveSession,
|
|
59
|
+
ReactivePeerSpec,
|
|
60
|
+
Reaction,
|
|
61
|
+
ReactorContext,
|
|
62
|
+
ReactorTurn,
|
|
63
|
+
read_recent_tool,
|
|
41
64
|
ProviderReplay,
|
|
42
65
|
FilteredExecutionPlane,
|
|
43
66
|
SubAgentOrchestrator,
|
|
@@ -137,6 +160,29 @@ __all__ = [
|
|
|
137
160
|
"InMemorySessionLog",
|
|
138
161
|
"FileSessionLog",
|
|
139
162
|
"SessionLog",
|
|
163
|
+
"RunGroup",
|
|
164
|
+
"GroupBudgetStore",
|
|
165
|
+
"GroupLedger",
|
|
166
|
+
"GroupMember",
|
|
167
|
+
"InMemoryGroupBudgetStore",
|
|
168
|
+
"SessionLogGroupBudgetStore",
|
|
169
|
+
"BlackboardEvent",
|
|
170
|
+
"EventStream",
|
|
171
|
+
"EventViewer",
|
|
172
|
+
"InMemoryEventStream",
|
|
173
|
+
"is_visible_to",
|
|
174
|
+
"PeerView",
|
|
175
|
+
"react_by_mention",
|
|
176
|
+
"director_driven",
|
|
177
|
+
"round_robin",
|
|
178
|
+
"first_non_empty",
|
|
179
|
+
"union",
|
|
180
|
+
"ReactiveSession",
|
|
181
|
+
"ReactivePeerSpec",
|
|
182
|
+
"Reaction",
|
|
183
|
+
"ReactorContext",
|
|
184
|
+
"ReactorTurn",
|
|
185
|
+
"read_recent_tool",
|
|
140
186
|
"ProviderReplay",
|
|
141
187
|
"ProviderReplay",
|
|
142
188
|
"ProviderReplay",
|