deepstrike 0.2.18__tar.gz → 0.2.20__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.18 → deepstrike-0.2.20}/Cargo.lock +5 -5
- {deepstrike-0.2.18 → deepstrike-0.2.20}/Cargo.toml +2 -2
- {deepstrike-0.2.18 → deepstrike-0.2.20}/PKG-INFO +1 -1
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/manager.rs +76 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/skill_catalog.rs +26 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/runtime/kernel.rs +42 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/state_machine/mod.rs +14 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/state_machine/tests.rs +141 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/__init__.py +10 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/__init__.py +23 -0
- deepstrike-0.2.20/deepstrike/runtime/eval.py +110 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/kernel_step.py +4 -0
- deepstrike-0.2.20/deepstrike/runtime/replay_fixture.py +52 -0
- deepstrike-0.2.20/deepstrike/runtime/replay_provider.py +183 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/runner.py +127 -13
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/skills/registry.py +9 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/pyproject.toml +1 -1
- {deepstrike-0.2.18 → deepstrike-0.2.20}/README.md +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/Cargo.toml +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/compression.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/config.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/dashboard.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/partitions.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/pressure.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/renderer.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/renewal.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/sections.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/snapshot.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/summarizer.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/task_state.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/text.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/context/token_engine.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/governance/audit.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/governance/constraint.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/governance/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/governance/permission.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/governance/pipeline.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/governance/quota.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/governance/rate_limit.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/governance/sandbox.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/governance/tool_decision.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/governance/veto.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/harness/eval.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/harness/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/lib.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/memory/curator.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/memory/durable.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/memory/extractor.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/memory/idle_pipeline.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/memory/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/memory/runtime.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/memory/semantic.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/memory/session.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/memory/synthesis.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/memory/trace_analyzer.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/memory/working.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/mm/handle.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/mm/memory.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/mm/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/orchestration/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/orchestration/task_graph.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/orchestration/tournament.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/orchestration/workflow/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/orchestration/workflow/run.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/proc/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/runtime/event_log.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/runtime/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/runtime/repair.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/runtime/replay.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/runtime/session.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/runtime/snapshot.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/milestone.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/policy.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/rollback.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/state_machine/capability.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/state_machine/eviction.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/state_machine/gate.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/state_machine/milestone_exec.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/state_machine/process.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/state_machine/signal.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/state_machine/workflow.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/tcb.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/signals/attention.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/signals/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/signals/queue.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/signals/router.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/syscall/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/agent.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/capability.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/contract.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/error.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/message.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/milestone.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/mod.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/model.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/policy.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/result.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/signal.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/skill.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/types/task.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-py/Cargo.toml +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-py/src/lib.rs +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/collaboration/__init__.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/collaboration/contract.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/collaboration/handoff.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/collaboration/harness.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/collaboration/modes.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/collaboration/pool.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/governance.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/harness/__init__.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/harness/harness.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/kernel/__init__.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/knowledge/__init__.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/knowledge/source.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/memory/__init__.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/memory/agent.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/memory/protocols.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/memory/working.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/__init__.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/anthropic.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/base.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/deepseek.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/gemini.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/glm.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/kimi.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/minimax.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/ollama.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/openai.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/openai_responses.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/qwen.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/replay.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/replay_validator.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/providers/stream.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/archive.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/credential_vault.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/execution_plane.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/filtered_plane.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/kernel_event_log.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/large_result_spool.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/mcp_proxy_plane.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/os_profile.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/os_snapshot.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/output_schema.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/process_sandbox_plane.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/provider_replay.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/reducers.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/remote_vpc_plane.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/replay_sanitize.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/session_log.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/session_repair.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/sub_agent_orchestrator.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/workflow_control_flow.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/workflow_store.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/runtime/worktree_plane.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/safety/__init__.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/safety/permissions.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/signals/__init__.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/signals/gateway.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/signals/scheduled.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/signals/types.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/skills/__init__.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/skills/watcher.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/tools/__init__.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/tools/builtin/__init__.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/tools/builtin/read_file.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/tools/execution.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/tools/registry.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/deepstrike/types/__init__.py +0 -0
- {deepstrike-0.2.18 → deepstrike-0.2.20}/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.20"
|
|
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.20"
|
|
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.20"
|
|
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.20"
|
|
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.20"
|
|
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.20"
|
|
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.20" }
|
|
13
13
|
notify = "6"
|
|
14
14
|
serde = { version = "1", features = ["derive"] }
|
|
15
15
|
serde_json = "1"
|
|
@@ -31,6 +31,15 @@ pub struct ContextManager {
|
|
|
31
31
|
pub sprint: u32,
|
|
32
32
|
pub last_handoff: Option<HandoffArtifact>,
|
|
33
33
|
pub skills: SkillCatalog,
|
|
34
|
+
/// P1-B tool gating: the set of skills the model has loaded this session (by name). Their
|
|
35
|
+
/// declared `allowed_tools` are unioned to narrow the exposed toolset in `emit_call_llm`.
|
|
36
|
+
/// A set (not a single value) because the model may load several skills and still needs each
|
|
37
|
+
/// one's tools (D1). v1 accumulates (no eviction). Snapshotted for wake/resume.
|
|
38
|
+
pub active_skills: std::collections::BTreeSet<CompactString>,
|
|
39
|
+
/// P1-B/D stable-core: tool ids that stay exposed even when a skill narrows the toolset (the
|
|
40
|
+
/// "everyone uses these" set — read/search/bash etc.). Configured once by the SDK; empty by
|
|
41
|
+
/// default (铁律: no config ⇒ skills narrow to exactly their declared tools + meta-tools).
|
|
42
|
+
pub stable_core_tools: std::collections::HashSet<CompactString>,
|
|
34
43
|
pub capabilities: CapabilityManifest,
|
|
35
44
|
pub sections: ContextSectionRegistry,
|
|
36
45
|
pub memory_enabled: bool,
|
|
@@ -82,6 +91,8 @@ impl ContextManager {
|
|
|
82
91
|
partitions, max_tokens, config, engine,
|
|
83
92
|
sprint: 0, last_handoff: None,
|
|
84
93
|
skills: SkillCatalog::new(),
|
|
94
|
+
active_skills: std::collections::BTreeSet::new(),
|
|
95
|
+
stable_core_tools: std::collections::HashSet::new(),
|
|
85
96
|
capabilities: CapabilityManifest::new(),
|
|
86
97
|
sections: ContextSectionRegistry::default_agent_sections(),
|
|
87
98
|
memory_enabled: false, knowledge_enabled: false, plan_tool_enabled: false,
|
|
@@ -487,6 +498,38 @@ impl ContextManager {
|
|
|
487
498
|
self.skills.set_available(skills);
|
|
488
499
|
}
|
|
489
500
|
|
|
501
|
+
/// P1-B/D: set the stable-core tool ids (always exposed under skill gating). Replaces any prior.
|
|
502
|
+
pub fn set_stable_core_tools(&mut self, ids: impl IntoIterator<Item = CompactString>) {
|
|
503
|
+
self.stable_core_tools = ids.into_iter().collect();
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/// P1-B: record that the model has loaded a skill (its content is now in context). Returns
|
|
507
|
+
/// `true` if this changed the active set — an epoch boundary the SDK can use to re-anchor the
|
|
508
|
+
/// prompt cache (D). No-op (returns false) when the skill was already active.
|
|
509
|
+
pub fn activate_skill(&mut self, name: impl Into<CompactString>) -> bool {
|
|
510
|
+
self.active_skills.insert(name.into())
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/// P1-B: the tool-id allow-set to narrow the exposed toolset to, given the active skills.
|
|
514
|
+
/// Returns `None` ⇒ **do not narrow** (no skill active, or some active skill declares no
|
|
515
|
+
/// `allowed_tools` ⇒ unbounded, errs-open per D3). `Some(set)` ⇒ narrow to `set` (the union of
|
|
516
|
+
/// every active skill's declared tools). Meta-tools and stable-core are layered on in
|
|
517
|
+
/// `emit_call_llm`, not here.
|
|
518
|
+
pub fn active_skill_tool_filter(&self) -> Option<std::collections::HashSet<CompactString>> {
|
|
519
|
+
if self.active_skills.is_empty() {
|
|
520
|
+
return None;
|
|
521
|
+
}
|
|
522
|
+
let mut union = std::collections::HashSet::new();
|
|
523
|
+
for name in &self.active_skills {
|
|
524
|
+
let declared = self.skills.allowed_tools(name);
|
|
525
|
+
if declared.is_empty() {
|
|
526
|
+
return None; // an unrestricted active skill ⇒ no narrowing (D3)
|
|
527
|
+
}
|
|
528
|
+
union.extend(declared.iter().cloned());
|
|
529
|
+
}
|
|
530
|
+
Some(union)
|
|
531
|
+
}
|
|
532
|
+
|
|
490
533
|
pub fn skill_tool_schema(&self) -> Option<ToolSchema> {
|
|
491
534
|
self.skills.build_tool_schema()
|
|
492
535
|
}
|
|
@@ -783,6 +826,39 @@ mod tests {
|
|
|
783
826
|
assert!(mgr.sections.get("capabilities.inventory").is_some());
|
|
784
827
|
}
|
|
785
828
|
|
|
829
|
+
#[test]
|
|
830
|
+
fn b1_active_skill_state_and_tool_filter() {
|
|
831
|
+
let mut mgr = ContextManager::new(1_000);
|
|
832
|
+
let mut debug = SkillMetadata::new("debug", "Debug helper");
|
|
833
|
+
debug.allowed_tools = vec![CompactString::new("read"), CompactString::new("grep")];
|
|
834
|
+
let mut review = SkillMetadata::new("review", "Reviewer");
|
|
835
|
+
review.allowed_tools = vec![CompactString::new("git_diff")];
|
|
836
|
+
let plain = SkillMetadata::new("plain", "No tools declared"); // empty allowed_tools
|
|
837
|
+
mgr.set_available_skills(vec![debug, review, plain]);
|
|
838
|
+
|
|
839
|
+
// No active skill ⇒ no narrowing.
|
|
840
|
+
assert!(mgr.active_skill_tool_filter().is_none());
|
|
841
|
+
|
|
842
|
+
// Activating returns the epoch-boundary changed flag.
|
|
843
|
+
assert!(mgr.activate_skill("debug"));
|
|
844
|
+
assert!(!mgr.activate_skill("debug")); // already active ⇒ no change
|
|
845
|
+
|
|
846
|
+
// One restricted skill ⇒ narrow to its tools.
|
|
847
|
+
let f = mgr.active_skill_tool_filter().unwrap();
|
|
848
|
+
assert_eq!(f.len(), 2);
|
|
849
|
+
assert!(f.contains(&CompactString::new("read")) && f.contains(&CompactString::new("grep")));
|
|
850
|
+
|
|
851
|
+
// Second restricted skill ⇒ union (D1).
|
|
852
|
+
mgr.activate_skill("review");
|
|
853
|
+
let f = mgr.active_skill_tool_filter().unwrap();
|
|
854
|
+
assert_eq!(f.len(), 3);
|
|
855
|
+
assert!(f.contains(&CompactString::new("git_diff")));
|
|
856
|
+
|
|
857
|
+
// An active skill with NO declared tools ⇒ unbounded ⇒ do not narrow (D3, errs-open).
|
|
858
|
+
mgr.activate_skill("plain");
|
|
859
|
+
assert!(mgr.active_skill_tool_filter().is_none());
|
|
860
|
+
}
|
|
861
|
+
|
|
786
862
|
#[test]
|
|
787
863
|
fn snapshot_hint_changes_when_capabilities_change() {
|
|
788
864
|
let mut mgr = ContextManager::new(1_000);
|
|
@@ -47,6 +47,16 @@ impl SkillCatalog {
|
|
|
47
47
|
self.available.len()
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/// P1-B tool gating: the tool ids the named skill declares it needs. Empty when the skill is
|
|
51
|
+
/// unknown or declares none (⇒ that skill does not narrow the toolset). The kernel unions these
|
|
52
|
+
/// across the active-skill set in `emit_call_llm`.
|
|
53
|
+
pub fn allowed_tools(&self, name: &str) -> &[CompactString] {
|
|
54
|
+
self.available
|
|
55
|
+
.get(name)
|
|
56
|
+
.map(|s| s.allowed_tools.as_slice())
|
|
57
|
+
.unwrap_or(&[])
|
|
58
|
+
}
|
|
59
|
+
|
|
50
60
|
pub fn is_empty(&self) -> bool {
|
|
51
61
|
self.available.is_empty()
|
|
52
62
|
}
|
|
@@ -152,4 +162,20 @@ mod tests {
|
|
|
152
162
|
assert_eq!(catalog.available_count(), 1);
|
|
153
163
|
assert!(!catalog.is_empty());
|
|
154
164
|
}
|
|
165
|
+
|
|
166
|
+
#[test]
|
|
167
|
+
fn allowed_tools_round_trip_through_catalog() {
|
|
168
|
+
// P1-B B0: a skill's declared `allowed_tools` survives registration and is looked up by name.
|
|
169
|
+
let mut skill = SkillMetadata::new("debug", "Debug helper");
|
|
170
|
+
skill.allowed_tools = vec![CompactString::new("read"), CompactString::new("grep")];
|
|
171
|
+
let mut catalog = SkillCatalog::new();
|
|
172
|
+
catalog.set_available(vec![skill]);
|
|
173
|
+
|
|
174
|
+
let tools = catalog.allowed_tools("debug");
|
|
175
|
+
assert_eq!(tools.len(), 2);
|
|
176
|
+
assert!(tools.iter().any(|t| t == "read"));
|
|
177
|
+
assert!(tools.iter().any(|t| t == "grep"));
|
|
178
|
+
// Unknown skill / skill with no declaration ⇒ empty (no narrowing).
|
|
179
|
+
assert!(catalog.allowed_tools("missing").is_empty());
|
|
180
|
+
}
|
|
155
181
|
}
|
|
@@ -115,6 +115,17 @@ pub enum KernelInputEvent {
|
|
|
115
115
|
SetAvailableSkills {
|
|
116
116
|
skills: Vec<SkillMetadata>,
|
|
117
117
|
},
|
|
118
|
+
/// P1-B tool gating: the model loaded a skill (`name`). The SDK emits this when it resolves a
|
|
119
|
+
/// `skill` tool call. The kernel records it in the active-skill set and resolves the skill's
|
|
120
|
+
/// `allowed_tools` from the catalog to narrow the toolset on subsequent turns.
|
|
121
|
+
SkillActivated {
|
|
122
|
+
name: String,
|
|
123
|
+
},
|
|
124
|
+
/// P1-B/D: configure the stable-core tool ids (always exposed under skill gating). Set once by
|
|
125
|
+
/// the SDK; empty/absent ⇒ skills narrow to exactly their declared tools + meta-tools.
|
|
126
|
+
SetStableCoreTools {
|
|
127
|
+
tool_ids: Vec<String>,
|
|
128
|
+
},
|
|
118
129
|
SetMemoryEnabled {
|
|
119
130
|
enabled: bool,
|
|
120
131
|
},
|
|
@@ -662,6 +673,16 @@ impl KernelRuntime {
|
|
|
662
673
|
self.sm.ctx.set_available_skills(skills);
|
|
663
674
|
return KernelStep::empty(self.sm.take_observations());
|
|
664
675
|
}
|
|
676
|
+
KernelInputEvent::SkillActivated { name } => {
|
|
677
|
+
// B1: record the activation (B2 reads it in emit_call_llm to narrow tools).
|
|
678
|
+
// The returned `changed` flag is the epoch boundary for D's cache re-anchor.
|
|
679
|
+
self.sm.ctx.activate_skill(name);
|
|
680
|
+
return KernelStep::empty(self.sm.take_observations());
|
|
681
|
+
}
|
|
682
|
+
KernelInputEvent::SetStableCoreTools { tool_ids } => {
|
|
683
|
+
self.sm.ctx.set_stable_core_tools(tool_ids.into_iter().map(Into::into));
|
|
684
|
+
return KernelStep::empty(self.sm.take_observations());
|
|
685
|
+
}
|
|
665
686
|
KernelInputEvent::SetMemoryEnabled { enabled } => {
|
|
666
687
|
self.sm.ctx.set_memory_enabled(enabled);
|
|
667
688
|
return KernelStep::empty(self.sm.take_observations());
|
|
@@ -1081,6 +1102,27 @@ mod tests {
|
|
|
1081
1102
|
assert_eq!(runtime.state_machine().tools.len(), 1);
|
|
1082
1103
|
}
|
|
1083
1104
|
|
|
1105
|
+
#[test]
|
|
1106
|
+
fn skill_activated_input_records_active_skill() {
|
|
1107
|
+
// P1-B B1: the SkillActivated event (serde `skill_activated`) records the active skill and,
|
|
1108
|
+
// via the catalog's declared tools, yields a narrowing filter — without itself acting.
|
|
1109
|
+
let mut runtime = KernelRuntime::new(LoopPolicy::default());
|
|
1110
|
+
let mut debug = SkillMetadata::new("debug", "Debug helper");
|
|
1111
|
+
debug.allowed_tools = vec!["read".into(), "grep".into()];
|
|
1112
|
+
runtime.step(KernelInput::new(KernelInputEvent::SetAvailableSkills {
|
|
1113
|
+
skills: vec![debug],
|
|
1114
|
+
}));
|
|
1115
|
+
|
|
1116
|
+
let step = runtime.step(KernelInput::new(KernelInputEvent::SkillActivated {
|
|
1117
|
+
name: "debug".to_string(),
|
|
1118
|
+
}));
|
|
1119
|
+
|
|
1120
|
+
assert!(step.actions.is_empty(), "activation is config, not an action");
|
|
1121
|
+
assert!(runtime.state_machine().ctx.active_skills.contains("debug"));
|
|
1122
|
+
let filter = runtime.state_machine().ctx.active_skill_tool_filter().unwrap();
|
|
1123
|
+
assert_eq!(filter.len(), 2);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1084
1126
|
#[test]
|
|
1085
1127
|
fn update_task_input_mutates_task_state() {
|
|
1086
1128
|
let mut runtime = KernelRuntime::new(LoopPolicy::default());
|
{deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/state_machine/mod.rs
RENAMED
|
@@ -810,6 +810,20 @@ impl LoopStateMachine {
|
|
|
810
810
|
});
|
|
811
811
|
}
|
|
812
812
|
|
|
813
|
+
// P1-B epoch skill gating (applied *after* the run-level filter ③, so A is the outer bound
|
|
814
|
+
// and B narrows within it — D6). When skills are active and declare tools, expose only
|
|
815
|
+
// `meta-tools ∪ stable-core ∪ ⋃(active skills' allowed_tools)`. `None` ⇒ no active/declared
|
|
816
|
+
// skill ⇒ no narrowing (D3, errs-open). Meta-tools are always exempt (D5) so the model can
|
|
817
|
+
// still load more skills. Byte-stable within an epoch: the set only changes on activation.
|
|
818
|
+
if let Some(allowed) = self.ctx.active_skill_tool_filter() {
|
|
819
|
+
let stable = &self.ctx.stable_core_tools;
|
|
820
|
+
tools.retain(|tool| {
|
|
821
|
+
matches!(tool.name.as_str(), "skill" | "memory" | "knowledge" | "update_plan")
|
|
822
|
+
|| stable.contains(&tool.name)
|
|
823
|
+
|| allowed.contains(&tool.name)
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
|
|
813
827
|
LoopAction::CallLLM { context, tools }
|
|
814
828
|
}
|
|
815
829
|
|
{deepstrike-0.2.18 → deepstrike-0.2.20}/crates/deepstrike-core/src/scheduler/state_machine/tests.rs
RENAMED
|
@@ -306,6 +306,84 @@
|
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
+
/// P1-B B2: once a skill with declared `allowed_tools` is active, `emit_call_llm` exposes only
|
|
310
|
+
/// `meta-tools ∪ stable-core ∪ allowed_tools`. Meta-tools stay (D5); undeclared/inactive = full.
|
|
311
|
+
#[test]
|
|
312
|
+
fn active_skill_gates_exposed_tools_with_stable_core() {
|
|
313
|
+
let mut sm = sm();
|
|
314
|
+
sm.tools = vec![
|
|
315
|
+
make_tool_schema("read"),
|
|
316
|
+
make_tool_schema("write"),
|
|
317
|
+
make_tool_schema("bash"),
|
|
318
|
+
make_tool_schema("grep"),
|
|
319
|
+
];
|
|
320
|
+
// A skill that declares only {read, grep}; stable-core keeps {bash}; `write` is gated out.
|
|
321
|
+
let mut debug = SkillMetadata::new("debug", "Debug helper");
|
|
322
|
+
debug.allowed_tools = vec![compact_str::CompactString::new("read"), compact_str::CompactString::new("grep")];
|
|
323
|
+
sm.ctx.set_available_skills(vec![debug]);
|
|
324
|
+
sm.ctx.set_stable_core_tools([compact_str::CompactString::new("bash")]);
|
|
325
|
+
|
|
326
|
+
// Before activation: all tools exposed (no narrowing).
|
|
327
|
+
match sm.start(RuntimeTask::new("go")) {
|
|
328
|
+
LoopAction::CallLLM { tools, .. } => {
|
|
329
|
+
let names: Vec<&str> = tools.iter().map(|t| t.name.as_str()).collect();
|
|
330
|
+
assert!(["read", "write", "bash", "grep"].iter().all(|n| names.contains(n)));
|
|
331
|
+
// skill meta-tool present (skills registered) and must survive gating later.
|
|
332
|
+
assert!(names.contains(&SKILL_TOOL_NAME));
|
|
333
|
+
}
|
|
334
|
+
_ => panic!("expected CallLLM"),
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Activate the skill → next emit narrows.
|
|
338
|
+
sm.ctx.activate_skill("debug");
|
|
339
|
+
match sm.emit_call_llm() {
|
|
340
|
+
LoopAction::CallLLM { tools, .. } => {
|
|
341
|
+
let names: Vec<&str> = tools.iter().map(|t| t.name.as_str()).collect();
|
|
342
|
+
assert!(names.contains(&"read") && names.contains(&"grep"), "declared: {names:?}");
|
|
343
|
+
assert!(names.contains(&"bash"), "stable-core kept: {names:?}");
|
|
344
|
+
assert!(names.contains(&SKILL_TOOL_NAME), "meta-tool exempt: {names:?}");
|
|
345
|
+
assert!(!names.contains(&"write"), "undeclared gated out: {names:?}");
|
|
346
|
+
}
|
|
347
|
+
_ => panic!("expected CallLLM"),
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/// P1-B B4: the gated toolset is byte-stable *within* an epoch (no active-set change ⇒ identical
|
|
352
|
+
/// tools every turn — the cache prefix never churns mid-epoch) and changes only when the active
|
|
353
|
+
/// set changes (the single epoch boundary where D re-anchors the cache).
|
|
354
|
+
#[test]
|
|
355
|
+
fn gating_is_byte_stable_within_an_epoch() {
|
|
356
|
+
fn names(action: LoopAction) -> Vec<String> {
|
|
357
|
+
match action {
|
|
358
|
+
LoopAction::CallLLM { tools, .. } => {
|
|
359
|
+
let mut n: Vec<String> = tools.iter().map(|t| t.name.to_string()).collect();
|
|
360
|
+
n.sort();
|
|
361
|
+
n
|
|
362
|
+
}
|
|
363
|
+
_ => panic!("expected CallLLM"),
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
let mut sm = sm();
|
|
367
|
+
sm.tools = vec![make_tool_schema("read"), make_tool_schema("write"), make_tool_schema("grep")];
|
|
368
|
+
let mut debug = SkillMetadata::new("debug", "d");
|
|
369
|
+
debug.allowed_tools = vec![compact_str::CompactString::new("read")];
|
|
370
|
+
let mut review = SkillMetadata::new("review", "r");
|
|
371
|
+
review.allowed_tools = vec![compact_str::CompactString::new("grep")];
|
|
372
|
+
sm.ctx.set_available_skills(vec![debug, review]);
|
|
373
|
+
|
|
374
|
+
sm.start(RuntimeTask::new("go"));
|
|
375
|
+
sm.ctx.activate_skill("debug");
|
|
376
|
+
let n1 = names(sm.emit_call_llm());
|
|
377
|
+
let n2 = names(sm.emit_call_llm()); // no activation change → identical
|
|
378
|
+
assert_eq!(n1, n2, "toolset must be byte-stable within an epoch");
|
|
379
|
+
assert!(n1.contains(&"read".to_string()) && !n1.contains(&"write".to_string()));
|
|
380
|
+
|
|
381
|
+
sm.ctx.activate_skill("review"); // epoch boundary
|
|
382
|
+
let n3 = names(sm.emit_call_llm());
|
|
383
|
+
assert_ne!(n1, n3, "activating another skill changes the toolset");
|
|
384
|
+
assert!(n3.contains(&"grep".to_string()), "union now includes review's tools: {n3:?}");
|
|
385
|
+
}
|
|
386
|
+
|
|
309
387
|
#[test]
|
|
310
388
|
fn skill_tool_injected_in_call_llm_when_skills_registered() {
|
|
311
389
|
let mut sm = sm();
|
|
@@ -332,6 +410,69 @@
|
|
|
332
410
|
}
|
|
333
411
|
}
|
|
334
412
|
|
|
413
|
+
/// P0-A (tool gating): a top-level run carrying an `AgentRunSpec` with a
|
|
414
|
+
/// capability filter must only expose the allow-listed tools each turn — the
|
|
415
|
+
/// static per-run profile. The filter is an allow-list (empty = allow all),
|
|
416
|
+
/// applied in `emit_call_llm`, and is byte-stable across the run, so it never
|
|
417
|
+
/// busts the cache prefix. Gates the same path sub-agents already use.
|
|
418
|
+
#[test]
|
|
419
|
+
fn top_level_run_capability_filter_gates_exposed_tools() {
|
|
420
|
+
use crate::types::agent::{
|
|
421
|
+
AgentCapabilityFilter, AgentIdentity, AgentRole, AgentRunSpec,
|
|
422
|
+
};
|
|
423
|
+
let mut sm = sm();
|
|
424
|
+
sm.tools = vec![
|
|
425
|
+
make_tool_schema("read"),
|
|
426
|
+
make_tool_schema("write"),
|
|
427
|
+
make_tool_schema("bash"),
|
|
428
|
+
make_tool_schema("search"),
|
|
429
|
+
];
|
|
430
|
+
// Static profile: only `read` + `search` are exposed for this run.
|
|
431
|
+
let mut spec = AgentRunSpec::new(
|
|
432
|
+
AgentIdentity::new("root", "root-session"),
|
|
433
|
+
AgentRole::Custom,
|
|
434
|
+
"do the task",
|
|
435
|
+
);
|
|
436
|
+
spec.capability_filter = AgentCapabilityFilter {
|
|
437
|
+
allowed_kinds: Vec::new(),
|
|
438
|
+
allowed_ids: vec![
|
|
439
|
+
compact_str::CompactString::new("read"),
|
|
440
|
+
compact_str::CompactString::new("search"),
|
|
441
|
+
],
|
|
442
|
+
};
|
|
443
|
+
sm.run_spec = Some(spec);
|
|
444
|
+
|
|
445
|
+
match sm.start(RuntimeTask::new("do the task")) {
|
|
446
|
+
LoopAction::CallLLM { tools, .. } => {
|
|
447
|
+
let names: Vec<&str> = tools.iter().map(|t| t.name.as_str()).collect();
|
|
448
|
+
assert!(names.contains(&"read"), "read should be exposed: {names:?}");
|
|
449
|
+
assert!(names.contains(&"search"), "search should be exposed: {names:?}");
|
|
450
|
+
assert!(!names.contains(&"write"), "write must be gated out: {names:?}");
|
|
451
|
+
assert!(!names.contains(&"bash"), "bash must be gated out: {names:?}");
|
|
452
|
+
}
|
|
453
|
+
_ => panic!("expected CallLLM"),
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/// Counterpart: with no run spec (the default top-level run), every base tool
|
|
458
|
+
/// is exposed — i.e. gating is strictly opt-in (铁律: no config = old behavior).
|
|
459
|
+
#[test]
|
|
460
|
+
fn top_level_run_without_spec_exposes_all_tools() {
|
|
461
|
+
let mut sm = sm();
|
|
462
|
+
sm.tools = vec![
|
|
463
|
+
make_tool_schema("read"),
|
|
464
|
+
make_tool_schema("write"),
|
|
465
|
+
make_tool_schema("bash"),
|
|
466
|
+
];
|
|
467
|
+
match sm.start(RuntimeTask::new("do the task")) {
|
|
468
|
+
LoopAction::CallLLM { tools, .. } => {
|
|
469
|
+
let names: Vec<&str> = tools.iter().map(|t| t.name.as_str()).collect();
|
|
470
|
+
assert!(names.contains(&"read") && names.contains(&"write") && names.contains(&"bash"));
|
|
471
|
+
}
|
|
472
|
+
_ => panic!("expected CallLLM"),
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
335
476
|
#[test]
|
|
336
477
|
fn compression_emits_observation() {
|
|
337
478
|
let mut sm = LoopStateMachine::new(LoopPolicy {
|
|
@@ -43,6 +43,16 @@ from deepstrike.runtime import (
|
|
|
43
43
|
default_sub_agent_orchestrator,
|
|
44
44
|
DEFAULT_SANDBOX_POLICY,
|
|
45
45
|
validate_declarative_policy,
|
|
46
|
+
ReplayProvider,
|
|
47
|
+
ReplayProviderOpts,
|
|
48
|
+
extract_recorded_messages,
|
|
49
|
+
Criterion,
|
|
50
|
+
Verdict,
|
|
51
|
+
VerdictDetail,
|
|
52
|
+
judge,
|
|
53
|
+
build_eval_messages,
|
|
54
|
+
parse_verdict,
|
|
55
|
+
verdict_output_schema,
|
|
46
56
|
)
|
|
47
57
|
from deepstrike.governance import Governance, GovernanceVerdict
|
|
48
58
|
from deepstrike.providers import (
|
|
@@ -7,6 +7,7 @@ from deepstrike.runtime.runner import (
|
|
|
7
7
|
RuntimeOptions,
|
|
8
8
|
RuntimeRunner,
|
|
9
9
|
SubAgentHarnessConfig,
|
|
10
|
+
TurnMetrics,
|
|
10
11
|
collect_text,
|
|
11
12
|
)
|
|
12
13
|
from deepstrike.runtime.session_log import (
|
|
@@ -60,10 +61,22 @@ from deepstrike.runtime.os_profile import (
|
|
|
60
61
|
os_profile,
|
|
61
62
|
validate_declarative_policy,
|
|
62
63
|
)
|
|
64
|
+
from deepstrike.runtime.replay_provider import ReplayProvider, ReplayProviderOpts
|
|
65
|
+
from deepstrike.runtime.replay_fixture import extract_recorded_messages
|
|
66
|
+
from deepstrike.runtime.eval import (
|
|
67
|
+
Criterion,
|
|
68
|
+
Verdict,
|
|
69
|
+
VerdictDetail,
|
|
70
|
+
judge,
|
|
71
|
+
build_eval_messages,
|
|
72
|
+
parse_verdict,
|
|
73
|
+
verdict_output_schema,
|
|
74
|
+
)
|
|
63
75
|
|
|
64
76
|
__all__ = [
|
|
65
77
|
"RuntimeRunner",
|
|
66
78
|
"RuntimeOptions",
|
|
79
|
+
"TurnMetrics",
|
|
67
80
|
"ResourceQuota",
|
|
68
81
|
"MemoryPolicy",
|
|
69
82
|
"MemoryWriteRateLimit",
|
|
@@ -116,4 +129,14 @@ __all__ = [
|
|
|
116
129
|
"assert_native_profile",
|
|
117
130
|
"os_profile",
|
|
118
131
|
"validate_declarative_policy",
|
|
132
|
+
"ReplayProvider",
|
|
133
|
+
"ReplayProviderOpts",
|
|
134
|
+
"extract_recorded_messages",
|
|
135
|
+
"Criterion",
|
|
136
|
+
"Verdict",
|
|
137
|
+
"VerdictDetail",
|
|
138
|
+
"judge",
|
|
139
|
+
"build_eval_messages",
|
|
140
|
+
"parse_verdict",
|
|
141
|
+
"verdict_output_schema",
|
|
119
142
|
]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""`judge()` — one-shot quality scoring against a goal + criteria.
|
|
2
|
+
|
|
3
|
+
Python port of node/src/runtime/eval.ts. Wraps the kernel's `gen_eval` free functions
|
|
4
|
+
(`build_eval_messages` / `parse_verdict` / `verdict_output_schema`) into a small typed
|
|
5
|
+
surface so callers can score one (goal, criteria, result) pair without standing up
|
|
6
|
+
HarnessLoop.
|
|
7
|
+
|
|
8
|
+
Single LLM call: build the eval prompt → stream → parse verdict.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from deepstrike._kernel import ( # type: ignore
|
|
17
|
+
Message,
|
|
18
|
+
build_eval_messages as _kernel_build_eval_messages,
|
|
19
|
+
parse_verdict as _kernel_parse_verdict,
|
|
20
|
+
verdict_output_schema as _kernel_verdict_output_schema,
|
|
21
|
+
)
|
|
22
|
+
from deepstrike.providers.base import LLMProvider, RenderedContext # type: ignore
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class Criterion:
|
|
27
|
+
text: str
|
|
28
|
+
required: bool = True
|
|
29
|
+
weight: float | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class VerdictDetail:
|
|
34
|
+
criterion: str
|
|
35
|
+
passed: bool
|
|
36
|
+
score: float
|
|
37
|
+
feedback: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class Verdict:
|
|
42
|
+
passed: bool
|
|
43
|
+
overall_score: float
|
|
44
|
+
feedback: str
|
|
45
|
+
details: list[VerdictDetail] = field(default_factory=list)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def build_eval_messages(goal: str, criteria: list[Criterion], result: str) -> list[Message]:
|
|
49
|
+
"""Render the kernel's eval prompt for (goal, criteria, result)."""
|
|
50
|
+
native_criteria = [
|
|
51
|
+
{"text": c.text, "required": c.required, **({"weight": c.weight} if c.weight is not None else {})}
|
|
52
|
+
for c in criteria
|
|
53
|
+
]
|
|
54
|
+
return _kernel_build_eval_messages(goal, native_criteria, result, 1, False)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def parse_verdict(text: str) -> Verdict:
|
|
58
|
+
"""Parse a Verdict from raw judge-LLM text."""
|
|
59
|
+
raw = _kernel_parse_verdict(text)
|
|
60
|
+
details_raw = raw.get("details") if isinstance(raw, dict) else getattr(raw, "details", None)
|
|
61
|
+
details: list[VerdictDetail] = []
|
|
62
|
+
for d in details_raw or []:
|
|
63
|
+
if isinstance(d, dict):
|
|
64
|
+
details.append(VerdictDetail(
|
|
65
|
+
criterion=d.get("criterion", ""),
|
|
66
|
+
passed=bool(d.get("passed", False)),
|
|
67
|
+
score=float(d.get("score", 0.0)),
|
|
68
|
+
feedback=d.get("feedback", ""),
|
|
69
|
+
))
|
|
70
|
+
if isinstance(raw, dict):
|
|
71
|
+
return Verdict(
|
|
72
|
+
passed=bool(raw.get("passed", False)),
|
|
73
|
+
overall_score=float(raw.get("overallScore", raw.get("overall_score", 0.0))),
|
|
74
|
+
feedback=raw.get("feedback", ""),
|
|
75
|
+
details=details,
|
|
76
|
+
)
|
|
77
|
+
return Verdict(
|
|
78
|
+
passed=bool(getattr(raw, "passed", False)),
|
|
79
|
+
overall_score=float(getattr(raw, "overall_score", 0.0)),
|
|
80
|
+
feedback=getattr(raw, "feedback", ""),
|
|
81
|
+
details=details,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def verdict_output_schema() -> dict[str, Any]:
|
|
86
|
+
"""The JSON Schema the kernel expects judge output to conform to."""
|
|
87
|
+
return json.loads(_kernel_verdict_output_schema(False))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
async def judge(
|
|
91
|
+
provider: LLMProvider,
|
|
92
|
+
goal: str,
|
|
93
|
+
criteria: list[Criterion],
|
|
94
|
+
result: str,
|
|
95
|
+
) -> Verdict:
|
|
96
|
+
"""Run one judge pass: render eval prompt, stream the provider, parse the verdict."""
|
|
97
|
+
msgs = build_eval_messages(goal, criteria, result)
|
|
98
|
+
sys_text = "\n\n".join(m.get("content", "") if isinstance(m, dict) else getattr(m, "content", "")
|
|
99
|
+
for m in msgs
|
|
100
|
+
if (m.get("role") if isinstance(m, dict) else getattr(m, "role", None)) == "system")
|
|
101
|
+
turns = [m for m in msgs if (m.get("role") if isinstance(m, dict) else getattr(m, "role", None)) != "system"]
|
|
102
|
+
ctx: RenderedContext = {"systemText": sys_text, "turns": turns} # type: ignore[assignment]
|
|
103
|
+
|
|
104
|
+
text = ""
|
|
105
|
+
async for evt in provider.stream(ctx, [], None):
|
|
106
|
+
if (evt.get("type") if isinstance(evt, dict) else getattr(evt, "type", None)) == "text_delta":
|
|
107
|
+
text += (evt.get("delta") if isinstance(evt, dict) else getattr(evt, "delta", "")) or ""
|
|
108
|
+
if not text:
|
|
109
|
+
raise RuntimeError("judge: provider produced no text")
|
|
110
|
+
return parse_verdict(text)
|
|
@@ -76,6 +76,10 @@ def skill_metadata_to_kernel(skill: Any) -> dict[str, Any]:
|
|
|
76
76
|
out["when_to_use"] = when_to_use
|
|
77
77
|
if effort is not None:
|
|
78
78
|
out["effort"] = effort
|
|
79
|
+
# P1-B: forward declared tool ids (additive; omitted when empty so existing skills' wire is unchanged).
|
|
80
|
+
allowed_tools = getattr(skill, "allowed_tools", None)
|
|
81
|
+
if allowed_tools:
|
|
82
|
+
out["allowed_tools"] = list(allowed_tools)
|
|
79
83
|
return out
|
|
80
84
|
|
|
81
85
|
|