feat(agent): 运行时内部错误上报到日志(onReport)

- PiAgentRuntimeOptions.onReport:本次运行扩展的系统提示 section、能力工具、beforeTool / beforeRequest 钩子抛错
  先上报(runId + 位置 + 脱敏信息)再原样抛出;pi-durable 的 Harness onReport 也接进来(source=harness)
- server runner / 视觉子流程用 runtimeReporter 写 warn 日志
- 测试:钩子抛错时上报带 runId 与位置、信息已脱敏,调用照常被 block

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-08 13:58:55 +08:00
1 parent 003815591c
commit 7c5215663d
5 files changed
+184 -62

No files matched your search

+4 -2
View File
@@ -24,7 +24,7 @@ import { BrowserServiceError, type ChallengeVision, registerChallengeVision } fr
import { resolveRunUpstream } from "../pi/index.ts";
import type { ServerContext } from "../types.ts";
import { parseFakeScripts } from "./fake-scripts.ts";
import { type AgentEnv, agentSettings, eventSink, publishStatus, sessionDirOf } from "./runner.ts";
import { type AgentEnv, agentSettings, eventSink, publishStatus, runtimeReporter, sessionDirOf } from "./runner.ts";
// VIS-1 browser.solve_challenge:有界的视觉子流程(REQ §4.4、§10)。
// - 服务端起一个受限的子 agent_run(父会话 = 调用它的 Agent 运行;白名单只有截图 / 坐标点击 / 输入 / 滚动 / 等待 / 读页面),
@@ -305,6 +305,7 @@ async function runChild(
const onEvent = eventSink(env);
const onEventError = (err: unknown) =>
ctx.logger.warn({ runId: child.id, err: { message: (err as Error).message } }, "视觉子流程事件落库失败");
const onReport = runtimeReporter(ctx);
let scripts: FakeScript[] = [FALLBACK_SCRIPT];
if (fake) {
try {
@@ -315,11 +316,12 @@ async function runChild(
}
}
const runtime = fake
? createFakeRuntime({ registry, scripts, onEvent, onEventError, fake: { vision: true } }).runtime
? createFakeRuntime({ registry, scripts, onEvent, onEventError, onReport, fake: { vision: true } }).runtime
: new PiAgentRuntime({
registry,
onEvent,
onEventError,
onReport,
upstream: { spec: upstream.spec, credentials: upstream.credentials },
});
const systemPrompt = buildSystemPrompt({
+13 -1
View File
@@ -10,6 +10,7 @@ import {
type FakeScript,
PiAgentRuntime,
type RunOutcome,
type RuntimeReport,
type SessionKind,
type SkillDoc,
selectSkills,
@@ -399,6 +400,15 @@ export async function buildRunPrompt(
return { systemPrompt, kind, skills, available };
}
/**
* 运行时内部错误(本次运行扩展的系统提示 / 能力工具 / 白名单与预算钩子抛错,pi-durable 的 onReport)→ warn 日志。
* message 已由运行时脱敏;只记位置与信息,不记原始错误对象。
*/
export function runtimeReporter(ctx: ServerContext): (r: RuntimeReport) => void {
return (r) =>
ctx.logger.warn({ runId: r.runId, source: r.source, err: { message: r.message } }, "agent 运行时内部错误");
}
/** 读 agent_run_event 里最后一轮的结束事件(崩溃发生在"模型已结束、业务状态未写"之间时复用) */
async function lastOutcomeEvent(env: AgentEnv, runId: string) {
const r = await env.ctx.db
@@ -460,6 +470,7 @@ export async function drive(
const onEvent = eventSink(env);
const onEventError = (err: unknown, e: AgentRunEvent) =>
ctx.logger.warn({ runId, seq: e.seq, err: { message: (err as Error).message } }, "agent 事件落库失败");
const onReport = runtimeReporter(ctx);
let scripts: FakeScript[] = DEMO_SCRIPTS;
if (fake) {
try {
@@ -475,11 +486,12 @@ export async function drive(
}
}
const runtime = fake
? createFakeRuntime({ registry, scripts, onEvent, onEventError, fake: { vision: true } }).runtime
? createFakeRuntime({ registry, scripts, onEvent, onEventError, onReport, fake: { vision: true } }).runtime
: new PiAgentRuntime({
registry,
onEvent,
onEventError,
onReport,
...(upstream ? { upstream: { spec: upstream.spec, credentials: upstream.credentials } } : {}),
});
const maxSeq = await ctx.db