@agent-inspect/circuit 6.29.3 → 6.29.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -25,12 +25,22 @@ function open(ruleId, message, evidence, severity = "error") {
25
25
  };
26
26
  }
27
27
  function isToolEvent(event) {
28
+ const kind = typeof event.kind === "string" ? event.kind.toLowerCase() : "";
29
+ if (kind === "tool") return true;
30
+ if (kind === "llm" || kind === "run" || kind === "agent" || kind === "logic" || kind === "log" || kind === "outcome" || kind === "chain" || kind === "retriever" || kind === "decision" || kind === "result" || kind === "error") {
31
+ return false;
32
+ }
28
33
  const name = event.name.toLowerCase();
29
- return event.kind === "tool" || name.startsWith("tool:") || name.startsWith("function:") || name.includes(".tool.") || name.startsWith("mcp:");
34
+ return name.startsWith("tool:") || name.startsWith("function:") || name.includes(".tool.") || name.startsWith("mcp:");
30
35
  }
31
36
  function isLlmEvent(event) {
37
+ const kind = typeof event.kind === "string" ? event.kind.toLowerCase() : "";
38
+ if (kind === "llm") return true;
39
+ if (kind === "tool" || kind === "run" || kind === "agent" || kind === "logic" || kind === "log" || kind === "outcome" || kind === "chain" || kind === "retriever" || kind === "decision" || kind === "result" || kind === "error") {
40
+ return false;
41
+ }
32
42
  const name = event.name.toLowerCase();
33
- return event.kind === "llm" || name.startsWith("llm:") || name.includes(".llm.") || name.includes("generation");
43
+ return name.startsWith("llm:") || name.includes(".llm.") || name.includes("generation");
34
44
  }
