feat(opencode): capture console output and surface it to the model
- console.log/warn/error/info/debug are now a real Rune interpreter builtin: a seeded global that formats its args (strings verbatim, objects/arrays as JSON, space-joined) and appends a line to a per-run LogCollector. It is not a tool call (spends no tool-call budget), returns undefined, and any other member throws. Formatting is charged to maxOperations and total captured output is bounded by maxAuditBytes so a logging loop can't exhaust memory. - The collector is shared by reference with parallel interpreter forks (like the operation budget) and lives in execute()'s outer scope, so logs are surfaced on every ExecuteResult path — success, thrown error, and timeout. - ExecuteResult (and its schema) gain a logs field; code mode appends captured logs to the model-facing output as a trailing '[level] message' section on both the success and error paths (withLogs). Logs go to the model only. - Documents console in rune.md; drops the now-satisfied 'not done yet' entry.
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
rankTools,
|
||||
renderType,
|
||||
toEnvelope,
|
||||
withLogs,
|
||||
type SearchEntry,
|
||||
} from "@/session/code-mode"
|
||||
import type { Tool as MCPToolDef } from "@modelcontextprotocol/sdk/types.js"
|
||||
@@ -406,6 +407,17 @@ describe("code mode execute", () => {
|
||||
expect(formatValue(undefined)).toBe("undefined")
|
||||
})
|
||||
|
||||
test("unit: withLogs", () => {
|
||||
// No logs: output is returned untouched.
|
||||
expect(withLogs("result", [])).toBe("result")
|
||||
// Logs are appended as a trailing section, one `[level] message` line each.
|
||||
expect(withLogs("result", [{ level: "log", message: "a" }, { level: "warn", message: "b" }])).toBe(
|
||||
"result\n\nLogs:\n[log] a\n[warn] b",
|
||||
)
|
||||
// Empty output still gets the section (no leading blank lines).
|
||||
expect(withLogs("", [{ level: "error", message: "boom" }])).toBe("Logs:\n[error] boom")
|
||||
})
|
||||
|
||||
test("terminates a runaway loop via the operation limit instead of hanging", async () => {
|
||||
const tool = await build({})
|
||||
const output = await Effect.runPromise(tool.execute({ code: "while (true) {}" }, ctx))
|
||||
|
||||
Reference in New Issue
Block a user