Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions cmd/odek/bg_tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,11 +323,12 @@ func (t *bgStartTool) Name() string { return "bg_start" }

func (t *bgStartTool) Description() string {
return `Start a shell command in the background and return immediately.
Use for long-running work: dev servers, watchers, fuzz runs, batch jobs.
Use for long-running work: builds, full test suites, dev servers, watchers, fuzz runs, batch jobs.
The command runs detached from the conversation: you keep working while it
runs, and a completion notice with the exit status and an output tail is
delivered to you automatically when it finishes (if notices are enabled).
If notices are disabled, poll with bg_status / bg_output instead.
drained into a later iteration when it finishes (if notices are enabled).
If notices are disabled, or your current turn depends on the result, poll
with bg_status / bg_output before ending the turn.
timeout_seconds: optional kill timer; 0 or absent = run until session end
(operator cap may clamp explicit values). Jobs are killed when the session
or the process ends. Output is capped; retrieve it with bg_output.`
Expand Down Expand Up @@ -415,7 +416,9 @@ func (t *bgStatusTool) Name() string { return "bg_status" }
func (t *bgStatusTool) Description() string {
return `Get the status of one background job: running/exited/failed/timeout/killed,
exit code, duration, and output size. Returns {"status":"unknown"} for ids
that never existed, were started by another session, or died with a restart.`
that never existed, were started by another session, died with a restart,
or whose finished record was evicted (the oldest finished jobs are pruned
when the per-session record cap is exceeded).`
}

func (t *bgStatusTool) Schema() any {
Expand Down
9 changes: 3 additions & 6 deletions cmd/odek/browser_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,18 +120,15 @@ func (t *browserTool) checkRedirect(req *http.Request, via []*http.Request) erro
func (t *browserTool) Name() string { return "browser" }

func (t *browserTool) Description() string {
return `Navigate and interact with web pages. Supports four actions:
return `Navigate and interact with web pages. Four actions:

navigate — Fetch a URL and extract page content + interactive elements
snapshot — Return the current page's text view with ref IDs for elements
click — Follow a link or interact with an element by ref ID
back — Return to the previous page in navigation history

Note: Uses regex-based HTML parsing with NO JavaScript execution. Best for server-rendered HTML pages. SPAs and JS-heavy sites may return limited content.

Use browser_navigate(url) first, then browser_snapshot() to see interactive
elements with their ref IDs (e.g. @e1, @e2), then browser_click(ref) to
follow links or interact with buttons.`
Typical flow: navigate(url), then snapshot() to get element ref IDs (e.g. @e1), then click(ref).
Note: regex-based HTML parsing with NO JavaScript execution. Best for server-rendered HTML pages; SPAs and JS-heavy sites may return limited content.`
}

// browserArgs holds all possible parameters for the browser tool.
Expand Down
9 changes: 4 additions & 5 deletions cmd/odek/file_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,7 @@ func (t *writeFileTool) Name() string { return "write_file" }
func (t *writeFileTool) Description() string {
return `Write content to a file, completely replacing existing content.
Creates parent directories automatically. OVERWRITES the entire file.
Use patch for targeted edits.
CRITICAL: Use the EXACT path specified in the task. Do not simplify or drop directories from the path.`
Use patch for targeted edits; never simplify or drop directories from the path.`
}

func (t *writeFileTool) Schema() any {
Expand Down Expand Up @@ -540,8 +539,9 @@ func (t *searchFilesTool) Description() string {
Two modes: target="content" searches inside files for a regex pattern,
target="files" finds files by glob pattern.
Results are sorted by modification time (newest first).
For performance, ALWAYS use file_glob (e.g. '*.go', '*.py', '*.md') and a
narrow path — without file_glob, every file in the tree is scanned.`
For 2+ patterns at once, use multi_grep instead — one parallel pass.
Always pass file_glob ('*.go', '*.py', …) and a narrow path; without
file_glob every file in the tree is scanned.`
}

func (t *searchFilesTool) Schema() any {
Expand Down Expand Up @@ -1552,7 +1552,6 @@ Zero-fork — pure Go filepath walk with no subprocess.
Examples:
glob(pattern="*.go") — all Go files in current directory
glob(pattern="**/*.py") — all Python files recursively
glob(pattern="*.json", path="config/") — JSON files in config/
glob(pattern="test_*") — files starting with test_

Returns an array of {path, size, is_dir} for each match.`
Expand Down
6 changes: 3 additions & 3 deletions cmd/odek/perf_tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ type batchPatchTool struct {

func (t *batchPatchTool) Name() string { return "batch_patch" }
func (t *batchPatchTool) Description() string {
return `Apply up to 10 find-replace edits across files in a single call. Edits are applied sequentially — this is NOT one atomic transaction: at the first failing edit the remaining edits are skipped (early-stop) and the edits already applied are kept. Each individual edit uses O_NOFOLLOW read + atomic temp+rename write, same as the patch tool.`
return `Apply up to 10 find-replace edits in one call — across one or more files, including several edits to the same file; prefer this over N sequential patch calls whenever you have more than one edit. Edits are applied sequentially — this is NOT one atomic transaction: at the first failing edit the remaining edits are skipped (early-stop) and the edits already applied are kept. Each individual edit uses O_NOFOLLOW read + atomic temp+rename write, same as the patch tool.`
}

type batchPatchArg struct {
Expand Down Expand Up @@ -652,7 +652,7 @@ func (t *httpBatchTool) checkRedirect(req *http.Request, via []*http.Request) er

func (t *httpBatchTool) Name() string { return "http_batch" }
func (t *httpBatchTool) Description() string {
return `Fetch multiple URLs in parallel. Returns status code, content length, and error for each URL. Does NOT parse HTML — it's a lightweight parallel fetch for APIs, docs, and data files. Max 10 URLs per call.`
return `Check multiple URLs in parallel — returns HTTP status code, content length, and error per URL; response BODIES are NOT returned (read content with browser or shell instead). Best for link-health and availability checks, not content fetching. Max 10 URLs per call.`
}

type httpBatchReq struct {
Expand Down Expand Up @@ -1327,7 +1327,7 @@ type multiGrepTool struct {

func (t *multiGrepTool) Name() string { return "multi_grep" }
func (t *multiGrepTool) Description() string {
return `Search for multiple regex patterns in parallel across files. Each pattern runs its own directory walk with bounded concurrency. Returns structured {pattern, path, line, content} results. Replaces N serial search_files calls. Directly targets the multi_search benchmark.`
return `Search for multiple regex patterns in parallel across files. Each pattern runs its own directory walk with bounded concurrency. Returns structured {pattern, path, line, content} results. Replaces N serial search_files calls.`
}

type grepMatch struct {
Expand Down
2 changes: 1 addition & 1 deletion cmd/odek/session_search_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func newSessionSearchTool(store *session.Store) *sessionSearchTool {

func (t *sessionSearchTool) Name() string { return "session_search" }
func (t *sessionSearchTool) Description() string {
return `Search and retrieve past agent sessions. Actions: list (recent sessions), search (semantic keyword search through full message content), get (full session by ID including ALL messages), find (sessions by task/title). Uses semantic vector search for the search action — it finds sessions whose conversation content is relevant to your query, even when titles don't match. Use OR between keywords for broad recall.
return `Search and retrieve past agent sessions. Actions: list (recent sessions), search (keyword search through full message content), get (full session by ID including ALL messages), find (sessions by task/title). Matching is thresholded keyword scoring — it finds sessions whose conversation content shares your query terms, even when titles don't match; use distinctive words from the original conversation, not paraphrases. Use OR between keywords for broad recall.

IMPORTANT: After search returns matching sessions, use get (not search) to read the actual conversation content. get returns the full session_messages array with every user and assistant message.`
}
Expand Down
5 changes: 2 additions & 3 deletions cmd/odek/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,15 @@ func (t *shellTool) Name() string { return "shell" }

func (t *shellTool) Description() string {
return `Run a shell command and return its output.
Use for: reading files, listing directories, running tests, building code, and git operations.
Use for builds, test suites, git operations, package management, and scripts that need a real process. For file inspection prefer the zero-fork tools: read_file, glob, tree, search_files, head_tail.
In sandbox mode (--sandbox), commands run inside the Docker container with restricted permissions.
In host mode (default), commands run with the same permissions as the odek process.

Risk classes: safe, local_write, system_write, destructive, network_egress, code_execution, install, unknown, blocked
High-risk operations may prompt for approval (configurable via dangerous section in odek.json).
The gate fails closed: an unrecognised command classifies as "unknown" and is denied by default.

Output is fully buffered: nothing is returned until the command finishes. For known long-running
commands (builds, test suites), set timeout_seconds explicitly so a stuck command fails fast.`
Output is fully buffered: nothing is returned until the command finishes; set timeout_seconds for known long-running commands so a stuck one fails fast. Work that would block the turn for minutes (builds, full test suites, dev servers, watchers) belongs in bg_start instead.`
}

func (t *shellTool) Schema() any {
Expand Down
16 changes: 7 additions & 9 deletions cmd/odek/subagent_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,25 +180,23 @@ func (t *delegateTasksTool) Description() string {
Example: decomposing "build a REST API" into "create user model", "create auth middleware", "create route handlers".

Key rules:
- Each sub-agent has a fresh context (no parent history)
- Sub-agents run in parallel up to the configured concurrency limit (subagent.max_concurrency, falling back to max_concurrency)
- Each sub-agent has a fresh context (no parent history) — pass everything it needs in goal/context
- Sub-agents run in parallel up to the configured concurrency cap
- Sub-agents NEVER prompt for approvals — denied operations are listed in each result's denials array (tool/class/reason); escalate by performing the operation yourself or asking the user
- Sub-agents get a wall-clock budget (subagent.timeout_seconds, default 30m, hard max 30m) and an iteration budget (subagent.max_iterations, default 15) — it is told both at spawn and warned as it approaches them
- Trust is non-increasing downward: a child's effective trust is min(parent trust, declared trust_level) — an untrusted task tree cannot spawn trusted children
- Sub-agents can use all tools (shell, read/write files, etc.), capped by trust_level and max_risk
- Delegation depth is capped (subagent.max_depth, default 2) — do leaf work yourself when close to the cap
- Sub-agents get a wall-clock budget and an iteration budget, and are told both at spawn
- Trust is non-increasing downward: an untrusted task tree cannot spawn trusted children
- Delegation depth is capped — do leaf work yourself when close to the cap
- After all complete, synthesize the results into a cohesive answer

Result delivery — two channels per sub-agent:
- Headline: the sub-agent's final answer, capped at ~2000 characters. Treat it as a status summary, not the full result; a trailing … means it was cut.
- Artifacts: deliverables the sub-agent wrote as files (it is instructed to stage anything larger than a headline in its task's artifact dir) are validated and listed under "artifacts:" — id, type, byte size, one-line summary. Text artifacts up to 32 KB are inlined in full; fetch larger ones with artifact_read(id).
- Artifacts: file deliverables are validated and listed under "artifacts:" — id, type, byte size, one-line summary. Only text/* artifacts 32 KB are inlined in full (JSON/binary are metadata-only); fetch anything else with artifact_read(id).
- For artifact-heavy tasks (reports, audits, reviews, generated files), put it in ` + "`guidance`" + `: "Write the full deliverable as a flat file in your artifact dir; keep the final answer to a short headline." Then read file-backed artifacts with artifact_read before synthesizing.

Output format per sub-agent (headline stays SHORT — status, artifact names, key decisions; the files carry the detail):
- Status: built / blocked / failed, one line
- Artifact file names (omit when everything fit inline)
- Key decisions made
- artifacts: file-backed deliverables (inlined when ≤32 KB; artifact_read otherwise)`
- artifacts: file-backed deliverables (inlined when text ≤32 KB; artifact_read otherwise)`
}

func (t *delegateTasksTool) Schema() any {
Expand Down
4 changes: 2 additions & 2 deletions cmd/odek/transcribe_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ func newTranscribeTool(dc danger.DangerousConfig, tc config.TranscriptionConfig)

func (t *transcribeTool) Name() string { return "transcribe" }
func (t *transcribeTool) Description() string {
return `Transcribe an audio file to text using a local whisper model (whisper.cpp CLI). Returns transcribed text with segments and duration. Requires whisper CLI and a model file to be installed locally.`
return `Transcribe an audio file to text using a local whisper model (whisper.cpp CLI). Returns transcribed text with segments and duration. Requires whisper CLI and a model file to be installed locally; native audio input is WAV/MP3/FLAC — other containers are auto-converted via ffmpeg, so if conversion fails, supply WAV/MP3/FLAC directly instead of retrying.`
}

type transcribeArgs struct {
Expand Down Expand Up @@ -193,7 +193,7 @@ func (t *transcribeTool) Schema() any {
"properties": map[string]any{
"path": map[string]any{
"type": "string",
"description": "Path to the audio file (OGG, WAV, MP3, etc.).",
"description": "Path to the audio file (WAV, MP3, FLAC native; others are converted via ffmpeg).",
},
"language": map[string]any{
"type": "string",
Expand Down
2 changes: 1 addition & 1 deletion cmd/odek/vision_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func newVisionTool(dc danger.DangerousConfig, vc config.VisionConfig) *visionToo

func (t *visionTool) Name() string { return "vision" }
func (t *visionTool) Description() string {
return `Analyze an image or video file using MiniCPM-V 4.6, a local 1.3B multimodal model (llama-mtmd-cli). Images are described directly; videos are sampled into evenly-spaced frames and analyzed together. Supports JPEG, PNG, GIF, WebP, BMP for images and MP4, MOV, AVI, MKV, WebM for video. Requires llama-mtmd-cli and MiniCPM-V 4.6 model files (bundled in the Docker image).`
return `Analyze an image or video file using MiniCPM-V 4.6, a local 1.3B multimodal model (llama-mtmd-cli). Images are described directly; videos are sampled into evenly-spaced frames and analyzed together. Image formats: JPEG, PNG, GIF, WebP, BMP. Video formats: MP4, MOV, AVI, MKV, WebM video analysis additionally requires ffmpeg and ffprobe in PATH; if video fails while images work, convert or extract frames instead of retrying. Requires llama-mtmd-cli and MiniCPM-V 4.6 model files (bundled in the Docker image).`
}

type visionArgs struct {
Expand Down
2 changes: 1 addition & 1 deletion cmd/odek/web_search_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (t *webSearchTool) checkRedirect(req *http.Request, via []*http.Request) er
func (t *webSearchTool) Name() string { return "web_search" }

func (t *webSearchTool) Description() string {
return `Search the web via a self-hosted SearXNG metasearch instance. Returns ranked results (title, url, snippet, engine) plus any direct answers. Use this to find pages, then fetch the most relevant URLs with the browser or http_batch tools. Results come from external search engines and are treated as untrusted content.`
return `Search the web via a self-hosted SearXNG metasearch instance. Returns ranked results (title, url, snippet, engine) plus any direct answers. Use this to find pages, then fetch the most relevant URLs with the browser (http_batch returns response metadata only, not page content). Results come from external search engines and are treated as untrusted content.`
}

type webSearchArgs struct {
Expand Down
1 change: 1 addition & 0 deletions internal/memory/tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func NewMemoryTool(mm *MemoryManager) *MemoryTool {
func (t *MemoryTool) Name() string { return "memory" }
func (t *MemoryTool) Description() string {
return "Manage persistent memory across sessions: read, add, update, remove facts, consolidate related entries, or search past episode summaries. " +
"For finding past sessions by their conversation content, use session_search — memory targets curated facts and episodes, not full session transcripts. " +
"You maintain the user/env fact files: when a target is at cap, remove or replace the lowest-value entries yourself — " +
"records recoverable from git/GitHub (release notes, merged PRs) evict first; pointers to untracked local work evict last. " +
"Use action=stats to check per-entry sizes and fill before writing."
Expand Down
Loading