惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
T
The Blog of Author Tim Ferriss
aimingoo的专栏
aimingoo的专栏
A
About on SuperTechFans
Stack Overflow Blog
Stack Overflow Blog
B
Blog RSS Feed
Microsoft Security Blog
Microsoft Security Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
人人都是产品经理
人人都是产品经理
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
J
Java Code Geeks
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog
MongoDB | Blog
MongoDB | Blog
L
LangChain Blog
WordPress大学
WordPress大学
小众软件
小众软件
IT之家
IT之家
腾讯CDC
月光博客
月光博客
量子位
Blog — PlanetScale
Blog — PlanetScale
P
Proofpoint News Feed
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
test: dedupe diagnostics otel mock calls · openclaw/openc...
steipete · 2026-05-13 · via Recent Commits to openclaw:main

@@ -216,6 +216,50 @@ function startedSpanOptions(name: string) {

216216

return startedSpanCall(name)?.[1];

217217

}

218218219+

function mockCall(mock: { mock: { calls: unknown[][] } }, callIndex = 0): unknown[] {

220+

const call = mock.mock.calls.at(callIndex);

221+

if (!call) {

222+

throw new Error(`Expected mock call at index ${callIndex}`);

223+

}

224+

return call;

225+

}

226+227+

function mockCallArg(mock: { mock: { calls: unknown[][] } }, argIndex: number, callIndex = 0) {

228+

return mockCall(mock, callIndex)[argIndex];

229+

}

230+231+

function firstExporterOptions(mock: { mock: { calls: unknown[][] } }): { url?: string } {

232+

return mockCallArg(mock, 0) as { url?: string };

233+

}

234+235+

function firstSetSpanContext(): Record<string, unknown> {

236+

return mockCallArg(telemetryState.tracer.setSpanContext, 1) as Record<string, unknown>;

237+

}

238+239+

function spanByName(name: string): (typeof telemetryState.spans)[number] {

240+

const span = telemetryState.spans.find((candidate) => candidate.name === name);

241+

if (!span) {

242+

throw new Error(`Expected span ${name}`);

243+

}

244+

return span;

245+

}

246+247+

function firstSpanAttributes(name: string): Record<string, unknown> {

248+

return mockCallArg(spanByName(name).setAttributes, 0) as Record<string, unknown>;

249+

}

250+251+

function firstSpanEndTime(name: string): unknown {

252+

return mockCallArg(spanByName(name).end, 0);

253+

}

254+255+

function firstCounterAddCall(name: string): [unknown, Record<string, unknown>?] {

256+

const counter = telemetryState.counters.get(name);

257+

if (!counter) {

258+

throw new Error(`Expected counter ${name}`);

259+

}

260+

return mockCall(counter.add) as [unknown, Record<string, unknown>?];

261+

}

262+219263

function lastHistogramRecord(name: string) {

220264

return telemetryState.histograms.get(name)?.record.mock.calls.at(-1) as

221265

| [unknown, Record<string, unknown>?]

@@ -246,7 +290,11 @@ async function emitAndCaptureLog(

246290

});

247291

await flushDiagnosticEvents();

248292

expect(logEmit).toHaveBeenCalled();

249-

const emitCall = logEmit.mock.calls.at(0)?.[0];

293+

const emitCall = mockCallArg(logEmit, 0) as {

294+

attributes?: Record<string, unknown>;

295+

body?: string;

296+

context?: unknown;

297+

};

250298

await service.stop?.(ctx);

251299

return emitCall;

252300

}

@@ -753,8 +801,8 @@ describe("diagnostics-otel service", () => {

753801

const ctx = createTraceOnlyContext("https://www.comet.com/opik/api/v1/private/otel");

754802

await service.start(ctx);

755803756-

const options = traceExporterCtor.mock.calls.at(0)?.[0] as { url?: string } | undefined;

757-

expect(options?.url).toBe("https://www.comet.com/opik/api/v1/private/otel/v1/traces");

804+

const options = firstExporterOptions(traceExporterCtor);

805+

expect(options.url).toBe("https://www.comet.com/opik/api/v1/private/otel/v1/traces");

758806

await service.stop?.(ctx);

759807

});