35
45
  function toolLabel(event) {
36
46
  const attrs = event.attributes ?? {};
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/analyze.ts"],"names":["createHash"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport type {\n CircuitEvidence,\n CircuitResult,\n CircuitRuleId,\n CircuitRunResult,\n CircuitTraceEvent,\n RunCircuitsOptions,\n} from \"./types.js\";\n\nconst ALL_RULES: CircuitRuleId[] = [\n \"circuit.same-tool-repetition\",\n \"circuit.same-args-repetition\",\n \"circuit.max-loop-iterations\",\n \"circuit.max-retries\",\n \"circuit.tool-timeout\",\n \"circuit.runaway-llm-loop\",\n \"circuit.excessive-branch-width\",\n];\n\nfunction closed(ruleId: string, message: string): CircuitResult {\n return { ruleId, status: \"closed\", severity: \"info\", message, evidence: [] };\n}\n\nfunction open(\n ruleId: string,\n message: string,\n evidence: CircuitEvidence[],\n severity: \"error\" | \"warning\" = \"error\",\n): CircuitResult {\n return {\n ruleId,\n status: severity === \"warning\" ? \"warn\" : \"open\",\n severity,\n message,\n evidence,\n };\n}\n\nfunction isToolEvent(event: CircuitTraceEvent): boolean {\n const name = event.name.toLowerCase();\n return (\n event.kind === \"tool\" ||\n name.startsWith(\"tool:\") ||\n name.startsWith(\"function:\") ||\n name.includes(\".tool.\") ||\n name.startsWith(\"mcp:\")\n );\n}\n\nfunction isLlmEvent(event: CircuitTraceEvent): boolean {\n const name = event.name.toLowerCase();\n return event.kind === \"llm\" || name.startsWith(\"llm:\") || name.includes(\".llm.\") || name.includes(\"generation\");\n}\n\nfunction toolLabel(event: CircuitTraceEvent): string {\n const attrs = event.attributes ?? {};\n const fromAttr = attrs.toolName ?? attrs.tool ?? attrs.function;\n if (typeof fromAttr === \"string\" && fromAttr.length > 0) return fromAttr;\n return event.name.replace(/^(tool:|function:|mcp:)/i, \"\");\n}\n\nfunction argsHash(toolName: string, args: unknown): string {\n return createHash(\"sha256\").update(`${toolName}:${JSON.stringify(args ?? null)}`).digest(\"hex\").slice(0, 16);\n}\n\nfunction toolArgs(event: CircuitTraceEvent): unknown {\n const attrs = event.attributes ?? {};\n return attrs.arguments ?? attrs.args ?? attrs.input ?? attrs.parameters;\n}\n\nfunction durationMs(event: CircuitTraceEvent): number | undefined {\n if (typeof event.durationMs === \"number\") return event.durationMs;\n const attrs = event.attributes ?? {};\n const fromAttr = attrs.durationMs ?? attrs.duration;\n return typeof fromAttr === \"number\" ? fromAttr : undefined;\n}\n\nfunction attemptNumber(event: CircuitTraceEvent): number | undefined {\n const attrs = event.attributes ?? {};\n const value = attrs.attempt ?? attrs.retryAttempt ?? attrs.retryCount;\n return typeof value === \"number\" ? value : undefined;\n}\n\nexport function evaluateSameToolRepetition(\n events: readonly CircuitTraceEvent[],\n maxRepeats: number,\n): CircuitResult {\n const ruleId = \"circuit.same-tool-repetition\";\n const counts = new Map<string, number>();\n for (const event of events.filter(isToolEvent)) {\n const label = toolLabel(event);\n counts.set(label, (counts.get(label) ?? 0) + 1);\n }\n const evidence: CircuitEvidence[] = [];\n for (const [toolName, count] of counts) {\n if (count > maxRepeats) {\n evidence.push({ ruleId, toolName, count, threshold: maxRepeats });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Tool repetition within threshold.\");\n }\n return open(ruleId, \"Same tool repeated beyond threshold.\", evidence);\n}\n\nexport function evaluateSameArgsRepetition(\n events: readonly CircuitTraceEvent[],\n maxRepeats: number,\n): CircuitResult {\n const ruleId = \"circuit.same-args-repetition\";\n const counts = new Map<string, { toolName: string; count: number }>();\n for (const event of events.filter(isToolEvent)) {\n const label = toolLabel(event);\n const hash = argsHash(label, toolArgs(event));\n const key = `${label}:${hash}`;\n const current = counts.get(key) ?? { toolName: label, count: 0 };\n current.count += 1;\n counts.set(key, current);\n }\n const evidence: CircuitEvidence[] = [];\n for (const entry of counts.values()) {\n if (entry.count > maxRepeats) {\n evidence.push({ ruleId, toolName: entry.toolName, count: entry.count, threshold: maxRepeats });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Tool argument repetition within threshold.\");\n }\n return open(ruleId, \"Same tool arguments repeated beyond threshold.\", evidence);\n}\n\nexport function evaluateMaxLoopIterations(\n events: readonly CircuitTraceEvent[],\n maxIterations: number,\n): CircuitResult {\n const ruleId = \"circuit.max-loop-iterations\";\n const iterationEvents = events.filter((event) => {\n const attrs = event.attributes ?? {};\n return typeof attrs.iteration === \"number\" || event.name.toLowerCase().includes(\"loop\");\n });\n const maxSeen = iterationEvents.reduce((max, event) => {\n const attrs = event.attributes ?? {};\n const iteration = typeof attrs.iteration === \"number\" ? attrs.iteration : max;\n return Math.max(max, iteration);\n }, iterationEvents.length);\n if (maxSeen <= maxIterations) {\n return closed(ruleId, \"Loop iterations within threshold.\");\n }\n return open(ruleId, \"Loop iterations exceeded threshold.\", [\n { ruleId, count: maxSeen, threshold: maxIterations },\n ]);\n}\n\nexport function evaluateMaxRetries(\n events: readonly CircuitTraceEvent[],\n maxRetries: number,\n): CircuitResult {\n const ruleId = \"circuit.max-retries\";\n const attempts = events.map(attemptNumber).filter((value): value is number => value !== undefined);\n const maxAttempt = attempts.length > 0 ? Math.max(...attempts) : 0;\n if (maxAttempt <= maxRetries) {\n return closed(ruleId, \"Retry count within threshold.\");\n }\n return open(ruleId, \"Retry count exceeded threshold.\", [\n { ruleId, count: maxAttempt, threshold: maxRetries },\n ]);\n}\n\nexport function evaluateToolTimeout(\n events: readonly CircuitTraceEvent[],\n maxDurationMs: number,\n): CircuitResult {\n const ruleId = \"circuit.tool-timeout\";\n const evidence: CircuitEvidence[] = [];\n for (const event of events.filter(isToolEvent)) {\n const duration = durationMs(event);\n if (duration !== undefined && duration > maxDurationMs) {\n evidence.push({\n ruleId,\n toolName: toolLabel(event),\n count: duration,\n threshold: maxDurationMs,\n eventId: event.eventId,\n });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Tool durations within timeout.\");\n }\n return open(ruleId, \"Tool call exceeded configured timeout.\", evidence, \"warning\");\n}\n\nexport function evaluateRunawayLlmLoop(\n events: readonly CircuitTraceEvent[],\n maxLlmCalls: number,\n): CircuitResult {\n const ruleId = \"circuit.runaway-llm-loop\";\n const llmCount = events.filter(isLlmEvent).length;\n const hasTerminal = events.some((event) => {\n const status = (event.status ?? event.attributes?.status ?? \"\").toString().toLowerCase();\n return status === \"ok\" || status === \"success\" || status === \"completed\";\n });\n if (llmCount <= maxLlmCalls || hasTerminal) {\n return closed(ruleId, \"LLM call count within threshold or run completed.\");\n }\n return open(ruleId, \"Runaway LLM loop detected.\", [\n { ruleId, count: llmCount, threshold: maxLlmCalls },\n ]);\n}\n\nexport function evaluateExcessiveBranchWidth(\n events: readonly CircuitTraceEvent[],\n maxWidth: number,\n): CircuitResult {\n const ruleId = \"circuit.excessive-branch-width\";\n const children = new Map<string, number>();\n for (const event of events) {\n const parentId = event.parentId;\n if (!parentId) continue;\n children.set(parentId, (children.get(parentId) ?? 0) + 1);\n }\n const evidence: CircuitEvidence[] = [];\n for (const [parentId, count] of children) {\n if (count > maxWidth) {\n evidence.push({ ruleId, path: parentId, count, threshold: maxWidth });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Branch width within threshold.\");\n }\n return open(ruleId, \"Excessive parallel branch width detected.\", evidence, \"warning\");\n}\n\nfunction runRule(\n ruleId: CircuitRuleId,\n events: readonly CircuitTraceEvent[],\n options: RunCircuitsOptions,\n): CircuitResult | undefined {\n switch (ruleId) {\n case \"circuit.same-tool-repetition\":\n if (options.sameToolRepetition === undefined) return undefined;\n return evaluateSameToolRepetition(events, options.sameToolRepetition.maxRepeats);\n case \"circuit.same-args-repetition\":\n if (options.sameArgsRepetition === undefined) return undefined;\n return evaluateSameArgsRepetition(events, options.sameArgsRepetition.maxRepeats);\n case \"circuit.max-loop-iterations\":\n if (options.maxLoopIterations === undefined) return undefined;\n return evaluateMaxLoopIterations(events, options.maxLoopIterations.maxIterations);\n case \"circuit.max-retries\":\n if (options.maxRetries === undefined) return undefined;\n return evaluateMaxRetries(events, options.maxRetries.maxRetries);\n case \"circuit.tool-timeout\":\n if (options.toolTimeout === undefined) return undefined;\n return evaluateToolTimeout(events, options.toolTimeout.maxDurationMs);\n case \"circuit.runaway-llm-loop\":\n if (options.runawayLlmLoop === undefined) return undefined;\n return evaluateRunawayLlmLoop(events, options.runawayLlmLoop.maxLlmCalls);\n case \"circuit.excessive-branch-width\":\n if (options.excessiveBranchWidth === undefined) return undefined;\n return evaluateExcessiveBranchWidth(events, options.excessiveBranchWidth.maxWidth);\n default:\n return undefined;\n }\n}\n\nexport function runCircuits(\n events: readonly CircuitTraceEvent[],\n options: RunCircuitsOptions = {},\n): CircuitRunResult {\n const selected = options.rules ?? ALL_RULES;\n const results: CircuitResult[] = [];\n for (const ruleId of selected) {\n const result = runRule(ruleId, events, options);\n if (result) results.push(result);\n }\n const ok = !results.some((result) => result.status === \"open\" && result.severity === \"error\");\n return { ok, results };\n}\n\nexport { ALL_RULES as DEFAULT_CIRCUIT_RULES };\n"]}
1
+ {"version":3,"sources":["../src/analyze.ts"],"names":["createHash"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport type {\n CircuitEvidence,\n CircuitResult,\n CircuitRuleId,\n CircuitRunResult,\n CircuitTraceEvent,\n RunCircuitsOptions,\n} from \"./types.js\";\n\nconst ALL_RULES: CircuitRuleId[] = [\n \"circuit.same-tool-repetition\",\n \"circuit.same-args-repetition\",\n \"circuit.max-loop-iterations\",\n \"circuit.max-retries\",\n \"circuit.tool-timeout\",\n \"circuit.runaway-llm-loop\",\n \"circuit.excessive-branch-width\",\n];\n\nfunction closed(ruleId: string, message: string): CircuitResult {\n return { ruleId, status: \"closed\", severity: \"info\", message, evidence: [] };\n}\n\nfunction open(\n ruleId: string,\n message: string,\n evidence: CircuitEvidence[],\n severity: \"error\" | \"warning\" = \"error\",\n): CircuitResult {\n return {\n ruleId,\n status: severity === \"warning\" ? \"warn\" : \"open\",\n severity,\n message,\n evidence,\n };\n}\n\nfunction isToolEvent(event: CircuitTraceEvent): boolean {\n const kind = typeof event.kind === \"string\" ? event.kind.toLowerCase() : \"\";\n if (kind === \"tool\") return true;\n if (\n kind === \"llm\" ||\n kind === \"run\" ||\n kind === \"agent\" ||\n kind === \"logic\" ||\n kind === \"log\" ||\n kind === \"outcome\" ||\n kind === \"chain\" ||\n kind === \"retriever\" ||\n kind === \"decision\" ||\n kind === \"result\" ||\n kind === \"error\"\n ) {\n return false;\n }\n const name = event.name.toLowerCase();\n return (\n name.startsWith(\"tool:\") ||\n name.startsWith(\"function:\") ||\n name.includes(\".tool.\") ||\n name.startsWith(\"mcp:\")\n );\n}\n\nfunction isLlmEvent(event: CircuitTraceEvent): boolean {\n const kind = typeof event.kind === \"string\" ? event.kind.toLowerCase() : \"\";\n if (kind === \"llm\") return true;\n if (\n kind === \"tool\" ||\n kind === \"run\" ||\n kind === \"agent\" ||\n kind === \"logic\" ||\n kind === \"log\" ||\n kind === \"outcome\" ||\n kind === \"chain\" ||\n kind === \"retriever\" ||\n kind === \"decision\" ||\n kind === \"result\" ||\n kind === \"error\"\n ) {\n return false;\n }\n const name = event.name.toLowerCase();\n return name.startsWith(\"llm:\") || name.includes(\".llm.\") || name.includes(\"generation\");\n}\n\nfunction toolLabel(event: CircuitTraceEvent): string {\n const attrs = event.attributes ?? {};\n const fromAttr = attrs.toolName ?? attrs.tool ?? attrs.function;\n if (typeof fromAttr === \"string\" && fromAttr.length > 0) return fromAttr;\n return event.name.replace(/^(tool:|function:|mcp:)/i, \"\");\n}\n\nfunction argsHash(toolName: string, args: unknown): string {\n return createHash(\"sha256\").update(`${toolName}:${JSON.stringify(args ?? null)}`).digest(\"hex\").slice(0, 16);\n}\n\nfunction toolArgs(event: CircuitTraceEvent): unknown {\n const attrs = event.attributes ?? {};\n return attrs.arguments ?? attrs.args ?? attrs.input ?? attrs.parameters;\n}\n\nfunction durationMs(event: CircuitTraceEvent): number | undefined {\n if (typeof event.durationMs === \"number\") return event.durationMs;\n const attrs = event.attributes ?? {};\n const fromAttr = attrs.durationMs ?? attrs.duration;\n return typeof fromAttr === \"number\" ? fromAttr : undefined;\n}\n\nfunction attemptNumber(event: CircuitTraceEvent): number | undefined {\n const attrs = event.attributes ?? {};\n const value = attrs.attempt ?? attrs.retryAttempt ?? attrs.retryCount;\n return typeof value === \"number\" ? value : undefined;\n}\n\nexport function evaluateSameToolRepetition(\n events: readonly CircuitTraceEvent[],\n maxRepeats: number,\n): CircuitResult {\n const ruleId = \"circuit.same-tool-repetition\";\n const counts = new Map<string, number>();\n for (const event of events.filter(isToolEvent)) {\n const label = toolLabel(event);\n counts.set(label, (counts.get(label) ?? 0) + 1);\n }\n const evidence: CircuitEvidence[] = [];\n for (const [toolName, count] of counts) {\n if (count > maxRepeats) {\n evidence.push({ ruleId, toolName, count, threshold: maxRepeats });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Tool repetition within threshold.\");\n }\n return open(ruleId, \"Same tool repeated beyond threshold.\", evidence);\n}\n\nexport function evaluateSameArgsRepetition(\n events: readonly CircuitTraceEvent[],\n maxRepeats: number,\n): CircuitResult {\n const ruleId = \"circuit.same-args-repetition\";\n const counts = new Map<string, { toolName: string; count: number }>();\n for (const event of events.filter(isToolEvent)) {\n const label = toolLabel(event);\n const hash = argsHash(label, toolArgs(event));\n const key = `${label}:${hash}`;\n const current = counts.get(key) ?? { toolName: label, count: 0 };\n current.count += 1;\n counts.set(key, current);\n }\n const evidence: CircuitEvidence[] = [];\n for (const entry of counts.values()) {\n if (entry.count > maxRepeats) {\n evidence.push({ ruleId, toolName: entry.toolName, count: entry.count, threshold: maxRepeats });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Tool argument repetition within threshold.\");\n }\n return open(ruleId, \"Same tool arguments repeated beyond threshold.\", evidence);\n}\n\nexport function evaluateMaxLoopIterations(\n events: readonly CircuitTraceEvent[],\n maxIterations: number,\n): CircuitResult {\n const ruleId = \"circuit.max-loop-iterations\";\n const iterationEvents = events.filter((event) => {\n const attrs = event.attributes ?? {};\n return typeof attrs.iteration === \"number\" || event.name.toLowerCase().includes(\"loop\");\n });\n const maxSeen = iterationEvents.reduce((max, event) => {\n const attrs = event.attributes ?? {};\n const iteration = typeof attrs.iteration === \"number\" ? attrs.iteration : max;\n return Math.max(max, iteration);\n }, iterationEvents.length);\n if (maxSeen <= maxIterations) {\n return closed(ruleId, \"Loop iterations within threshold.\");\n }\n return open(ruleId, \"Loop iterations exceeded threshold.\", [\n { ruleId, count: maxSeen, threshold: maxIterations },\n ]);\n}\n\nexport function evaluateMaxRetries(\n events: readonly CircuitTraceEvent[],\n maxRetries: number,\n): CircuitResult {\n const ruleId = \"circuit.max-retries\";\n const attempts = events.map(attemptNumber).filter((value): value is number => value !== undefined);\n const maxAttempt = attempts.length > 0 ? Math.max(...attempts) : 0;\n if (maxAttempt <= maxRetries) {\n return closed(ruleId, \"Retry count within threshold.\");\n }\n return open(ruleId, \"Retry count exceeded threshold.\", [\n { ruleId, count: maxAttempt, threshold: maxRetries },\n ]);\n}\n\nexport function evaluateToolTimeout(\n events: readonly CircuitTraceEvent[],\n maxDurationMs: number,\n): CircuitResult {\n const ruleId = \"circuit.tool-timeout\";\n const evidence: CircuitEvidence[] = [];\n for (const event of events.filter(isToolEvent)) {\n const duration = durationMs(event);\n if (duration !== undefined && duration > maxDurationMs) {\n evidence.push({\n ruleId,\n toolName: toolLabel(event),\n count: duration,\n threshold: maxDurationMs,\n eventId: event.eventId,\n });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Tool durations within timeout.\");\n }\n return open(ruleId, \"Tool call exceeded configured timeout.\", evidence, \"warning\");\n}\n\nexport function evaluateRunawayLlmLoop(\n events: readonly CircuitTraceEvent[],\n maxLlmCalls: number,\n): CircuitResult {\n const ruleId = \"circuit.runaway-llm-loop\";\n const llmCount = events.filter(isLlmEvent).length;\n const hasTerminal = events.some((event) => {\n const status = (event.status ?? event.attributes?.status ?? \"\").toString().toLowerCase();\n return status === \"ok\" || status === \"success\" || status === \"completed\";\n });\n if (llmCount <= maxLlmCalls || hasTerminal) {\n return closed(ruleId, \"LLM call count within threshold or run completed.\");\n }\n return open(ruleId, \"Runaway LLM loop detected.\", [\n { ruleId, count: llmCount, threshold: maxLlmCalls },\n ]);\n}\n\nexport function evaluateExcessiveBranchWidth(\n events: readonly CircuitTraceEvent[],\n maxWidth: number,\n): CircuitResult {\n const ruleId = \"circuit.excessive-branch-width\";\n const children = new Map<string, number>();\n for (const event of events) {\n const parentId = event.parentId;\n if (!parentId) continue;\n children.set(parentId, (children.get(parentId) ?? 0) + 1);\n }\n const evidence: CircuitEvidence[] = [];\n for (const [parentId, count] of children) {\n if (count > maxWidth) {\n evidence.push({ ruleId, path: parentId, count, threshold: maxWidth });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Branch width within threshold.\");\n }\n return open(ruleId, \"Excessive parallel branch width detected.\", evidence, \"warning\");\n}\n\nfunction runRule(\n ruleId: CircuitRuleId,\n events: readonly CircuitTraceEvent[],\n options: RunCircuitsOptions,\n): CircuitResult | undefined {\n switch (ruleId) {\n case \"circuit.same-tool-repetition\":\n if (options.sameToolRepetition === undefined) return undefined;\n return evaluateSameToolRepetition(events, options.sameToolRepetition.maxRepeats);\n case \"circuit.same-args-repetition\":\n if (options.sameArgsRepetition === undefined) return undefined;\n return evaluateSameArgsRepetition(events, options.sameArgsRepetition.maxRepeats);\n case \"circuit.max-loop-iterations\":\n if (options.maxLoopIterations === undefined) return undefined;\n return evaluateMaxLoopIterations(events, options.maxLoopIterations.maxIterations);\n case \"circuit.max-retries\":\n if (options.maxRetries === undefined) return undefined;\n return evaluateMaxRetries(events, options.maxRetries.maxRetries);\n case \"circuit.tool-timeout\":\n if (options.toolTimeout === undefined) return undefined;\n return evaluateToolTimeout(events, options.toolTimeout.maxDurationMs);\n case \"circuit.runaway-llm-loop\":\n if (options.runawayLlmLoop === undefined) return undefined;\n return evaluateRunawayLlmLoop(events, options.runawayLlmLoop.maxLlmCalls);\n case \"circuit.excessive-branch-width\":\n if (options.excessiveBranchWidth === undefined) return undefined;\n return evaluateExcessiveBranchWidth(events, options.excessiveBranchWidth.maxWidth);\n default:\n return undefined;\n }\n}\n\nexport function runCircuits(\n events: readonly CircuitTraceEvent[],\n options: RunCircuitsOptions = {},\n): CircuitRunResult {\n const selected = options.rules ?? ALL_RULES;\n const results: CircuitResult[] = [];\n for (const ruleId of selected) {\n const result = runRule(ruleId, events, options);\n if (result) results.push(result);\n }\n const ok = !results.some((result) => result.status === \"open\" && result.severity === \"error\");\n return { ok, results };\n}\n\nexport { ALL_RULES as DEFAULT_CIRCUIT_RULES };\n"]}
package/dist/index.mjs CHANGED
@@ -23,12 +23,22 @@ function open(ruleId, message, evidence, severity = "error") {
23
23
  };
