import { agent_session_inject_feedback } from "std/agent/state"
fn __default_done_sentinel(opts) {
return opts?.done_sentinel
}
fn __sentinel_hit(text, sentinel, parsed_done_marker) {
if sentinel == nil {
return false
}
if parsed_done_marker != nil && parsed_done_marker != "" {
return contains(parsed_done_marker, sentinel)
}
return contains(text, sentinel)
}
fn __dispatch_results_list(dispatch) {
if dispatch == nil {
return []
}
if type_of(dispatch) == "list" {
return dispatch
}
return dispatch?.results ?? []
}
fn __tool_result_ok(result) {
if result?.ok != nil {
return result.ok ? true : false
}
if result?.success != nil {
return result.success ? true : false
}
let status = result?.status ?? ""
return status == "ok" || status == "success"
}
fn __tool_result_name(result) {
return result?.tool_name ?? result?.name ?? ""
}
fn __tool_names_by_status(dispatch, want_ok) {
let results = __dispatch_results_list(dispatch)
var names = []
for result in results {
let name = __tool_result_name(result)
if name != "" && __tool_result_ok(result) == want_ok {
names = names.push(name)
}
}
return names
}
fn __post_turn_callback_verdict(opts, payload) {
let cb = opts?.post_turn_callback
if cb == nil {
return {kind: "none"}
}
let verdict = cb(payload)
return __interpret_verdict(verdict)
}
fn __interpret_verdict(verdict) {
if verdict == nil || verdict == "" {
return {kind: "none"}
}
if type_of(verdict) == "bool" {
return if verdict {
{kind: "stop"}
} else {
{kind: "none"}
}
}
if type_of(verdict) == "string" {
return {kind: "inject", message: verdict}
}
if type_of(verdict) == "dict" {
let stop = verdict?.stop ?? false
let base_llm_options = if type_of(verdict?.llm_options) == "dict" {
verdict.llm_options
} else {
{}
}
let llm_options = if verdict?.prefill != nil && verdict?.prefill != "" {
base_llm_options + {prefill: verdict?.prefill}
} else {
verdict?.llm_options
}
return {
kind: "rich",
stop: stop,
stop_reason: verdict?.stop_reason,
message: verdict?.message,
next_options: verdict?.next_options,
llm_options: llm_options,
}
}
return {kind: "none"}
}
fn __successful_tool_names(dispatch) {
return __tool_names_by_status(dispatch, true)
}
fn __rejected_tool_names(dispatch) {
return __tool_names_by_status(dispatch, false)
}
fn __stop_after_successful_tools(opts, dispatch) {
let names = opts?.stop_after_successful_tools
if names == nil || len(names) == 0 {
return false
}
let successful = __successful_tool_names(dispatch)
for required in names {
if contains(successful, required) {
return true
}
}
return false
}
fn __native_tool_text_completion(opts, has_tool_calls, text) {
if !(opts?.loop_until_done ?? false) || opts?.daemon {
return false
}
if opts?.tool_format != "native" || opts?.tools == nil {
return false
}
if __default_done_sentinel(opts) != nil {
return false
}
if has_tool_calls {
return false
}
return trim(text) != ""
}
fn __done_judge_config(opts) {
let judge = opts?.done_judge
if judge == nil {
return nil
}
if type_of(judge) == "bool" {
return if judge {
{}
} else {
nil
}
}
if type_of(judge) == "dict" {
return judge
}
return nil
}
fn __done_judge_cadence(opts) {
let judge = __done_judge_config(opts)
if judge == nil {
return nil
}
let cadence = judge?.cadence
if type_of(cadence) == "dict" {
return cadence
}
return {}
}
fn __done_judge_loop_state(opts, completion_proposed) {
let state = opts?._done_judge_loop_state ?? {}
let base_completion = state?.completion ?? {}
let completion = base_completion + {proposed: completion_proposed}
return state + {completion: completion}
}
fn __done_judge_when_due(opts, cadence, state) {
let when = cadence?.when ?? "always"
if type_of(when) == "closure" {
return when(state) ? true : false
}
if when == "always" {
return true
}
if when == "stalled" {
let trigger = opts?._done_judge_trigger ?? ""
let stalled = state?.stall?.triggered ?? false
return trigger == "stalled" || stalled
}
return false
}
fn __done_judge_every_due(cadence, turn_number) {
let every = cadence?.every
if every == nil {
return true
}
return turn_number % every == 0
}
fn __done_judge_past_warmup(cadence, turn_number) {
let min_iterations = cadence?.min_iterations_before_first
if min_iterations == nil {
return true
}
return turn_number > min_iterations
}
fn __done_judge_under_cap(opts, cadence) {
let max_invocations = cadence?.max_invocations
if max_invocations == nil {
return true
}
let invocations = opts?._done_judge_invocations ?? 0
return invocations < max_invocations
}
fn __done_judge_due(opts, payload, completion_proposed) {
let cadence = __done_judge_cadence(opts)
if cadence == nil {
return false
}
let raw_iteration = payload?.iteration ?? 0
let turn_number = raw_iteration + 1
let state = __done_judge_loop_state(opts, completion_proposed)
return __done_judge_under_cap(opts, cadence)
&& __done_judge_past_warmup(cadence, turn_number)
&& __done_judge_every_due(cadence, turn_number)
&& __done_judge_when_due(opts, cadence, state)
}
fn __completion_result(opts, payload, stop_reason) {
let verify_due = opts?.verify_completion != nil || opts?.verify_completion_judge != nil
let done_due = __done_judge_due(opts, payload, true)
if verify_due || done_due {
return {kind: "break", stop_reason: stop_reason, needs_verify: true, done_judge_due: done_due}
}
if __done_judge_config(opts) != nil {
return {kind: "continue", done_judge_due: false}
}
return {kind: "break", stop_reason: stop_reason, needs_verify: false, done_judge_due: false}
}
fn __post_turn_verdict_result(session, opts, verdict) {
if verdict.kind == "stop" {
return {kind: "break", stop_reason: "post_turn_stop", needs_verify: opts?.verify_completion != nil}
}
if verdict.kind == "inject" {
agent_session_inject_feedback(session.session_id, "post_turn", verdict.message)
return {kind: "continue"}
}
if verdict.kind != "rich" {
return nil
}
if verdict.message != nil {
agent_session_inject_feedback(session.session_id, "post_turn", verdict.message)
}
if verdict.stop {
let stop_reason = if verdict?.stop_reason != nil && verdict.stop_reason != "" {
to_string(verdict.stop_reason)
} else {
"post_turn_stop"
}
return {
kind: "break",
stop_reason: stop_reason,
needs_verify: opts?.verify_completion != nil,
next_options: verdict?.next_options,
llm_options: verdict?.llm_options,
}
}
if verdict.message != nil || verdict?.next_options != nil || verdict?.llm_options != nil {
return {kind: "continue", next_options: verdict?.next_options, llm_options: verdict?.llm_options}
}
return nil
}
/** agent_extract_tool_calls. */
pub fn agent_extract_tool_calls(llm_result, opts) {
if len(llm_result?.tool_calls ?? []) > 0 {
return llm_result.tool_calls
}
if opts?.tools == nil {
return []
}
let parsed = agent_parse_tool_calls(llm_result?.text ?? "", opts.tools)
return parsed?.calls ?? []
}
/** agent_compute_post_turn. */
pub fn agent_compute_post_turn(session, llm_result, dispatch, opts, iteration) {
let sentinel = __default_done_sentinel(opts)
let text = llm_result?.text ?? ""
let sentinel_text = llm_result?.raw_text ?? text
let parsed_done_marker = llm_result?.parsed_done_marker
let dispatch_results = __dispatch_results_list(dispatch)
let successful_tool_names = __successful_tool_names(dispatch)
let rejected_tool_names = __rejected_tool_names(dispatch)
let has_tool_calls = len(llm_result?.tool_calls ?? []) > 0 || len(dispatch_results) > 0
let payload = {
session_id: session.session_id,
session: {id: session.session_id},
iteration: iteration,
has_tool_calls: has_tool_calls,
dispatch: dispatch,
tool_results: dispatch_results,
tool_count: len(dispatch_results),
successful_tool_names: successful_tool_names,
rejected_tool_names: rejected_tool_names,
session_successful_tools: opts?._session_successful_tools ?? successful_tool_names,
session_rejected_tools: opts?._session_rejected_tools ?? rejected_tool_names,
text: text,
visible_text: text,
}
__host_fire_session_hook("post_turn", payload)
if __sentinel_hit(sentinel_text, sentinel, parsed_done_marker) {
return __completion_result(opts, payload, "sentinel")
}
if __stop_after_successful_tools(opts, dispatch) {
return {kind: "break", stop_reason: "natural", needs_verify: false, done_judge_due: false}
}
let verdict = __post_turn_callback_verdict(opts, payload)
let verdict_result = __post_turn_verdict_result(session, opts, verdict)
if verdict_result != nil {
return verdict_result
}
if !has_tool_calls && trim(text) != "" && opts?.done_judge != nil {
return __completion_result(opts, payload, "natural")
}
if __native_tool_text_completion(opts, has_tool_calls, text) {
return __completion_result(opts, payload, "natural")
}
let loop_until_done = opts?.loop_until_done ?? false
let daemon = opts?.daemon ?? false
if !has_tool_calls && !loop_until_done && !daemon {
return __completion_result(opts, payload, "natural")
}
return {kind: "continue", done_judge_due: false}
}