760808

@@ -763,8 +811,8 @@ describe("diagnostics-otel service", () => {

763811

const ctx = createTraceOnlyContext("https://collector.example.com/v1/traces");

764812

await service.start(ctx);

765813766-

const options = traceExporterCtor.mock.calls.at(0)?.[0] as { url?: string } | undefined;

767-

expect(options?.url).toBe("https://collector.example.com/v1/traces");

814+

const options = firstExporterOptions(traceExporterCtor);

815+

expect(options.url).toBe("https://collector.example.com/v1/traces");

768816

await service.stop?.(ctx);

769817

});

770818

@@ -773,8 +821,8 @@ describe("diagnostics-otel service", () => {

773821

const ctx = createTraceOnlyContext("https://collector.example.com/v1/traces?timeout=30s");

774822

await service.start(ctx);

775823776-

const options = traceExporterCtor.mock.calls.at(0)?.[0] as { url?: string } | undefined;

777-

expect(options?.url).toBe("https://collector.example.com/v1/traces?timeout=30s");

824+

const options = firstExporterOptions(traceExporterCtor);

825+

expect(options.url).toBe("https://collector.example.com/v1/traces?timeout=30s");

778826

await service.stop?.(ctx);

779827

});

780828

@@ -783,8 +831,8 @@ describe("diagnostics-otel service", () => {

783831

const ctx = createTraceOnlyContext("https://collector.example.com/v1/Traces");

784832

await service.start(ctx);

785833786-

const options = traceExporterCtor.mock.calls.at(0)?.[0] as { url?: string } | undefined;

787-

expect(options?.url).toBe("https://collector.example.com/v1/Traces");

834+

const options = firstExporterOptions(traceExporterCtor);

835+

expect(options.url).toBe("https://collector.example.com/v1/Traces");

788836

await service.stop?.(ctx);

789837

});

790838

@@ -801,12 +849,12 @@ describe("diagnostics-otel service", () => {

801849802850

await service.start(ctx);

803851804-

const traceOptions = traceExporterCtor.mock.calls.at(0)?.[0] as { url?: string } | undefined;

805-

const metricOptions = metricExporterCtor.mock.calls.at(0)?.[0] as { url?: string } | undefined;

806-

const logOptions = logExporterCtor.mock.calls.at(0)?.[0] as { url?: string } | undefined;

807-

expect(traceOptions?.url).toBe("https://trace.example.com/otlp/v1/traces");

808-

expect(metricOptions?.url).toBe("https://metric.example.com/v1/metrics");

809-

expect(logOptions?.url).toBe("https://log.example.com/otlp/v1/logs");

852+

const traceOptions = firstExporterOptions(traceExporterCtor);

853+

const metricOptions = firstExporterOptions(metricExporterCtor);

854+

const logOptions = firstExporterOptions(logExporterCtor);

855+

expect(traceOptions.url).toBe("https://trace.example.com/otlp/v1/traces");

856+

expect(metricOptions.url).toBe("https://metric.example.com/v1/metrics");

857+

expect(logOptions.url).toBe("https://log.example.com/otlp/v1/logs");

810858

await service.stop?.(ctx);

811859

});

812860

@@ -823,12 +871,12 @@ describe("diagnostics-otel service", () => {

823871

});

824872

await service.start(ctx);

825873826-

const traceOptions = traceExporterCtor.mock.calls.at(0)?.[0] as { url?: string } | undefined;

827-

const metricOptions = metricExporterCtor.mock.calls.at(0)?.[0] as { url?: string } | undefined;

828-

const logOptions = logExporterCtor.mock.calls.at(0)?.[0] as { url?: string } | undefined;

