harness: LiteLLM client, Redmine intake, REPORT writeback, gated bash tool

OpenAI-compatible chat client for the LiteLLM proxy (base_url normalization,
Bearer auth, retry/backoff on 429/5xx/transport, usage accounting) - stdlib
http only. Intake lists issues in scope from /issues.json with the task
class read from a configurable custom field. Writeback lands each turn as
REPORT-<vertical>-<task>-<ts>.md plus REPORT-latest.md (atomic rename) with
model/tier/token telemetry. The bash tool ports maki's permission semantics
without tree-sitter: segment-by-segment compound-command checks, deny beats
allow, word-boundary "cmd *" matching, $()/backtick/subshell denied, output
truncation, per-command timeout with process-group cleanup.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
This commit is contained in:
2026-08-28 19:20:57 -05:00
parent 591d345371
commit aefa73aa90
12 changed files with 1230 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
package intake
import (
"git.knownelement.com/reachableceo/MOPAC/harness/internal/config"
"git.knownelement.com/reachableceo/MOPAC/harness/internal/task"
)
// DemoTask builds the MVP demo issue from the [demo] config section: the
// smoke path that exercises the whole loop (intake -> turn -> REPORT) without
// needing a reachable Redmine.
func DemoTask(d config.DemoConfig) task.Task {
return task.Task{
ID: d.ID,
Subject: d.Subject,
Prompt: d.Prompt,
Class: d.Class,
Source: "demo",
}
}