Skip to content

agents.runtime/statuses / agents.runtime/limits

Clojure (JVM) + cljgo · package net.clojars.muthuishere/toolnexus · SPEC §7D · clojure/src/toolnexus/agents/runtime.cljc

;; the §7D TaskResult status vocabulary — SEVEN values
toolnexus.agents.runtime/statuses
;; => #{"done" "pending" "incomplete" "interrupted" "closed" "timeout" "error"}
;; the §7D TaskResult.limit vocabulary — NINE values, WHICH limit stopped the task
toolnexus.agents.runtime/limits
;; => #{"maxTurns" "maxTokens" "maxToolCalls" "maxWallMs"
;; "maxChildren" "maxConcurrent" "maxDepth" "completion" "timeout"}
;; the named values, so a caller never types the string literal
toolnexus.agents.runtime/limit-max-turns ; "maxTurns"
toolnexus.agents.runtime/limit-max-tokens ; "maxTokens"
toolnexus.agents.runtime/limit-max-tool-calls ; "maxToolCalls"
toolnexus.agents.runtime/limit-max-wall-ms ; "maxWallMs"
toolnexus.agents.runtime/limit-max-children ; "maxChildren"
toolnexus.agents.runtime/limit-max-concurrent ; "maxConcurrent"
toolnexus.agents.runtime/limit-max-depth ; "maxDepth"
toolnexus.agents.runtime/limit-completion ; "completion"
toolnexus.agents.runtime/limit-timeout ; "timeout"
;; a SEPARATE, smaller §8 client-level vocabulary — do not confuse the two
toolnexus.client/statuses ; => #{"done" "pending" "incomplete"} — never "timeout"
toolnexus.client/limits ; => #{"maxTurns" "contentPart" "timeout"}

The canonical, byte-identical-across-ports string constants a host branches on: task statuses, run statuses, and the nine stop-limit reasons a task can end on. These are the closed vocabularies a toolnexus.agents.runtime TaskResult — the value spawn, wait and close hand back — reports on its :status and (when the status is a limit stop) its :limit fields.

  • Branching on a TaskResult’s :status returned by spawn/wait/interrupt/close on toolnexus.agents.runtime — check membership in statuses rather than hardcoding the string, so a host fails loudly if it ever meets an eighth value instead of silently falling through a case. When the status is "incomplete" or "timeout", the same result carries a :limit naming which budget stopped it — check that against limits.
  • Building a dashboard or a log line that needs to render every possible outcome a spawned agent can reach, exhaustively, without guessing at undocumented strings.
  • Writing a completion/guardrail check in toolnexus.agents.loop that inspects :limit to decide whether a stop is retryable (e.g. "maxTurns" might be worth bumping and re-running; "maxDepth" is a structural ceiling, not a transient one).

The vocabularies are plain #{} sets and named defs rather than an enum type or a keyword namespace, matching this port’s general style of carrying closed vocabularies as data (clojure/src/toolnexus/agents/runtime.cljc:142, :174) — a host checks membership with contains? the same way it would check .contains() on any other port’s array/list constant, with no additional API to learn.

1. The smallest useful call — check a spawned task’s outcome

Section titled “1. The smallest useful call — check a spawned task’s outcome”
(require '[toolnexus.agents.runtime :as rt]
'[koine.json :as json])
(println)
(defn- text-response [t]
{:choices [{:message {:role "assistant" :content t}}]
:usage {:prompt_tokens 10 :completion_tokens 5 :total_tokens 15}})
(def runtime
(rt/create-runtime
{:registry {"w" {:name "w" :does "writes" :model "wm"}}
:llm {:base-url "http://mock.local" :model "default"}
:http-client (fn [_url _headers _body]
{:status 200 :headers {} :body (json/write-str (text-response "done writing"))})}))
(def h (rt/spawn runtime rt/root "w"))
(rt/wake runtime h "go")
(let [r (rt/wait runtime h)]
(assert (contains? rt/statuses (:status r)) "unrecognised task status")
(case (:status r)
"done" (println (:text r))
"pending" (println "parked, waiting on" (:pending r))
"incomplete" (println "stopped on limit:" (:limit r))
(println "ended:" (:status r))))
(println "OK")

2. The realistic case — react to WHICH limit stopped the task

Section titled “2. The realistic case — react to WHICH limit stopped the task”
(require '[toolnexus.agents.runtime :as rt]
'[koine.json :as json])
(println)
(defn- text-response [t]
{:choices [{:message {:role "assistant" :content t}}]
:usage {:prompt_tokens 10 :completion_tokens 5 :total_tokens 15}})
(defn handle-stop [r]
(when (= "incomplete" (:status r))
(assert (contains? rt/limits (:limit r)) "unrecognised limit reason")
(cond
(= rt/limit-max-turns (:limit r))
(println "hit the turn budget — consider raising it and re-running")
(= rt/limit-max-depth (:limit r))
(println "hit the delegation depth ceiling — this is structural, don't retry blindly")
(= rt/limit-max-wall-ms (:limit r))
(println "ran out of wall-clock time")
:else
(println "stopped on" (:limit r)))))
(def runtime
(rt/create-runtime
{:registry {"w" {:name "w" :does "writes" :model "wm" :budget {:max-turns 1}}}
:llm {:base-url "http://mock.local" :model "default"}
:http-client (fn [_url _headers _body]
{:status 200 :headers {} :body (json/write-str (text-response "ok"))})}))
(def h (rt/spawn runtime rt/root "w"))
(rt/wake runtime h "first")
(rt/wait runtime h)
(rt/wake runtime h "second") ; exceeds the one-turn budget
(def r (rt/wait runtime h))
(assert (= "incomplete" (:status r)))
(assert (= rt/limit-max-turns (:limit r)))
(handle-stop r)
(println "OK")

3. The full surface — distinguishing the two vocabularies side by side

Section titled “3. The full surface — distinguishing the two vocabularies side by side”
(require '[toolnexus.agents.runtime :as rt]
'[toolnexus.client :as client])
(println)
;; the §7D task-level set (seven statuses, nine limits)
(assert (= #{"done" "pending" "incomplete" "interrupted" "closed" "timeout" "error"}
rt/statuses))
(assert (= #{"maxTurns" "maxTokens" "maxToolCalls" "maxWallMs"
"maxChildren" "maxConcurrent" "maxDepth" "completion" "timeout"}
rt/limits))
;; the §8 client-level set — smaller, and NEVER contains "timeout" as a status
(assert (= #{"done" "pending" "incomplete"} client/statuses))
(assert (= #{"maxTurns" "contentPart" "timeout"} client/limits))
(assert (not (contains? client/statuses "timeout")))
;; a host that gets this wrong learns immediately rather than silently
;; mis-branching: membership checks against the WRONG set simply never match
(assert (not (contains? client/limits "maxDepth"))
"maxDepth is a task-level admission limit; the client run never reports it")
(println "OK")
  • toolnexus.agents.loop — run-gated drives a run under a completion check and reads these same status/limit strings to decide whether the gate has passed.
  • toolnexus.agents.runtime — The host verbs (spawn, wait, interrupt, close) whose TaskResult carries :status and, on a limit stop, :limit.