24
24
  }
25
25
  function isToolEvent(event) {
26
+ const kind = typeof event.kind === "string" ? event.kind.toLowerCase() : "";
27
+ if (kind === "tool") return true;
28
+ if (kind === "llm" || kind === "run" || kind === "agent" || kind === "logic" || kind === "log" || kind === "outcome" || kind === "chain" || kind === "retriever" || kind === "decision" || kind === "result" || kind === "error") {
29
+ return false;
30
+ }
26
31
  const name = event.name.toLowerCase();
27
- return event.kind === "tool" || name.startsWith("tool:") || name.startsWith("function:") || name.includes(".tool.") || name.startsWith("mcp:");
32
+ return name.startsWith("tool:") || name.startsWith("function:") || name.includes(".tool.") || name.startsWith("mcp:");
28
33
  }
29
34
  function isLlmEvent(event) {
35
+ const kind = typeof event.kind === "string" ? event.kind.toLowerCase() : "";
36
+ if (kind === "llm") return true;
37
+ if (kind === "tool" || kind === "run" || kind === "agent" || kind === "logic" || kind === "log" || kind === "outcome" || kind === "chain" || kind === "retriever" || kind === "decision" || kind === "result" || kind === "error") {
38
+ return false;
39
+ }
30
40
  const name = event.name.toLowerCase();
31
- return event.kind === "llm" || name.startsWith("llm:") || name.includes(".llm.") || name.includes("generation");
41
+ return name.startsWith("llm:") || name.includes(".llm.") || name.includes("generation");
32
42
  }
