import { agent_emit_event } from "std/agent/state"
fn __agent_progress_status(value, label) {
if type_of(value) != "string" {
throw label + ".status must be one of pending, in_progress, completed"
}
if contains(["pending", "in_progress", "completed"], value) {
return value
}
throw label + ".status must be one of pending, in_progress, completed"
}
fn __agent_progress_priority(value, label) {
if value == nil {
return nil
}
if type_of(value) != "string" {
throw label + ".priority must be one of high, medium, low, or nil"
}
if contains(["high", "medium", "low"], value) {
return value
}
throw label + ".priority must be one of high, medium, low, or nil"
}
fn __agent_progress_entries(value) {
if value == nil {
return []
}
if type_of(value) != "list" {
throw "agent_progress: entries must be a list"
}
var entries = []
var index = 0
for raw in value {
let label = "agent_progress: entries[" + to_string(index) + "]"
if type_of(raw) != "dict" {
throw label + " must be a dict"
}
let content = raw?.content
if type_of(content) != "string" || trim(content) == "" {
throw label + ".content must be a non-empty string"
}
var entry = {content: content, status: __agent_progress_status(raw?.status, label)}
let priority = __agent_progress_priority(raw?.priority, label)
if priority != nil {
entry = entry + {priority: priority}
}
entries = entries.push(entry)
index = index + 1
}
return entries
}
fn __agent_progress_payload(input) {
if type_of(input) != "dict" {
throw "agent_progress: argument must be a dict"
}
let message = input?.message
if message != nil && type_of(message) != "string" {
throw "agent_progress: message must be a string or nil"
}
let entries = __agent_progress_entries(input?.entries)
if (message == nil || trim(message) == "") && len(entries) == 0 {
throw "agent_progress: message or entries is required"
}
let replace = input?.replace ?? true
if type_of(replace) != "bool" {
throw "agent_progress: replace must be a bool"
}
let metadata = input?.metadata ?? {}
if type_of(metadata) != "dict" {
throw "agent_progress: metadata must be a dict"
}
return {
message: if message == nil {
nil
} else {
message
},
entries: entries,
replace: replace,
metadata: metadata,
}
}
fn __agent_progress_tool_name(config) {
let name = config?.name ?? "agent_progress"
if type_of(name) != "string" || trim(name) == "" {
throw "agent_loop: progress_tool.name must be a non-empty string"
}
return name
}
fn __agent_progress_tool_description(config) {
let description = config?.description
?? "Report concise task progress to the host without ending the turn."
if type_of(description) != "string" || trim(description) == "" {
throw "agent_loop: progress_tool.description must be a non-empty string"
}
return description
}
fn __agent_progress_tool_nudge(config) {
let tool_name = __agent_progress_tool_name(config)
let nudge = config?.system_prompt_nudge
?? "When useful, call "
+ tool_name
+ " to report concise progress. Use message for narration, entries for task-list state, and replace=true unless the update should append."
if nudge == nil {
return nil
}
if type_of(nudge) != "string" {
throw "agent_loop: progress_tool.system_prompt_nudge must be a string or nil"
}
let trimmed = trim(nudge)
if trimmed == "" {
return nil
}
return trimmed
}
fn __agent_progress_tool_config(value) {
if value == nil {
return nil
}
let kind = type_of(value)
if kind == "bool" {
if value {
return {}
}
return nil
}
if kind != "dict" {
throw "agent_loop: progress_tool must be true, false, a dict, or nil"
}
return value
}
/** agent_progress emits a structured progress_reported event for the current agent session. */
pub fn agent_progress(input) {
let payload = __agent_progress_payload(input)
let session_id = agent_session_current_id()
if session_id == nil || session_id == "" {
throw "agent_progress: no active agent session"
}
agent_emit_event(session_id, "progress_reported", payload)
return nil
}
/** agent_progress_tool adds a handler-backed agent_progress tool to a registry. */
pub fn agent_progress_tool(registry = nil, options = nil) {
let config = options ?? {}
return tool_define(
registry ?? tool_registry(),
__agent_progress_tool_name(config),
__agent_progress_tool_description(config),
{
parameters: {
message: {type: "string", description: "Short progress narration", required: false},
entries: {
type: "array",
description: "Task-list entries with content, status, and optional priority",
required: false,
},
replace: {
type: "boolean",
description: "Whether this update replaces the prior progress view",
required: false,
},
metadata: {type: "object", description: "Free-form progress metadata", required: false},
},
returns: {type: "null"},
handler: { args -> agent_progress(args) },
},
)
}
/** agent_progress_apply_options injects the progress tool when agent_loop opts in. */
pub fn agent_progress_apply_options(options = nil) {
let opts = options ?? {}
let config = __agent_progress_tool_config(opts?.progress_tool)
if config == nil {
return opts
}
let tools = agent_progress_tool(opts?.tools, config)
let nudge = __agent_progress_tool_nudge(config)
var out = opts + {tools: tools}
if nudge != nil {
out = out + {_progress_tool_system_prompt_nudge: nudge}
}
return out
}