829-

expect(traceOptions?.url).toBe("https://trace-env.example.com/v1/traces");

830-

expect(metricOptions?.url).toBe("https://metric-env.example.com/otlp/v1/metrics");

831-

expect(logOptions?.url).toBe("https://log-env.example.com/otlp/v1/logs");

874+

const traceOptions = firstExporterOptions(traceExporterCtor);

875+

const metricOptions = firstExporterOptions(metricExporterCtor);

876+

const logOptions = firstExporterOptions(logExporterCtor);

877+

expect(traceOptions.url).toBe("https://trace-env.example.com/v1/traces");

878+

expect(metricOptions.url).toBe("https://metric-env.example.com/otlp/v1/metrics");

879+

expect(logOptions.url).toBe("https://log-env.example.com/otlp/v1/logs");

832880

await service.stop?.(ctx);

833881

});

834882

@@ -895,13 +943,11 @@ describe("diagnostics-otel service", () => {

895943

);

896944897945

expect(telemetryState.tracer.setSpanContext).toHaveBeenCalledTimes(1);

898-

const trustedSpanContext = telemetryState.tracer.setSpanContext.mock.calls.at(0)?.[1] as

899-

| Record<string, unknown>

900-

| undefined;

901-

expect(trustedSpanContext?.traceId).toBe(TRACE_ID);

902-

expect(trustedSpanContext?.spanId).toBe(SPAN_ID);

903-

expect(trustedSpanContext?.traceFlags).toBe(1);

904-

expect(trustedSpanContext?.isRemote).toBe(true);

946+

const trustedSpanContext = firstSetSpanContext();

947+

expect(trustedSpanContext.traceId).toBe(TRACE_ID);

948+

expect(trustedSpanContext.spanId).toBe(SPAN_ID);

949+

expect(trustedSpanContext.traceFlags).toBe(1);

950+

expect(trustedSpanContext.isRemote).toBe(true);

905951

const emitContext = emitCall?.context as { spanContext?: Record<string, unknown> } | undefined;

906952

const emitSpanContext = emitContext?.spanContext;

907953

expect(emitSpanContext?.traceId).toBe(TRACE_ID);

@@ -935,24 +981,27 @@ describe("diagnostics-otel service", () => {

935981

} as Parameters<typeof emitDiagnosticEvent>[0]);

936982

await flushDiagnosticEvents();

937983938-

const emitCall = logEmit.mock.calls.at(0)?.[0];

939-

expect(emitCall?.body.length).toBeLessThanOrEqual(4200);

940-

expect(String(emitCall?.attributes?.["openclaw.good"])).toMatch(/^y+/);

941-

expect(emitCall?.attributes?.["code.lineno"]).toBe(42);

942-

expect(emitCall?.attributes?.["code.function"]).toBe("handler");

943-

expect(String(emitCall?.attributes?.["openclaw.good"]).length).toBeLessThanOrEqual(4200);

944-

expect(Object.hasOwn(emitCall?.attributes ?? {}, `openclaw.${PROTO_KEY}`)).toBe(false);

945-

expect(Object.hasOwn(emitCall?.attributes ?? {}, "openclaw.constructor")).toBe(false);

946-

expect(Object.hasOwn(emitCall?.attributes ?? {}, "openclaw.prototype")).toBe(false);

984+

const emitCall = mockCallArg(logEmit, 0) as {

985+

attributes: Record<string, unknown>;

986+

body: string;

987+

};

988+

expect(emitCall.body.length).toBeLessThanOrEqual(4200);

989+

expect(String(emitCall.attributes["openclaw.good"])).toMatch(/^y+/);

990+

expect(emitCall.attributes["code.lineno"]).toBe(42);

991+

expect(emitCall.attributes["code.function"]).toBe("handler");

992+

expect(String(emitCall.attributes["openclaw.good"]).length).toBeLessThanOrEqual(4200);

993+

expect(Object.hasOwn(emitCall.attributes, `openclaw.${PROTO_KEY}`)).toBe(false);

994+

expect(Object.hasOwn(emitCall.attributes, "openclaw.constructor")).toBe(false);

995+

expect(Object.hasOwn(emitCall.attributes, "openclaw.prototype")).toBe(false);

947996

expect(

948997

Object.hasOwn(

949-

emitCall?.attributes ?? {},

998+

emitCall.attributes,

950999

"openclaw.sk-1234567890abcdef1234567890abcdef", // pragma: allowlist secret

9511000

),

9521001

).toBe(false);

953-

expect(Object.hasOwn(emitCall?.attributes ?? {}, "openclaw.bad key")).toBe(false);

954-

expect(Object.hasOwn(emitCall?.attributes ?? {}, "code.filepath")).toBe(false);

955-

expect(Object.hasOwn(emitCall?.attributes ?? {}, "openclaw.code.location")).toBe(false);

1002+

expect(Object.hasOwn(emitCall.attributes, "openclaw.bad key")).toBe(false);

1003+

expect(Object.hasOwn(emitCall.attributes, "code.filepath")).toBe(false);

1004+

expect(Object.hasOwn(emitCall.attributes, "openclaw.code.location")).toBe(false);

9561005

await service.stop?.(ctx);

9571006

});