33
43
  function toolLabel(event) {
34
44
  const attrs = event.attributes ?? {};
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/analyze.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport type {\n CircuitEvidence,\n CircuitResult,\n CircuitRuleId,\n CircuitRunResult,\n CircuitTraceEvent,\n RunCircuitsOptions,\n} from \"./types.js\";\n\nconst ALL_RULES: CircuitRuleId[] = [\n \"circuit.same-tool-repetition\",\n \"circuit.same-args-repetition\",\n \"circuit.max-loop-iterations\",\n \"circuit.max-retries\",\n \"circuit.tool-timeout\",\n \"circuit.runaway-llm-loop\",\n \"circuit.excessive-branch-width\",\n];\n\nfunction closed(ruleId: string, message: string): CircuitResult {\n return { ruleId, status: \"closed\", severity: \"info\", message, evidence: [] };\n}\n\nfunction open(\n ruleId: string,\n message: string,\n evidence: CircuitEvidence[],\n severity: \"error\" | \"warning\" = \"error\",\n): CircuitResult {\n return {\n ruleId,\n status: severity === \"warning\" ? \"warn\" : \"open\",\n severity,\n message,\n evidence,\n };\n}\n\nfunction isToolEvent(event: CircuitTraceEvent): boolean {\n const name = event.name.toLowerCase();\n return (\n event.kind === \"tool\" ||\n name.startsWith(\"tool:\") ||\n name.startsWith(\"function:\") ||\n name.includes(\".tool.\") ||\n name.startsWith(\"mcp:\")\n );\n}\n\nfunction isLlmEvent(event: CircuitTraceEvent): boolean {\n const name = event.name.toLowerCase();\n return event.kind === \"llm\" || name.startsWith(\"llm:\") || name.includes(\".llm.\") || name.includes(\"generation\");\n}\n\nfunction toolLabel(event: CircuitTraceEvent): string {\n const attrs = event.attributes ?? {};\n const fromAttr = attrs.toolName ?? attrs.tool ?? attrs.function;\n if (typeof fromAttr === \"string\" && fromAttr.length > 0) return fromAttr;\n return event.name.replace(/^(tool:|function:|mcp:)/i, \"\");\n}\n\nfunction argsHash(toolName: string, args: unknown): string {\n return createHash(\"sha256\").update(`${toolName}:${JSON.stringify(args ?? null)}`).digest(\"hex\").slice(0, 16);\n}\n\nfunction toolArgs(event: CircuitTraceEvent): unknown {\n const attrs = event.attributes ?? {};\n return attrs.arguments ?? attrs.args ?? attrs.input ?? attrs.parameters;\n}\n\nfunction durationMs(event: CircuitTraceEvent): number | undefined {\n if (typeof event.durationMs === \"number\") return event.durationMs;\n const attrs = event.attributes ?? {};\n const fromAttr = attrs.durationMs ?? attrs.duration;\n return typeof fromAttr === \"number\" ? fromAttr : undefined;\n}\n\nfunction attemptNumber(event: CircuitTraceEvent): number | undefined {\n const attrs = event.attributes ?? {};\n const value = attrs.attempt ?? attrs.retryAttempt ?? attrs.retryCount;\n return typeof value === \"number\" ? value : undefined;\n}\n\nexport function evaluateSameToolRepetition(\n events: readonly CircuitTraceEvent[],\n maxRepeats: number,\n): CircuitResult {\n const ruleId = \"circuit.same-tool-repetition\";\n const counts = new Map<string, number>();\n for (const event of events.filter(isToolEvent)) {\n const label = toolLabel(event);\n counts.set(label, (counts.get(label) ?? 0) + 1);\n }\n const evidence: CircuitEvidence[] = [];\n for (const [toolName, count] of counts) {\n if (count > maxRepeats) {\n evidence.push({ ruleId, toolName, count, threshold: maxRepeats });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Tool repetition within threshold.\");\n }\n return open(ruleId, \"Same tool repeated beyond threshold.\", evidence);\n}\n\nexport function evaluateSameArgsRepetition(\n events: readonly CircuitTraceEvent[],\n maxRepeats: number,\n): CircuitResult {\n const ruleId = \"circuit.same-args-repetition\";\n const counts = new Map<string, { toolName: string; count: number }>();\n for (const event of events.filter(isToolEvent)) {\n const label = toolLabel(event);\n const hash = argsHash(label, toolArgs(event));\n const key = `${label}:${hash}`;\n const current = counts.get(key) ?? { toolName: label, count: 0 };\n current.count += 1;\n counts.set(key, current);\n }\n const evidence: CircuitEvidence[] = [];\n for (const entry of counts.values()) {\n if (entry.count > maxRepeats) {\n evidence.push({ ruleId, toolName: entry.toolName, count: entry.count, threshold: maxRepeats });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Tool argument repetition within threshold.\");\n }\n return open(ruleId, \"Same tool arguments repeated beyond threshold.\", evidence);\n}\n\nexport function evaluateMaxLoopIterations(\n events: readonly CircuitTraceEvent[],\n maxIterations: number,\n): CircuitResult {\n const ruleId = \"circuit.max-loop-iterations\";\n const iterationEvents = events.filter((event) => {\n const attrs = event.attributes ?? {};\n return typeof attrs.iteration === \"number\" || event.name.toLowerCase().includes(\"loop\");\n });\n const maxSeen = iterationEvents.reduce((max, event) => {\n const attrs = event.attributes ?? {};\n const iteration = typeof attrs.iteration === \"number\" ? attrs.iteration : max;\n return Math.max(max, iteration);\n }, iterationEvents.length);\n if (maxSeen <= maxIterations) {\n return closed(ruleId, \"Loop iterations within threshold.\");\n }\n return open(ruleId, \"Loop iterations exceeded threshold.\", [\n { ruleId, count: maxSeen, threshold: maxIterations },\n ]);\n}\n\nexport function evaluateMaxRetries(\n events: readonly CircuitTraceEvent[],\n maxRetries: number,\n): CircuitResult {\n const ruleId = \"circuit.max-retries\";\n const attempts = events.map(attemptNumber).filter((value): value is number => value !== undefined);\n const maxAttempt = attempts.length > 0 ? Math.max(...attempts) : 0;\n if (maxAttempt <= maxRetries) {\n return closed(ruleId, \"Retry count within threshold.\");\n }\n return open(ruleId, \"Retry count exceeded threshold.\", [\n { ruleId, count: maxAttempt, threshold: maxRetries },\n ]);\n}\n\nexport function evaluateToolTimeout(\n events: readonly CircuitTraceEvent[],\n maxDurationMs: number,\n): CircuitResult {\n const ruleId = \"circuit.tool-timeout\";\n const evidence: CircuitEvidence[] = [];\n for (const event of events.filter(isToolEvent)) {\n const duration = durationMs(event);\n if (duration !== undefined && duration > maxDurationMs) {\n evidence.push({\n ruleId,\n toolName: toolLabel(event),\n count: duration,\n threshold: maxDurationMs,\n eventId: event.eventId,\n });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Tool durations within timeout.\");\n }\n return open(ruleId, \"Tool call exceeded configured timeout.\", evidence, \"warning\");\n}\n\nexport function evaluateRunawayLlmLoop(\n events: readonly CircuitTraceEvent[],\n maxLlmCalls: number,\n): CircuitResult {\n const ruleId = \"circuit.runaway-llm-loop\";\n const llmCount = events.filter(isLlmEvent).length;\n const hasTerminal = events.some((event) => {\n const status = (event.status ?? event.attributes?.status ?? \"\").toString().toLowerCase();\n return status === \"ok\" || status === \"success\" || status === \"completed\";\n });\n if (llmCount <= maxLlmCalls || hasTerminal) {\n return closed(ruleId, \"LLM call count within threshold or run completed.\");\n }\n return open(ruleId, \"Runaway LLM loop detected.\", [\n { ruleId, count: llmCount, threshold: maxLlmCalls },\n ]);\n}\n\nexport function evaluateExcessiveBranchWidth(\n events: readonly CircuitTraceEvent[],\n maxWidth: number,\n): CircuitResult {\n const ruleId = \"circuit.excessive-branch-width\";\n const children = new Map<string, number>();\n for (const event of events) {\n const parentId = event.parentId;\n if (!parentId) continue;\n children.set(parentId, (children.get(parentId) ?? 0) + 1);\n }\n const evidence: CircuitEvidence[] = [];\n for (const [parentId, count] of children) {\n if (count > maxWidth) {\n evidence.push({ ruleId, path: parentId, count, threshold: maxWidth });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Branch width within threshold.\");\n }\n return open(ruleId, \"Excessive parallel branch width detected.\", evidence, \"warning\");\n}\n\nfunction runRule(\n ruleId: CircuitRuleId,\n events: readonly CircuitTraceEvent[],\n options: RunCircuitsOptions,\n): CircuitResult | undefined {\n switch (ruleId) {\n case \"circuit.same-tool-repetition\":\n if (options.sameToolRepetition === undefined) return undefined;\n return evaluateSameToolRepetition(events, options.sameToolRepetition.maxRepeats);\n case \"circuit.same-args-repetition\":\n if (options.sameArgsRepetition === undefined) return undefined;\n return evaluateSameArgsRepetition(events, options.sameArgsRepetition.maxRepeats);\n case \"circuit.max-loop-iterations\":\n if (options.maxLoopIterations === undefined) return undefined;\n return evaluateMaxLoopIterations(events, options.maxLoopIterations.maxIterations);\n case \"circuit.max-retries\":\n if (options.maxRetries === undefined) return undefined;\n return evaluateMaxRetries(events, options.maxRetries.maxRetries);\n case \"circuit.tool-timeout\":\n if (options.toolTimeout === undefined) return undefined;\n return evaluateToolTimeout(events, options.toolTimeout.maxDurationMs);\n case \"circuit.runaway-llm-loop\":\n if (options.runawayLlmLoop === undefined) return undefined;\n return evaluateRunawayLlmLoop(events, options.runawayLlmLoop.maxLlmCalls);\n case \"circuit.excessive-branch-width\":\n if (options.excessiveBranchWidth === undefined) return undefined;\n return evaluateExcessiveBranchWidth(events, options.excessiveBranchWidth.maxWidth);\n default:\n return undefined;\n }\n}\n\nexport function runCircuits(\n events: readonly CircuitTraceEvent[],\n options: RunCircuitsOptions = {},\n): CircuitRunResult {\n const selected = options.rules ?? ALL_RULES;\n const results: CircuitResult[] = [];\n for (const ruleId of selected) {\n const result = runRule(ruleId, events, options);\n if (result) results.push(result);\n }\n const ok = !results.some((result) => result.status === \"open\" && result.severity === \"error\");\n return { ok, results };\n}\n\nexport { ALL_RULES as DEFAULT_CIRCUIT_RULES };\n"]}
1
+ {"version":3,"sources":["../src/analyze.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport type {\n CircuitEvidence,\n CircuitResult,\n CircuitRuleId,\n CircuitRunResult,\n CircuitTraceEvent,\n RunCircuitsOptions,\n} from \"./types.js\";\n\nconst ALL_RULES: CircuitRuleId[] = [\n \"circuit.same-tool-repetition\",\n \"circuit.same-args-repetition\",\n \"circuit.max-loop-iterations\",\n \"circuit.max-retries\",\n \"circuit.tool-timeout\",\n \"circuit.runaway-llm-loop\",\n \"circuit.excessive-branch-width\",\n];\n\nfunction closed(ruleId: string, message: string): CircuitResult {\n return { ruleId, status: \"closed\", severity: \"info\", message, evidence: [] };\n}\n\nfunction open(\n ruleId: string,\n message: string,\n evidence: CircuitEvidence[],\n severity: \"error\" | \"warning\" = \"error\",\n): CircuitResult {\n return {\n ruleId,\n status: severity === \"warning\" ? \"warn\" : \"open\",\n severity,\n message,\n evidence,\n };\n}\n\nfunction isToolEvent(event: CircuitTraceEvent): boolean {\n const kind = typeof event.kind === \"string\" ? event.kind.toLowerCase() : \"\";\n if (kind === \"tool\") return true;\n if (\n kind === \"llm\" ||\n kind === \"run\" ||\n kind === \"agent\" ||\n kind === \"logic\" ||\n kind === \"log\" ||\n kind === \"outcome\" ||\n kind === \"chain\" ||\n kind === \"retriever\" ||\n kind === \"decision\" ||\n kind === \"result\" ||\n kind === \"error\"\n ) {\n return false;\n }\n const name = event.name.toLowerCase();\n return (\n name.startsWith(\"tool:\") ||\n name.startsWith(\"function:\") ||\n name.includes(\".tool.\") ||\n name.startsWith(\"mcp:\")\n );\n}\n\nfunction isLlmEvent(event: CircuitTraceEvent): boolean {\n const kind = typeof event.kind === \"string\" ? event.kind.toLowerCase() : \"\";\n if (kind === \"llm\") return true;\n if (\n kind === \"tool\" ||\n kind === \"run\" ||\n kind === \"agent\" ||\n kind === \"logic\" ||\n kind === \"log\" ||\n kind === \"outcome\" ||\n kind === \"chain\" ||\n kind === \"retriever\" ||\n kind === \"decision\" ||\n kind === \"result\" ||\n kind === \"error\"\n ) {\n return false;\n }\n const name = event.name.toLowerCase();\n return name.startsWith(\"llm:\") || name.includes(\".llm.\") || name.includes(\"generation\");\n}\n\nfunction toolLabel(event: CircuitTraceEvent): string {\n const attrs = event.attributes ?? {};\n const fromAttr = attrs.toolName ?? attrs.tool ?? attrs.function;\n if (typeof fromAttr === \"string\" && fromAttr.length > 0) return fromAttr;\n return event.name.replace(/^(tool:|function:|mcp:)/i, \"\");\n}\n\nfunction argsHash(toolName: string, args: unknown): string {\n return createHash(\"sha256\").update(`${toolName}:${JSON.stringify(args ?? null)}`).digest(\"hex\").slice(0, 16);\n}\n\nfunction toolArgs(event: CircuitTraceEvent): unknown {\n const attrs = event.attributes ?? {};\n return attrs.arguments ?? attrs.args ?? attrs.input ?? attrs.parameters;\n}\n\nfunction durationMs(event: CircuitTraceEvent): number | undefined {\n if (typeof event.durationMs === \"number\") return event.durationMs;\n const attrs = event.attributes ?? {};\n const fromAttr = attrs.durationMs ?? attrs.duration;\n return typeof fromAttr === \"number\" ? fromAttr : undefined;\n}\n\nfunction attemptNumber(event: CircuitTraceEvent): number | undefined {\n const attrs = event.attributes ?? {};\n const value = attrs.attempt ?? attrs.retryAttempt ?? attrs.retryCount;\n return typeof value === \"number\" ? value : undefined;\n}\n\nexport function evaluateSameToolRepetition(\n events: readonly CircuitTraceEvent[],\n maxRepeats: number,\n): CircuitResult {\n const ruleId = \"circuit.same-tool-repetition\";\n const counts = new Map<string, number>();\n for (const event of events.filter(isToolEvent)) {\n const label = toolLabel(event);\n counts.set(label, (counts.get(label) ?? 0) + 1);\n }\n const evidence: CircuitEvidence[] = [];\n for (const [toolName, count] of counts) {\n if (count > maxRepeats) {\n evidence.push({ ruleId, toolName, count, threshold: maxRepeats });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Tool repetition within threshold.\");\n }\n return open(ruleId, \"Same tool repeated beyond threshold.\", evidence);\n}\n\nexport function evaluateSameArgsRepetition(\n events: readonly CircuitTraceEvent[],\n maxRepeats: number,\n): CircuitResult {\n const ruleId = \"circuit.same-args-repetition\";\n const counts = new Map<string, { toolName: string; count: number }>();\n for (const event of events.filter(isToolEvent)) {\n const label = toolLabel(event);\n const hash = argsHash(label, toolArgs(event));\n const key = `${label}:${hash}`;\n const current = counts.get(key) ?? { toolName: label, count: 0 };\n current.count += 1;\n counts.set(key, current);\n }\n const evidence: CircuitEvidence[] = [];\n for (const entry of counts.values()) {\n if (entry.count > maxRepeats) {\n evidence.push({ ruleId, toolName: entry.toolName, count: entry.count, threshold: maxRepeats });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Tool argument repetition within threshold.\");\n }\n return open(ruleId, \"Same tool arguments repeated beyond threshold.\", evidence);\n}\n\nexport function evaluateMaxLoopIterations(\n events: readonly CircuitTraceEvent[],\n maxIterations: number,\n): CircuitResult {\n const ruleId = \"circuit.max-loop-iterations\";\n const iterationEvents = events.filter((event) => {\n const attrs = event.attributes ?? {};\n return typeof attrs.iteration === \"number\" || event.name.toLowerCase().includes(\"loop\");\n });\n const maxSeen = iterationEvents.reduce((max, event) => {\n const attrs = event.attributes ?? {};\n const iteration = typeof attrs.iteration === \"number\" ? attrs.iteration : max;\n return Math.max(max, iteration);\n }, iterationEvents.length);\n if (maxSeen <= maxIterations) {\n return closed(ruleId, \"Loop iterations within threshold.\");\n }\n return open(ruleId, \"Loop iterations exceeded threshold.\", [\n { ruleId, count: maxSeen, threshold: maxIterations },\n ]);\n}\n\nexport function evaluateMaxRetries(\n events: readonly CircuitTraceEvent[],\n maxRetries: number,\n): CircuitResult {\n const ruleId = \"circuit.max-retries\";\n const attempts = events.map(attemptNumber).filter((value): value is number => value !== undefined);\n const maxAttempt = attempts.length > 0 ? Math.max(...attempts) : 0;\n if (maxAttempt <= maxRetries) {\n return closed(ruleId, \"Retry count within threshold.\");\n }\n return open(ruleId, \"Retry count exceeded threshold.\", [\n { ruleId, count: maxAttempt, threshold: maxRetries },\n ]);\n}\n\nexport function evaluateToolTimeout(\n events: readonly CircuitTraceEvent[],\n maxDurationMs: number,\n): CircuitResult {\n const ruleId = \"circuit.tool-timeout\";\n const evidence: CircuitEvidence[] = [];\n for (const event of events.filter(isToolEvent)) {\n const duration = durationMs(event);\n if (duration !== undefined && duration > maxDurationMs) {\n evidence.push({\n ruleId,\n toolName: toolLabel(event),\n count: duration,\n threshold: maxDurationMs,\n eventId: event.eventId,\n });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Tool durations within timeout.\");\n }\n return open(ruleId, \"Tool call exceeded configured timeout.\", evidence, \"warning\");\n}\n\nexport function evaluateRunawayLlmLoop(\n events: readonly CircuitTraceEvent[],\n maxLlmCalls: number,\n): CircuitResult {\n const ruleId = \"circuit.runaway-llm-loop\";\n const llmCount = events.filter(isLlmEvent).length;\n const hasTerminal = events.some((event) => {\n const status = (event.status ?? event.attributes?.status ?? \"\").toString().toLowerCase();\n return status === \"ok\" || status === \"success\" || status === \"completed\";\n });\n if (llmCount <= maxLlmCalls || hasTerminal) {\n return closed(ruleId, \"LLM call count within threshold or run completed.\");\n }\n return open(ruleId, \"Runaway LLM loop detected.\", [\n { ruleId, count: llmCount, threshold: maxLlmCalls },\n ]);\n}\n\nexport function evaluateExcessiveBranchWidth(\n events: readonly CircuitTraceEvent[],\n maxWidth: number,\n): CircuitResult {\n const ruleId = \"circuit.excessive-branch-width\";\n const children = new Map<string, number>();\n for (const event of events) {\n const parentId = event.parentId;\n if (!parentId) continue;\n children.set(parentId, (children.get(parentId) ?? 0) + 1);\n }\n const evidence: CircuitEvidence[] = [];\n for (const [parentId, count] of children) {\n if (count > maxWidth) {\n evidence.push({ ruleId, path: parentId, count, threshold: maxWidth });\n }\n }\n if (evidence.length === 0) {\n return closed(ruleId, \"Branch width within threshold.\");\n }\n return open(ruleId, \"Excessive parallel branch width detected.\", evidence, \"warning\");\n}\n\nfunction runRule(\n ruleId: CircuitRuleId,\n events: readonly CircuitTraceEvent[],\n options: RunCircuitsOptions,\n): CircuitResult | undefined {\n switch (ruleId) {\n case \"circuit.same-tool-repetition\":\n if (options.sameToolRepetition === undefined) return undefined;\n return evaluateSameToolRepetition(events, options.sameToolRepetition.maxRepeats);\n case \"circuit.same-args-repetition\":\n if (options.sameArgsRepetition === undefined) return undefined;\n return evaluateSameArgsRepetition(events, options.sameArgsRepetition.maxRepeats);\n case \"circuit.max-loop-iterations\":\n if (options.maxLoopIterations === undefined) return undefined;\n return evaluateMaxLoopIterations(events, options.maxLoopIterations.maxIterations);\n case \"circuit.max-retries\":\n if (options.maxRetries === undefined) return undefined;\n return evaluateMaxRetries(events, options.maxRetries.maxRetries);\n case \"circuit.tool-timeout\":\n if (options.toolTimeout === undefined) return undefined;\n return evaluateToolTimeout(events, options.toolTimeout.maxDurationMs);\n case \"circuit.runaway-llm-loop\":\n if (options.runawayLlmLoop === undefined) return undefined;\n return evaluateRunawayLlmLoop(events, options.runawayLlmLoop.maxLlmCalls);\n case \"circuit.excessive-branch-width\":\n if (options.excessiveBranchWidth === undefined) return undefined;\n return evaluateExcessiveBranchWidth(events, options.excessiveBranchWidth.maxWidth);\n default:\n return undefined;\n }\n}\n\nexport function runCircuits(\n events: readonly CircuitTraceEvent[],\n options: RunCircuitsOptions = {},\n): CircuitRunResult {\n const selected = options.rules ?? ALL_RULES;\n const results: CircuitResult[] = [];\n for (const ruleId of selected) {\n const result = runRule(ruleId, events, options);\n if (result) results.push(result);\n }\n const ok = !results.some((result) => result.status === \"open\" && result.severity === \"error\");\n return { ok, results };\n}\n\nexport { ALL_RULES as DEFAULT_CIRCUIT_RULES };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-inspect/circuit",
3
- "version": "6.29.3",
3
+ "version": "6.29.5",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Local circuit-breaker analyzers for AgentInspect trace patterns",
@@ -31,6 +31,7 @@
31
31
  },
32
32
  "files": [
33
33
  "README.md",
34
+ "LICENSE",
34
35
  "dist"
35
36
  ],
36
37
  "peerDependencies": {},