deepstrike 0.2.35__tar.gz → 0.2.36__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.35 → deepstrike-0.2.36}/Cargo.lock +5 -5
- {deepstrike-0.2.35 → deepstrike-0.2.36}/Cargo.toml +2 -2
- {deepstrike-0.2.35 → deepstrike-0.2.36}/PKG-INFO +4 -4
- {deepstrike-0.2.35 → deepstrike-0.2.36}/README.md +3 -3
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/config.rs +11 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/manager.rs +237 -11
- deepstrike-0.2.36/crates/deepstrike-core/src/context/partitions.rs +339 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/renderer.rs +1 -2
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/renewal.rs +5 -4
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/governance/mod.rs +1 -0
- deepstrike-0.2.36/crates/deepstrike-core/src/governance/repeat_fuse.rs +48 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/mm/mod.rs +11 -49
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/proc/mod.rs +1 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/runtime/kernel.rs +331 -25
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/runtime/snapshot.rs +87 -1
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/state_machine/eviction.rs +31 -16
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/state_machine/gate.rs +91 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/state_machine/mod.rs +100 -4
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/state_machine/signal.rs +2 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/state_machine/tests.rs +269 -14
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/result.rs +5 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/large_result_spool.py +20 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/runner.py +385 -91
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/sub_agent_orchestrator.py +4 -3
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/types/agent.py +10 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/pyproject.toml +1 -1
- deepstrike-0.2.35/crates/deepstrike-core/src/context/partitions.rs +0 -127
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/Cargo.toml +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/compression.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/dashboard.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/mod.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/pressure.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/sections.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/skill_catalog.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/snapshot.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/summarizer.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/task_state.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/text.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/context/token_engine.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/governance/audit.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/governance/constraint.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/governance/permission.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/governance/pipeline.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/governance/quota.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/governance/rate_limit.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/governance/sandbox.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/governance/tool_decision.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/governance/veto.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/harness/eval.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/harness/mod.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/lib.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/memory/curator.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/memory/durable.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/memory/extractor.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/memory/idle_pipeline.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/memory/mod.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/memory/runtime.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/memory/semantic.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/memory/session.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/memory/synthesis.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/memory/trace_analyzer.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/memory/working.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/mm/handle.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/mm/memory.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/orchestration/mod.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/orchestration/task_graph.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/orchestration/tournament.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/orchestration/workflow/mod.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/orchestration/workflow/run.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/runtime/event_log.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/runtime/mod.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/runtime/repair.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/runtime/replay.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/runtime/session.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/milestone.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/mod.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/policy.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/rollback.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/state_machine/capability.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/state_machine/milestone_exec.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/state_machine/process.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/state_machine/workflow.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/scheduler/tcb.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/signals/attention.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/signals/mod.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/signals/queue.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/signals/router.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/syscall/mod.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/agent.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/capability.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/contract.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/error.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/message.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/milestone.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/mod.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/model.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/policy.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/signal.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/skill.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-core/src/types/task.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-py/Cargo.toml +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/crates/deepstrike-py/src/lib.rs +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/collaboration/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/collaboration/contract.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/collaboration/handoff.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/collaboration/harness.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/collaboration/modes.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/collaboration/pool.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/governance.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/harness/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/harness/harness.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/harness/judge.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/kernel/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/knowledge/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/knowledge/source.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/memory/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/memory/agent.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/memory/in_memory_store.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/memory/protocols.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/memory/working.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/anthropic.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/anthropic_compatible.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/base.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/deepseek.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/factories.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/gemini.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/glm.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/kimi.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/minimax.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/ollama.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/openai.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/openai_responses.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/qwen.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/replay.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/replay_validator.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/stream.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/providers/vendor_profiles.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/archive.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/credential_vault.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/eval.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/event_stream.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/execution_plane.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/facade.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/filtered_plane.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/kernel_event_log.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/kernel_step.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/mcp_proxy_plane.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/os_profile.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/os_snapshot.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/output_schema.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/process_sandbox_plane.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/provider_replay.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/reactive_session.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/reducers.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/remote_vpc_plane.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/replay_fixture.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/replay_provider.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/replay_sanitize.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/run_group.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/session_log.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/session_repair.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/turn_policy.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/workflow_control_flow.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/workflow_store.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/runtime/worktree_plane.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/safety/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/safety/permissions.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/signals/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/signals/gateway.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/signals/scheduled.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/signals/types.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/skills/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/skills/registry.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/skills/watcher.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/tools/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/tools/builtin/__init__.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/tools/builtin/read_file.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/tools/errors.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/tools/execution.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/deepstrike/tools/registry.py +0 -0
- {deepstrike-0.2.35 → deepstrike-0.2.36}/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.36"
|
|
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.36"
|
|
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.36"
|
|
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.36"
|
|
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.36"
|
|
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.36"
|
|
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.36" }
|
|
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.36
|
|
4
4
|
Requires-Dist: httpx>=0.27
|
|
5
5
|
Requires-Dist: pyyaml>=6.0
|
|
6
6
|
Requires-Dist: anyio>=4.0
|
|
@@ -149,7 +149,7 @@ The mechanisms above are not internal refactors — they change what you can bui
|
|
|
149
149
|
Tool calls, spawns, compression, and signals pass through one kernel gate with an explicit lifecycle (Ready / Running / Blocked / Suspended). You implement I/O; the kernel decides *when* and *whether*. Node, Python, and Rust share the same decision path, so `wake(session_id)` and cross-language tooling see consistent behavior.
|
|
150
150
|
|
|
151
151
|
**Longer, sturdier sessions (Layer-1 spool + semantic page-out)**
|
|
152
|
-
Oversized tool results (> 50 KB) stay in context as a preview plus a `.spool/` reference — the model reads the full payload on demand via ordinary file tools. When pressure triggers semantic eviction, the SDK summarizes archived content into `DreamStore
|
|
152
|
+
Oversized tool results (> 50 KB) stay in context as a preview plus a `.spool/` reference — the model reads the full payload on demand via ordinary file tools. When pressure triggers semantic eviction, the SDK summarizes archived content into `DreamStore`. Long tasks survive token pressure instead of failing mid-run.
|
|
153
153
|
|
|
154
154
|
**Safety and governance by default (OS native profile)**
|
|
155
155
|
Every run loads declarative `governance_policy` (deny / ask_user / rate-limit / param rules) and in-kernel signal routing (`attention_policy`, default queue 64). Dangerous tools, external interrupts, and approval flows are policy — not ad-hoc checks in your handlers.
|
|
@@ -443,9 +443,9 @@ effort: 1
|
|
|
443
443
|
|
|
444
444
|
## Knowledge
|
|
445
445
|
|
|
446
|
-
Implement `KnowledgeSource` — the kernel injects a `knowledge` meta-tool. Runtime retrieval results land in **history** as tool results. Use `initial_memory` for durable preload into Slot 2.
|
|
446
|
+
Implement `KnowledgeSource` — the kernel injects a `knowledge` meta-tool. Runtime retrieval results land in **history** as tool results (single-use fact content that decays with compaction), not in the durable `knowledge` partition. Use `initial_memory` for durable preload into Slot 2.
|
|
447
447
|
|
|
448
|
-
|
|
448
|
+
For durable content at runtime use `runner.push_knowledge(content, key=..., pinned=...)` — a **keyed** entry upserts on a repeated key and `runner.remove_knowledge(key)` removes it, both applied at the next compaction/renewal boundary. `knowledge_budget_ratio` (default 0.25, 0 disables) caps the partition: over budget, the oldest unpinned, non-skill entries are evicted at boundaries while `pinned=True` entries survive. A loaded skill's body is pinned here as `skill:<name>` and unpinned by `runner.deactivate_skill(name)` or a `skill_lease_turns` expiry.
|
|
449
449
|
|
|
450
450
|
```python
|
|
451
451
|
from deepstrike import KnowledgeSource
|
|
@@ -136,7 +136,7 @@ The mechanisms above are not internal refactors — they change what you can bui
|
|
|
136
136
|
Tool calls, spawns, compression, and signals pass through one kernel gate with an explicit lifecycle (Ready / Running / Blocked / Suspended). You implement I/O; the kernel decides *when* and *whether*. Node, Python, and Rust share the same decision path, so `wake(session_id)` and cross-language tooling see consistent behavior.
|
|
137
137
|
|
|
138
138
|
**Longer, sturdier sessions (Layer-1 spool + semantic page-out)**
|
|
139
|
-
Oversized tool results (> 50 KB) stay in context as a preview plus a `.spool/` reference — the model reads the full payload on demand via ordinary file tools. When pressure triggers semantic eviction, the SDK summarizes archived content into `DreamStore
|
|
139
|
+
Oversized tool results (> 50 KB) stay in context as a preview plus a `.spool/` reference — the model reads the full payload on demand via ordinary file tools. When pressure triggers semantic eviction, the SDK summarizes archived content into `DreamStore`. Long tasks survive token pressure instead of failing mid-run.
|
|
140
140
|
|
|
141
141
|
**Safety and governance by default (OS native profile)**
|
|
142
142
|
Every run loads declarative `governance_policy` (deny / ask_user / rate-limit / param rules) and in-kernel signal routing (`attention_policy`, default queue 64). Dangerous tools, external interrupts, and approval flows are policy — not ad-hoc checks in your handlers.
|
|
@@ -430,9 +430,9 @@ effort: 1
|
|
|
430
430
|
|
|
431
431
|
## Knowledge
|
|
432
432
|
|
|
433
|
-
Implement `KnowledgeSource` — the kernel injects a `knowledge` meta-tool. Runtime retrieval results land in **history** as tool results. Use `initial_memory` for durable preload into Slot 2.
|
|
433
|
+
Implement `KnowledgeSource` — the kernel injects a `knowledge` meta-tool. Runtime retrieval results land in **history** as tool results (single-use fact content that decays with compaction), not in the durable `knowledge` partition. Use `initial_memory` for durable preload into Slot 2.
|
|
434
434
|
|
|
435
|
-
|
|
435
|
+
For durable content at runtime use `runner.push_knowledge(content, key=..., pinned=...)` — a **keyed** entry upserts on a repeated key and `runner.remove_knowledge(key)` removes it, both applied at the next compaction/renewal boundary. `knowledge_budget_ratio` (default 0.25, 0 disables) caps the partition: over budget, the oldest unpinned, non-skill entries are evicted at boundaries while `pinned=True` entries survive. A loaded skill's body is pinned here as `skill:<name>` and unpinned by `runner.deactivate_skill(name)` or a `skill_lease_turns` expiry.
|
|
436
436
|
|
|
437
437
|
```python
|
|
438
438
|
from deepstrike import KnowledgeSource
|
|
@@ -83,6 +83,16 @@ pub struct ContextConfig {
|
|
|
83
83
|
|
|
84
84
|
/// Preview byte budget kept in context when a tool result is spooled. Default: 2 KiB.
|
|
85
85
|
pub spool_preview_bytes: u32,
|
|
86
|
+
|
|
87
|
+
// ── K2: knowledge budget ─────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
/// Max share of `max_tokens` the knowledge partition may occupy. Exceeding it emits a
|
|
90
|
+
/// `KnowledgeBudgetExceeded` observation (once per cache generation) and marks the OLDEST
|
|
91
|
+
/// unpinned, non-skill entries for eviction at the next compaction/renewal boundary until the
|
|
92
|
+
/// projected usage fits. Pinned entries and `skill:`-keyed pins are never budget-evicted
|
|
93
|
+
/// (skills are governed by deactivation/lease, not the budget). `0.0` disables (no cap).
|
|
94
|
+
/// Default: 0.25.
|
|
95
|
+
pub knowledge_budget_ratio: f64,
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
fn default_micro_compact_idle_minutes() -> u32 {
|
|
@@ -119,6 +129,7 @@ impl Default for ContextConfig {
|
|
|
119
129
|
autocompact_buffer: 13_000,
|
|
120
130
|
spool_threshold_bytes: 50 * 1024,
|
|
121
131
|
spool_preview_bytes: 2 * 1024,
|
|
132
|
+
knowledge_budget_ratio: 0.25,
|
|
122
133
|
}
|
|
123
134
|
}
|
|
124
135
|
}
|
|
@@ -17,6 +17,8 @@ use compact_str::CompactString;
|
|
|
17
17
|
|
|
18
18
|
pub const MEMORY_TOOL_NAME: &str = "memory";
|
|
19
19
|
pub const KNOWLEDGE_TOOL_NAME: &str = "knowledge";
|
|
20
|
+
/// O7: the evicted-result re-fetch meta-tool (see `read_result_tool_schema`).
|
|
21
|
+
pub const READ_RESULT_TOOL_NAME: &str = "read_result";
|
|
20
22
|
|
|
21
23
|
/// Control-plane meta-tools: kernel-handled tools that drive state/capabilities rather than do task
|
|
22
24
|
/// work. Excluded from the `recent_actions` progress log (2b) so the footer reflects real progress.
|
|
@@ -25,11 +27,14 @@ const META_TOOL_NAMES: &[&str] = &[
|
|
|
25
27
|
"skill",
|
|
26
28
|
MEMORY_TOOL_NAME,
|
|
27
29
|
KNOWLEDGE_TOOL_NAME,
|
|
30
|
+
READ_RESULT_TOOL_NAME,
|
|
28
31
|
"submit_workflow_nodes",
|
|
29
32
|
"start_workflow",
|
|
30
33
|
];
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
/// Control-plane meta-tools are noise, not task progress — filtered out of the recency log (2b)
|
|
36
|
+
/// and out of the O6 repeat-fuse signature (the two must agree on what "an action" is).
|
|
37
|
+
pub(crate) fn is_meta_tool(name: &str) -> bool {
|
|
33
38
|
META_TOOL_NAMES.contains(&name)
|
|
34
39
|
}
|
|
35
40
|
|
|
@@ -46,11 +51,14 @@ pub struct ContextManager {
|
|
|
46
51
|
pub sprint: u32,
|
|
47
52
|
pub last_handoff: Option<HandoffArtifact>,
|
|
48
53
|
pub skills: SkillCatalog,
|
|
49
|
-
/// P1-B tool gating: the
|
|
50
|
-
///
|
|
51
|
-
///
|
|
52
|
-
///
|
|
53
|
-
|
|
54
|
+
/// P1-B tool gating: the skills the model has loaded this session (by name), each with an
|
|
55
|
+
/// optional lease expiry turn (K3: `None` = permanent, today's default). Their declared
|
|
56
|
+
/// `allowed_tools` are unioned to narrow the exposed toolset in `emit_call_llm`. A map (not a
|
|
57
|
+
/// single value) because the model may load several skills and still needs each one's tools
|
|
58
|
+
/// (D1). K3 adds eviction: explicit `deactivate_skill` or lease expiry — both also unpin the
|
|
59
|
+
/// skill's `skill:<name>` knowledge entry (boundary-swept). NOT snapshotted — rebuilt on wake
|
|
60
|
+
/// by replaying `skill` tool calls (graceful).
|
|
61
|
+
pub active_skills: std::collections::BTreeMap<CompactString, Option<u32>>,
|
|
54
62
|
/// P1-B/D stable-core: tool ids that stay exposed even when a skill narrows the toolset (the
|
|
55
63
|
/// "everyone uses these" set — read/search/bash etc.). Configured once by the SDK; empty by
|
|
56
64
|
/// default (铁律: no config ⇒ skills narrow to exactly their declared tools + meta-tools).
|
|
@@ -90,6 +98,14 @@ pub struct ContextManager {
|
|
|
90
98
|
/// compaction (no frozen region yet). Not snapshotted: on resume it resets to 0 and rebuilds at
|
|
91
99
|
/// the next compaction (graceful — only the deep-cache durability lapses, never correctness).
|
|
92
100
|
frozen_history_len: usize,
|
|
101
|
+
|
|
102
|
+
/// K1: boundary-sweep results awaiting drain into `KnowledgeSwept` observations. Not
|
|
103
|
+
/// snapshotted (observation-only bookkeeping, same class as `frozen_history_len`).
|
|
104
|
+
pending_knowledge_sweeps: Vec<crate::context::partitions::KnowledgeSweep>,
|
|
105
|
+
|
|
106
|
+
/// K2: whether the budget warning already fired this cache generation (warn-once; reset by
|
|
107
|
+
/// the boundary sweep). Not snapshotted — a resume re-warns at most once, harmless.
|
|
108
|
+
knowledge_budget_warned: bool,
|
|
93
109
|
}
|
|
94
110
|
|
|
95
111
|
impl ContextManager {
|
|
@@ -106,7 +122,7 @@ impl ContextManager {
|
|
|
106
122
|
partitions, max_tokens, config, engine,
|
|
107
123
|
sprint: 0, last_handoff: None,
|
|
108
124
|
skills: SkillCatalog::new(),
|
|
109
|
-
active_skills: std::collections::
|
|
125
|
+
active_skills: std::collections::BTreeMap::new(),
|
|
110
126
|
stable_core_tools: std::collections::HashSet::new(),
|
|
111
127
|
capabilities: CapabilityManifest::new(),
|
|
112
128
|
sections: ContextSectionRegistry::default_agent_sections(),
|
|
@@ -118,6 +134,8 @@ impl ContextManager {
|
|
|
118
134
|
handles: HandleTable::new(),
|
|
119
135
|
next_handle_id: 0,
|
|
120
136
|
frozen_history_len: 0,
|
|
137
|
+
pending_knowledge_sweeps: Vec::new(),
|
|
138
|
+
knowledge_budget_warned: false,
|
|
121
139
|
}
|
|
122
140
|
}
|
|
123
141
|
|
|
@@ -311,6 +329,9 @@ impl ContextManager {
|
|
|
311
329
|
// Compaction rewrote the history prefix — start a fresh collapse generation so
|
|
312
330
|
// surviving handles re-evaluate from Resident (P0-C: the one cache-free un-collapse point).
|
|
313
331
|
self.reset_collapse_generation();
|
|
332
|
+
// K1: the prompt-cache prefix is being rebuilt anyway — the one cache-free moment to
|
|
333
|
+
// apply deferred knowledge upserts/removals (rewriting system[1] bytes).
|
|
334
|
+
self.sweep_knowledge_at_boundary();
|
|
314
335
|
}
|
|
315
336
|
// P2-D × P1-E: re-anchor the frozen-prefix boundary only when the compaction actually broke
|
|
316
337
|
// the prompt-cache prefix (`result.3` = the planner's per-step `cache_at` cost, `Some` ⇒ a
|
|
@@ -335,6 +356,9 @@ impl ContextManager {
|
|
|
335
356
|
// Compaction rewrote the history prefix — start a fresh collapse generation so
|
|
336
357
|
// surviving handles re-evaluate from Resident (P0-C: the one cache-free un-collapse point).
|
|
337
358
|
self.reset_collapse_generation();
|
|
359
|
+
// K1: the prompt-cache prefix is being rebuilt anyway — the one cache-free moment to
|
|
360
|
+
// apply deferred knowledge upserts/removals (rewriting system[1] bytes).
|
|
361
|
+
self.sweep_knowledge_at_boundary();
|
|
338
362
|
}
|
|
339
363
|
// P2-D × P1-E: re-anchor the frozen-prefix boundary only when the compaction actually broke
|
|
340
364
|
// the prompt-cache prefix (`result.3` = the planner's per-step `cache_at` cost, `Some` ⇒ a
|
|
@@ -373,6 +397,9 @@ impl ContextManager {
|
|
|
373
397
|
// Compaction rewrote the history prefix — start a fresh collapse generation so
|
|
374
398
|
// surviving handles re-evaluate from Resident (P0-C: the one cache-free un-collapse point).
|
|
375
399
|
self.reset_collapse_generation();
|
|
400
|
+
// K1: the prompt-cache prefix is being rebuilt anyway — the one cache-free moment to
|
|
401
|
+
// apply deferred knowledge upserts/removals (rewriting system[1] bytes).
|
|
402
|
+
self.sweep_knowledge_at_boundary();
|
|
376
403
|
}
|
|
377
404
|
// P2-D × P1-E: re-anchor the frozen-prefix boundary only when the compaction actually broke
|
|
378
405
|
// the prompt-cache prefix (`result.3` = the planner's per-step `cache_at` cost, `Some` ⇒ a
|
|
@@ -412,6 +439,8 @@ impl ContextManager {
|
|
|
412
439
|
// and start a fresh collapse generation (P0-C) since the whole prefix changed.
|
|
413
440
|
self.prune_orphaned_handles();
|
|
414
441
|
self.reset_collapse_generation();
|
|
442
|
+
// K1: renewal is a boundary — apply deferred knowledge upserts/removals now.
|
|
443
|
+
self.sweep_knowledge_at_boundary();
|
|
415
444
|
// P1-E: the renewed history is the new frozen base.
|
|
416
445
|
self.frozen_history_len = self.partitions.history.messages.len();
|
|
417
446
|
}
|
|
@@ -438,7 +467,7 @@ impl ContextManager {
|
|
|
438
467
|
ContextSnapshot {
|
|
439
468
|
turn,
|
|
440
469
|
system_messages: self.partitions.system.messages.clone(),
|
|
441
|
-
knowledge_messages: self.partitions.knowledge.messages.
|
|
470
|
+
knowledge_messages: self.partitions.knowledge.messages().cloned().collect(),
|
|
442
471
|
history_messages: self.partitions.history.messages.clone(),
|
|
443
472
|
task_state: self.partitions.task_state.clone(),
|
|
444
473
|
}
|
|
@@ -478,6 +507,83 @@ impl ContextManager {
|
|
|
478
507
|
self.partitions.knowledge.push(msg, tokens);
|
|
479
508
|
}
|
|
480
509
|
|
|
510
|
+
/// K1: keyed knowledge push — fresh key appends immediately (cache-cheap direction), an
|
|
511
|
+
/// existing key stages a boundary-deferred upsert. `pinned` entries are exempt from the
|
|
512
|
+
/// K2 budget sweep.
|
|
513
|
+
pub fn push_knowledge_entry(
|
|
514
|
+
&mut self,
|
|
515
|
+
key: Option<CompactString>,
|
|
516
|
+
msg: Message,
|
|
517
|
+
tokens: u32,
|
|
518
|
+
pinned: bool,
|
|
519
|
+
) {
|
|
520
|
+
self.partitions.knowledge.push_entry(key, msg, tokens, pinned);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/// K1: mark a keyed knowledge entry for removal at the next compaction/renewal boundary.
|
|
524
|
+
/// Errs-open: unknown key is a no-op (returns false).
|
|
525
|
+
pub fn remove_knowledge(&mut self, key: &str) -> bool {
|
|
526
|
+
self.partitions.knowledge.remove(key)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/// K1: run the boundary sweep (apply pending upserts, drop marked entries) and stash the
|
|
530
|
+
/// result for the state machine to drain into a `KnowledgeSwept` observation. Called only
|
|
531
|
+
/// from the compaction/renewal boundary blocks — the one place system[1] bytes may change.
|
|
532
|
+
fn sweep_knowledge_at_boundary(&mut self) {
|
|
533
|
+
let sweep = self.partitions.knowledge.sweep_at_boundary();
|
|
534
|
+
if sweep.changed {
|
|
535
|
+
self.pending_knowledge_sweeps.push(sweep);
|
|
536
|
+
}
|
|
537
|
+
// K2: a boundary starts a fresh cache generation — the budget warning may fire again.
|
|
538
|
+
self.knowledge_budget_warned = false;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/// K2: knowledge-budget check, run per turn before render. Over budget ⇒ mark the OLDEST
|
|
542
|
+
/// unpinned, non-skill entries for eviction at the next boundary until the projected usage
|
|
543
|
+
/// (used − already-marked) fits, and return `Some((used, budget))` ONCE per cache generation
|
|
544
|
+
/// for the `KnowledgeBudgetExceeded` observation (marking itself is idempotent and repeats
|
|
545
|
+
/// harmlessly). Skill pins are exempt — deactivation/lease governs them, the budget never
|
|
546
|
+
/// silently unloads a skill the model believes is active. If marking every eligible entry
|
|
547
|
+
/// still exceeds the budget, the warning stands and the overweight remainder is the host's
|
|
548
|
+
/// explicit choice (errs-open). `knowledge_budget_ratio <= 0.0` disables.
|
|
549
|
+
pub fn enforce_knowledge_budget(&mut self) -> Option<(u32, u32)> {
|
|
550
|
+
let ratio = self.config.knowledge_budget_ratio;
|
|
551
|
+
if ratio <= 0.0 {
|
|
552
|
+
return None;
|
|
553
|
+
}
|
|
554
|
+
let budget = (self.max_tokens as f64 * ratio) as u32;
|
|
555
|
+
let used = self.partitions.knowledge.token_count;
|
|
556
|
+
if used <= budget {
|
|
557
|
+
return None;
|
|
558
|
+
}
|
|
559
|
+
let entries = &mut self.partitions.knowledge.entries;
|
|
560
|
+
let marked: u32 = entries.iter().filter(|e| e.evict_at_boundary).map(|e| e.tokens).sum();
|
|
561
|
+
let mut projected = used.saturating_sub(marked);
|
|
562
|
+
for entry in entries.iter_mut() {
|
|
563
|
+
if projected <= budget {
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
if entry.evict_at_boundary || entry.pinned {
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if entry.key.as_deref().is_some_and(|k| k.starts_with("skill:")) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
entry.evict_at_boundary = true;
|
|
573
|
+
projected = projected.saturating_sub(entry.tokens);
|
|
574
|
+
}
|
|
575
|
+
if self.knowledge_budget_warned {
|
|
576
|
+
return None;
|
|
577
|
+
}
|
|
578
|
+
self.knowledge_budget_warned = true;
|
|
579
|
+
Some((used, budget))
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/// K1: drain boundary-sweep results (state-machine side turns these into observations).
|
|
583
|
+
pub fn take_knowledge_sweeps(&mut self) -> Vec<crate::context::partitions::KnowledgeSweep> {
|
|
584
|
+
std::mem::take(&mut self.pending_knowledge_sweeps)
|
|
585
|
+
}
|
|
586
|
+
|
|
481
587
|
/// Push a runtime signal into the current turn's State slot.
|
|
482
588
|
/// Signals are ephemeral — cleared after each render.
|
|
483
589
|
pub fn push_signal(&mut self, text: String) {
|
|
@@ -539,9 +645,45 @@ impl ContextManager {
|
|
|
539
645
|
|
|
540
646
|
/// P1-B: record that the model has loaded a skill (its content is now in context). Returns
|
|
541
647
|
/// `true` if this changed the active set — an epoch boundary the SDK can use to re-anchor the
|
|
542
|
-
/// prompt cache (D).
|
|
648
|
+
/// prompt cache (D). Re-activating an already-active skill refreshes its lease (K3) but
|
|
649
|
+
/// returns false (no epoch change).
|
|
543
650
|
pub fn activate_skill(&mut self, name: impl Into<CompactString>) -> bool {
|
|
544
|
-
self.
|
|
651
|
+
self.activate_skill_leased(name, None)
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/// K3: activate with an optional lease expiry turn (`None` = permanent). Same epoch semantics
|
|
655
|
+
/// as [`Self::activate_skill`]; a re-activation overwrites the prior lease (latest wins).
|
|
656
|
+
pub fn activate_skill_leased(
|
|
657
|
+
&mut self,
|
|
658
|
+
name: impl Into<CompactString>,
|
|
659
|
+
expires_at_turn: Option<u32>,
|
|
660
|
+
) -> bool {
|
|
661
|
+
self.active_skills.insert(name.into(), expires_at_turn).is_none()
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/// K3: deactivate a skill — the toolset re-widens at the next `emit_call_llm` (an epoch event,
|
|
665
|
+
/// same cache cost class as activation) and the skill's `skill:<name>` knowledge pin is marked
|
|
666
|
+
/// for the next boundary sweep. Errs-open: not-active is a no-op (returns false).
|
|
667
|
+
pub fn deactivate_skill(&mut self, name: &str) -> bool {
|
|
668
|
+
if self.active_skills.remove(name).is_none() {
|
|
669
|
+
return false;
|
|
670
|
+
}
|
|
671
|
+
self.partitions.knowledge.remove(&format!("skill:{name}"));
|
|
672
|
+
true
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/// K3: expire skill leases whose turn has passed (mirrors the capability lease sweep — runs at
|
|
676
|
+
/// the head of every event). Each expiry takes the same path as an explicit deactivation.
|
|
677
|
+
pub fn sweep_expired_skill_leases(&mut self, current_turn: u32) {
|
|
678
|
+
let expired: Vec<CompactString> = self
|
|
679
|
+
.active_skills
|
|
680
|
+
.iter()
|
|
681
|
+
.filter(|(_, lease)| lease.is_some_and(|t| current_turn >= t))
|
|
682
|
+
.map(|(name, _)| name.clone())
|
|
683
|
+
.collect();
|
|
684
|
+
for name in expired {
|
|
685
|
+
self.deactivate_skill(&name);
|
|
686
|
+
}
|
|
545
687
|
}
|
|
546
688
|
|
|
547
689
|
/// P1-B: the tool-id allow-set to narrow the exposed toolset to, given the active skills.
|
|
@@ -554,7 +696,7 @@ impl ContextManager {
|
|
|
554
696
|
return None;
|
|
555
697
|
}
|
|
556
698
|
let mut union = std::collections::HashSet::new();
|
|
557
|
-
for name in
|
|
699
|
+
for name in self.active_skills.keys() {
|
|
558
700
|
let declared = self.skills.allowed_tools(name);
|
|
559
701
|
if declared.is_empty() {
|
|
560
702
|
return None; // an unrestricted active skill ⇒ no narrowing (D3)
|
|
@@ -608,10 +750,42 @@ impl ContextManager {
|
|
|
608
750
|
if let Some(t) = self.memory_tool_schema() { tools.push(t); }
|
|
609
751
|
if let Some(t) = self.knowledge_tool_schema() { tools.push(t); }
|
|
610
752
|
if let Some(t) = self.plan_tool_schema() { tools.push(t); }
|
|
753
|
+
if let Some(t) = self.read_result_tool_schema() { tools.push(t); }
|
|
611
754
|
tools.sort_by(|a, b| a.name.cmp(&b.name));
|
|
612
755
|
tools
|
|
613
756
|
}
|
|
614
757
|
|
|
758
|
+
/// O7: the `read_result` meta-tool — re-fetch a tool result the kernel evicted from context
|
|
759
|
+
/// (spooled to disk / collapsed / paged out). Exposed DYNAMICALLY: only once at least one
|
|
760
|
+
/// handle has actually left residency, so runs that never evict see an unchanged toolset
|
|
761
|
+
/// (progressive disclosure; golden fixtures and cache prefixes stay byte-stable). Content is
|
|
762
|
+
/// host-resolved (spool file / session log) — the kernel only advertises the capability.
|
|
763
|
+
pub fn read_result_tool_schema(&self) -> Option<ToolSchema> {
|
|
764
|
+
let any_evicted = self
|
|
765
|
+
.handles
|
|
766
|
+
.all()
|
|
767
|
+
.iter()
|
|
768
|
+
.any(|h| !h.residency.occupies_context());
|
|
769
|
+
if !any_evicted { return None; }
|
|
770
|
+
Some(ToolSchema {
|
|
771
|
+
name: CompactString::new(READ_RESULT_TOOL_NAME),
|
|
772
|
+
description: "Re-read a tool result that was evicted from context (you see a \
|
|
773
|
+
placeholder like '[…tool result spooled…]' or a collapsed entry). \
|
|
774
|
+
Pass the tool call's call_id; use offset/max_bytes to page through \
|
|
775
|
+
large content."
|
|
776
|
+
.to_string(),
|
|
777
|
+
parameters: serde_json::json!({
|
|
778
|
+
"type": "object",
|
|
779
|
+
"properties": {
|
|
780
|
+
"call_id": { "type": "string" },
|
|
781
|
+
"offset": { "type": "integer", "description": "Byte offset to start from (default 0)." },
|
|
782
|
+
"max_bytes": { "type": "integer", "description": "Max bytes to return (default 4000)." }
|
|
783
|
+
},
|
|
784
|
+
"required": ["call_id"]
|
|
785
|
+
}),
|
|
786
|
+
})
|
|
787
|
+
}
|
|
788
|
+
|
|
615
789
|
pub fn plan_tool_schema(&self) -> Option<ToolSchema> {
|
|
616
790
|
if !self.plan_tool_enabled { return None; }
|
|
617
791
|
Some(ToolSchema {
|
|
@@ -1213,4 +1387,56 @@ mod tests {
|
|
|
1213
1387
|
assert_eq!(mgr.handles.residency_for_source("c0"), Some(&Residency::Collapsed));
|
|
1214
1388
|
assert_eq!(mgr.handles.residency_for_source("c5"), Some(&Residency::Resident));
|
|
1215
1389
|
}
|
|
1390
|
+
|
|
1391
|
+
// ── K2: knowledge budget ─────────────────────────────────────────────────
|
|
1392
|
+
|
|
1393
|
+
#[test]
|
|
1394
|
+
fn knowledge_budget_marks_oldest_unpinned_first_and_warns_once() {
|
|
1395
|
+
// max_tokens 100 × default ratio 0.25 ⇒ budget 25. Four 10-token entries (40 used):
|
|
1396
|
+
// two evictable, one pinned, one skill pin.
|
|
1397
|
+
let mut mgr = ContextManager::new(100);
|
|
1398
|
+
mgr.push_knowledge(Message::system("oldest unkeyed"), 10);
|
|
1399
|
+
mgr.push_knowledge_entry(Some("a".into()), Message::system("keyed"), 10, false);
|
|
1400
|
+
mgr.push_knowledge_entry(Some("p".into()), Message::system("pinned"), 10, true);
|
|
1401
|
+
mgr.push_knowledge_entry(Some("skill:x".into()), Message::system("skill"), 10, false);
|
|
1402
|
+
|
|
1403
|
+
let warn = mgr.enforce_knowledge_budget();
|
|
1404
|
+
assert_eq!(warn, Some((40, 25)));
|
|
1405
|
+
// Oldest-first: unkeyed (10) then "a" (10) marked ⇒ projected 20 ≤ 25 stops there.
|
|
1406
|
+
let e = &mgr.partitions.knowledge.entries;
|
|
1407
|
+
assert!(e[0].evict_at_boundary);
|
|
1408
|
+
assert!(e[1].evict_at_boundary);
|
|
1409
|
+
assert!(!e[2].evict_at_boundary, "pinned exempt");
|
|
1410
|
+
assert!(!e[3].evict_at_boundary, "skill pin exempt");
|
|
1411
|
+
|
|
1412
|
+
// Warn-once per generation; marking stays idempotent.
|
|
1413
|
+
assert_eq!(mgr.enforce_knowledge_budget(), None);
|
|
1414
|
+
|
|
1415
|
+
// The boundary sweep drops the marked entries and re-arms the warning.
|
|
1416
|
+
let sweep = mgr.partitions.knowledge.sweep_at_boundary();
|
|
1417
|
+
assert_eq!(sweep.tokens_freed, 20);
|
|
1418
|
+
assert_eq!(mgr.partitions.knowledge.token_count, 20);
|
|
1419
|
+
// Back under budget ⇒ no further warning even though it re-armed.
|
|
1420
|
+
assert_eq!(mgr.enforce_knowledge_budget(), None);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
#[test]
|
|
1424
|
+
fn knowledge_budget_warning_stands_when_only_exempt_weight_remains() {
|
|
1425
|
+
let mut mgr = ContextManager::new(100);
|
|
1426
|
+
mgr.push_knowledge_entry(Some("p".into()), Message::system("pinned heavy"), 30, true);
|
|
1427
|
+
mgr.push_knowledge_entry(Some("skill:x".into()), Message::system("skill heavy"), 30, false);
|
|
1428
|
+
|
|
1429
|
+
// Over budget (60 > 25) but nothing evictable — warning fires, nothing marked.
|
|
1430
|
+
assert_eq!(mgr.enforce_knowledge_budget(), Some((60, 25)));
|
|
1431
|
+
assert!(mgr.partitions.knowledge.entries.iter().all(|e| !e.evict_at_boundary));
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
#[test]
|
|
1435
|
+
fn knowledge_budget_ratio_zero_disables() {
|
|
1436
|
+
let mut mgr = ContextManager::new(100);
|
|
1437
|
+
mgr.config.knowledge_budget_ratio = 0.0;
|
|
1438
|
+
mgr.push_knowledge(Message::system("huge"), 90);
|
|
1439
|
+
assert_eq!(mgr.enforce_knowledge_budget(), None);
|
|
1440
|
+
assert!(!mgr.partitions.knowledge.entries[0].evict_at_boundary);
|
|
1441
|
+
}
|
|
1216
1442
|
}
|