9581007

@@ -1384,13 +1433,10 @@ describe("diagnostics-otel service", () => {

13841433

expect(timeToFirstByte?.[0]).toBe(45);

13851434

expect(timeToFirstByte?.[1]?.["openclaw.provider"]).toBe("openai");

13861435

expect(timeToFirstByte?.[1]?.["openclaw.model"]).toBe("gpt-5.4");

1387-

const modelCallSpan = telemetryState.spans.find((span) => span.name === "openclaw.model.call");

1388-

const modelSpanAttributes = modelCallSpan?.setAttributes.mock.calls.at(0)?.[0] as

1389-

| Record<string, unknown>

1390-

| undefined;

1391-

expect(modelSpanAttributes?.["openclaw.model_call.request_bytes"]).toBe(1234);

1392-

expect(modelSpanAttributes?.["openclaw.model_call.response_bytes"]).toBe(567);

1393-

expect(modelSpanAttributes?.["openclaw.model_call.time_to_first_byte_ms"]).toBe(45);

1436+

const modelSpanAttributes = firstSpanAttributes("openclaw.model.call");

1437+

expect(modelSpanAttributes["openclaw.model_call.request_bytes"]).toBe(1234);

1438+

expect(modelSpanAttributes["openclaw.model_call.response_bytes"]).toBe(567);

1439+

expect(modelSpanAttributes["openclaw.model_call.time_to_first_byte_ms"]).toBe(45);

13941440

const runDuration = lastHistogramRecord("openclaw.run.duration_ms");

13951441

expect(runDuration?.[0]).toBe(100);

13961442

expect(Object.hasOwn(runDuration?.[1] ?? {}, "openclaw.runId")).toBe(false);

@@ -1406,12 +1452,12 @@ describe("diagnostics-otel service", () => {

14061452

expect(Object.hasOwn(toolDuration?.[1] ?? {}, "openclaw.errorCode")).toBe(false);

14071453

expect(Object.hasOwn(toolDuration?.[1] ?? {}, "openclaw.runId")).toBe(false);

140814541409-

const toolSpan = telemetryState.spans.find((span) => span.name === "openclaw.tool.execution");

1455+

const toolSpan = spanByName("openclaw.tool.execution");

14101456

expect(toolSpan?.setStatus).toHaveBeenCalledWith({

14111457

code: 2,

14121458

message: "TypeError",

14131459

});

1414-

expect(toolSpan?.end.mock.calls.at(0)?.[0]).toBeTypeOf("number");

1460+

expect(firstSpanEndTime("openclaw.tool.execution")).toBeTypeOf("number");

14151461

expect(telemetryState.tracer.setSpanContext).not.toHaveBeenCalled();

14161462

await service.stop?.(ctx);

14171463

});

@@ -1447,10 +1493,7 @@ describe("diagnostics-otel service", () => {

14471493

expect(Object.hasOwn(failoverOptions?.attributes ?? {}, "openclaw.sessionId")).toBe(false);

14481494

expect(Object.hasOwn(failoverOptions?.attributes ?? {}, "openclaw.sessionKey")).toBe(false);

14491495

expect(failoverOptions?.startTime).toBeTypeOf("number");

1450-

const span = telemetryState.spans.find(

1451-

(candidate) => candidate.name === "openclaw.model.failover",

1452-

);

1453-

expect(span?.end.mock.calls.at(0)?.[0]).toBeTypeOf("number");

1496+

expect(firstSpanEndTime("openclaw.model.failover")).toBeTypeOf("number");

14541497

await service.stop?.(ctx);

14551498

});

14561499

@@ -1648,11 +1691,9 @@ describe("diagnostics-otel service", () => {

16481691

expect(contextOptions?.startTime).toBeTypeOf("number");

16491692

expect(JSON.stringify(contextCall)).not.toContain("session-key");

16501693

expect(JSON.stringify(contextCall)).not.toContain("prompt text");

1651-

const linkedSpanContext = telemetryState.tracer.setSpanContext.mock.calls.at(0)?.[1] as

1652-

| Record<string, unknown>

1653-

| undefined;

1654-

expect(linkedSpanContext?.traceId).toBe(TRACE_ID);

1655-

expect(linkedSpanContext?.spanId).toBe(runSpanId);

1694+

const linkedSpanContext = firstSetSpanContext();

1695+

expect(linkedSpanContext.traceId).toBe(TRACE_ID);

1696+

expect(linkedSpanContext.spanId).toBe(runSpanId);

16561697

expect(

16571698

(contextCall?.[2] as { spanContext?: { spanId?: string } } | undefined)?.spanContext?.spanId,

16581699

).toBe(runSpanId);

@@ -1942,16 +1983,14 @@ describe("diagnostics-otel service", () => {

19421983

(call) => call[0] === "openclaw.model.usage",

19431984

);

194419851945-

const linkedSpanContext = telemetryState.tracer.setSpanContext.mock.calls.at(0)?.[1] as

1946-

| Record<string, unknown>

1947-

| undefined;

1948-

expect(linkedSpanContext?.traceId).toBe(TRACE_ID);

1949-

expect(linkedSpanContext?.spanId).toBe(runSpanId);

1986+

const linkedSpanContext = firstSetSpanContext();

1987+

expect(linkedSpanContext.traceId).toBe(TRACE_ID);

1988+

expect(linkedSpanContext.spanId).toBe(runSpanId);

19501989

expect(

19511990

(modelUsageCall?.[2] as { spanContext?: { spanId?: string } } | undefined)?.spanContext

19521991

?.spanId,

19531992

).toBe(runSpanId);

1954-

expect(runSpan?.end.mock.calls.at(0)?.[0]).toBeTypeOf("number");

1993+

expect(firstSpanEndTime("openclaw.run")).toBeTypeOf("number");

19551994

await service.stop?.(ctx);

19561995

});

19571996

@@ -2224,12 +2263,12 @@ describe("diagnostics-otel service", () => {

22242263

expect(Object.hasOwn(execOptions?.attributes ?? {}, "openclaw.sessionKey")).toBe(false);

22252264

expect(execOptions?.startTime).toBeTypeOf("number");

222622652227-

const execSpan = telemetryState.spans.find((span) => span.name === "openclaw.exec");

2266+

const execSpan = spanByName("openclaw.exec");

22282267

expect(execSpan?.setStatus).toHaveBeenCalledWith({

22292268

code: 2,

22302269

message: "runtime-error",

22312270

});

2232-

expect(execSpan?.end.mock.calls.at(0)?.[0]).toBeTypeOf("number");

2271+

expect(firstSpanEndTime("openclaw.exec")).toBeTypeOf("number");

22332272

await service.stop?.(ctx);

22342273

});

22352274

@@ -2400,20 +2439,16 @@ describe("diagnostics-otel service", () => {

24002439

});

24012440

await flushDiagnosticEvents();

240224412403-

const recoveryRequestedCall = telemetryState.counters

2404-

.get("openclaw.session.recovery.requested")

2405-

?.add.mock.calls.at(0);

2406-

expect(recoveryRequestedCall?.[0]).toBe(1);

2407-

expect(recoveryRequestedCall?.[1]?.["openclaw.state"]).toBe("processing");

2408-

expect(recoveryRequestedCall?.[1]?.["openclaw.action"]).toBe("abort");

2409-

expect(recoveryRequestedCall?.[1]?.["openclaw.active_work_kind"]).toBe("tool_call");

2410-

const recoveryCompletedCall = telemetryState.counters

2411-

.get("openclaw.session.recovery.completed")

2412-

?.add.mock.calls.at(0);

2413-

expect(recoveryCompletedCall?.[0]).toBe(1);

2414-

expect(recoveryCompletedCall?.[1]?.["openclaw.state"]).toBe("processing");

2415-

expect(recoveryCompletedCall?.[1]?.["openclaw.status"]).toBe("released");

2416-

expect(recoveryCompletedCall?.[1]?.["openclaw.action"]).toBe("abort-active-run");

2442+

const recoveryRequestedCall = firstCounterAddCall("openclaw.session.recovery.requested");

2443+

expect(recoveryRequestedCall[0]).toBe(1);

2444+

expect(recoveryRequestedCall[1]?.["openclaw.state"]).toBe("processing");

2445+

expect(recoveryRequestedCall[1]?.["openclaw.action"]).toBe("abort");

2446+

expect(recoveryRequestedCall[1]?.["openclaw.active_work_kind"]).toBe("tool_call");

2447+

const recoveryCompletedCall = firstCounterAddCall("openclaw.session.recovery.completed");

2448+

expect(recoveryCompletedCall[0]).toBe(1);

2449+

expect(recoveryCompletedCall[1]?.["openclaw.state"]).toBe("processing");

2450+

expect(recoveryCompletedCall[1]?.["openclaw.status"]).toBe("released");

2451+

expect(recoveryCompletedCall[1]?.["openclaw.action"]).toBe("abort-active-run");

24172452

const recoveryAgeRecord = lastHistogramRecord("openclaw.session.recovery.age_ms");

24182453

expect(recoveryAgeRecord?.[0]).toBe(13_000);

24192454

expect(recoveryAgeRecord?.[1]?.["openclaw.status"]).toBe("released");

@@ -2598,9 +2633,9 @@ describe("diagnostics-otel service", () => {

25982633

reason: "token=ghp_abcdefghijklmnopqrstuvwxyz123456", // pragma: allowlist secret

25992634

});

260026352601-

const sessionCounter = telemetryState.counters.get("openclaw.session.state");

2602-

const attrs = sessionCounter?.add.mock.calls.at(0)?.[1] as Record<string, unknown> | undefined;

2603-

expect(sessionCounter?.add.mock.calls.at(0)?.[0]).toBe(1);

2636+

const sessionStateCall = firstCounterAddCall("openclaw.session.state");

2637+

const attrs = sessionStateCall[1];

2638+

expect(sessionStateCall[0]).toBe(1);

26042639

expect(String(attrs?.["openclaw.reason"])).toContain("…");

26052640

expect(typeof attrs?.["openclaw.reason"]).toBe("string");

26062641

expect(String(attrs?.["openclaw.reason"])).not.toContain(