deepstrike 0.2.10__tar.gz → 0.2.12__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.10 → deepstrike-0.2.12}/Cargo.lock +5 -5
- {deepstrike-0.2.10 → deepstrike-0.2.12}/Cargo.toml +2 -2
- {deepstrike-0.2.10 → deepstrike-0.2.12}/PKG-INFO +39 -1
- {deepstrike-0.2.10 → deepstrike-0.2.12}/README.md +38 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/manager.rs +164 -20
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/pressure.rs +8 -1
- deepstrike-0.2.12/crates/deepstrike-core/src/context/renderer.rs +668 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/snapshot.rs +4 -1
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/governance/quota.rs +6 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/harness/eval_pipeline.rs +9 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/mm/handle.rs +9 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/orchestration/mod.rs +0 -3
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/orchestration/tournament.rs +3 -3
- deepstrike-0.2.10/crates/deepstrike-core/src/orchestration/workflow.rs → deepstrike-0.2.12/crates/deepstrike-core/src/orchestration/workflow/mod.rs +36 -0
- deepstrike-0.2.10/crates/deepstrike-core/src/scheduler/workflow_run.rs → deepstrike-0.2.12/crates/deepstrike-core/src/orchestration/workflow/run.rs +442 -15
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/runtime/kernel.rs +122 -4
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/scheduler/mod.rs +0 -1
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/scheduler/state_machine.rs +112 -8
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/scheduler/state_machine_tests.rs +178 -1
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/syscall/mod.rs +5 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/task.rs +1 -1
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-py/src/lib.rs +10 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/__init__.py +8 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/harness/harness.py +11 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/__init__.py +7 -5
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/anthropic.py +138 -15
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/base.py +86 -1
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/deepseek.py +21 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/gemini.py +33 -5
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/glm.py +21 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/kimi.py +21 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/minimax.py +8 -2
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/ollama.py +2 -2
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/openai.py +21 -2
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/qwen.py +42 -2
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/replay.py +2 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/stream.py +16 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/__init__.py +3 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/kernel_step.py +8 -0
- deepstrike-0.2.12/deepstrike/runtime/output_schema.py +123 -0
- deepstrike-0.2.12/deepstrike/runtime/reducers.py +72 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/runner.py +232 -27
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/session_repair.py +17 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/sub_agent_orchestrator.py +18 -3
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/types/agent.py +127 -22
- {deepstrike-0.2.10 → deepstrike-0.2.12}/pyproject.toml +1 -1
- deepstrike-0.2.10/crates/deepstrike-core/src/context/renderer.rs +0 -383
- deepstrike-0.2.10/crates/deepstrike-core/src/orchestration/executor.rs +0 -45
- deepstrike-0.2.10/crates/deepstrike-core/src/orchestration/gen_eval.rs +0 -153
- deepstrike-0.2.10/crates/deepstrike-core/src/orchestration/planner.rs +0 -47
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/Cargo.toml +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/compression.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/config.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/dashboard.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/mod.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/partitions.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/renewal.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/sections.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/skill_catalog.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/summarizer.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/task_state.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/text.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/context/token_engine.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/governance/audit.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/governance/constraint.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/governance/mod.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/governance/permission.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/governance/pipeline.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/governance/rate_limit.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/governance/sandbox.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/governance/tool_decision.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/governance/veto.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/harness/mod.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/lib.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/memory/curator.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/memory/durable.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/memory/extractor.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/memory/idle_pipeline.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/memory/mod.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/memory/runtime.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/memory/semantic.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/memory/session.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/memory/synthesis.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/memory/trace_analyzer.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/memory/working.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/mm/memory.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/mm/mod.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/orchestration/task_graph.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/proc/mod.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/runtime/event_log.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/runtime/mod.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/runtime/repair.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/runtime/replay.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/runtime/session.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/runtime/snapshot.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/scheduler/milestone.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/scheduler/policy.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/scheduler/rollback.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/scheduler/tcb.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/signals/attention.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/signals/mod.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/signals/queue.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/signals/router.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/agent.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/capability.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/contract.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/error.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/message.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/milestone.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/mod.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/model.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/policy.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/result.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/signal.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-core/src/types/skill.rs +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/crates/deepstrike-py/Cargo.toml +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/collaboration/__init__.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/collaboration/contract.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/collaboration/handoff.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/collaboration/harness.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/collaboration/modes.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/collaboration/pool.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/governance.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/harness/__init__.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/kernel/__init__.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/knowledge/__init__.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/knowledge/source.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/memory/__init__.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/memory/agent.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/memory/protocols.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/memory/working.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/providers/replay_validator.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/archive.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/credential_vault.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/execution_plane.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/filtered_plane.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/kernel_event_log.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/large_result_spool.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/mcp_proxy_plane.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/os_profile.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/os_snapshot.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/process_sandbox_plane.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/provider_replay.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/remote_vpc_plane.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/replay_sanitize.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/runtime/session_log.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/safety/__init__.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/safety/permissions.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/signals/__init__.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/signals/gateway.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/signals/scheduled.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/signals/types.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/skills/__init__.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/skills/registry.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/skills/watcher.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/tools/__init__.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/tools/builtin/__init__.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/tools/builtin/read_file.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/tools/execution.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/tools/registry.py +0 -0
- {deepstrike-0.2.10 → deepstrike-0.2.12}/deepstrike/types/__init__.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.12"
|
|
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.12"
|
|
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.12"
|
|
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.12"
|
|
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.12"
|
|
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.12"
|
|
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.12" }
|
|
13
13
|
notify = "6"
|
|
14
14
|
serde = { version = "1", features = ["derive"] }
|
|
15
15
|
serde_json = "1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepstrike
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.12
|
|
4
4
|
Requires-Dist: httpx>=0.27
|
|
5
5
|
Requires-Dist: pyyaml>=6.0
|
|
6
6
|
Requires-Dist: anyio>=4.0
|
|
@@ -150,6 +150,44 @@ Spool, page-out, signals, processes, budgets, and memory events land in `Session
|
|
|
150
150
|
|
|
151
151
|
---
|
|
152
152
|
|
|
153
|
+
## Dynamic workflows
|
|
154
|
+
|
|
155
|
+
Instead of planning **and** executing a hard task in one long context window, hand the kernel a declarative DAG and let it spawn a fresh-context sub-agent per node. The kernel owns the control flow (gate · budget · suspend-on-join · resume); your SDK runs the agents. See the [top-level overview](../README.md#the-six-harness-patterns-as-first-class-kernel-nodes) for the full pattern catalog.
|
|
156
|
+
|
|
157
|
+
```py
|
|
158
|
+
from deepstrike import WorkflowSpec, WorkflowNodeSpec
|
|
159
|
+
|
|
160
|
+
# One fresh-context verifier per rule (no inherited author context → can't rubber-stamp),
|
|
161
|
+
# then a skeptic that reviews their flags. The kernel spawns the 3 verifiers as one gated
|
|
162
|
+
# batch, suspends on the join, and runs the skeptic once they complete.
|
|
163
|
+
outcome = await runner.run_workflow(WorkflowSpec(nodes=[
|
|
164
|
+
WorkflowNodeSpec(task="Rule: money is integer cents — violated?", role="verify"),
|
|
165
|
+
WorkflowNodeSpec(task="Rule: all errors propagate — violated?", role="verify"),
|
|
166
|
+
WorkflowNodeSpec(task="Rule: timestamps are UTC — violated?", role="verify"),
|
|
167
|
+
WorkflowNodeSpec(task="Skeptic: which flags are real violations?", role="verify", depends_on=[0, 1, 2]),
|
|
168
|
+
]))
|
|
169
|
+
# => {"completed": ["wf-node0", …], "failed": []}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
A node's `kind` selects the control-flow shape; the same executor drives them all, every spawn passing the syscall gate:
|
|
173
|
+
|
|
174
|
+
| Node kind | Behavior |
|
|
175
|
+
|---|---|
|
|
176
|
+
| spawn (default) | Run the node's agent once |
|
|
177
|
+
| loop (`max_iters`) | Re-run until the agent signals it's done, capped at `max_iters` |
|
|
178
|
+
| classify (`branches`) | The classifier's result selects one branch; the rest are pruned |
|
|
179
|
+
| tournament (`entrants`) | Generate N entrants, then a pairwise-judge bracket to one winner |
|
|
180
|
+
| reduce (`reducer`) | **Tokenless host-compute** — a pure function (`dedupe_lines` / `merge_json_arrays` / `concat` / `count`, or your own via the `reducers` option) over the node's dependency outputs |
|
|
181
|
+
|
|
182
|
+
### 0.2.11 capabilities
|
|
183
|
+
|
|
184
|
+
- **Runtime fan-out** — give a node the `submit_workflow_nodes_tool` and its agent can append nodes to the live DAG mid-run (true loop-until-done; one verifier per claim it discovers). Recorded and replayed on `resume_workflow`.
|
|
185
|
+
- **Quarantine, no escape** — set `trust="quarantined"` on a node that reads untrusted content; it's denied write-capable isolation in-kernel, and any nodes it submits are coerced to quarantined too (no privilege escalation).
|
|
186
|
+
- **Structured output** — set `output_schema` on a node; the runner instructs the agent, validates the result against the JSON-Schema subset, and re-runs once with the errors on mismatch. A node that never conforms fails (its dependents starve).
|
|
187
|
+
- **Budget as signal** — with a `max_workflow_nodes` / `max_concurrent_subagents` quota installed, each spawned node's goal carries its remaining headroom so a coordinator can size its fan-out to fit.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
153
191
|
## Providers
|
|
154
192
|
|
|
155
193
|
Resource quotas are opt-in and flow through the same replayable kernel event ABI:
|
|
@@ -137,6 +137,44 @@ Spool, page-out, signals, processes, budgets, and memory events land in `Session
|
|
|
137
137
|
|
|
138
138
|
---
|
|
139
139
|
|
|
140
|
+
## Dynamic workflows
|
|
141
|
+
|
|
142
|
+
Instead of planning **and** executing a hard task in one long context window, hand the kernel a declarative DAG and let it spawn a fresh-context sub-agent per node. The kernel owns the control flow (gate · budget · suspend-on-join · resume); your SDK runs the agents. See the [top-level overview](../README.md#the-six-harness-patterns-as-first-class-kernel-nodes) for the full pattern catalog.
|
|
143
|
+
|
|
144
|
+
```py
|
|
145
|
+
from deepstrike import WorkflowSpec, WorkflowNodeSpec
|
|
146
|
+
|
|
147
|
+
# One fresh-context verifier per rule (no inherited author context → can't rubber-stamp),
|
|
148
|
+
# then a skeptic that reviews their flags. The kernel spawns the 3 verifiers as one gated
|
|
149
|
+
# batch, suspends on the join, and runs the skeptic once they complete.
|
|
150
|
+
outcome = await runner.run_workflow(WorkflowSpec(nodes=[
|
|
151
|
+
WorkflowNodeSpec(task="Rule: money is integer cents — violated?", role="verify"),
|
|
152
|
+
WorkflowNodeSpec(task="Rule: all errors propagate — violated?", role="verify"),
|
|
153
|
+
WorkflowNodeSpec(task="Rule: timestamps are UTC — violated?", role="verify"),
|
|
154
|
+
WorkflowNodeSpec(task="Skeptic: which flags are real violations?", role="verify", depends_on=[0, 1, 2]),
|
|
155
|
+
]))
|
|
156
|
+
# => {"completed": ["wf-node0", …], "failed": []}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
A node's `kind` selects the control-flow shape; the same executor drives them all, every spawn passing the syscall gate:
|
|
160
|
+
|
|
161
|
+
| Node kind | Behavior |
|
|
162
|
+
|---|---|
|
|
163
|
+
| spawn (default) | Run the node's agent once |
|
|
164
|
+
| loop (`max_iters`) | Re-run until the agent signals it's done, capped at `max_iters` |
|
|
165
|
+
| classify (`branches`) | The classifier's result selects one branch; the rest are pruned |
|
|
166
|
+
| tournament (`entrants`) | Generate N entrants, then a pairwise-judge bracket to one winner |
|
|
167
|
+
| reduce (`reducer`) | **Tokenless host-compute** — a pure function (`dedupe_lines` / `merge_json_arrays` / `concat` / `count`, or your own via the `reducers` option) over the node's dependency outputs |
|
|
168
|
+
|
|
169
|
+
### 0.2.11 capabilities
|
|
170
|
+
|
|
171
|
+
- **Runtime fan-out** — give a node the `submit_workflow_nodes_tool` and its agent can append nodes to the live DAG mid-run (true loop-until-done; one verifier per claim it discovers). Recorded and replayed on `resume_workflow`.
|
|
172
|
+
- **Quarantine, no escape** — set `trust="quarantined"` on a node that reads untrusted content; it's denied write-capable isolation in-kernel, and any nodes it submits are coerced to quarantined too (no privilege escalation).
|
|
173
|
+
- **Structured output** — set `output_schema` on a node; the runner instructs the agent, validates the result against the JSON-Schema subset, and re-runs once with the errors on mismatch. A node that never conforms fails (its dependents starve).
|
|
174
|
+
- **Budget as signal** — with a `max_workflow_nodes` / `max_concurrent_subagents` quota installed, each spawned node's goal carries its remaining headroom so a coordinator can size its fan-out to fit.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
140
178
|
## Providers
|
|
141
179
|
|
|
142
180
|
Resource quotas are opt-in and flow through the same replayable kernel event ABI:
|
|
@@ -59,6 +59,13 @@ pub struct ContextManager {
|
|
|
59
59
|
pub handles: HandleTable,
|
|
60
60
|
/// Monotonic allocator for [`HandleId`]s.
|
|
61
61
|
next_handle_id: HandleId,
|
|
62
|
+
|
|
63
|
+
/// P1-E: history length (message count) as of the last compaction/renewal. Messages below this
|
|
64
|
+
/// index are the **frozen prefix** — byte-stable until the next compaction — so the renderer can
|
|
65
|
+
/// hand providers a `frozen_prefix_len` for a long-lived deep cache breakpoint. 0 before any
|
|
66
|
+
/// compaction (no frozen region yet). Not snapshotted: on resume it resets to 0 and rebuilds at
|
|
67
|
+
/// the next compaction (graceful — only the deep-cache durability lapses, never correctness).
|
|
68
|
+
frozen_history_len: usize,
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
impl ContextManager {
|
|
@@ -84,6 +91,7 @@ impl ContextManager {
|
|
|
84
91
|
last_compact_ms: None,
|
|
85
92
|
handles: HandleTable::new(),
|
|
86
93
|
next_handle_id: 0,
|
|
94
|
+
frozen_history_len: 0,
|
|
87
95
|
}
|
|
88
96
|
}
|
|
89
97
|
|
|
@@ -113,16 +121,24 @@ impl ContextManager {
|
|
|
113
121
|
|
|
114
122
|
/// Recompute tool-result handle residency for Layer-4 read-time projection (call before
|
|
115
123
|
/// `render`). When pressure (`rho`) reaches `collapse_threshold`, all but the most recent
|
|
116
|
-
/// `preserve_recent_msgs` tool results are marked `Collapsed` (rendered as previews)
|
|
117
|
-
///
|
|
118
|
-
///
|
|
124
|
+
/// `preserve_recent_msgs` tool results are marked `Collapsed` (rendered as previews).
|
|
125
|
+
///
|
|
126
|
+
/// **Monotonic within a cache generation (P0-C):** collapse is one-way here —
|
|
127
|
+
/// `Resident → Collapsed` only, never the reverse. The old two-way version un-collapsed when
|
|
128
|
+
/// `rho` fell back below the threshold, which (a) rewrote mid-history bytes and invalidated the
|
|
129
|
+
/// prompt-cache prefix on every threshold oscillation, and (b) re-billed a full tool-result body
|
|
130
|
+
/// for near-zero attention gain (an old result that already faded). Un-collapsing now happens
|
|
131
|
+
/// only at compaction/renewal boundaries via [`Self::reset_collapse_generation`] — the one moment
|
|
132
|
+
/// the prefix is rewritten anyway, so the cache cost is already paid. Non-destructive:
|
|
133
|
+
/// `partitions` is untouched. Spooled/paged-out handles are left as-is.
|
|
119
134
|
pub fn recompute_handle_residency(&mut self) {
|
|
120
|
-
|
|
135
|
+
// Monotonic: below the threshold we never *un*-collapse, so there is nothing to do.
|
|
136
|
+
if self.rho() < self.config.collapse_threshold {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
121
139
|
let keep = self.config.preserve_recent_msgs;
|
|
122
|
-
// Single mutable pass in insertion order.
|
|
123
|
-
//
|
|
124
|
-
// long-session cliff. `tool_result_handles_mut().enumerate()` yields the same set in the
|
|
125
|
-
// same order, so `i`/`cutoff` are bit-identical to the old logic, now in O(handles).
|
|
140
|
+
// Single mutable pass in insertion order. `tool_result_handles_mut().enumerate()` yields the
|
|
141
|
+
// collapse candidates oldest-first; `i < cutoff` protects the most recent `keep` results.
|
|
126
142
|
let total = self
|
|
127
143
|
.handles
|
|
128
144
|
.all()
|
|
@@ -131,14 +147,23 @@ impl ContextManager {
|
|
|
131
147
|
.count();
|
|
132
148
|
let cutoff = total.saturating_sub(keep);
|
|
133
149
|
for (i, handle) in self.handles.tool_result_handles_mut().enumerate() {
|
|
134
|
-
// Only
|
|
135
|
-
//
|
|
136
|
-
if matches!(handle.residency, Residency::Resident
|
|
137
|
-
handle.residency =
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
150
|
+
// Only fold the reversible Resident → Collapsed axis; never clobber a handle that has
|
|
151
|
+
// been spooled or paged out, and never reverse an existing collapse mid-generation.
|
|
152
|
+
if i < cutoff && matches!(handle.residency, Residency::Resident) {
|
|
153
|
+
handle.residency = Residency::Collapsed;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Start a fresh collapse generation: un-collapse every `Collapsed` handle back to `Resident`.
|
|
159
|
+
/// Called only at compaction/renewal boundaries — the sole points where un-collapsing is
|
|
160
|
+
/// cache-free, since the rendered prefix is rewritten there regardless. Between boundaries
|
|
161
|
+
/// [`Self::recompute_handle_residency`] keeps collapse strictly one-way (P0-C). Spooled/paged-out
|
|
162
|
+
/// handles are untouched (they leave the Resident↔Collapsed cycle deliberately).
|
|
163
|
+
pub fn reset_collapse_generation(&mut self) {
|
|
164
|
+
for handle in self.handles.all_mut() {
|
|
165
|
+
if matches!(handle.residency, Residency::Collapsed) {
|
|
166
|
+
handle.residency = Residency::Resident;
|
|
142
167
|
}
|
|
143
168
|
}
|
|
144
169
|
}
|
|
@@ -257,6 +282,18 @@ impl ContextManager {
|
|
|
257
282
|
// Archived messages have left history — drop their now-orphaned handles (bounds the table).
|
|
258
283
|
if !result.2.is_empty() {
|
|
259
284
|
self.prune_orphaned_handles();
|
|
285
|
+
// Compaction rewrote the history prefix — start a fresh collapse generation so
|
|
286
|
+
// surviving handles re-evaluate from Resident (P0-C: the one cache-free un-collapse point).
|
|
287
|
+
self.reset_collapse_generation();
|
|
288
|
+
}
|
|
289
|
+
// P2-D × P1-E: re-anchor the frozen-prefix boundary only when the compaction actually broke
|
|
290
|
+
// the prompt-cache prefix (`result.3` = the planner's per-step `cache_at` cost, `Some` ⇒ a
|
|
291
|
+
// prefix break). A prefix-safe compaction (late Snip/Excerpt that touches no early message)
|
|
292
|
+
// leaves `[0..frozen]` byte-stable, so the deep cache survives the compaction and the boundary
|
|
293
|
+
// holds — strictly more precise than the old `archived`-keyed reset, which missed an early
|
|
294
|
+
// in-place Snip and needlessly re-anchored after a prefix-safe pass.
|
|
295
|
+
if result.3.is_some() {
|
|
296
|
+
self.frozen_history_len = self.partitions.history.messages.len();
|
|
260
297
|
}
|
|
261
298
|
|
|
262
299
|
result
|
|
@@ -269,6 +306,18 @@ impl ContextManager {
|
|
|
269
306
|
let result = self.compression.compress(&mut self.partitions, PressureAction::AutoCompact, self.max_tokens, 0, &self.engine);
|
|
270
307
|
if !result.2.is_empty() {
|
|
271
308
|
self.prune_orphaned_handles();
|
|
309
|
+
// Compaction rewrote the history prefix — start a fresh collapse generation so
|
|
310
|
+
// surviving handles re-evaluate from Resident (P0-C: the one cache-free un-collapse point).
|
|
311
|
+
self.reset_collapse_generation();
|
|
312
|
+
}
|
|
313
|
+
// P2-D × P1-E: re-anchor the frozen-prefix boundary only when the compaction actually broke
|
|
314
|
+
// the prompt-cache prefix (`result.3` = the planner's per-step `cache_at` cost, `Some` ⇒ a
|
|
315
|
+
// prefix break). A prefix-safe compaction (late Snip/Excerpt that touches no early message)
|
|
316
|
+
// leaves `[0..frozen]` byte-stable, so the deep cache survives the compaction and the boundary
|
|
317
|
+
// holds — strictly more precise than the old `archived`-keyed reset, which missed an early
|
|
318
|
+
// in-place Snip and needlessly re-anchored after a prefix-safe pass.
|
|
319
|
+
if result.3.is_some() {
|
|
320
|
+
self.frozen_history_len = self.partitions.history.messages.len();
|
|
272
321
|
}
|
|
273
322
|
result
|
|
274
323
|
}
|
|
@@ -295,6 +344,18 @@ impl ContextManager {
|
|
|
295
344
|
}
|
|
296
345
|
if !result.2.is_empty() {
|
|
297
346
|
self.prune_orphaned_handles();
|
|
347
|
+
// Compaction rewrote the history prefix — start a fresh collapse generation so
|
|
348
|
+
// surviving handles re-evaluate from Resident (P0-C: the one cache-free un-collapse point).
|
|
349
|
+
self.reset_collapse_generation();
|
|
350
|
+
}
|
|
351
|
+
// P2-D × P1-E: re-anchor the frozen-prefix boundary only when the compaction actually broke
|
|
352
|
+
// the prompt-cache prefix (`result.3` = the planner's per-step `cache_at` cost, `Some` ⇒ a
|
|
353
|
+
// prefix break). A prefix-safe compaction (late Snip/Excerpt that touches no early message)
|
|
354
|
+
// leaves `[0..frozen]` byte-stable, so the deep cache survives the compaction and the boundary
|
|
355
|
+
// holds — strictly more precise than the old `archived`-keyed reset, which missed an early
|
|
356
|
+
// in-place Snip and needlessly re-anchored after a prefix-safe pass.
|
|
357
|
+
if result.3.is_some() {
|
|
358
|
+
self.frozen_history_len = self.partitions.history.messages.len();
|
|
298
359
|
}
|
|
299
360
|
result
|
|
300
361
|
}
|
|
@@ -321,8 +382,12 @@ impl ContextManager {
|
|
|
321
382
|
self.partitions = renewed;
|
|
322
383
|
self.last_handoff = Some(artifact);
|
|
323
384
|
self.sprint += 1;
|
|
324
|
-
// History was rebuilt wholesale — drop handles anchored to messages it no longer carries
|
|
385
|
+
// History was rebuilt wholesale — drop handles anchored to messages it no longer carries,
|
|
386
|
+
// and start a fresh collapse generation (P0-C) since the whole prefix changed.
|
|
325
387
|
self.prune_orphaned_handles();
|
|
388
|
+
self.reset_collapse_generation();
|
|
389
|
+
// P1-E: the renewed history is the new frozen base.
|
|
390
|
+
self.frozen_history_len = self.partitions.history.messages.len();
|
|
326
391
|
}
|
|
327
392
|
|
|
328
393
|
// ── Render ────────────────────────────────────────────────────────────────
|
|
@@ -334,6 +399,7 @@ impl ContextManager {
|
|
|
334
399
|
&self.engine,
|
|
335
400
|
self.config.preserve_recent_msgs,
|
|
336
401
|
&self.handles,
|
|
402
|
+
self.frozen_history_len,
|
|
337
403
|
)
|
|
338
404
|
}
|
|
339
405
|
|
|
@@ -586,12 +652,14 @@ mod tests {
|
|
|
586
652
|
}
|
|
587
653
|
|
|
588
654
|
#[test]
|
|
589
|
-
fn
|
|
655
|
+
fn render_includes_task_state_in_state_turn_not_system() {
|
|
590
656
|
let mut mgr = ContextManager::new(10_000);
|
|
591
657
|
mgr.init_task("find anomalies".to_string(), vec![]);
|
|
592
658
|
let rc = mgr.render();
|
|
593
659
|
assert!(!rc.system_text.contains("[TASK STATE]"), "task_state must not be in system_text");
|
|
594
|
-
|
|
660
|
+
// State turn is separated from the cacheable history (turns).
|
|
661
|
+
let state = rc.state_turn.as_ref().expect("should have a state turn");
|
|
662
|
+
assert!(state.content.as_text().unwrap().contains("[TASK STATE] goal: find anomalies"));
|
|
595
663
|
}
|
|
596
664
|
|
|
597
665
|
#[test]
|
|
@@ -744,12 +812,88 @@ mod tests {
|
|
|
744
812
|
assert_eq!(mgr.handles.residency_for_source("c0"), Some(&Residency::Collapsed));
|
|
745
813
|
assert_eq!(mgr.handles.residency_for_source("c9"), Some(&Residency::Resident));
|
|
746
814
|
|
|
747
|
-
//
|
|
815
|
+
// P0-C — monotonic within a generation: once collapsed, dropping pressure does NOT
|
|
816
|
+
// un-collapse (un-collapsing would re-bill the body and churn the cache prefix).
|
|
748
817
|
mgr.set_observed_prompt_tokens(100); // 0.10 < 0.90
|
|
749
818
|
mgr.recompute_handle_residency();
|
|
819
|
+
assert_eq!(
|
|
820
|
+
mgr.handles.residency_for_source("c0"),
|
|
821
|
+
Some(&Residency::Collapsed),
|
|
822
|
+
"collapse is sticky until a compaction boundary"
|
|
823
|
+
);
|
|
824
|
+
|
|
825
|
+
// Only a generation reset (compaction/renewal) un-collapses.
|
|
826
|
+
mgr.reset_collapse_generation();
|
|
750
827
|
assert_eq!(mgr.handles.residency_for_source("c0"), Some(&Residency::Resident));
|
|
751
828
|
}
|
|
752
829
|
|
|
830
|
+
#[test]
|
|
831
|
+
fn frozen_prefix_len_anchors_at_compaction_and_holds_across_appends() {
|
|
832
|
+
let mut mgr = ContextManager::new(1_000);
|
|
833
|
+
// Pre-compaction: no frozen region yet → providers use the rolling-pair fallback.
|
|
834
|
+
for i in 0..30 {
|
|
835
|
+
mgr.push_history(Message::user(format!("turn {i}: {}", "ctx ".repeat(30))), 150);
|
|
836
|
+
}
|
|
837
|
+
assert!(mgr.render().frozen_prefix_len.is_none(), "no frozen region before any compaction");
|
|
838
|
+
|
|
839
|
+
let (saved, _, archived, _) = mgr.compress(PressureAction::AutoCompact);
|
|
840
|
+
assert!(saved > 0 && !archived.is_empty(), "expected archival");
|
|
841
|
+
|
|
842
|
+
// Immediately after compaction the hot tail is empty → deep would coincide with the tail → None.
|
|
843
|
+
assert!(mgr.render().frozen_prefix_len.is_none(), "deep == tail right after compaction");
|
|
844
|
+
|
|
845
|
+
// As turns are appended, the deep boundary holds fixed while the tail grows.
|
|
846
|
+
mgr.push_history(Message::user("new 1"), 5);
|
|
847
|
+
let f1 = mgr.render().frozen_prefix_len.expect("frozen region exists once the tail grows");
|
|
848
|
+
mgr.push_history(Message::assistant("reply 1"), 5);
|
|
849
|
+
mgr.push_history(Message::user("new 2"), 5);
|
|
850
|
+
let rc = mgr.render();
|
|
851
|
+
let f2 = rc.frozen_prefix_len.expect("frozen region holds");
|
|
852
|
+
assert_eq!(f1, f2, "the deep boundary is fixed between compactions; only the tail grows");
|
|
853
|
+
assert!(f2 < rc.turns.len(), "deep boundary is distinct from the rolling tail");
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
#[test]
|
|
857
|
+
fn frozen_boundary_holds_through_a_prefix_safe_compaction() {
|
|
858
|
+
// P2-D × P1-E: the boundary re-anchors on a prefix-breaking compaction (cache_at = Some) but
|
|
859
|
+
// is preserved through a prefix-safe one (cache_at = None) — the deep cache survives.
|
|
860
|
+
let mut mgr = ContextManager::new(10_000);
|
|
861
|
+
for i in 0..5 {
|
|
862
|
+
mgr.push_history(Message::user(format!("m{i}")), 5);
|
|
863
|
+
}
|
|
864
|
+
mgr.frozen_history_len = 3; // pretend a prior compaction anchored the deep cache here
|
|
865
|
+
|
|
866
|
+
// A no-op / prefix-safe compaction (PressureAction::None ⇒ cache_at None) must NOT move the
|
|
867
|
+
// anchor — the cached [0..3] prefix is untouched, so the deep breakpoint stays put.
|
|
868
|
+
let (_, _, _, cache_at) = mgr.compress(PressureAction::None);
|
|
869
|
+
assert!(cache_at.is_none(), "no-op compaction is prefix-safe");
|
|
870
|
+
assert_eq!(mgr.frozen_history_len, 3, "prefix-safe compaction preserves the deep-cache anchor");
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
#[test]
|
|
874
|
+
fn collapse_generation_resets_on_autocompact() {
|
|
875
|
+
let mut mgr = ContextManager::new(1_000);
|
|
876
|
+
// Many oversized tool results: some will be archived by AutoCompact, the survivors
|
|
877
|
+
// should come back Resident (fresh generation), not stay stuck Collapsed.
|
|
878
|
+
for i in 0..20 {
|
|
879
|
+
mgr.push_history(tool_result_msg(&format!("c{i}"), &"x".repeat(120)), 60);
|
|
880
|
+
}
|
|
881
|
+
mgr.set_observed_prompt_tokens(980); // force collapse of the older results
|
|
882
|
+
mgr.recompute_handle_residency();
|
|
883
|
+
assert_eq!(mgr.handles.residency_for_source("c0"), Some(&Residency::Collapsed));
|
|
884
|
+
|
|
885
|
+
let (saved, _, archived, _) = mgr.compress(PressureAction::AutoCompact);
|
|
886
|
+
assert!(saved > 0 && !archived.is_empty(), "expected archival");
|
|
887
|
+
|
|
888
|
+
// Every surviving tool-result handle is Resident again — the compaction boundary
|
|
889
|
+
// rewrote the prefix, so the next pressure cycle re-decides from scratch.
|
|
890
|
+
for h in mgr.handles.all() {
|
|
891
|
+
if matches!(h.kind, HandleKind::ToolResult) {
|
|
892
|
+
assert_eq!(h.residency, Residency::Resident, "generation reset un-collapses survivors");
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
753
897
|
#[test]
|
|
754
898
|
fn mark_spooled_sets_residency_and_survives_residency_recompute() {
|
|
755
899
|
let mut mgr = ContextManager::new(1_000);
|
|
@@ -2,7 +2,14 @@ use super::config::ContextConfig;
|
|
|
2
2
|
use super::partitions::ContextPartitions;
|
|
3
3
|
use super::token_engine::ContextTokenEngine;
|
|
4
4
|
|
|
5
|
-
/// Action recommended by the pressure monitor.
|
|
5
|
+
/// Action recommended by the pressure monitor — the *pressure-level* vocabulary.
|
|
6
|
+
///
|
|
7
|
+
/// This is distinct from [`crate::mm::EvictionOp`] (the *planner-op* vocabulary) and the two must
|
|
8
|
+
/// **not** be collapsed: `PressureAction` is what [`PressureMonitor::recommend`] /
|
|
9
|
+
/// `ContextManager::should_compress` return, the `Ord`-keyed cascade selector inside the compression
|
|
10
|
+
/// pipeline, and the canonical compaction wire label. `EvictionOp` is what `plan_eviction` emits, and
|
|
11
|
+
/// carries per-op data this enum has no place for. The single bridge between the two layers is
|
|
12
|
+
/// `LoopStateMachine::execute_eviction_op`. See the layer-boundary note on [`crate::mm::EvictionOp`].
|
|
6
13
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
7
14
|
pub enum PressureAction {
|
|
8
15
|
None,
|