deepstrike 0.2.41__tar.gz → 0.2.43__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.41 → deepstrike-0.2.43}/Cargo.lock +6 -6
- {deepstrike-0.2.41 → deepstrike-0.2.43}/Cargo.toml +2 -2
- {deepstrike-0.2.41 → deepstrike-0.2.43}/PKG-INFO +2 -2
- {deepstrike-0.2.41 → deepstrike-0.2.43}/README.md +1 -1
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/manager.rs +3 -3
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/renderer.rs +11 -6
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/summarizer.rs +57 -7
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/task_state.rs +55 -3
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/utility.rs +36 -29
- deepstrike-0.2.43/crates/deepstrike-core/src/lexical.rs +178 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/lib.rs +1 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/memory/curator.rs +16 -12
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/mm/handle.rs +19 -10
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/runtime/kernel/protocol.rs +10 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/runtime/kernel/tests.rs +103 -32
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/rollback.rs +11 -11
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/state_machine/eviction.rs +21 -1
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/state_machine/gate.rs +89 -60
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/state_machine/milestone_exec.rs +1 -2
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/state_machine/mod.rs +102 -74
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/state_machine/process.rs +11 -19
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/state_machine/tests.rs +506 -17
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/state_machine/workflow.rs +69 -58
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/types/milestone.rs +2 -1
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/__init__.py +2 -2
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/harness/harness.py +7 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/runner.py +265 -75
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/types/agent.py +9 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/pyproject.toml +1 -1
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/Cargo.toml +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/benches/kernel_baseline.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/benchmark/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/compression.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/config.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/fault.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/partitions.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/policy.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/pressure.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/renewal.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/skill_catalog.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/text.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/token_engine.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/context/units.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/governance/constraint.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/governance/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/governance/permission.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/governance/pipeline.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/governance/quota.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/governance/rate_limit.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/governance/repeat_fuse.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/governance/veto.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/harness/eval.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/harness/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/memory/durable.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/memory/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/mm/memory.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/mm/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/mm/value.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/orchestration/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/orchestration/task_graph.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/orchestration/tournament.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/orchestration/workflow/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/orchestration/workflow/run.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/proc/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/runtime/event_log.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/runtime/kernel/runtime.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/runtime/kernel.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/runtime/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/runtime/repair.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/runtime/replay.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/runtime/session.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/entropy.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/milestone.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/policy.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/state_machine/cancellation.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/state_machine/capability.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/state_machine/signal.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/scheduler/tcb.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/signals/attention.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/signals/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/signals/queue.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/signals/router.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/syscall/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/types/agent.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/types/capability.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/types/contract.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/types/error.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/types/message.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/types/mod.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/types/policy.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/types/result.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/types/signal.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/types/skill.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-core/src/types/task.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-py/Cargo.toml +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/crates/deepstrike-py/src/lib.rs +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/collaboration/__init__.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/collaboration/contract.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/collaboration/handoff.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/collaboration/harness.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/collaboration/modes.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/collaboration/pool.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/governance.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/harness/__init__.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/harness/judge.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/kernel/__init__.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/knowledge/__init__.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/knowledge/source.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/memory/__init__.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/memory/agent.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/memory/extraction.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/memory/in_memory_store.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/memory/protocols.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/memory/ranking.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/memory/retention.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/memory/working.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/__init__.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/anthropic.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/anthropic_compatible.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/base.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/deepseek.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/factories.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/gemini.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/glm.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/kimi.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/minimax.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/ollama.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/openai.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/openai_responses.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/qwen.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/replay.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/replay_validator.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/stream.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/providers/vendor_profiles.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/__init__.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/archive.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/context_policy.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/credential_vault.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/eval.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/event_stream.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/execution_plane.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/facade.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/filtered_plane.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/kernel_event_log.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/kernel_rebuild.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/kernel_step.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/kernel_transaction_log.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/large_result_spool.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/loop_driver.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/mcp_proxy_plane.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/os_profile.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/os_snapshot.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/output_schema.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/process_sandbox_plane.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/provider_replay.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/reaction_checkpoint.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/reactive_session.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/reducers.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/reliability.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/remote_vpc_plane.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/replay_fixture.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/replay_provider.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/replay_sanitize.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/run_group.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/session_log.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/session_repair.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/sub_agent_orchestrator.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/turn_policy.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/workflow_control_flow.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/workflow_store.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/runtime/worktree_plane.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/safety/__init__.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/safety/permissions.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/signals/__init__.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/signals/gateway.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/signals/scheduled.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/signals/types.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/skills/__init__.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/skills/loader.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/skills/registry.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/skills/watcher.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/tools/__init__.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/tools/builtin/__init__.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/tools/builtin/read_file.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/tools/errors.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/tools/execution.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/deepstrike/tools/registry.py +0 -0
- {deepstrike-0.2.41 → deepstrike-0.2.43}/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.43"
|
|
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-lab"
|
|
209
|
-
version = "0.2.
|
|
209
|
+
version = "0.2.43"
|
|
210
210
|
dependencies = [
|
|
211
211
|
"deepstrike-core",
|
|
212
212
|
"serde",
|
|
@@ -216,7 +216,7 @@ dependencies = [
|
|
|
216
216
|
|
|
217
217
|
[[package]]
|
|
218
218
|
name = "deepstrike-node"
|
|
219
|
-
version = "0.2.
|
|
219
|
+
version = "0.2.43"
|
|
220
220
|
dependencies = [
|
|
221
221
|
"compact_str",
|
|
222
222
|
"deepstrike-core",
|
|
@@ -228,7 +228,7 @@ dependencies = [
|
|
|
228
228
|
|
|
229
229
|
[[package]]
|
|
230
230
|
name = "deepstrike-py"
|
|
231
|
-
version = "0.2.
|
|
231
|
+
version = "0.2.43"
|
|
232
232
|
dependencies = [
|
|
233
233
|
"compact_str",
|
|
234
234
|
"deepstrike-core",
|
|
@@ -239,7 +239,7 @@ dependencies = [
|
|
|
239
239
|
|
|
240
240
|
[[package]]
|
|
241
241
|
name = "deepstrike-sdk"
|
|
242
|
-
version = "0.2.
|
|
242
|
+
version = "0.2.43"
|
|
243
243
|
dependencies = [
|
|
244
244
|
"async-stream",
|
|
245
245
|
"async-trait",
|
|
@@ -273,7 +273,7 @@ dependencies = [
|
|
|
273
273
|
|
|
274
274
|
[[package]]
|
|
275
275
|
name = "deepstrike-wasm"
|
|
276
|
-
version = "0.2.
|
|
276
|
+
version = "0.2.43"
|
|
277
277
|
dependencies = [
|
|
278
278
|
"compact_str",
|
|
279
279
|
"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.43"
|
|
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.43" }
|
|
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.43
|
|
4
4
|
Requires-Dist: httpx>=0.27
|
|
5
5
|
Requires-Dist: pyyaml>=6.0
|
|
6
6
|
Requires-Dist: anyio>=4.0
|
|
@@ -206,7 +206,7 @@ A node's `kind` selects the control-flow shape; the same executor drives them al
|
|
|
206
206
|
|
|
207
207
|
### 0.2.11 capabilities
|
|
208
208
|
|
|
209
|
-
- **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`.
|
|
209
|
+
- **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`. Governance rejection fails the submitting node instead of acknowledging work that was never appended.
|
|
210
210
|
- **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).
|
|
211
211
|
- **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).
|
|
212
212
|
- **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.
|
|
@@ -193,7 +193,7 @@ A node's `kind` selects the control-flow shape; the same executor drives them al
|
|
|
193
193
|
|
|
194
194
|
### 0.2.11 capabilities
|
|
195
195
|
|
|
196
|
-
- **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`.
|
|
196
|
+
- **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`. Governance rejection fails the submitting node instead of acknowledging work that was never appended.
|
|
197
197
|
- **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).
|
|
198
198
|
- **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).
|
|
199
199
|
- **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.
|
|
@@ -798,9 +798,9 @@ impl ContextManager {
|
|
|
798
798
|
}
|
|
799
799
|
Some(ToolSchema {
|
|
800
800
|
name: CompactString::new(READ_RESULT_TOOL_NAME),
|
|
801
|
-
description: "Re-read a tool result that was
|
|
802
|
-
|
|
803
|
-
Pass
|
|
801
|
+
description: "Re-read the full content of a tool result that was truncated to save \
|
|
802
|
+
context (marked '[Output truncated: … call the read_result tool …]'). \
|
|
803
|
+
Pass that marker's call_id; use offset/max_bytes to page through \
|
|
804
804
|
large content."
|
|
805
805
|
.to_string(),
|
|
806
806
|
parameters: serde_json::json!({
|
|
@@ -256,7 +256,10 @@ fn normalize_turn_prefix(turns: &mut Vec<Message>) {
|
|
|
256
256
|
/// preview, leaving a marker. Non-destructive — the full output stays in `partitions.history`;
|
|
257
257
|
/// only the rendered copy shrinks. Un-collapse is boundary-only (P0-C): handles re-evaluate
|
|
258
258
|
/// from Resident at the next compaction/renewal, never mid-generation (cache-safe monotonic).
|
|
259
|
-
|
|
259
|
+
/// Trained-convention truncation marker: says what was cut and how to get it back (the
|
|
260
|
+
/// `read_result` tool + this result's `call_id`), instead of kernel-internal vocabulary
|
|
261
|
+
/// ("projected out of view") the model has never seen in training.
|
|
262
|
+
fn collapse_preview(output: &str, call_id: &str) -> String {
|
|
260
263
|
const PREVIEW_BYTES: usize = 160;
|
|
261
264
|
let mut end = PREVIEW_BYTES.min(output.len());
|
|
262
265
|
while end > 0 && !output.is_char_boundary(end) {
|
|
@@ -264,7 +267,7 @@ fn collapse_preview(output: &str) -> String {
|
|
|
264
267
|
}
|
|
265
268
|
let dropped = output.len().saturating_sub(end);
|
|
266
269
|
format!(
|
|
267
|
-
"{}…\n[
|
|
270
|
+
"{}…\n[Output truncated: {dropped} bytes omitted. Call the read_result tool with call_id \"{call_id}\" to re-read the full result.]",
|
|
268
271
|
&output[..end]
|
|
269
272
|
)
|
|
270
273
|
}
|
|
@@ -322,7 +325,7 @@ fn project_message(msg: &Message, handles: &HandleTable) -> Option<Message> {
|
|
|
322
325
|
changed = true;
|
|
323
326
|
ContentPart::ToolResult {
|
|
324
327
|
call_id: call_id.clone(),
|
|
325
|
-
output: collapse_preview(output),
|
|
328
|
+
output: collapse_preview(output, call_id.as_str()),
|
|
326
329
|
is_error: *is_error,
|
|
327
330
|
}
|
|
328
331
|
}
|
|
@@ -656,8 +659,10 @@ mod tests {
|
|
|
656
659
|
_ => None,
|
|
657
660
|
})
|
|
658
661
|
.expect("tool result rendered");
|
|
659
|
-
// Rendered copy is a preview
|
|
660
|
-
assert!(rendered.contains("[
|
|
662
|
+
// Rendered copy is a preview with the trained truncation phrasing + retrieval instruction.
|
|
663
|
+
assert!(rendered.contains("[Output truncated:"));
|
|
664
|
+
assert!(rendered.contains("read_result"));
|
|
665
|
+
assert!(rendered.contains("\"c1\""));
|
|
661
666
|
assert!(rendered.len() < long.len());
|
|
662
667
|
let stored = match &c.history.messages[0].content {
|
|
663
668
|
Content::Parts(parts) => match &parts[0] {
|
|
@@ -700,7 +705,7 @@ mod tests {
|
|
|
700
705
|
})
|
|
701
706
|
.expect("tool result rendered");
|
|
702
707
|
assert_eq!(rendered, body);
|
|
703
|
-
assert!(!rendered.contains("[
|
|
708
|
+
assert!(!rendered.contains("[Output truncated:"));
|
|
704
709
|
}
|
|
705
710
|
|
|
706
711
|
// ── P1-F: state-turn recency footer ───────────────────────────────────
|
|
@@ -5,6 +5,11 @@ use crate::types::message::{Content, ContentPart, Message};
|
|
|
5
5
|
/// Deterministic six-slot summariser used before archived units page out.
|
|
6
6
|
pub struct RuleSummarizer;
|
|
7
7
|
|
|
8
|
+
/// Items rendered per slot before an honest `(+N more)` line. Bounds each digest so the
|
|
9
|
+
/// task-state compression history can afford to keep MANY digests visible instead of a few
|
|
10
|
+
/// bloated ones.
|
|
11
|
+
const SLOT_ITEM_CAP: usize = 6;
|
|
12
|
+
|
|
8
13
|
impl RuleSummarizer {
|
|
9
14
|
/// Produce a structured summary whose char-approx token count never exceeds
|
|
10
15
|
/// `max_tokens`. Slot order is the deterministic truncation priority.
|
|
@@ -95,10 +100,18 @@ impl RuleSummarizer {
|
|
|
95
100
|
if values.is_empty() {
|
|
96
101
|
push_line(&mut output, "- none", max_tokens, &engine);
|
|
97
102
|
} else {
|
|
98
|
-
for value in values {
|
|
103
|
+
for value in values.iter().take(SLOT_ITEM_CAP) {
|
|
99
104
|
push_line(
|
|
100
105
|
&mut output,
|
|
101
|
-
&format!("- {}", compact(
|
|
106
|
+
&format!("- {}", compact(value, 240)),
|
|
107
|
+
max_tokens,
|
|
108
|
+
&engine,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
if values.len() > SLOT_ITEM_CAP {
|
|
112
|
+
push_line(
|
|
113
|
+
&mut output,
|
|
114
|
+
&format!("- (+{} more)", values.len() - SLOT_ITEM_CAP),
|
|
102
115
|
max_tokens,
|
|
103
116
|
&engine,
|
|
104
117
|
);
|
|
@@ -126,6 +139,9 @@ struct SummarySlots {
|
|
|
126
139
|
|
|
127
140
|
fn classify_text(text: &str, slots: &mut SummarySlots) {
|
|
128
141
|
for statement in statements(text) {
|
|
142
|
+
if is_diff_noise(&statement) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
129
145
|
let folded = statement.to_lowercase();
|
|
130
146
|
if contains_any(
|
|
131
147
|
&folded,
|
|
@@ -199,11 +215,45 @@ fn classify_text(text: &str, slots: &mut SummarySlots) {
|
|
|
199
215
|
}
|
|
200
216
|
|
|
201
217
|
fn statements(text: &str) -> Vec<String> {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
218
|
+
let mut output = Vec::new();
|
|
219
|
+
let mut current = String::new();
|
|
220
|
+
let mut chars = text.chars().peekable();
|
|
221
|
+
while let Some(character) = chars.next() {
|
|
222
|
+
let boundary = match character {
|
|
223
|
+
'\n' | '!' | '?' | ';' | '。' | '!' | '?' | ';' => true,
|
|
224
|
+
// A '.' splits only at a sentence boundary (followed by whitespace or end of
|
|
225
|
+
// text). Splitting inside paths/versions shredded `src/auth.js` into useless
|
|
226
|
+
// `js b/src/auth` fragments that polluted every digest.
|
|
227
|
+
'.' => chars.peek().is_none_or(|next| next.is_whitespace()),
|
|
228
|
+
_ => false,
|
|
229
|
+
};
|
|
230
|
+
if boundary {
|
|
231
|
+
flush_statement(&mut current, &mut output);
|
|
232
|
+
} else {
|
|
233
|
+
current.push(character);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
flush_statement(&mut current, &mut output);
|
|
237
|
+
output
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
fn flush_statement(current: &mut String, output: &mut Vec<String>) {
|
|
241
|
+
let statement = current.trim();
|
|
242
|
+
if !statement.is_empty() {
|
|
243
|
+
output.push(compact(statement, 240));
|
|
244
|
+
}
|
|
245
|
+
current.clear();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/// Structural diff/patch header lines carry no summarizable content — dropping them keeps
|
|
249
|
+
/// digests dense so more real process state survives a given summary budget.
|
|
250
|
+
fn is_diff_noise(statement: &str) -> bool {
|
|
251
|
+
let trimmed = statement.trim_start();
|
|
252
|
+
trimmed.starts_with("diff --git")
|
|
253
|
+
|| trimmed.starts_with("+++")
|
|
254
|
+
|| trimmed.starts_with("---")
|
|
255
|
+
|| trimmed.starts_with("@@")
|
|
256
|
+
|| trimmed.starts_with("index ")
|
|
207
257
|
}
|
|
208
258
|
|
|
209
259
|
fn contains_any(text: &str, markers: &[&str]) -> bool {
|
|
@@ -46,10 +46,19 @@ pub struct TaskState {
|
|
|
46
46
|
/// so updating it never churns the prompt cache. Bounded + recency-ordered; newest last.
|
|
47
47
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
48
48
|
pub recent_actions: Vec<String>,
|
|
49
|
-
///
|
|
49
|
+
/// Rolling log of compression events, newest last. Bounded at [`MAX_COMPRESSION_LOG`]
|
|
50
|
+
/// (oldest dropped past the cap, counted in `compression_log_dropped`).
|
|
50
51
|
/// Rendered into systemVolatile so the model always sees compression history.
|
|
51
52
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
52
53
|
pub compression_log: Vec<CompressionEntry>,
|
|
54
|
+
/// Entries dropped from `compression_log` past its cap — kept so the render stays honest
|
|
55
|
+
/// about how much history is no longer visible.
|
|
56
|
+
#[serde(default, skip_serializing_if = "u64_is_zero")]
|
|
57
|
+
pub compression_log_dropped: u64,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn u64_is_zero(value: &u64) -> bool {
|
|
61
|
+
*value == 0
|
|
53
62
|
}
|
|
54
63
|
|
|
55
64
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
@@ -73,6 +82,9 @@ pub const MAX_DIRECTIVES: usize = 8;
|
|
|
73
82
|
/// Maximum recent action-turns retained for the recency footer (bounded ring).
|
|
74
83
|
pub const MAX_RECENT_ACTIONS: usize = 6;
|
|
75
84
|
|
|
85
|
+
/// Compression-log entries retained in state (rolling window; render shows the last 3).
|
|
86
|
+
pub const MAX_COMPRESSION_LOG: usize = 64;
|
|
87
|
+
|
|
76
88
|
/// Partial update applied by the SDK or via `update_plan` meta-tool.
|
|
77
89
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
78
90
|
pub struct TaskUpdate {
|
|
@@ -136,7 +148,14 @@ impl TaskState {
|
|
|
136
148
|
lines.push(format!("scratchpad: {}", self.scratchpad));
|
|
137
149
|
}
|
|
138
150
|
|
|
139
|
-
// Render the most recent compression events (cap
|
|
151
|
+
// Render only the most recent compression events (fixed cap of 3). A wider budgeted
|
|
152
|
+
// window was tried and withdrawn: live A/B at n≤4 (12-PR review @ 2048, DeepSeek)
|
|
153
|
+
// could not distinguish it from provider drift — a control run on the unmodified
|
|
154
|
+
// kernel showed the same failures — and a window full of old `tool X args` digest
|
|
155
|
+
// lines is plausible re-execution bait. Absent replay-lab evidence FOR widening, the
|
|
156
|
+
// proven shape stays. Older digests remain in `compression_log` (bounded at
|
|
157
|
+
// [`MAX_COMPRESSION_LOG`]); making their *content* durably useful to the model is
|
|
158
|
+
// semantic-summary (P2) territory, not a bigger raw window.
|
|
140
159
|
if !self.compression_log.is_empty() {
|
|
141
160
|
lines.push("compression_history:".to_string());
|
|
142
161
|
let start = self.compression_log.len().saturating_sub(3);
|
|
@@ -184,12 +203,18 @@ impl TaskState {
|
|
|
184
203
|
}
|
|
185
204
|
}
|
|
186
205
|
|
|
187
|
-
/// Append a compression event to the log
|
|
206
|
+
/// Append a compression event to the log (bounded at [`MAX_COMPRESSION_LOG`]; the oldest
|
|
207
|
+
/// entries are dropped past the cap and counted so the render can report them).
|
|
188
208
|
pub fn log_compression(&mut self, action: &str, summary: String) {
|
|
189
209
|
self.compression_log.push(CompressionEntry {
|
|
190
210
|
action: action.to_string(),
|
|
191
211
|
summary,
|
|
192
212
|
});
|
|
213
|
+
if self.compression_log.len() > MAX_COMPRESSION_LOG {
|
|
214
|
+
let overflow = self.compression_log.len() - MAX_COMPRESSION_LOG;
|
|
215
|
+
self.compression_log.drain(0..overflow);
|
|
216
|
+
self.compression_log_dropped += overflow as u64;
|
|
217
|
+
}
|
|
193
218
|
}
|
|
194
219
|
|
|
195
220
|
pub fn apply(&mut self, update: TaskUpdate) {
|
|
@@ -349,6 +374,33 @@ mod tests {
|
|
|
349
374
|
assert_eq!(ts.directives.len(), MAX_DIRECTIVES);
|
|
350
375
|
}
|
|
351
376
|
|
|
377
|
+
#[test]
|
|
378
|
+
fn compression_render_shows_exactly_the_last_three_digests() {
|
|
379
|
+
// Pin the 3-entry window (see format_compact for why widening was withdrawn) so a
|
|
380
|
+
// future widening must re-justify itself with replay-lab evidence.
|
|
381
|
+
let mut ts = TaskState::default();
|
|
382
|
+
ts.goal = "review PRs".into();
|
|
383
|
+
for n in 1..=10 {
|
|
384
|
+
ts.log_compression("auto_compact", format!("digest {n}"));
|
|
385
|
+
}
|
|
386
|
+
let rendered = ts.format_compact();
|
|
387
|
+
assert!(!rendered.contains("digest 7\n"));
|
|
388
|
+
assert!(rendered.contains("digest 8"));
|
|
389
|
+
assert!(rendered.contains("digest 9"));
|
|
390
|
+
assert!(rendered.contains("digest 10"));
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
#[test]
|
|
394
|
+
fn compression_log_is_bounded_and_counts_drops() {
|
|
395
|
+
let mut ts = TaskState::default();
|
|
396
|
+
for n in 0..(MAX_COMPRESSION_LOG + 5) {
|
|
397
|
+
ts.log_compression("micro_compact", format!("d{n}"));
|
|
398
|
+
}
|
|
399
|
+
assert_eq!(ts.compression_log.len(), MAX_COMPRESSION_LOG);
|
|
400
|
+
assert_eq!(ts.compression_log_dropped, 5);
|
|
401
|
+
assert_eq!(ts.compression_log[0].summary, "d5");
|
|
402
|
+
}
|
|
403
|
+
|
|
352
404
|
#[test]
|
|
353
405
|
fn apply_updates_fields() {
|
|
354
406
|
let mut ts = TaskState::default();
|
|
@@ -6,6 +6,7 @@ use std::ops::Range;
|
|
|
6
6
|
|
|
7
7
|
use super::token_engine::ContextTokenEngine;
|
|
8
8
|
use super::units::unit_boundaries;
|
|
9
|
+
use crate::lexical::{overlap_count, terms};
|
|
9
10
|
use crate::types::message::{Content, ContentPart, Message};
|
|
10
11
|
|
|
11
12
|
pub struct UtilitySelectionContext<'a> {
|
|
@@ -215,35 +216,6 @@ fn unit_text(messages: &[Message]) -> String {
|
|
|
215
216
|
parts.join("\n")
|
|
216
217
|
}
|
|
217
218
|
|
|
218
|
-
fn terms(text: &str) -> BTreeSet<String> {
|
|
219
|
-
let mut output = BTreeSet::new();
|
|
220
|
-
let mut ascii = String::new();
|
|
221
|
-
for character in text.chars().flat_map(char::to_lowercase) {
|
|
222
|
-
if character.is_ascii_alphanumeric() || matches!(character, '_' | '-' | '/' | '.' | ':') {
|
|
223
|
-
ascii.push(character);
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
if !ascii.is_empty() {
|
|
227
|
-
if ascii.chars().count() > 1 {
|
|
228
|
-
output.insert(std::mem::take(&mut ascii));
|
|
229
|
-
} else {
|
|
230
|
-
ascii.clear();
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if !character.is_whitespace() && !character.is_ascii_punctuation() {
|
|
234
|
-
output.insert(character.to_string());
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
if ascii.chars().count() > 1 {
|
|
238
|
-
output.insert(ascii);
|
|
239
|
-
}
|
|
240
|
-
output
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
fn overlap_count(left: &BTreeSet<String>, right: &BTreeSet<String>) -> u32 {
|
|
244
|
-
left.intersection(right).count() as u32
|
|
245
|
-
}
|
|
246
|
-
|
|
247
219
|
fn contains_folded(text: &str, pattern: &str) -> bool {
|
|
248
220
|
!pattern.trim().is_empty() && text.to_lowercase().contains(&pattern.to_lowercase())
|
|
249
221
|
}
|
|
@@ -367,6 +339,41 @@ mod tests {
|
|
|
367
339
|
assert_eq!(plan.retained_tokens, 40);
|
|
368
340
|
}
|
|
369
341
|
|
|
342
|
+
#[test]
|
|
343
|
+
fn chinese_directive_dependency_requires_bigram_overlap_not_shared_characters() {
|
|
344
|
+
// Under the old per-character CJK vocabulary, any Chinese unit sharing two
|
|
345
|
+
// common characters (中/文/回…) with an active directive was marked mandatory,
|
|
346
|
+
// so compression could never archive unrelated Chinese history.
|
|
347
|
+
let mut unrelated = Message::assistant("我们在文中回顾了天气");
|
|
348
|
+
unrelated.token_count = Some(30);
|
|
349
|
+
let mut on_topic = Message::user("已按要求保持中文回答");
|
|
350
|
+
on_topic.token_count = Some(30);
|
|
351
|
+
let mut recent = Message::user("recent");
|
|
352
|
+
recent.token_count = Some(10);
|
|
353
|
+
let messages = vec![unrelated, on_topic, recent];
|
|
354
|
+
let plan = plan_utility_archive(
|
|
355
|
+
&messages,
|
|
356
|
+
70,
|
|
357
|
+
10,
|
|
358
|
+
1,
|
|
359
|
+
&ContextTokenEngine::char_approx(),
|
|
360
|
+
&UtilitySelectionContext {
|
|
361
|
+
goal: "",
|
|
362
|
+
criteria: &[],
|
|
363
|
+
preserved_refs: &[],
|
|
364
|
+
active_directives: &["必须用中文回答".into()],
|
|
365
|
+
},
|
|
366
|
+
);
|
|
367
|
+
assert!(
|
|
368
|
+
!plan.scores[0].mandatory,
|
|
369
|
+
"unrelated Chinese text must not bind to the directive"
|
|
370
|
+
);
|
|
371
|
+
assert!(
|
|
372
|
+
plan.scores[1].mandatory,
|
|
373
|
+
"text restating the directive must stay mandatory"
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
370
377
|
#[test]
|
|
371
378
|
fn preserved_ref_keeps_complete_tool_unit() {
|
|
372
379
|
let mut call = Message::assistant("read artifact");
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
//! Shared lexical vocabulary for deterministic in-kernel value scoring.
|
|
2
|
+
//!
|
|
3
|
+
//! Compression utility (goal overlap, directive dependency) and memory curation
|
|
4
|
+
//! (fuzzy dedupe) must speak ONE term vocabulary — and it must discriminate for
|
|
5
|
+
//! CJK text, where there is no whitespace between words: per-character unigrams
|
|
6
|
+
//! make every Chinese unit overlap every Chinese goal, and whitespace tokens
|
|
7
|
+
//! make a whole Chinese sentence a single token. Terms here are lowercased word
|
|
8
|
+
//! runs plus Han character bigrams, matching the host-side recall rankers
|
|
9
|
+
//! (node `memory/ranking.ts`, python `memory/ranking.py`) so knowledge
|
|
10
|
+
//! residency, compression, and memory recall share one notion of "relevant".
|
|
11
|
+
|
|
12
|
+
use std::collections::BTreeSet;
|
|
13
|
+
|
|
14
|
+
/// Deterministic term set of `text`.
|
|
15
|
+
///
|
|
16
|
+
/// - ASCII word runs (`[a-z0-9_\-/.:]`, lowercased) of more than one char are
|
|
17
|
+
/// one term each — call ids, paths, and URLs stay whole.
|
|
18
|
+
/// - Non-ASCII alphanumeric runs of more than one char are one term each; a
|
|
19
|
+
/// lone non-ASCII char is kept as its own term.
|
|
20
|
+
/// - Runs containing Han chars additionally emit every adjacent-char bigram,
|
|
21
|
+
/// the standard whitespace-free segmentation proxy.
|
|
22
|
+
///
|
|
23
|
+
/// Punctuation (ASCII and fullwidth alike) and whitespace never become terms.
|
|
24
|
+
pub fn terms(text: &str) -> BTreeSet<String> {
|
|
25
|
+
let mut output = BTreeSet::new();
|
|
26
|
+
let mut ascii_run = String::new();
|
|
27
|
+
let mut wide_run: Vec<char> = Vec::new();
|
|
28
|
+
for character in text.chars().flat_map(char::to_lowercase) {
|
|
29
|
+
if character.is_ascii_alphanumeric() || matches!(character, '_' | '-' | '/' | '.' | ':') {
|
|
30
|
+
flush_wide(&mut wide_run, &mut output);
|
|
31
|
+
ascii_run.push(character);
|
|
32
|
+
} else if !character.is_ascii() && character.is_alphanumeric() {
|
|
33
|
+
flush_ascii(&mut ascii_run, &mut output);
|
|
34
|
+
wide_run.push(character);
|
|
35
|
+
} else {
|
|
36
|
+
flush_ascii(&mut ascii_run, &mut output);
|
|
37
|
+
flush_wide(&mut wide_run, &mut output);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
flush_ascii(&mut ascii_run, &mut output);
|
|
41
|
+
flush_wide(&mut wide_run, &mut output);
|
|
42
|
+
output
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Number of terms shared by both sets.
|
|
46
|
+
pub fn overlap_count(left: &BTreeSet<String>, right: &BTreeSet<String>) -> u32 {
|
|
47
|
+
left.intersection(right).count() as u32
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Term-set Jaccard similarity over the shared vocabulary.
|
|
51
|
+
///
|
|
52
|
+
/// Inputs whose combined vocabulary is empty score 0.0 — no lexical evidence is
|
|
53
|
+
/// treated as "not a duplicate", so degenerate content fails open (both records
|
|
54
|
+
/// are kept) rather than merging on nothing.
|
|
55
|
+
pub fn jaccard(left: &str, right: &str) -> f64 {
|
|
56
|
+
let left = terms(left);
|
|
57
|
+
let right = terms(right);
|
|
58
|
+
let union = left.union(&right).count();
|
|
59
|
+
if union == 0 {
|
|
60
|
+
return 0.0;
|
|
61
|
+
}
|
|
62
|
+
left.intersection(&right).count() as f64 / union as f64
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fn flush_ascii(run: &mut String, output: &mut BTreeSet<String>) {
|
|
66
|
+
// All-ASCII, so byte length equals char count.
|
|
67
|
+
if run.len() > 1 {
|
|
68
|
+
output.insert(std::mem::take(run));
|
|
69
|
+
} else {
|
|
70
|
+
run.clear();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fn flush_wide(run: &mut Vec<char>, output: &mut BTreeSet<String>) {
|
|
75
|
+
match run.len() {
|
|
76
|
+
0 => return,
|
|
77
|
+
1 => {
|
|
78
|
+
output.insert(run[0].to_string());
|
|
79
|
+
}
|
|
80
|
+
_ => {
|
|
81
|
+
output.insert(run.iter().collect());
|
|
82
|
+
if run.iter().copied().any(is_han) {
|
|
83
|
+
for pair in run.windows(2) {
|
|
84
|
+
output.insert(pair.iter().collect());
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
run.clear();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Han ranges mirrored from the host rankers (BMP unified + ext A, compat, ext B+).
|
|
93
|
+
fn is_han(character: char) -> bool {
|
|
94
|
+
matches!(
|
|
95
|
+
u32::from(character),
|
|
96
|
+
0x3400..=0x4DBF | 0x4E00..=0x9FFF | 0xF900..=0xFAFF | 0x20000..=0x3134F
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#[cfg(test)]
|
|
101
|
+
mod tests {
|
|
102
|
+
use super::*;
|
|
103
|
+
|
|
104
|
+
#[test]
|
|
105
|
+
fn ascii_words_paths_and_ids_stay_whole() {
|
|
106
|
+
let output = terms("Read src/main.rs via call_abc-123, then STOP.");
|
|
107
|
+
assert!(output.contains("read"));
|
|
108
|
+
assert!(output.contains("src/main.rs"));
|
|
109
|
+
assert!(output.contains("call_abc-123"));
|
|
110
|
+
// Single ASCII chars never become terms; punctuation is stripped.
|
|
111
|
+
assert!(!output.contains("a"));
|
|
112
|
+
assert!(!output.contains(","));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#[test]
|
|
116
|
+
fn han_runs_emit_bigrams_not_unigrams() {
|
|
117
|
+
let output = terms("实现用户登录");
|
|
118
|
+
assert!(output.contains("实现"));
|
|
119
|
+
assert!(output.contains("用户"));
|
|
120
|
+
assert!(output.contains("户登"));
|
|
121
|
+
assert!(output.contains("实现用户登录"));
|
|
122
|
+
assert!(!output.contains("实"), "unigram noise must be gone");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#[test]
|
|
126
|
+
fn chinese_goal_overlap_discriminates() {
|
|
127
|
+
let goal = terms("实现用户登录功能");
|
|
128
|
+
let on_topic = terms("已完成用户登录表单");
|
|
129
|
+
let off_topic = terms("今天天气很好我们去公园");
|
|
130
|
+
assert!(overlap_count(&goal, &on_topic) >= 2);
|
|
131
|
+
assert_eq!(overlap_count(&goal, &off_topic), 0);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
#[test]
|
|
135
|
+
fn fullwidth_punctuation_is_not_a_term() {
|
|
136
|
+
let output = terms("完成了。下一步:测试!");
|
|
137
|
+
assert!(!output.contains("。"));
|
|
138
|
+
assert!(!output.contains(":"));
|
|
139
|
+
assert!(output.contains("完成"));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#[test]
|
|
143
|
+
fn lone_wide_char_is_kept() {
|
|
144
|
+
assert!(terms("改 a").contains("改"));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
#[test]
|
|
148
|
+
fn mixed_ascii_and_han_split_into_both_vocabularies() {
|
|
149
|
+
let output = terms("部署v2服务到prod环境");
|
|
150
|
+
assert!(output.contains("v2"));
|
|
151
|
+
assert!(output.contains("prod"));
|
|
152
|
+
assert!(output.contains("部署"));
|
|
153
|
+
assert!(output.contains("服务"));
|
|
154
|
+
assert!(output.contains("环境"));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#[test]
|
|
158
|
+
fn jaccard_near_duplicate_chinese_scores_high() {
|
|
159
|
+
let near = jaccard("用户偏好深色模式界面", "用户偏好浅色模式界面");
|
|
160
|
+
let unrelated = jaccard("用户偏好深色模式界面", "周五之前完成部署上线");
|
|
161
|
+
assert!(near > 0.5, "near-duplicates must be detectable: {near}");
|
|
162
|
+
assert!(unrelated < 0.2, "unrelated must stay low: {unrelated}");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
#[test]
|
|
166
|
+
fn jaccard_identical_english_is_one_and_empty_vocabulary_is_zero() {
|
|
167
|
+
assert_eq!(jaccard("prefer cargo nextest", "prefer cargo nextest"), 1.0);
|
|
168
|
+
assert_eq!(jaccard("", ""), 0.0);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#[test]
|
|
172
|
+
fn non_han_scripts_keep_whole_words_without_bigrams() {
|
|
173
|
+
let output = terms("привет мир");
|
|
174
|
+
assert!(output.contains("привет"));
|
|
175
|
+
assert!(output.contains("мир"));
|
|
176
|
+
assert!(!output.contains("пр"));
|
|
177
|
+
}
|
|
178
|
+
}
|