packages feed

shikumi-tools 0.4.1.0 → 0.4.1.1

raw patch · 14 files changed

+76/−62 lines, 14 filesdep ~baikaidep ~shikumidep ~shikumi-cachePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: baikai, shikumi, shikumi-cache, shikumi-tools

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,5 +1,19 @@ # Changelog +## Unreleased++## 0.4.1.1 — 2026-10-16++### Changed++- Require the compatible Shikumi patch releases built against Baikai 0.8.++- Upgrade `mori://shinzui/baikai/packages/baikai` to `>=0.8.0.0 && <0.9`.++### Fixed++- Correct Haddock markup and identifier links; no runtime or public API changes.+ ## 0.4.1.0 — 2026-09-30  - Give `ReAct`'s `ProtocolAuto` its own tool-calling check instead of reusing `Shikumi.Adapter.capabilityFor`. Behavior is unchanged: only `openai` over Chat Completions or Responses and `anthropic` over Messages use provider-native tool calling. CLI models, which `shikumi` 0.4.1.0 now treats as schema-native, still use the prompt tool protocol, because Baikai's CLI providers drop tools. Raise the internal `shikumi` bound to `^>=0.4.1.0` so this check never pairs with an older `shikumi`.
shikumi-tools.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.4 name: shikumi-tools-version: 0.4.1.0+version: 0.4.1.1 synopsis:   Typed tools and ReAct agents for shikumi LM programs (EP-11) @@ -68,7 +68,7 @@    build-depends:     aeson >=2.2 && <2.3,-    baikai >=0.7.1.0 && <0.8,+    baikai >=0.8.0.0 && <0.9,     base >=4.20 && <5,     bytestring >=0.11 && <0.13,     containers >=0.6 && <0.9,@@ -82,7 +82,7 @@     lens ^>=5.3,     process >=1.6 && <1.7,     regex-tdfa >=1.3 && <1.4,-    shikumi ^>=0.4.1.0,+    shikumi ^>=0.4.1.1,     text ^>=2.1,     vector >=0.13 && <0.14, @@ -122,7 +122,7 @@    build-depends:     aeson,-    baikai >=0.7.1.0 && <0.8,+    baikai >=0.8.0.0 && <0.9,     base,     bytestring,     containers,@@ -131,10 +131,10 @@     filepath,     generic-lens,     lens,-    shikumi ^>=0.4.1.0,-    shikumi-cache ^>=0.2.0.0,+    shikumi ^>=0.4.1.1,+    shikumi-cache ^>=0.2.0.2,     shikumi-testing ^>=0.1.0.0,-    shikumi-tools ^>=0.4.0.0,+    shikumi-tools ^>=0.4.1.1,     tasty,     tasty-hunit,     text,
src/Shikumi/Agent/ReAct.hs view
@@ -8,14 +8,14 @@ -- finished. The runtime executes the selected tool (via "Shikumi.Tool"), records the -- result as an /observation/, and asks the model again — until the model finishes or -- a @maxIters@ bound is hit. A final /extract/ step turns the finished--- 'Trajectory' into the typed output the caller wanted.+-- t'Trajectory' into the typed output the caller wanted. -- -- The whole loop is wrapped in 'Shikumi.Program.Embed', so @react@ is an ordinary--- @'Program' i o@: it runs under 'Shikumi.Program.runProgram' (the body needs only+-- @t'Program' i o@: it runs under 'Shikumi.Program.runProgram' (the body needs only -- @LLM@ + @Error ShikumiError@, integration point #4's exact row), is structurally -- inspectable, and composes with every other shikumi program and combinator. ----- Two tool /protocols/ are supported behind one internal interface ('ProtocolImpl'),+-- Two tool /protocols/ are supported behind one internal interface ([ProtocolImpl]("Shikumi.Agent.ReAct#t:ProtocolImpl")), -- selected by a 'ToolProtocol' value ('ProtocolAuto' resolves per model from a -- tool-calling table of first-party HTTP hosts, kept separate from -- "Shikumi.Adapter"'s schema-only 'Shikumi.Adapter.capabilityFor'): a provider-native function-calling path@@ -125,7 +125,7 @@  -- | One recorded (thought, action, observation) step. @observation@ is 'Nothing' -- for 'Finish'; for a tool call it is the tool result text or the rendered--- 'Shikumi.Tool.ToolError'.+-- t'Shikumi.Tool.ToolError'. data Step = Step   { thought :: !Text,     action :: !Action,@@ -136,7 +136,7 @@ -- | Why the loop stopped. 'TerminatedBudget' is retained for forward -- compatibility; the loop itself produces only 'TerminatedFinish' and -- 'TerminatedMaxIters' (the budget ceiling is enforced one layer down by the--- resilient @LLM@ interpreter, surfacing as a 'ShikumiError').+-- resilient @LLM@ interpreter, surfacing as a t'ShikumiError'). data Termination   = TerminatedFinish   | TerminatedMaxIters !Int@@ -182,7 +182,7 @@ -- Building agents -- --------------------------------------------------------------------------- --- | Build a ReAct agent as a @'Program' i o@ that returns the typed answer and+-- | Build a ReAct agent as a @t'Program' i o@ that returns the typed answer and -- drops the trajectory. The ergonomic default. react ::   forall i o.@@ -193,7 +193,7 @@   Program i o react sig reg cfg = FMap fst (reactWithTrajectory sig reg cfg) --- | Build a ReAct agent that also returns the recorded 'Trajectory', for+-- | Build a ReAct agent that also returns the recorded t'Trajectory', for -- evaluators/optimizers and tests that assert on the steps. When compaction ran, -- the trajectory contains 'Summarized' steps whose observations carry the summary. reactWithTrajectory ::@@ -205,7 +205,7 @@   Program i (o, Trajectory) reactWithTrajectory sig reg cfg = embed (reactLoop sig reg cfg) --- | The agent loop, embedded into a 'Program' by 'reactWithTrajectory'. Runs in+-- | The agent loop, embedded into a t'Program' by 'reactWithTrajectory'. Runs in -- exactly 'Shikumi.Program.runProgram'\'s effect row. reactLoop ::   forall i o es.@@ -242,7 +242,7 @@               loop (iter + 1) acc''      -- The final extract call: render, issue, decode into @o@ (a decode failure-    -- here is the agent's final-answer failure, surfaced as a 'ShikumiError').+    -- here is the agent's final-answer failure, surfaced as a t'ShikumiError').     extract :: Trajectory -> Eff es (o, Trajectory)     extract traj = do       let (ctx, opts) = renderExtract impl i traj@@ -380,7 +380,7 @@              ("anthropic", B.AnthropicMessages)            ] --- | Build the concrete 'ProtocolImpl' for a model, choosing the native or prompt+-- | Build the concrete t'ProtocolImpl' for a model, choosing the native or prompt -- renderers from 'resolveProtocolKind'. resolveProtocol ::   forall i o.@@ -584,7 +584,7 @@ actionToProposal (_, Summarized) =   error "internal invariant violated: Summarized is never parsed from model output" --- | Strip a leading/trailing Markdown code fence (```… / ```json … ```), if any,+-- | Strip a leading\/trailing Markdown code fence (```… \/ ```json … ```), if any, -- so a fenced JSON reply still decodes. Falls back to the input unchanged. stripFences :: Text -> Text stripFences t =
src/Shikumi/CodeExec/CodeAct.hs view
@@ -2,9 +2,9 @@ {-# LANGUAGE ScopedTypeVariables #-}  -- | @codeAct@ (EP-27): a multi-turn loop where each /action/ is a code snippet that--- may call provided tools, accumulating a 'Trajectory', then extracting the typed+-- may call provided tools, accumulating a t'Trajectory', then extracting the typed -- answer. It mirrors DSPy's @CodeAct@ (a ReAct loop whose action is code) and reuses--- 'Shikumi.Agent.ReAct'\'s @Trajectory@/@Step@/@Termination@ data model.+-- "Shikumi.Agent.ReAct"\'s @Trajectory@\/@Step@\/@Termination@ data model. -- -- The hermetic restricted interpreter does not itself call host functions, so tool -- use is handled at the /protocol/ level: a snippet of the exact form@@ -74,7 +74,7 @@   Program i o codeAct sig reg = FMap fst (codeActWithTrajectory defaultCodeActConfig sig reg) --- | Build a @codeAct@ agent that also returns the recorded 'Trajectory'.+-- | Build a @codeAct@ agent that also returns the recorded t'Trajectory'. codeActWithTrajectory ::   (ToPrompt i, ToSchema o, FromModel o, Validatable o) =>   CodeActConfig ->@@ -184,7 +184,7 @@     turnSys = getInstruction sig <> "\n\n" <> toolMenu reg <> "\n\n" <> codeActGuide     extractSys = getInstruction sig <> "\n\n" <> schemaInstruction (toSchema (Proxy @o)) --- | Parse a model turn @{"code": "...", "finished": <bool>}@ (fences stripped).+-- | Parse a model turn @{"code": "...", "finished": \<bool\>}@ (fences stripped). parseCodeReply :: Text -> Either Text (Text, Bool) parseCodeReply raw = case eitherDecodeStrict (encodeUtf8 (stripFences raw)) of   Left e -> Left ("not valid JSON: " <> T.pack e)@@ -199,7 +199,7 @@     pure (code, fin)   Right _ -> Left "expected a JSON object" --- | Recognize a tool-call snippet @call("name", <argsJSON>)@, decoding the name and+-- | Recognize a tool-call snippet @call("name", \<argsJSON\>)@, decoding the name and -- arguments. Anything else returns 'Nothing' (it is handed to the sandbox). parseCall :: Text -> Maybe (Text, Value) parseCall code = do
src/Shikumi/CodeExec/Interpreter.hs view
@@ -2,10 +2,10 @@  -- | The sandbox as a swappable /value/ (EP-27). ----- A 'CodeInterpreter' takes a string of model-emitted code and returns either a+-- A t'CodeInterpreter' takes a string of model-emitted code and returns either a -- recoverable error message (fed back to the model) or the code's textual output. -- It is captured in an 'Shikumi.Program.Embed' closure exactly as a--- 'Shikumi.Tool.ToolRegistry' is — never added to the effect row — because the+-- t'Shikumi.Tool.ToolRegistry' is — never added to the effect row — because the -- @Embed@ body is fixed to the row @(LLM, Error ShikumiError)@. The hermetic -- interpreters here are /pure/ (they ignore the effects and 'pure' their result), so -- they fit inside any row.@@ -17,12 +17,12 @@ -- and evaluates it purely — there is no syscall it can make, no filesystem, no -- network, no unbounded loop primitive (a step/expression-size cap bounds runtime and -- returns a typed error past it). The dangerous path is a real subprocess--- interpreter (DSPy uses a @deno@/Pyodide sandbox); that is intentionally /not/+-- interpreter (DSPy uses a @deno@\/Pyodide sandbox); that is intentionally /not/ -- shipped here. A future, gated, non-CI @subprocessInterpreter@ (EP-27 M4) would have -- to enforce, at minimum: no network; no host filesystem access beyond a single fresh--- scratch dir; no environment inheritance; CPU/memory/wall-clock limits (killing the+-- scratch dir; no environment inheritance; CPU\/memory\/wall-clock limits (killing the -- child and surfacing a 'Shikumi.Error.Timeout'); and loud failure (any limit breach--- becomes a 'Shikumi.Error.ShikumiError', never a silent empty result). The effect+-- becomes a t'Shikumi.Error.ShikumiError', never a silent empty result). The effect -- row does not type-enforce purity: the built-in tool environments already perform -- real IO inside the same row via 'Shikumi.Tool.Env.toolIO' and -- 'Shikumi.Tool.Web.httpIO'. The hermeticity of the shipped interpreters is a@@ -34,7 +34,7 @@ -- -- A single expression (optionally prefixed @result = @), built from: integer and -- rational literals, unary minus, and @+ - * /@ with parentheses; string literals--- with @\"@, @\\@, @\n@, and @\t@ escapes plus @++@ concatenation and the functions+-- with @\\"@, @\\\\@, @\\n@, and @\\t@ escapes plus @++@ concatenation and the functions -- @len@, @upper@, @lower@; and list literals @[a, b, …]@ with @sum@, @length@, -- @concat@. The value of the expression is rendered to text as the output. A parse -- error, an unknown identifier/function, a type error, division by zero, or@@ -66,7 +66,7 @@ -- | A sandbox as a plain value: run a code string, get back either a recoverable -- error message (@Left@, fed to the model) or the program's textual output -- (@Right@). A genuine infrastructure failure (e.g. a real subprocess dying) would--- be thrown as a 'ShikumiError' from inside 'runCode'; the hermetic interpreters+-- be thrown as a t'ShikumiError' from inside 'runCode'; the hermetic interpreters -- never do that. newtype CodeInterpreter = CodeInterpreter   { runCode :: forall es. (LLM :> es, Error ShikumiError :> es) => Text -> Eff es (Either Text Text)
src/Shikumi/CodeExec/ProgramOfThought.hs view
@@ -6,11 +6,11 @@ -- a typed answer. -- -- The loop mirrors DSPy's @ProgramOfThought.forward@: ask the model for a code--- snippet that computes the answer; run it through the captured 'CodeInterpreter';+-- snippet that computes the answer; run it through the captured t'CodeInterpreter'; -- if it errors, feed the error back and ask for a correction (up to @maxIters@ -- attempts); once it runs, ask the model to extract the typed output @o@ from the -- code and its output. The whole loop is a single 'Shikumi.Program.Embed' node, so--- @programOfThought@ is an ordinary @'Program' i o@ that carries no tunable+-- @programOfThought@ is an ordinary @t'Program' i o@ that carries no tunable -- parameters (the parameter-count invariant holds: @foldParams (programOfThought -- sig) == []@) and composes/serializes exactly like @react@. module Shikumi.CodeExec.ProgramOfThought@@ -56,7 +56,7 @@ programOfThought = programOfThoughtWith defaultPoTConfig  -- | @programOfThought@ with an explicit config (e.g. a different @maxIters@ or a--- different 'CodeInterpreter').+-- different t'CodeInterpreter'). programOfThoughtWith ::   (ToPrompt i, ToSchema o, FromModel o, Validatable o) =>   PoTConfig ->
src/Shikumi/CodeExec/Prompt.hs view
@@ -1,6 +1,6 @@ -- | Small prompt/parse helpers shared by the code-execution modules--- ('Shikumi.CodeExec.ProgramOfThought', 'Shikumi.CodeExec.CodeAct'). These mirror--- the equivalents in 'Shikumi.Agent.ReAct' (which keeps its own copies private);+-- ("Shikumi.CodeExec.ProgramOfThought", "Shikumi.CodeExec.CodeAct"). These mirror+-- the equivalents in "Shikumi.Agent.ReAct" (which keeps its own copies private); -- they are factored here to avoid duplicating the few small helpers across the two -- code-execution modules (EP-27). module Shikumi.CodeExec.Prompt@@ -29,9 +29,9 @@     emptyOptions   ) --- | Strip a leading/trailing Markdown code fence (@```@ / @```json … ```@), if any,+-- | Strip a leading\/trailing Markdown code fence (@```@ \/ @```json … ```@), if any, -- so a fenced reply still decodes. Falls back to the trimmed input. (Same logic as--- 'Shikumi.Agent.ReAct'\'s private @stripFences@.)+-- "Shikumi.Agent.ReAct"\'s private @stripFences@.) stripFences :: Text -> Text stripFences t =   let trimmed = T.strip t
src/Shikumi/Tool.hs view
@@ -5,19 +5,19 @@ -- | Typed tools and their lowering to baikai's wire tool — the MasterPlan's -- integration point #8, owned here and nowhere else. ----- A @'Tool' i o@ is a named function from an input record @i@ to an output @o@,+-- A @t'Tool' i o@ is a named function from an input record @i@ to an output @o@, -- runnable in any effect stack that has the @LLM@ and @Error ShikumiError@ -- capabilities (so a tool body may itself call sub-models and may signal failure -- through the typed error channel — see the plan's Decision Log on why the row is -- @(LLM, Error ShikumiError)@ rather than @IOE@). From a tool shikumi derives the--- JSON Schema of @i@ (reusing 'Shikumi.Schema'), lowers it to baikai's untyped+-- JSON Schema of @i@ (reusing "Shikumi.Schema"), lowers it to baikai's untyped -- @Baikai.Tool { name, description, parameters }@, decodes the model's argument--- JSON into a typed @i@ (or a 'ToolError', never a crash), runs the body, and+-- JSON into a typed @i@ (or a t'ToolError', never a crash), runs the body, and -- encodes the @o@ result back to observation text. ----- Heterogeneity is handled by the 'SomeTool' existential: it hides @i@/@o@ while--- retaining, at wrap time, the schema/decode/encode dictionaries the wire boundary--- needs. A 'ToolRegistry' is a name-keyed map of 'SomeTool's that a ReAct loop+-- Heterogeneity is handled by the t'SomeTool' existential: it hides @i@/@o@ while+-- retaining, at wrap time, the schema\/decode\/encode dictionaries the wire boundary+-- needs. A t'ToolRegistry' is a name-keyed map of t'SomeTool's that a ReAct loop -- dispatches against by the tool name the model emits at run time. module Shikumi.Tool   ( -- * Typed tools@@ -87,8 +87,8 @@   }  -- | Build a tool from a name, a description, and an effectful body. A pure tool is--- just @\\i -> pure (f i)@. The schema/decode/encode dictionaries are not needed--- here — they are captured later, at 'SomeTool' wrap time.+-- just @\\i -> pure (f i)@. The schema\/decode\/encode dictionaries are not needed+-- here — they are captured later, at t'SomeTool' wrap time. mkTool ::   Text ->   Text ->@@ -159,7 +159,7 @@  -- | Run an erased tool against a raw JSON arguments object: decode to the hidden -- @i@, run the body, encode the @o@ to text. A decode failure becomes--- 'ToolArgsInvalid', a body that throws a recoverable 'ShikumiError' becomes+-- 'ToolArgsInvalid', a body that throws a recoverable t'ShikumiError' becomes -- 'ToolRunFailed', and a body that throws an infrastructure error -- ('isInfraToolError') rethrows to abort the caller's loop. runErased ::@@ -216,17 +216,17 @@ -- | A tool-call failure carried as a /value/. The agent feeds the rendered text -- back to the model as an observation so it can recover, and records it in the -- trajectory; infrastructure faults ('isInfraToolError': budget and--- context-window exhaustion) bubble up as a 'ShikumiError' and abort the loop.+-- context-window exhaustion) bubble up as a t'ShikumiError' and abort the loop. data ToolError   = -- | the model named a tool the registry does not have     ToolNotFound !Text   | -- | tool name, decode-error message: the arguments did not match @i@     ToolArgsInvalid !Text !Text-  | -- | tool name, failure message: the body signalled a 'ShikumiError'+  | -- | tool name, failure message: the body signalled a t'ShikumiError'     ToolRunFailed !Text !Text   deriving stock (Show, Eq) --- | Render a 'ToolError' as the observation text fed back to the model.+-- | Render a t'ToolError' as the observation text fed back to the model. renderToolError :: ToolError -> Text renderToolError = \case   ToolNotFound nm -> "Error: no such tool \"" <> nm <> "\"."@@ -262,7 +262,7 @@ shikumiErrorText :: ShikumiError -> Text shikumiErrorText = renderShikumiError --- | Which 'ShikumiError's must escape the agent loop rather than become+-- | Which t'ShikumiError's must escape the agent loop rather than become -- observations. Budget and context-window exhaustion are infrastructure faults: -- the model cannot recover from them by reading an observation, and continuing -- the loop would either overspend (budget) or deterministically re-fail
src/Shikumi/Tool/Builtin/Fs.hs view
@@ -1,8 +1,8 @@ {-# LANGUAGE RankNTypes #-} --- | Built-in filesystem tools over 'ToolEnv'.+-- | Built-in filesystem tools over t'ToolEnv'. ----- The search tools prefer host-provided @rg@/@fd@ through 'ToolEnv.exec' when+-- The search tools prefer host-provided @rg@/@fd@ through 'Shikumi.Tool.Env.envExec' when -- available, and fall back to a bounded in-process traversal through the same -- environment record. The fallback skips noisy directories, binary files, and -- large files, and caps traversal depth and result count.
src/Shikumi/Tool/Builtin/Shell.hs view
@@ -1,10 +1,10 @@--- | Built-in shell tool over 'ToolEnv.exec'.+-- | Built-in shell tool over 'Shikumi.Tool.Env.envExec'. -- -- Security posture: 'bashTool' executes arbitrary model-chosen shell commands--- through the supplied 'ToolEnv', with whatever filesystem access, environment,--- and process privileges that environment grants. With 'localToolEnv' this is+-- through the supplied t'ToolEnv', with whatever filesystem access, environment,+-- and process privileges that environment grants. With 'Shikumi.Tool.Env.localToolEnv' this is -- deliberately non-hermetic host execution for trusted, local, single-operator--- workflows. Sandboxing is achieved by supplying a confining 'ToolEnv', not by+-- workflows. Sandboxing is achieved by supplying a confining t'ToolEnv', not by -- this module. module Shikumi.Tool.Builtin.Shell   ( BashReq (..),
src/Shikumi/Tool/Env.hs view
@@ -5,13 +5,13 @@ -- -- Filesystem and process-backed tools should depend on this record instead of -- calling the host operating system directly. A future sandbox can provide a--- different 'ToolEnv' value while the tool definitions stay unchanged.+-- different t'ToolEnv' value while the tool definitions stay unchanged. -- -- Security posture: 'localToolEnv' is deliberately non-hermetic. It has full -- access to the host filesystem paths and inherited environment visible to the -- current process, and it can execute arbitrary shell commands with the current -- user's privileges. Its filesystem and process IO is performed inside the--- @(LLM, Error ShikumiError)@ row via 'unsafeEff_'. Supply a different 'ToolEnv'+-- @(LLM, Error ShikumiError)@ row via 'unsafeEff_'. Supply a different t'ToolEnv' -- to confine tools; this module is the sandboxing seam, not the sandbox. module Shikumi.Tool.Env   ( Path,
src/Shikumi/Tool/Web.hs view
@@ -3,14 +3,14 @@  -- | Swappable HTTP operations for the built-in web tools. ----- 'web_fetch' is functional with only a TLS 'Manager'. 'web_search' requires a+-- @web_fetch@ is functional with only a TLS 'Manager'. @web_search@ requires a -- configured provider because search APIs need provider-specific credentials. -- -- Security posture: the default fetch policy denies non-http(s) schemes and the -- most common SSRF destinations (loopback, link-local, and RFC-1918 private host -- literals) before opening a connection, and caps response bytes while reading. -- It does not resolve hostnames to detect private addresses behind DNS; supply a--- stricter 'FetchPolicy' through 'localWebClientWith' when deployments need that.+-- stricter t'FetchPolicy' through 'localWebClientWith' when deployments need that. module Shikumi.Tool.Web   ( FetchResult (..),     SearchHit (..),
test/Fixtures.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE DeriveAnyClass #-}  -- | Shared fixtures for the @shikumi-tools@ specs: the weather records from the--- plan's Purpose, their schema/decode/prompt instances, the typed @weatherTool@ and+-- plan's Purpose, their schema\/decode\/prompt instances, the typed @weatherTool@ and -- its registry, the mock-LM scripts for both protocols, and the expected schema and -- answer the specs assert against. module Fixtures
test/RestrictedSpec.hs view
@@ -1,5 +1,5 @@ -- | EP-27 M1: the hermetic restricted-DSL evaluator behind 'restrictedInterpreter'.--- Pure unit tests over 'runRestricted': it computes arithmetic/string/list results+-- Pure unit tests over 'runRestricted': it computes arithmetic\/string\/list results -- and returns a @Left@ error message for division by zero, a parse error, or an -- unknown function — the recoverable errors the @programOfThought@ loop feeds back. module RestrictedSpec (tests) where