





















@@ -87,16 +87,17 @@ describe("before_tool_call loop detection behavior", () => {
8787}
88888989async function withToolExecutionEvents(
90-run: (emitted: DiagnosticEventPayload[]) => Promise<void>,
90+run: (emitted: DiagnosticEventPayload[], flush: () => Promise<void>) => Promise<void>,
9191) {
9292const emitted: DiagnosticEventPayload[] = [];
9393const stop = onDiagnosticEvent((evt) => {
9494if (evt.type.startsWith("tool.execution.")) {
9595emitted.push(evt);
9696}
9797});
98+const flush = () => new Promise<void>((resolve) => setImmediate(resolve));
9899try {
99-await run(emitted);
100+await run(emitted, flush);
100101} finally {
101102stop();
102103}
@@ -367,13 +368,14 @@ describe("before_tool_call loop detection behavior", () => {
367368loopDetection: { enabled: false },
368369});
369370370-await withToolExecutionEvents(async (emitted) => {
371+await withToolExecutionEvents(async (emitted, flush) => {
371372await tool.execute(
372373"tool-call-1",
373374{ command: "pwd", token: "sk-1234567890abcdef1234567890abcdef" },
374375undefined,
375376undefined,
376377);
378+await flush();
377379378380expect(emitted.map((evt) => evt.type)).toEqual([
379381"tool.execution.started",
@@ -412,10 +414,11 @@ describe("before_tool_call loop detection behavior", () => {
412414loopDetection: { enabled: false },
413415});
414416415-await withToolExecutionEvents(async (emitted) => {
417+await withToolExecutionEvents(async (emitted, flush) => {
416418await expect(
417419tool.execute("tool-call-error", { path: "/tmp/file" }, undefined, undefined),
418420).rejects.toThrow("failed with key");
421+await flush();
419422420423expect(emitted.map((evt) => evt.type)).toEqual([
421424"tool.execution.started",
@@ -432,6 +435,71 @@ describe("before_tool_call loop detection behavior", () => {
432435});
433436});
434437438+it("does not let hostile thrown values break diagnostic error emission", async () => {
439+const hostileError = new Proxy(
440+{},
441+{
442+get() {
443+throw new Error("diagnostic getter should not run");
444+},
445+getOwnPropertyDescriptor() {
446+throw new Error("diagnostic descriptor failed");
447+},
448+},
449+);
450+const execute = vi.fn().mockRejectedValue(hostileError);
451+const tool = wrapToolWithBeforeToolCallHook({ name: "read", execute } as any, {
452+agentId: "main",
453+sessionKey: "session-key",
454+loopDetection: { enabled: false },
455+});
456+457+await withToolExecutionEvents(async (emitted, flush) => {
458+await expect(
459+tool.execute("tool-call-hostile-error", { path: "/tmp/file" }, undefined, undefined),
460+).rejects.toBe(hostileError);
461+await flush();
462+463+expect(emitted.map((evt) => evt.type)).toEqual([
464+"tool.execution.started",
465+"tool.execution.error",
466+]);
467+expect(emitted[1]).toMatchObject({
468+type: "tool.execution.error",
469+toolName: "read",
470+toolCallId: "tool-call-hostile-error",
471+errorCategory: "object",
472+});
473+expect(emitted[1]).not.toHaveProperty("errorCode");
474+});
475+});
476+477+it("emits only numeric HTTP status codes as diagnostic tool error codes", async () => {
478+const error = Object.assign(new Error("rate limited"), {
479+code: "SECRET_TOKEN",
480+status: 429,
481+});
482+const execute = vi.fn().mockRejectedValue(error);
483+const tool = wrapToolWithBeforeToolCallHook({ name: "read", execute } as any, {
484+agentId: "main",
485+sessionKey: "session-key",
486+loopDetection: { enabled: false },
487+});
488+489+await withToolExecutionEvents(async (emitted, flush) => {
490+await expect(
491+tool.execute("tool-call-status-code", { path: "/tmp/file" }, undefined, undefined),
492+).rejects.toThrow("rate limited");
493+await flush();
494+495+expect(emitted[1]).toMatchObject({
496+type: "tool.execution.error",
497+errorCode: "429",
498+});
499+expect(JSON.stringify(emitted[1])).not.toContain("SECRET_TOKEN");
500+});
501+});
502+435503it("summarizes hostile object params without enumerating keys", async () => {
436504const execute = vi.fn().mockResolvedValue({ content: [{ type: "text", text: "ok" }] });
437505const tool = wrapToolWithBeforeToolCallHook({ name: "bash", execute } as any, {
@@ -448,8 +516,9 @@ describe("before_tool_call loop detection behavior", () => {
448516},
449517);
450518451-await withToolExecutionEvents(async (emitted) => {
519+await withToolExecutionEvents(async (emitted, flush) => {
452520await tool.execute("tool-call-proxy", params, undefined, undefined);
521+await flush();
453522454523expect(emitted[0]).toMatchObject({
455524type: "tool.execution.started",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。