agent-workflow-inspector 0.0.0-stage → 1.0.0
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/CHANGELOG.md +13 -0
- package/CODE_OF_CONDUCT.md +5 -0
- package/CONTRIBUTING.md +9 -0
- package/LICENSE +21 -0
- package/README.md +95 -2
- package/SECURITY.md +5 -0
- package/awi.config.example.json +34 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1179 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +232 -0
- package/dist/index.js +1071 -0
- package/dist/index.js.map +1 -0
- package/docs/architecture.md +21 -0
- package/docs/audit.md +91 -0
- package/docs/ci-integration.md +17 -0
- package/docs/configuration.md +15 -0
- package/docs/detection-rules.md +28 -0
- package/docs/getting-started.md +29 -0
- package/docs/limitations.md +11 -0
- package/docs/releases/v1.0.0.md +32 -0
- package/docs/releasing.md +112 -0
- package/docs/report-schema.md +22 -0
- package/docs/schemas/config.schema.json +145 -0
- package/docs/schemas/report.schema.json +117 -0
- package/docs/schemas/trace.schema.json +98 -0
- package/docs/security.md +11 -0
- package/docs/trace-formats.md +41 -0
- package/docs/troubleshooting.md +12 -0
- package/docs/verification.md +38 -0
- package/examples/healthy/basic.json +30 -0
- package/examples/opentelemetry/tools.json +210 -0
- package/examples/suspicious/cycle.json +78 -0
- package/examples/suspicious/retries.json +116 -0
- package/package.json +77 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/analysis.ts","../src/security/index.ts","../src/core/timestamps.ts","../src/adapters/index.ts","../src/config/index.ts","../src/core/fingerprinting.ts","../src/core/normalization.ts","../src/core/reporting.ts","../src/core/report-schema.ts"],"sourcesContent":["import { z } from 'zod';\nimport { timeNanoseconds } from './timestamps.js';\nimport { traceSchema } from '../adapters/index.js';\nimport { resolveConfig } from '../config/index.js';\nimport { AwiError, LIMITS, redact, validateValue } from '../security/index.js';\nimport { fingerprint, hash } from './fingerprinting.js';\nimport { normalize, sequentialRuns } from './normalization.js';\nimport type { Operation, Report, RuleId, Severity, Trace } from './models.js';\nexport const severityRank: Record<Severity, number> = {\n low: 1,\n medium: 2,\n high: 3,\n};\nconst titles: Record<RuleId, string> = {\n AWI001: 'Repeated identical tool call',\n AWI002: 'Repeated identical failing tool call',\n AWI003: 'Repeating execution cycle',\n AWI004: 'Retry storm',\n AWI005: 'Excessive execution steps or depth',\n AWI006: 'Recorded resource usage associated with suspicious execution',\n};\nexport function analyze(\n traces: Trace[],\n configInput: unknown = {},\n showContent = false,\n): Report {\n const config = resolveConfig(configInput);\n validateValue(traces, true);\n if (!Array.isArray(traces) || traces.length > LIMITS.traces)\n throw new AwiError('Invalid or oversized trace collection');\n const analysisTraceSchema = traceSchema.extend({\n operations: z\n .array(\n traceSchema.shape.operations.element.extend({\n source: z\n .strictObject({\n format: z.enum(['awi', 'otel']),\n index: z\n .number()\n .int()\n .nonnegative()\n .max(Number.MAX_SAFE_INTEGER),\n })\n .optional(),\n }),\n )\n .max(LIMITS.spans),\n });\n const validated: Trace[] = [];\n let total = 0;\n for (const trace of traces) {\n if (!trace || !Array.isArray(trace.operations))\n throw new AwiError('Invalid canonical trace supplied to analysis');\n total += trace.operations.length;\n if (total > LIMITS.spans)\n throw new AwiError('Maximum total span count exceeded');\n const result = analysisTraceSchema.safeParse({\n ...trace,\n operations: trace.operations.map((op) =>\n op && typeof op === 'object'\n ? Object.fromEntries(\n Object.entries(op).filter(([, value]) => value !== undefined),\n )\n : op,\n ),\n });\n if (!result.success)\n throw new AwiError('Invalid canonical trace supplied to analysis');\n // Known optional fields may be explicitly undefined in native TypeScript callers.\n if (result.data.metadata === undefined) delete result.data.metadata;\n for (const op of result.data.operations) {\n if (op.error)\n op.error = Object.fromEntries(\n Object.entries(op.error).filter(([, value]) => value !== undefined),\n );\n if (op.usage)\n op.usage = Object.fromEntries(\n Object.entries(op.usage).filter(([, value]) => value !== undefined),\n );\n }\n validateValue(result.data);\n validated.push(result.data);\n }\n const report: Report = {\n version: 1,\n fingerprintVersion: 'awi-sha256-v1',\n complete: true,\n summaries: [],\n findings: [],\n warnings: [],\n };\n const grouped = new Map<string, Trace>();\n for (const t of validated) {\n const existing = grouped.get(t.traceId);\n if (existing) for (const op of t.operations) existing.operations.push(op);\n else grouped.set(t.traceId, { ...t, operations: [...t.operations] });\n }\n let capped = false;\n function add(\n ruleId: RuleId,\n traceId: string,\n ops: Operation[],\n evidence: Record<string, unknown>,\n explanation: string,\n confidence: 'high' | 'moderate' = 'moderate',\n ) {\n if (report.findings.length >= LIMITS.findings) {\n capped = true;\n return;\n }\n report.findings.push({\n ruleId,\n traceId,\n severity: ruleId === 'AWI002' || ruleId === 'AWI004' ? 'high' : 'medium',\n confidence,\n title: titles[ruleId],\n description:\n 'Heuristic finding based on recorded execution evidence; intentional behavior may match.',\n explanation,\n remediation:\n 'Review the referenced operations and configured policy before changing the workflow.',\n operationIds: ops.map((o) => o.id),\n evidence: {\n ...evidence,\n references: ops.map((op) => ({\n operationId: op.id,\n ...(op.source\n ? { source: { format: op.source.format, index: op.source.index } }\n : {}),\n })),\n },\n });\n }\n for (const trace of [...grouped.values()].sort((a, b) =>\n a.traceId < b.traceId ? -1 : a.traceId > b.traceId ? 1 : 0,\n )) {\n trace.operations.sort((a, b) => compare(a.id, b.id));\n const normalized = normalize(trace);\n report.warnings.push(...normalized.warnings);\n const starts = trace.operations.flatMap((o) =>\n o.startTime ? [timeNanoseconds(o.startTime)] : [],\n ),\n ends = trace.operations.flatMap((o) =>\n o.endTime ? [timeNanoseconds(o.endTime)] : [],\n );\n const leaves = trace.operations.filter(\n (o) => !normalized.children.has(o.id),\n );\n const usage: NonNullable<Operation['usage']> = {};\n for (const key of ['inputTokens', 'outputTokens', 'totalTokens'] as const) {\n const values = leaves.flatMap((o) =>\n o.usage?.[key] !== undefined ? [o.usage[key]!] : [],\n );\n if (values.length) {\n const total = values.reduce((a, b) => a + b, 0);\n if (!Number.isSafeInteger(total))\n throw new AwiError(\n 'Recorded token aggregate exceeds safe integer range',\n );\n usage[key] = total;\n }\n }\n report.summaries.push({\n traceId: trace.traceId,\n operations: trace.operations.length,\n toolCalls: trace.operations.filter((o) => o.type === 'tool').length,\n maxDepth: normalized.depth,\n ...(starts.length === trace.operations.length &&\n ends.length === trace.operations.length &&\n starts.length\n ? {\n durationMs:\n Number(\n ends.reduce((a, b) => (a > b ? a : b)) -\n starts.reduce((a, b) => (a < b ? a : b)),\n ) / 1000000,\n }\n : {}),\n ...(Object.keys(usage).length ? { usage } : {}),\n });\n if (\n trace.operations.some(\n (o) => o.type === 'tool' && o.arguments === undefined,\n )\n )\n report.warnings.push(\n 'Tool calls without recorded arguments excluded from identity-based rules.',\n );\n if (\n trace.operations.some((o) => o.type === 'tool' && !toolIdentityKnown(o))\n )\n report.warnings.push(\n 'Tool calls without recorded tool identity excluded from identity-based rules.',\n );\n const before = report.findings.length;\n const fps = new Map(\n trace.operations\n .filter((o) => o.type === 'tool' && toolIdentityKnown(o))\n .map((o) => [o.id, fingerprint(o, config.fingerprint.ignorePaths)]),\n );\n for (const siblings of normalized.children.values()) {\n if (\n siblings.some(\n (o) =>\n !o.startTime ||\n !o.endTime ||\n timeNanoseconds(o.endTime) <= timeNanoseconds(o.startTime),\n )\n )\n report.warnings.push(\n 'Sibling groups with missing or zero-duration timing excluded from sequence rules; recorded order does not establish sequential execution.',\n );\n const runs = sequentialRuns(siblings);\n if (\n runs.flat().length < siblings.length &&\n siblings.every(\n (o) =>\n o.startTime &&\n o.endTime &&\n timeNanoseconds(o.endTime) > timeNanoseconds(o.startTime),\n )\n )\n report.warnings.push(\n 'Overlapping sibling operations excluded from sequence rules.',\n );\n for (const run of runs) {\n // Project known tool calls; non-tool spans still constrain timing and parent context.\n let segment: Operation[] = [];\n const segments: Operation[][] = [];\n for (const op of run) {\n if (op.type !== 'tool') continue;\n if (op.arguments !== undefined && toolIdentityKnown(op))\n segment.push(op);\n else if (segment.length) {\n segments.push(segment);\n segment = [];\n }\n }\n if (segment.length) segments.push(segment);\n for (const tools of segments) {\n const groups = new Map<string, Operation[]>();\n // A bounded 100-operation window prevents distant repetitions accumulating forever.\n for (\n let base = 0;\n base < tools.length;\n base += LIMITS.repetitionWindow\n ) {\n groups.clear();\n for (const op of tools.slice(\n base,\n base + LIMITS.repetitionWindow,\n )) {\n const fp = fps.get(op.id)!;\n const group = groups.get(fp) ?? [];\n group.push(op);\n groups.set(fp, group);\n }\n for (const group of groups.values()) {\n const failures = new Map<string, Operation[]>();\n for (const op of group)\n if (\n op.status === 'error' &&\n (!!op.error?.code?.trim() || !!op.error?.message?.trim())\n ) {\n const key = hash(op.error);\n const bucket = failures.get(key) ?? [];\n bucket.push(op);\n failures.set(key, bucket);\n }\n const specific = new Set<string>();\n if (config.rules.AWI002.enabled)\n for (const bucket of failures.values())\n if (bucket.length >= config.rules.AWI002.threshold) {\n add(\n 'AWI002',\n trace.traceId,\n bucket,\n {\n occurrences: bucket.length,\n identicalArguments: true,\n equivalentFailureOutcomes: true,\n },\n 'Equivalent recorded tool calls failed with matching error evidence. This does not establish a defective retry policy.',\n 'high',\n );\n bucket.forEach((o) => specific.add(o.id));\n }\n const general = group.filter((o) => !specific.has(o.id));\n if (\n config.rules.AWI001.enabled &&\n general.length >= config.rules.AWI001.threshold\n )\n add(\n 'AWI001',\n trace.traceId,\n general,\n { occurrences: general.length, identicalArguments: true },\n 'Identical calls occur in a bounded group of sequential sibling tool calls. Polling may be intentional.',\n 'high',\n );\n if (config.rules.AWI004.enabled) {\n let cursor = 0;\n while (cursor < group.length) {\n const first = group[cursor]!;\n let end = cursor + 1;\n while (\n end < group.length &&\n group[end - 1]!.status === 'error' &&\n timeNanoseconds(group[end]!.startTime!) -\n timeNanoseconds(first.startTime!) <=\n BigInt(config.rules.AWI004.windowSeconds) * 1000000000n\n )\n end++;\n const attempts = group.slice(cursor, end);\n if (\n attempts.length >= config.rules.AWI004.threshold &&\n attempts.slice(0, -1).every((o) => o.status === 'error')\n ) {\n add(\n 'AWI004',\n trace.traceId,\n attempts,\n {\n attempts: attempts.length,\n windowSeconds: config.rules.AWI004.windowSeconds,\n priorAttemptsExplicitlyFailed: true,\n },\n 'Rapid equivalent attempts follow explicit failures; retry intent remains inferred.',\n );\n cursor = end;\n } else cursor++;\n }\n }\n }\n }\n if (config.rules.AWI003.enabled) {\n const { maxCycleLength, minRepetitions } = config.rules.AWI003;\n for (let i = 0; i < tools.length;) {\n let consumed = 0;\n for (\n let length = 2;\n length <= maxCycleLength &&\n i + length * minRepetitions <= tools.length;\n length++\n ) {\n const pattern = tools\n .slice(i, i + length)\n .map((o) => fps.get(o.id)!);\n if (new Set(pattern).size < 2) continue;\n let repeats = 1;\n while (\n i + (repeats + 1) * length <= tools.length &&\n tools\n .slice(i + repeats * length, i + (repeats + 1) * length)\n .every((o, j) => fps.get(o.id) === pattern[j])\n )\n repeats++;\n if (repeats >= minRepetitions) {\n consumed = length * repeats;\n add(\n 'AWI003',\n trace.traceId,\n tools.slice(i, i + consumed),\n { cycleLength: length, repetitions: repeats },\n 'A bounded tool fingerprint sequence repeats among timed, non-overlapping siblings. Intent and causality are unknown.',\n );\n break;\n }\n }\n i += consumed || 1;\n }\n }\n }\n }\n }\n const policy = config.rules.AWI005;\n if (\n policy.enabled &&\n (trace.operations.length > policy.maxOperations ||\n normalized.depth > policy.maxDepth)\n )\n add(\n 'AWI005',\n trace.traceId,\n [],\n {\n operations: trace.operations.length,\n maxDepth: normalized.depth,\n limits: { operations: policy.maxOperations, depth: policy.maxDepth },\n },\n 'Observed workflow size exceeds configured policy; complexity alone is not a defect.',\n 'high',\n );\n if (config.rules.AWI006.enabled) {\n const related = report.findings\n .slice(before)\n .filter((f) => f.ruleId !== 'AWI005');\n const implicated = new Set(related.flatMap((f) => f.operationIds));\n // Only implicated leaf spans: no attribution from an enclosing aggregate or unrelated model generation.\n const recorded = leaves.filter(\n (o) =>\n implicated.has(o.id) &&\n (Object.keys(o.usage ?? {}).length > 0 || o.cost),\n );\n if (recorded.length) {\n const tokens: Record<string, number> = {};\n const costs: Record<string, number> = {};\n for (const op of recorded) {\n for (const [key, n] of Object.entries(op.usage ?? {}))\n if (n !== undefined) tokens[key] = (tokens[key] ?? 0) + n;\n if (op.cost)\n costs[op.cost.currency] =\n (costs[op.cost.currency] ?? 0) + op.cost.amount;\n if (Object.values(costs).some((n) => !Number.isFinite(n)))\n throw new AwiError('Recorded cost aggregate exceeds numeric range');\n }\n add(\n 'AWI006',\n trace.traceId,\n recorded,\n {\n recordedUsage: tokens,\n recordedCosts: costs,\n coverage:\n 'Only implicated leaf operations with recorded usage or cost',\n relatedRules: [...new Set(related.map((f) => f.ruleId))],\n },\n 'Recorded resources belong directly to operations in suspicious patterns. Association does not establish waste.',\n );\n }\n }\n }\n if (capped) {\n report.complete = false;\n report.warnings.push('Finding limit reached; report is truncated.');\n }\n report.findings.sort(\n (a, b) =>\n severityRank[b.severity] - severityRank[a.severity] ||\n compare(a.traceId, b.traceId) ||\n compare(a.ruleId, b.ruleId) ||\n compare(a.operationIds.join('\\0'), b.operationIds.join('\\0')),\n );\n report.warnings = [...new Set(report.warnings)].sort();\n try {\n validateValue(report);\n } catch (error) {\n if (error instanceof AwiError)\n throw new AwiError('Report exceeds resource limits');\n throw error;\n }\n if (showContent)\n report.content = [...grouped.values()]\n .sort((a, b) => compare(a.traceId, b.traceId))\n .map((t) => ({\n traceId: t.traceId,\n operations: redact(t.operations) as unknown[],\n }));\n return report;\n}\nfunction compare(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\nfunction toolIdentityKnown(op: Operation): boolean {\n return (\n op.source?.format !== 'otel' ||\n !!op.toolName ||\n op.metadata?.['awi.operation_name_recorded'] !== false\n );\n}\n","export const LIMITS = Object.freeze({\n fileBytes: 16 * 1024 * 1024,\n spans: 50000,\n traces: 50000,\n repetitionWindow: 100,\n aggregateBytes: 64 * 1024 * 1024,\n processedBytes: 64 * 1024 * 1024,\n depth: 64,\n stringLength: 65536,\n nodes: 1000000,\n files: 256,\n findings: 1000,\n});\nexport class AwiError extends Error {}\nexport function safeText(value: string): string {\n return value.replace(/[\\p{Cc}\\p{Cf}\\p{Zl}\\p{Zp}]/gu, '?');\n}\nexport function validateValue(\n value: unknown,\n allowUndefinedProperties = false,\n): void {\n let nodes = 0;\n let bytes = 0;\n function charge(amount: number): void {\n bytes += amount;\n if (bytes > LIMITS.processedBytes)\n throw new AwiError('JSON content exceeds processed size limit');\n }\n // Recursion cannot exceed 65 frames; descriptors are read one at a time, not materialized for an entire wide object.\n function visit(item: unknown, depth: number): void {\n if (++nodes > LIMITS.nodes || depth > LIMITS.depth)\n throw new AwiError('JSON structure exceeds resource limits');\n if (typeof item === 'string' && item.length > LIMITS.stringLength)\n throw new AwiError('JSON string exceeds resource limit');\n if (typeof item === 'number' && !Number.isFinite(item))\n throw new AwiError('Non-finite number');\n if (item !== null && typeof item === 'object') {\n charge(2);\n const array = Array.isArray(item);\n const prototype = Object.getPrototypeOf(item);\n if (\n array\n ? prototype !== Array.prototype\n : prototype !== Object.prototype && prototype !== null\n )\n throw new AwiError('Only plain JSON objects and arrays are supported');\n if (array && nodes + item.length > LIMITS.nodes)\n throw new AwiError('JSON structure exceeds resource limits');\n const keys = Reflect.ownKeys(item);\n if (nodes + keys.length - (array ? 1 : 0) > LIMITS.nodes)\n throw new AwiError('JSON structure exceeds resource limits');\n let arrayIndex = 0;\n for (const key of keys) {\n if (typeof key === 'symbol')\n throw new AwiError('Symbol keys are not JSON');\n if (array && key === 'length') continue;\n if (array && key !== String(arrayIndex++))\n throw new AwiError('Sparse or extended arrays are not JSON');\n if (key.length > LIMITS.stringLength)\n throw new AwiError('JSON key exceeds resource limit');\n if (['__proto__', 'prototype', 'constructor'].includes(key))\n throw new AwiError('Unsafe object key');\n const descriptor = Object.getOwnPropertyDescriptor(item, key)!;\n if (descriptor.get || descriptor.set)\n throw new AwiError('Accessors are not JSON');\n if (!descriptor.enumerable)\n throw new AwiError('Non-enumerable properties are not JSON');\n if (\n descriptor.value === undefined &&\n allowUndefinedProperties &&\n !array\n )\n continue;\n if (!array) charge(Buffer.byteLength(JSON.stringify(key)) + 1);\n charge(1);\n visit(descriptor.value, depth + 1);\n }\n if (array && arrayIndex !== item.length)\n throw new AwiError('Sparse or extended arrays are not JSON');\n } else if (\n !['string', 'number', 'boolean'].includes(typeof item) &&\n item !== null\n )\n throw new AwiError('Unsupported JSON value');\n else charge(Buffer.byteLength(JSON.stringify(item)));\n }\n visit(value, 0);\n}\nexport function parseJson(text: string): unknown {\n if (Buffer.byteLength(text) > LIMITS.fileBytes)\n throw new AwiError('Input exceeds maximum file size');\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n throw new AwiError('Invalid JSON');\n }\n validateValue(value);\n return value;\n}\n// Content is opt-in and still redacted by structural key, never a promise of anonymization.\nexport function redact(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(redact);\n if (value && typeof value === 'object')\n return Object.fromEntries(\n Object.entries(value).map(([k, v]) => [\n k,\n /authorization|password|secret|token|api.?key|cookie|credential|prompt|message|output|result/i.test(\n k,\n )\n ? '[REDACTED]'\n : redact(v),\n ]),\n );\n return typeof value === 'string' ? safeText(value) : value;\n}\n\nexport function validateIgnorePaths(paths: readonly string[]): void {\n if (\n !Array.isArray(paths) ||\n paths.length > 100 ||\n paths.some(\n (path) =>\n typeof path !== 'string' ||\n path.length > LIMITS.stringLength ||\n !/^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(path) ||\n path.split('.').length > LIMITS.depth ||\n path\n .split('.')\n .some((key) =>\n ['__proto__', 'constructor', 'prototype'].includes(key),\n ),\n )\n )\n throw new AwiError('Invalid fingerprint ignore paths');\n}\n","import { AwiError } from '../security/index.js';\n/** Preserve up to nine fractional digits; Date.parse alone would erase concurrency evidence. */\nexport function timeNanoseconds(value: string): bigint {\n const milliseconds = Date.parse(value);\n const fraction = /\\.(\\d+)(?:Z|[+-]\\d{2}:\\d{2})$/.exec(value)?.[1] ?? '';\n if (!Number.isFinite(milliseconds) || fraction.length > 9)\n throw new AwiError('Invalid or unsupported timestamp precision');\n return (\n BigInt(milliseconds) * 1000000n +\n BigInt(fraction.padEnd(9, '0').slice(3) || '0')\n );\n}\nexport function validTime(value: string): boolean {\n try {\n timeNanoseconds(value);\n return true;\n } catch {\n return false;\n }\n}\n","import { validTime } from '../core/timestamps.js';\nimport { z } from 'zod';\nimport { AwiError, LIMITS, validateValue } from '../security/index.js';\nimport type { Operation, Trace } from '../core/models.js';\nconst time = z.string().datetime({ offset: true }).refine(validTime);\nconst count = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);\nconst operation = z.strictObject({\n id: z.string().min(1),\n parentId: z.string().min(1).optional(),\n type: z.enum(['tool', 'agent', 'model', 'workflow', 'generic']),\n name: z.string().min(1),\n toolName: z.string().optional(),\n startTime: time.optional(),\n endTime: time.optional(),\n status: z.enum(['ok', 'error', 'unknown']).default('unknown'),\n error: z\n .strictObject({\n code: z.string().optional(),\n message: z.string().optional(),\n })\n .optional(),\n arguments: z.unknown().optional(),\n resultMetadata: z.record(z.string(), z.unknown()).optional(),\n usage: z\n .strictObject({\n inputTokens: count.optional(),\n outputTokens: count.optional(),\n totalTokens: count.optional(),\n })\n .optional(),\n cost: z\n .strictObject({\n amount: z.number().finite().nonnegative(),\n currency: z.string().regex(/^[A-Z]{3}$/),\n })\n .optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\nexport const traceSchema = z.strictObject({\n version: z.literal(1),\n traceId: z.string().min(1),\n metadata: z.record(z.string(), z.unknown()).optional(),\n operations: z.array(operation).max(LIMITS.spans),\n});\nconst record = (v: unknown): Record<string, unknown> =>\n v !== null && typeof v === 'object' && !Array.isArray(v)\n ? (v as Record<string, unknown>)\n : {};\nfunction list(v: unknown): unknown[] {\n if (!Array.isArray(v)) throw new AwiError('Invalid OTLP array');\n return v;\n}\nfunction string(v: unknown): string | undefined {\n return typeof v === 'string' ? v : undefined;\n}\nfunction anyValue(v: unknown): unknown {\n const r = record(v);\n const fields = [\n 'stringValue',\n 'boolValue',\n 'intValue',\n 'doubleValue',\n 'bytesValue',\n 'arrayValue',\n 'kvlistValue',\n ].filter((key) => key in r);\n if (!fields.length) return undefined;\n if (fields.length !== 1) throw new AwiError('Invalid OTLP AnyValue');\n const field = fields[0]!;\n const value = r[field];\n if (field === 'stringValue' || field === 'bytesValue') {\n if (typeof value !== 'string')\n throw new AwiError('Invalid OTLP string value');\n return value;\n }\n if (field === 'boolValue') {\n if (typeof value !== 'boolean')\n throw new AwiError('Invalid OTLP boolean value');\n return value;\n }\n if (field === 'doubleValue') {\n if (typeof value !== 'number' || !Number.isFinite(value))\n throw new AwiError('Invalid OTLP double value');\n return value;\n }\n if (field === 'intValue') {\n if (\n (typeof value !== 'string' || !/^[-]?\\d{1,19}$/.test(value)) &&\n (typeof value !== 'number' || !Number.isSafeInteger(value))\n )\n throw new AwiError('Invalid OTLP integer value');\n const integer = BigInt(value as string | number);\n if (integer < -9223372036854775808n || integer > 9223372036854775807n)\n throw new AwiError('OTLP integer out of range');\n const number = Number(integer);\n return Number.isSafeInteger(number) ? number : String(value);\n }\n if (value === null || typeof value !== 'object' || Array.isArray(value))\n throw new AwiError('Invalid OTLP structured value');\n if (field === 'arrayValue') {\n const items = list(record(value).values ?? []).map(anyValue);\n return items.some((item) => item === undefined) ? undefined : items;\n }\n return attributes(record(value).values, true);\n}\nfunction attributes(v: unknown): Record<string, unknown>;\nfunction attributes(\n v: unknown,\n complete: true,\n): Record<string, unknown> | undefined;\nfunction attributes(\n v: unknown,\n complete = false,\n): Record<string, unknown> | undefined {\n const entries: [string, unknown][] = [];\n for (const attribute of list(v ?? [])) {\n const raw = record(attribute);\n if (typeof raw.key !== 'string')\n throw new AwiError('Invalid OTLP attribute key');\n const value = anyValue(raw.value);\n if (value === undefined && complete) return undefined;\n entries.push([raw.key, value]);\n }\n return Object.fromEntries(\n [...new Map(entries)].filter(([, value]) => value !== undefined),\n );\n}\nfunction attributeEncoding(\n values: unknown,\n key: string,\n): Record<string, unknown> {\n const attribute = list(values ?? []).findLast(\n (item) => record(item).key === key,\n );\n return record(record(attribute).value);\n}\nfunction textAttribute(values: unknown, key: string): string | undefined {\n return string(attributeEncoding(values, key).stringValue);\n}\nfunction hasBinaryValue(value: unknown): boolean {\n return (\n value !== null &&\n typeof value === 'object' &&\n ('bytesValue' in value || Object.values(value).some(hasBinaryValue))\n );\n}\nfunction timestamp(v: unknown): string | undefined {\n if (v === undefined || v === '0' || v === 0) return undefined;\n if (\n (typeof v !== 'string' || !/^\\d{1,20}$/.test(v)) &&\n (typeof v !== 'number' || !Number.isSafeInteger(v))\n )\n throw new AwiError('Invalid OTLP nanosecond timestamp');\n const ns = BigInt(v as string | number);\n if (ns === 0n) return undefined;\n if (ns < 0n || ns > 18446744073709551615n)\n throw new AwiError('OTLP timestamp out of range');\n const iso = new Date(Number(ns / 1000000n)).toISOString();\n return ns % 1000000n === 0n\n ? iso\n : iso.replace(\n /\\.\\d{3}Z$/,\n '.' + (ns % 1000000000n).toString().padStart(9, '0') + 'Z',\n );\n}\nfunction otel(value: unknown): Trace[] {\n const grouped = new Map<string, Trace>();\n let total = 0;\n for (const resource of list(record(value).resourceSpans)) {\n const r = record(resource);\n const resourceAttributes = attributes(record(r.resource).attributes);\n for (const scope of list(r.scopeSpans ?? []))\n for (const raw of list(record(scope).spans ?? [])) {\n if (++total > LIMITS.spans)\n throw new AwiError('Maximum span count exceeded');\n const s = record(raw);\n const traceId = string(s.traceId),\n id = string(s.spanId);\n if (\n !traceId ||\n !/^[a-fA-F0-9]{32}$/.test(traceId) ||\n /^0+$/.test(traceId) ||\n !id ||\n !/^[a-fA-F0-9]{16}$/.test(id) ||\n /^0+$/.test(id)\n )\n throw new AwiError('Invalid OTLP trace/span identity');\n const a = attributes(s.attributes);\n const kind = textAttribute(s.attributes, 'gen_ai.operation.name');\n const tool =\n textAttribute(s.attributes, 'gen_ai.tool.name') ??\n textAttribute(s.attributes, 'tool.name');\n const type: Operation['type'] =\n tool || kind === 'execute_tool'\n ? 'tool'\n : kind === 'invoke_agent'\n ? 'agent'\n : [\n 'chat',\n 'generate_content',\n 'text_completion',\n 'embeddings',\n ].includes(typeof kind === 'string' ? kind : '')\n ? 'model'\n : kind === 'workflow'\n ? 'workflow'\n : 'generic';\n const statusRecord = record(s.status);\n const status = statusRecord.code;\n const events = list(s.events ?? []).map(record);\n const exception = events.find((e) => e.name === 'exception');\n if (exception) attributes(exception.attributes);\n const exceptionType = textAttribute(\n exception?.attributes,\n 'exception.type',\n ),\n exceptionMessage = textAttribute(\n exception?.attributes,\n 'exception.message',\n );\n const usage: NonNullable<Operation['usage']> = {};\n for (const [target, key] of [\n ['inputTokens', 'gen_ai.usage.input_tokens'],\n ['outputTokens', 'gen_ai.usage.output_tokens'],\n ['totalTokens', 'gen_ai.usage.total_tokens'],\n ] as const) {\n const v = a[key];\n if (typeof v === 'number' && Number.isSafeInteger(v) && v >= 0)\n usage[target] = v;\n }\n let args: unknown = hasBinaryValue(\n attributeEncoding(s.attributes, 'gen_ai.tool.call.arguments'),\n )\n ? undefined\n : a['gen_ai.tool.call.arguments'];\n if (typeof args === 'string') {\n try {\n args = JSON.parse(args);\n } catch {\n /* Preserve recorded string. */\n }\n }\n const startTime = timestamp(s.startTimeUnixNano),\n endTime = timestamp(s.endTimeUnixNano);\n const parent = string(s.parentSpanId);\n if (s.parentSpanId !== undefined && typeof s.parentSpanId !== 'string')\n throw new AwiError('Invalid OTLP parent identity');\n if (parent && !/^[a-fA-F0-9]{16}$/.test(parent))\n throw new AwiError('Invalid OTLP parent identity');\n const op: Operation = {\n id: id.toLowerCase(),\n type,\n name: string(s.name) || 'unnamed',\n status:\n status === 2 || status === 'STATUS_CODE_ERROR'\n ? 'error'\n : status === 1 || status === 'STATUS_CODE_OK'\n ? 'ok'\n : 'unknown',\n ...(parent && !/^0+$/.test(parent)\n ? { parentId: parent.toLowerCase() }\n : {}),\n ...(tool ? { toolName: tool } : {}),\n ...(startTime ? { startTime } : {}),\n ...(endTime ? { endTime } : {}),\n ...(args !== undefined ? { arguments: args } : {}),\n ...(Object.keys(usage).length ? { usage } : {}),\n ...(exception ||\n ((status === 2 || status === 'STATUS_CODE_ERROR') &&\n typeof statusRecord.message === 'string')\n ? {\n error: {\n ...(exceptionType !== undefined\n ? { code: exceptionType }\n : {}),\n ...(exceptionMessage !== undefined\n ? { message: exceptionMessage }\n : typeof statusRecord.message === 'string'\n ? { message: statusRecord.message }\n : {}),\n },\n }\n : {}),\n metadata: {\n 'awi.operation_name_recorded':\n typeof s.name === 'string' && s.name.length > 0,\n attributes: a,\n resource: resourceAttributes,\n scope: record(scope).scope ?? {},\n events: s.events ?? [],\n },\n source: { format: 'otel', index: total - 1 },\n };\n if (args !== undefined) validateValue(args);\n const key = traceId.toLowerCase();\n if (!grouped.has(key))\n grouped.set(key, { version: 1, traceId: key, operations: [] });\n grouped.get(key)!.operations.push(op);\n }\n }\n const result = [...grouped.values()];\n validateValue(result);\n return result;\n}\nexport function ingest(\n value: unknown,\n format: 'auto' | 'awi' | 'otel' = 'auto',\n): Trace[] {\n validateValue(value);\n const root = record(value);\n const isOtel = 'resourceSpans' in root;\n const isAwi = 'operations' in root || 'traces' in root;\n if (format === 'auto') {\n if (isOtel === isAwi)\n throw new AwiError('Ambiguous or unsupported trace format');\n format = isOtel ? 'otel' : 'awi';\n }\n if (format === 'otel') return otel(value);\n if (format !== 'awi') throw new AwiError('Unsupported input format');\n const collection =\n 'traces' in root\n ? z\n .strictObject({\n version: z.literal(1),\n traces: z.array(traceSchema).max(LIMITS.traces),\n })\n .safeParse(value)\n : null;\n if (collection) {\n if (!collection.success) throw new AwiError('Invalid AWI trace collection');\n if (\n collection.data.traces.reduce((n, t) => n + t.operations.length, 0) >\n LIMITS.spans\n )\n throw new AwiError('Maximum total span count exceeded');\n return collection.data.traces.map((t) => ({\n ...t,\n operations: t.operations.map((op, index) => ({\n ...op,\n source: { format: 'awi', index },\n })),\n }));\n }\n const parsed = traceSchema.safeParse(value);\n if (!parsed.success)\n throw new AwiError(\n 'Invalid AWI trace schema: ' +\n parsed.error.issues\n .map((i) => i.path.join('.') + ': ' + i.code)\n .join('; '),\n );\n return [\n {\n ...parsed.data,\n operations: parsed.data.operations.map((op, index) => ({\n ...op,\n source: { format: 'awi', index },\n })),\n },\n ];\n}\n","import { z } from 'zod';\nimport { AwiError, LIMITS, validateValue } from '../security/index.js';\nconst positive = z.number().int().min(1);\nconst repeat = (threshold: number) =>\n z.strictObject({\n enabled: z.boolean().default(true),\n threshold: positive.min(2).max(LIMITS.repetitionWindow).default(threshold),\n });\nexport const configSchema = z.strictObject({\n version: z.literal(1).default(1),\n rules: z\n .strictObject({\n AWI001: repeat(4).default({ enabled: true, threshold: 4 }),\n AWI002: repeat(3).default({ enabled: true, threshold: 3 }),\n AWI003: z\n .strictObject({\n enabled: z.boolean().default(true),\n minRepetitions: positive.min(2).max(100).default(3),\n maxCycleLength: positive.min(2).max(16).default(6),\n })\n .default({ enabled: true, minRepetitions: 3, maxCycleLength: 6 }),\n AWI004: z\n .strictObject({\n enabled: z.boolean().default(true),\n threshold: positive.min(2).max(LIMITS.repetitionWindow).default(5),\n windowSeconds: positive.max(3600).default(30),\n })\n .default({ enabled: true, threshold: 5, windowSeconds: 30 }),\n AWI005: z\n .strictObject({\n enabled: z.boolean().default(true),\n maxOperations: positive.max(50000).default(100),\n maxDepth: positive.max(1000).default(20),\n })\n .default({ enabled: true, maxOperations: 100, maxDepth: 20 }),\n AWI006: z\n .strictObject({ enabled: z.boolean().default(true) })\n .default({ enabled: true }),\n })\n .default({\n AWI001: { enabled: true, threshold: 4 },\n AWI002: { enabled: true, threshold: 3 },\n AWI003: { enabled: true, minRepetitions: 3, maxCycleLength: 6 },\n AWI004: { enabled: true, threshold: 5, windowSeconds: 30 },\n AWI005: { enabled: true, maxOperations: 100, maxDepth: 20 },\n AWI006: { enabled: true },\n }),\n fingerprint: z\n .strictObject({\n ignorePaths: z\n .array(\n z\n .string()\n .regex(/^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$/)\n .refine(\n (p) =>\n p.split('.').length <= LIMITS.depth &&\n !p\n .split('.')\n .some((k) =>\n ['__proto__', 'constructor', 'prototype'].includes(k),\n ),\n ),\n )\n .max(100)\n .default([]),\n })\n .default({ ignorePaths: [] }),\n});\nexport type Config = z.infer<typeof configSchema>;\nexport function resolveConfig(value: unknown = {}): Config {\n validateValue(value);\n const parsed = configSchema.safeParse(value);\n if (!parsed.success)\n throw new AwiError(\n 'Invalid configuration: ' +\n parsed.error.issues\n .map((i) => i.path.join('.') + ': ' + i.code)\n .join('; '),\n );\n return parsed.data;\n}\n","import { createHash } from 'node:crypto';\nimport {\n validateValue,\n validateIgnorePaths,\n AwiError,\n} from '../security/index.js';\nimport type { Operation } from './models.js';\ninterface IgnoreNode {\n ignored: boolean;\n children: Map<string, IgnoreNode>;\n}\nconst ignoredPathCache = new WeakMap<\n readonly string[],\n { snapshot: string[]; root: IgnoreNode | undefined }\n>();\nfunction compileIgnorePaths(paths: readonly string[]): IgnoreNode | undefined {\n if (\n !Array.isArray(paths) ||\n Object.getPrototypeOf(paths) !== Array.prototype ||\n paths.length > 100\n )\n throw new AwiError('Invalid fingerprint ignore paths');\n const values: string[] = [];\n if (Reflect.ownKeys(paths).length !== paths.length + 1)\n throw new AwiError('Invalid fingerprint ignore paths');\n for (let index = 0; index < paths.length; index++) {\n const descriptor = Object.getOwnPropertyDescriptor(paths, String(index));\n if (\n !descriptor ||\n descriptor.get ||\n descriptor.set ||\n typeof descriptor.value !== 'string'\n )\n throw new AwiError('Invalid fingerprint ignore paths');\n values.push(descriptor.value);\n }\n const cached = ignoredPathCache.get(paths);\n if (\n cached &&\n cached.snapshot.length === values.length &&\n values.every((value, index) => value === cached.snapshot[index])\n )\n return cached.root;\n validateIgnorePaths(values);\n const root: IgnoreNode | undefined = values.length\n ? { ignored: false, children: new Map() }\n : undefined;\n for (const path of values) {\n let node = root!;\n for (const part of path.split('.')) {\n let child = node.children.get(part);\n if (!child) {\n child = { ignored: false, children: new Map() };\n node.children.set(part, child);\n }\n node = child;\n }\n node.ignored = true;\n }\n ignoredPathCache.set(paths, { snapshot: values, root });\n return root;\n}\nexport function canonicalize(\n value: unknown,\n ignorePaths: string[] = [],\n): string {\n validateValue(value);\n const root = compileIgnorePaths(ignorePaths);\n function encode(v: unknown, node: IgnoreNode | undefined): string {\n if (Array.isArray(v))\n return '[' + v.map((item) => encode(item, undefined)).join(',') + ']';\n if (v && typeof v === 'object')\n return (\n '{' +\n Object.keys(v)\n .sort()\n .filter((key) => !node?.children.get(key)?.ignored)\n .map(\n (key) =>\n JSON.stringify(key) +\n ':' +\n encode(\n (v as Record<string, unknown>)[key],\n node?.children.get(key),\n ),\n )\n .join(',') +\n '}'\n );\n return JSON.stringify(v);\n }\n return encode(value, root);\n}\nexport function hash(value: unknown): string {\n return createHash('sha256').update(canonicalize(value)).digest('hex');\n}\nexport function fingerprint(op: Operation, ignorePaths: string[] = []): string {\n validateValue(op, true);\n compileIgnorePaths(ignorePaths);\n const tool = op.toolName ?? op.name;\n if (\n typeof tool !== 'string' ||\n !['tool', 'agent', 'model', 'workflow', 'generic'].includes(op.type)\n )\n throw new AwiError('Invalid operation fingerprint identity');\n // Preserve the exact v1 byte contract without treating generated canonical JSON as an input string field.\n const digest = createHash('sha256').update('{\"arguments\":');\n if (op.arguments === undefined) digest.update('{\"present\":false}');\n else\n digest\n .update('{\"present\":true,\"value\":')\n .update(JSON.stringify(canonicalize(op.arguments, ignorePaths)))\n .update('}');\n return digest\n .update(',\"tool\":')\n .update(JSON.stringify(tool))\n .update(',\"type\":')\n .update(JSON.stringify(op.type))\n .update(',\"version\":\"awi-sha256-v1\"}')\n .digest('hex');\n}\n","import { timeNanoseconds } from './timestamps.js';\nimport type { Operation, Trace } from './models.js';\nimport { AwiError, LIMITS } from '../security/index.js';\nexport interface Normalized {\n trace: Trace;\n depth: number;\n warnings: string[];\n children: Map<string, Operation[]>;\n}\nexport function normalize(trace: Trace): Normalized {\n if (trace.operations.length > LIMITS.spans)\n throw new AwiError('Maximum span count exceeded');\n const ids = new Map<string, Operation>();\n const warnings: string[] = [];\n for (const op of trace.operations) {\n if (ids.has(op.id)) throw new AwiError('Duplicate operation identity');\n ids.set(op.id, op);\n if (\n op.startTime &&\n op.endTime &&\n timeNanoseconds(op.endTime) < timeNanoseconds(op.startTime)\n )\n throw new AwiError('End timestamp precedes start timestamp');\n }\n const depths = new Map<string, number>();\n let depth = 0;\n for (const op of trace.operations) {\n const path: Operation[] = [];\n const seen = new Set<string>();\n let current: Operation | undefined = op;\n while (current && !depths.has(current.id)) {\n if (seen.has(current.id)) throw new AwiError('Cyclic parent references');\n seen.add(current.id);\n path.push(current);\n const parent: Operation | undefined = current.parentId\n ? ids.get(current.parentId)\n : undefined;\n if (current.parentId && !parent)\n warnings.push(\n 'Missing parent reference; observed depth is a lower bound.',\n );\n current = parent;\n }\n let d = current ? depths.get(current.id)! : 0;\n for (const item of path.reverse()) {\n depths.set(item.id, ++d);\n depth = Math.max(depth, d);\n }\n }\n const children = new Map<string, Operation[]>();\n for (const op of trace.operations) {\n const key = op.parentId ?? '';\n const group = children.get(key) ?? [];\n group.push(op);\n children.set(key, group);\n }\n // Preserve recorded order; only fully timed, non-overlapping sibling operations establish sequence evidence.\n return { trace, depth, warnings: [...new Set(warnings)], children };\n}\nexport function sequentialRuns(ops: Operation[]): Operation[][] {\n if (\n ops.some(\n (o) =>\n !o.startTime ||\n !o.endTime ||\n timeNanoseconds(o.endTime) <= timeNanoseconds(o.startTime),\n )\n )\n return [];\n const timed = ops\n .map((op) => ({\n op,\n start: timeNanoseconds(op.startTime!),\n end: timeNanoseconds(op.endTime!),\n }))\n .sort((a, b) =>\n a.start < b.start\n ? -1\n : a.start > b.start\n ? 1\n : a.op.id < b.op.id\n ? -1\n : a.op.id > b.op.id\n ? 1\n : 0,\n );\n const runs: Operation[][] = [];\n let run: typeof timed = [];\n let overlapEnd: bigint | undefined;\n for (const item of timed) {\n if (overlapEnd !== undefined) {\n if (item.start < overlapEnd) {\n if (item.end > overlapEnd) overlapEnd = item.end;\n continue;\n }\n overlapEnd = undefined;\n }\n const previous = run.at(-1);\n if (previous && item.start < previous.end) {\n run.pop();\n if (run.length) runs.push(run.map((value) => value.op));\n run = [];\n overlapEnd = previous.end > item.end ? previous.end : item.end;\n continue;\n }\n run.push(item);\n }\n if (run.length) runs.push(run.map((value) => value.op));\n return runs;\n}\n","import { safeText } from '../security/index.js';\nimport type { Report } from './models.js';\nexport function humanReport(\n report: Report,\n options: { color?: boolean } = {},\n): string {\n const paint = (text: string, severity: string) =>\n options.color\n ? `\\u001b[${severity === 'high' ? 31 : 33}m${text}\\u001b[0m`\n : text;\n const lines = [\n 'Agent Workflow Inspector — Find loops, retries, and wasted work in AI agent traces.',\n ];\n for (const t of report.summaries)\n lines.push(\n `Trace ${safeText(t.traceId)}: ${t.operations} operations, ${t.toolCalls} tool calls, depth ${t.maxDepth}; duration ${t.durationMs === undefined ? 'unknown' : t.durationMs + ' ms'}; recorded leaf usage ${t.usage ? JSON.stringify(t.usage) : 'unknown'}`,\n );\n for (const f of report.findings)\n lines.push(\n `\\n${paint('[' + f.severity.toUpperCase() + ']', f.severity)} ${safeText(f.ruleId)} ${safeText(f.title)} (${safeText(f.confidence)} evidence)`,\n ` Trace: ${safeText(f.traceId)}; operations: ${f.operationIds.map(safeText).join(', ') || 'trace-wide'}`,\n ` ${safeText(f.explanation)}`,\n ` Evidence: ${safeText(JSON.stringify(f.evidence))}`,\n );\n for (const warning of report.warnings)\n lines.push('Warning: ' + safeText(warning));\n if (report.content)\n lines.push(\n 'Content (opt-in, key-based redaction): ' +\n safeText(JSON.stringify(report.content)),\n );\n lines.push(\n `\\n${report.findings.length} findings; analysis ${report.complete ? 'complete' : 'INCOMPLETE'}.`,\n );\n return lines.join('\\n') + '\\n';\n}\n\nexport function jsonReport(report: Report): string {\n return (\n JSON.stringify(report, null, 2).replace(\n /[\\u007f-\\u009f\\p{Cf}\\p{Zl}\\p{Zp}]/gu,\n (value) =>\n value\n .split('')\n .map((c) => '\\\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'))\n .join(''),\n ) + '\\n'\n );\n}\n","import { z } from 'zod';\nconst usage = z.strictObject({\n inputTokens: z\n .number()\n .int()\n .nonnegative()\n .max(Number.MAX_SAFE_INTEGER)\n .optional(),\n outputTokens: z\n .number()\n .int()\n .nonnegative()\n .max(Number.MAX_SAFE_INTEGER)\n .optional(),\n totalTokens: z\n .number()\n .int()\n .nonnegative()\n .max(Number.MAX_SAFE_INTEGER)\n .optional(),\n});\nexport const reportSchema = z.strictObject({\n version: z.literal(1),\n fingerprintVersion: z.literal('awi-sha256-v1'),\n complete: z.boolean(),\n summaries: z.array(\n z.strictObject({\n traceId: z.string(),\n operations: z.number().int().nonnegative(),\n toolCalls: z.number().int().nonnegative(),\n maxDepth: z.number().int().nonnegative(),\n durationMs: z.number().nonnegative().optional(),\n usage: usage.optional(),\n }),\n ),\n findings: z.array(\n z.strictObject({\n ruleId: z.enum([\n 'AWI001',\n 'AWI002',\n 'AWI003',\n 'AWI004',\n 'AWI005',\n 'AWI006',\n ]),\n severity: z.enum(['low', 'medium', 'high']),\n confidence: z.enum(['high', 'moderate']),\n title: z.string(),\n description: z.string(),\n explanation: z.string(),\n remediation: z.string(),\n traceId: z.string(),\n operationIds: z.array(z.string()),\n evidence: z.record(z.string(), z.unknown()),\n }),\n ),\n warnings: z.array(z.string()),\n content: z\n .array(\n z.strictObject({\n traceId: z.string(),\n operations: z.array(z.unknown()),\n }),\n )\n .optional(),\n});\n"],"mappings":"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","names":["z","usage","key","z","z","z","usage","total","z"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
Adapters validate canonical JSON and OTLP. Core normalization validates identity/graph structure and derives observed depth. Fingerprinting implements call identity. Analysis runs bounded rules and associates recorded leaf resources. Reporting renders safe terminal text. Config owns strict non-executable settings; security owns parsing, structural limits, sanitization and opt-in redaction. CLI alone accesses files and process streams. src/index.ts is the ESM library entry.
|
|
4
|
+
|
|
5
|
+
The supplied brief was truncated in its redaction section. The present requirements were implemented; no unseen requirements are claimed.
|
|
6
|
+
|
|
7
|
+
## Dependencies and support
|
|
8
|
+
|
|
9
|
+
Commander handles help/option validation and Zod validates configuration and trace/report schemas. Native Node APIs provide crypto, files and JSON. There are no HTTP clients or framework SDKs. TypeScript 5.9 stays within typescript-eslint's peer range. ESLint 10, Vitest 5, Prettier 3 and tsup 9 are pinned or constrained and locked. Their engines determine the supported Node floor. pnpm-workspace.yaml allows only esbuild's required install script.
|
|
10
|
+
|
|
11
|
+
Supported Node lines: 22.18+ on 22.x, 24.11+ on 24.x, and 26.x. CI is configured for Windows/macOS/Linux and Node 22/24/26. Future majors require CI evidence before release compatibility claims. Builds target Node 22. CommonJS/browser builds are outside V1.
|
|
12
|
+
|
|
13
|
+
## Algorithms
|
|
14
|
+
|
|
15
|
+
SHA-256 fingerprints sort JSON keys. Ignored fields use bounded compiled path maps instead of serializing ancestor paths per key. Rules group by trace and parent. Only fully timed, positive-duration, non-overlapping siblings form sequences. Untimed/zero-duration siblings disable sequence scanning for the group. Every member of an overlap cluster is excluded; known model/generic spans are projected out of tool sequences; calls without arguments or recorded identity interrupt them. Neither parent relationships nor timestamps establish causality.
|
|
16
|
+
|
|
17
|
+
Repetition uses disjoint 100-call windows. Cycles are at most 16 calls long (default 6), with O(n × maximum cycle length squared) bounded scanning. Depth uses iterative memoized traversal and cycle detection. JSON recursion is bounded at depth 64. Findings, resource IDs and warnings sort deterministically; CLI sorts files. Nanosecond arithmetic preserves concurrency evidence. Reports omit clocks, absolute paths, raw errors and hashes.
|
|
18
|
+
|
|
19
|
+
## Release checks
|
|
20
|
+
|
|
21
|
+
Run pnpm check, pnpm format:check, pnpm schemas, pnpm audit, pnpm smoke:package and pnpm pack --out agent-workflow-inspector-1.0.0.tgz. Regenerate schemas when contracts change. The smoke check installs the archive in a clean OS temporary consumer outside the repository, checks tarball permissions/contents, all shipped examples, CLI options, ESM exports and strict consumer TypeScript declarations. No release credentials are needed locally. Remote pushes, publication, tags and releases require maintainer approval; CI publishes nothing.
|
package/docs/audit.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Pre-release audit — AWI V1
|
|
2
|
+
|
|
3
|
+
Historical audit record for the local audit commit. Its hosted-CI gate was subsequently satisfied for d2514af by [run 39208844432](https://github.com/jigar48/awi/actions/runs/39208844432). Later release-preparation commits require their own CI. See the current [verification record](verification.md) and [release procedure](releasing.md); the original findings and audit-time recommendation below are preserved.
|
|
4
|
+
|
|
5
|
+
Date: October 19, 2026. Baseline: commit `9bc61c1`, branch `codex/awi-v1`. This audit inspected the implementation, original supplied brief, all original 37 tests, documentation, packaged artifacts and CI configuration. The brief ends mid-sentence in report redaction; no absent requirements are claimed. Changes fix V1 defects and release verification gaps; no new product features or remote actions were introduced.
|
|
6
|
+
|
|
7
|
+
## Recommendation
|
|
8
|
+
|
|
9
|
+
**NO-GO for publishing until the complete hosted CI matrix passes, especially macOS.** The exercised final implementation has no known remaining critical/high defect from this audit, but local Windows and WSL evidence does not prove every advertised platform/runtime combination. Complete the configured nine Windows/macOS/Linux × Node 22/24/26 jobs before publishing. Publication also requires the maintainer's explicit authorization; none was performed here.
|
|
10
|
+
|
|
11
|
+
## Findings and fixes
|
|
12
|
+
|
|
13
|
+
The following are finding groups, not a count of every affected assertion. All listed implementation defects are fixed locally. Residual design limitations follow separately.
|
|
14
|
+
|
|
15
|
+
| ID | Severity | Reproduction / observed defect | Fix and regression evidence |
|
|
16
|
+
| --- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| H01 | High | 2,000 spans sharing a 20,000-element OTLP resource attribute exhausted a 96 MiB child heap (baseline exit 134, heap-out-of-memory). Wide JSON and repeated native string references also allowed disproportionate work; long IDs amplified default reports. Long ancestor keys with wide descendants required potentially multi-gigabyte ancestor-path serialization for a sub-megabyte input (baseline subprocess timed out). | Decode resource attributes once; apply raw/normalized node and escaped-content budgets, use bounded-depth descriptor-at-a-time validation, cap default report content before serialization. Compile/reuse bounded ignore-path maps, cap path depth, and safely invalidate changed caller options. The 96 MiB subprocess now exits normally with controlled limits for shared resources, a million-element JSON array and repeated strings; report amplification and long-key CPU amplification have separate regressions, including cache mutation/accessor safety. |
|
|
18
|
+
| H02 | High | Direct `analyze()` accepted `source.index={password:"AUDIT_SECRET"}` and copied it into an ordinary report. It validated a stripped copy but analyzed original values, skipped defaults and evaluated getters before validation. Malformed native input could throw an uncontrolled TypeError. | Security preflight before schema access; strict source schema; analyze parsed/defaulted data; controlled AwiError failures. Known optional native fields normalize explicit undefined to missing without accepting undefined inside arbitrary JSON argument/metadata values. Regressions cover source leakage, getters, malformed inputs and unknown-status defaults. This exploit affected the library boundary; canonical-file CLI ingestion already rejected source fields. |
|
|
19
|
+
| M01 | Medium | Fractional timestamps lost submillisecond precision and made overlapping calls appear sequential. Reversed nanosecond endpoints could pass. The first member of an overlap cluster could remain in a qualifying run. Known interleaved model/generic spans also hid qualifying repeated tools by resetting the tool window. | Preserve OTLP nanoseconds; use exact integer comparisons for normalization, overlap and retry windows; exclude every overlap-cluster member and warn; project known tool calls across interleaved non-tool spans while retaining timing/parent constraints. Tests cover true/false overlap, valid sequential short spans, reversed endpoints, timezone offsets and window boundaries. |
|
|
20
|
+
| M02 | Medium | `error,error,error,error,ok,error` inside the retry window yielded no AWI004; the later attempt erased the qualifying recovery chain. Blank error fields could satisfy AWI002 failure equivalence. | Stop retry chains at first non-error; require nonblank failure evidence. Positive/negative regressions preserve explicit-failure and inferred-retry distinctions. |
|
|
21
|
+
| M03 | Medium | OTLP intValue null/empty values became zero usage, absent AnyValue arguments became null, arrays could classify as model operations, and negative numeric timestamps were accepted. Recorded error status messages were ignored. | Validate known AnyValue types and integer ranges, omit unknown/incomplete argument evidence, prevent unnamed display placeholders from establishing tool identity, classify only strings, bound unsigned nanosecond timestamps, preserve explicit error status messages without printing them. Tests exercise malformed/unknown/nested values and status evidence, plus binary/text distinctions and duplicate unknown attributes. |
|
|
22
|
+
| M04 | Medium | AWI002 default threshold changed from 3 to 4 when `{enabled:true}` or `{}` was supplied. Thresholds above the fixed 100-call window silently made repetition rules impossible to trigger. | Rule-specific defaults; reject thresholds above 100. Configuration, generated schema and documentation agree. |
|
|
23
|
+
| M05 | Medium | Reordering fragments of one trace changed AWI006 operation/reference ordering and could change which findings survived the finding cap. | Stable operation identity ordering before grouping, accounting and selection. Tests compare reversed fragments, capped results, resource evidence and caller-data immutability. |
|
|
24
|
+
| M06 | Medium | An input/configuration/report path below a junction or directory symlink bypassed the advertised no-symlink policy. Aggregate input accounting used pre-read sizes. | Validate path components, use O_NOFOLLOW where available, compare opened-file identity, count actual read bytes and bound directory enumeration/sorting. Use a fixed-length temporary name so valid long report filenames do not exceed filesystem component limits. Tests cover input/config/output ancestors, final output aliases, valid long filenames, depth, file count and aggregate byte limits. Parent replacement races still require trusted directories. |
|
|
25
|
+
| M07 | Medium | The public humanReport renderer emitted raw terminal escapes from a schema-valid report warning. Internally generated CLI warnings were constant, so this was a public-renderer boundary issue. | Sanitize report strings including warnings/titles/explanations; keep trusted color escapes separate. Existing CLI tests also cover ANSI/bidi identifiers and JSON escaping. |
|
|
26
|
+
| M08 | Medium | Malformed UTF-8 inside identifiers was silently replaced with U+FFFD, changing recorded evidence and potentially collapsing distinct values. UTF-8 BOM exports failed unexpectedly. | Fatal UTF-8 decoding with BOM support; regression tests reject invalid bytes and accept a BOM export. |
|
|
27
|
+
| M09 | Medium | A valid argument object containing two 40,000-character fields failed the input single-string limit after canonicalization, despite neither input string exceeding 65,536 units. | Hash the exact ordered V1 envelope in chunks without revalidating generated canonical JSON as an input field. Existing fixed hash vector and an independent reference envelope test confirm unchanged V1 bytes. |
|
|
28
|
+
| L01 | Low | Direct fingerprint/canonicalize callers bypassed configuration ignore-path syntax restrictions; direct fingerprint getters could execute. | Enforce safe paths and preflight JSON descriptors at public fingerprint boundaries; malformed-path/getter regressions. |
|
|
29
|
+
| L02 | Low | A closed stdout pipe caused an uncaught EPIPE stack trace including installation paths. | Controlled output-stream failure with exit 1; actual closed-pipe subprocess regression. |
|
|
30
|
+
| L03 | Low | Maintainer documents linked from the README were absent from the package. The old consumer test lived below the repository and could inherit development dependencies; it did not verify all examples, tar permissions or declarations. Broad test discovery also collected scratch checkouts. | Include maintainer documents; use a clean OS temporary consumer outside the repository; verify allowlist/bin mode, all examples/options, ESM exports and strict consumer TypeScript. Scope Vitest discovery to tests/. CLI setup builds only runtime JS; full builds/package checks still verify declarations. |
|
|
31
|
+
| L04 | Low | CI used mutable major tags and retained checkout credentials by default. Original setup actions used a Node 20 action runtime. | Pin verified upstream Node 24 action commits, disable persisted checkout credentials and automatic package-manager caching, retain read-only permissions and add a job timeout. Package engine ranges now match the supported 22/24/26 major lines rather than implying unverified future-major support. No hosted workflow was executed by this audit. |
|
|
32
|
+
|
|
33
|
+
No critical finding was identified. None of the tests or reviews establishes absence of every possible defect.
|
|
34
|
+
|
|
35
|
+
## Rule completeness review
|
|
36
|
+
|
|
37
|
+
| Rule | Audit conclusion after fixes |
|
|
38
|
+
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
39
|
+
| AWI001 | Fingerprints preserve object key order normalization, array order, types, Unicode code points, null vs absent and explicit ignored paths. Threshold 4, disjoint 100-call windows, same trace/parent, reliable sequential evidence. Intentional polling remains a documented false positive. |
|
|
40
|
+
| AWI002 | Default 3; requires explicit error status and matching nonblank recorded error information. Matching hashes are internal only; more specific findings suppress AWI001 for their exact covered operations. Unknown status and differing outcomes do not qualify. |
|
|
41
|
+
| AWI003 | Exact contiguous bounded cycles; default 3 repeats, maximum default length 6/configuration cap 16; shortest matching period wins and consumed sequences are not rescanned. Single-call repetition is excluded. Concurrent/missing evidence cannot establish a cycle. |
|
|
42
|
+
| AWI004 | Default 5 attempts/30 seconds; preceding attempts must explicitly fail. A terminal success/unknown stops the chain; later calls cannot erase an earlier match. Exact time boundaries; absent timing excludes sequences. Retry intent remains inferred and may coexist with AWI002 for distinct timing evidence. |
|
|
43
|
+
| AWI005 | Separately counts all operations and observed parent depth (roots=1). Thresholds are policy, not proof of bugs. Missing parents warn and give lower bounds; duplicate IDs, parent cycles and reversed endpoints fail explicitly. |
|
|
44
|
+
| AWI006 | Associates only directly implicated leaf operations with recorded usage/cost, unions related IDs once, keeps input/output/total independent and currencies separate. Missing values remain absent; parent totals and unrelated model usage are not attributed. Empty usage does not fabricate a finding. |
|
|
45
|
+
|
|
46
|
+
Cycle scan lengths and repeat thresholds are capped; failed candidate scans have a bounded repeat count before the configured minimum would qualify and consume the sequence. No combinatorial subsequence search is present. The existing 50,000-operation regression guard remains; tests did not relax detector performance assertions. Findings cap at 1,000 with complete:false and exit 1. Content budgets fail explicitly rather than returning a falsely clean partial analysis.
|
|
47
|
+
|
|
48
|
+
## Ingestion, interfaces and reporting
|
|
49
|
+
|
|
50
|
+
Canonical v1 trace/collection input is strict, supports nested operations/handoffs via parent structure and metadata, preserves generic spans and optional resource evidence, and groups by trace identity across files. The OTLP adapter supports only the documented protobuf JSON subset. Format ambiguity and malformed required fields fail; missing/unknown evidence is not invented. Unsupported AnyValue encodings are omitted from interpretation; this is not universal exporter compatibility.
|
|
51
|
+
|
|
52
|
+
All required commands/options, ESM public imports, configuration defaults/rejections and exit codes were exercised. Exit 1 takes precedence over severity; exit 2 means completed analysis meets fail-on; fail-on none does not suppress operational failures. JSON stdout contains only JSON; error paths use stderr without raw trace content. Report version and fingerprint version remain 1. Evidence references identify trace/operation IDs and validated adapter source indices. Schema files were regenerated; global resource/graph checks require ingest/analyze, not structural schemas alone.
|
|
53
|
+
|
|
54
|
+
## Test quality and security evidence
|
|
55
|
+
|
|
56
|
+
Baseline: 37 passing tests. Final suite: **69 tests**, adding 32 targeted cases plus stronger assertions in existing CLI tests. The additions focus on failures reproduced during the audit, resource budgets, exact boundary behavior, deterministic capped selection and public/native input validation. No thresholds/assertions were weakened to accept a product defect. Test setup uses a separate bounded compilation budget and runtime-only bundling because WSL declaration compilation from an NTFS mount exceeded the previous 10-second setup hook; declaration validation remains in full builds and consumer typechecking. CLI end-to-end cases have a separate 15-second I/O envelope after measuring WSL cold-start timeouts in cases launching multiple Node processes. Individual CLI subprocesses are also bounded at 15 seconds. Functional assertions and the 50,000-operation detector timing assertion remain unchanged.
|
|
57
|
+
|
|
58
|
+
The hostile-content subprocess blocks network, DNS, fetch and subprocess APIs, then analyzes URLs, path traversal strings, embedded instructions, tool/name/error/metadata secrets. It returns the expected finding exit code without any attempted external effect or those secrets in default output. Identifier strings deliberately remain visible by contract. JSON prototype keys, unsafe native shapes, nesting/string limits, oversized files, symlink aliases, overwrite/hardlink protection, private POSIX output mode and broken pipes are covered. Heap-budget tests are specific adversarial regression vectors, not a promise that every valid maximum-size input fits a 96 MiB process.
|
|
59
|
+
|
|
60
|
+
## Package verification
|
|
61
|
+
|
|
62
|
+
The actual package is built and packed, checked as a tar archive, installed with lifecycle scripts disabled into a new OS temporary consumer outside the repository, and removed after testing. Verification checks allowlisted contents, no traversal/.git/node_modules/scratch payloads, executable mode and shebang for the awi bin, correct ESM/type export mappings, presence of README/license/maintainer docs, strict TypeScript imports and the public API. No undeclared development dependency is inherited from the project.
|
|
63
|
+
|
|
64
|
+
| Installed example | Expected result |
|
|
65
|
+
| ------------------------ | ------------------------------------------------------- |
|
|
66
|
+
| healthy/basic.json | exit 0, no findings |
|
|
67
|
+
| suspicious/retries.json | exit 2, AWI002/AWI004 and recorded resource association |
|
|
68
|
+
| suspicious/cycle.json | exit 0 at default high threshold, AWI003 present |
|
|
69
|
+
| opentelemetry/tools.json | exit 2, OTLP failure/retry findings |
|
|
70
|
+
|
|
71
|
+
Installed checks also exercise help/version, explicit/auto formats, config, directory/multi-example inspection, JSON/human output, fail-on none, output creation, no-color and opt-in content. A local tarball is prepared; no npm publication or global installation was performed.
|
|
72
|
+
|
|
73
|
+
## Verification status
|
|
74
|
+
|
|
75
|
+
See [verification record](verification.md) for final platform/runtime results. Windows Node 24.19.0 passed the final 69 tests, strict typecheck, lint, full build, formatting, frozen install, full dependency audit, schemas and isolated package smoke. Linux WSL Node 22.18.0, 24.19.0 and 26.0.0 each passed all 69 tests, strict typecheck, lint, full build and isolated package smoke. These runs use native Linux Node binaries with official SHA-256 manifest verification and an isolated checkout/dependency tree. macOS and the hosted GitHub matrix are unavailable/unexecuted in this session.
|
|
76
|
+
|
|
77
|
+
## Remaining limitations and release gates
|
|
78
|
+
|
|
79
|
+
- **Medium, known privacy limitation:** trace/operation identifiers may themselves contain secrets and are included by the evidence contract. Default reports are not a general anonymizer. Export opaque identifiers and review reports before sharing.
|
|
80
|
+
- **Medium, explicit opt-in risk:** show-content can expose arbitrary strings, tool names, IDs and error codes. Key-based redaction catches selected structured keys, not every credential or private value. No guarantee of secret-free opt-in output is made.
|
|
81
|
+
- **Low, filesystem assumptions:** parent-component replacement races require trusted directories; existing symlink aliases must be resolved first. Hard-link report publication must be supported. POSIX mode 0600 is checked; Windows uses inherited ACLs. Cleanup is best-effort under OS failures/crashes.
|
|
82
|
+
- **Detection limitations:** disjoint windows miss boundary-spanning patterns; incomplete/untimed/concurrent groups can hide genuine problems; exact fingerprints are not semantic equivalence or proof of real-world effects. Leaf-only resource association undercounts some exports. No framework certification or universal OTLP support is claimed.
|
|
83
|
+
- **Verification limits:** macOS and hosted runner jobs remain release gates; functional tests are not an exhaustive fuzzing campaign, formal proof, independent penetration test, or Node/OS vulnerability assessment. Dependency audit results are time-dependent.
|
|
84
|
+
|
|
85
|
+
## Git and external actions
|
|
86
|
+
|
|
87
|
+
Changes remain on `codex/awi-v1` and are saved in the local audit commit containing this report; the exact commit is reported by git log after commit creation. The starting tree was clean at 9bc61c1. No remote push, publication, release, tag, deployment, trace upload or remote repository modification occurred. Scratch runtimes/checkouts live under ignored .awi-audit/; consumer directories are temporary and cleaned up on ordinary test completion.
|
|
88
|
+
|
|
89
|
+
## Upstream action evidence
|
|
90
|
+
|
|
91
|
+
Pinned action runtimes and inputs were checked against the upstream repositories: [checkout action metadata](https://github.com/actions/checkout/blob/d23441a48e516b6c34aea4fa41551a30e30af803/action.yml), [setup-node action metadata](https://github.com/actions/setup-node/blob/249970729cb0ef3589644e2896645e5dc5ba9c38/action.yml), [pnpm setup action metadata](https://github.com/pnpm/action-setup/blob/a8198c4bff370c8506180b035930dea56dbd5288/action.yml). This inspection is not a substitute for running the workflow.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# CI integration
|
|
2
|
+
|
|
3
|
+
AWI does not upload traces. Supply local synthetic/recorded JSON from your own job and treat reports as potentially sensitive.
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
|
7
|
+
with:
|
|
8
|
+
node-version: '24'
|
|
9
|
+
- uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5
|
|
10
|
+
- run: pnpm install --frozen-lockfile
|
|
11
|
+
- run: pnpm build
|
|
12
|
+
- run: node dist/cli/index.js inspect ./traces --format json --fail-on high --output awi-report.json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Repository CI is configured to check Node 22/24/26 on Windows/Linux/macOS with typecheck, lint, tests, build, format, schema drift, production audit and packaging. Local Windows and Linux WSL checks do not prove the hosted matrix outcomes; macOS remains unverified. Workflow actions are pinned to verified upstream commits using Node 24; checkout does not persist credentials. Permissions are read-only and nothing is published.
|
|
16
|
+
|
|
17
|
+
Exit 1 takes precedence over finding thresholds: structural errors prevent a report; truncation emits incomplete JSON and exits 1. Exit 2 means completed analysis meets severity. fail-on none never masks errors. Warnings are structured in JSON, not mixed into stdout. Missing evidence is documented even when all applicable rules complete.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Use --config path.json. No automatic home lookup, executable settings, remote loading or environment substitution. Partial objects receive deterministic defaults. Unknown keys, versions, unsafe paths and invalid thresholds fail. See [example](../awi.config.example.json) and [schema](schemas/config.schema.json).
|
|
4
|
+
|
|
5
|
+
Version 1; every rule supports enabled. AWI001/002 threshold 2–1,000 (defaults 4/3). AWI003 minRepetitions 2–100 (default 3), maxCycleLength 2–16 (default 6). AWI004 threshold 2–1,000 (default 5), windowSeconds 1–3,600 (default 30). AWI005 maxOperations 1–50,000 (default 100), maxDepth 1–1,000 (default 20). AWI006 reports recorded association, so needs no spending threshold.
|
|
6
|
+
|
|
7
|
+
fingerprint.ignorePaths accepts at most 100 dot-separated ASCII identifiers, e.g. metadata.trace_id, applied to arguments only, with at most 64 path segments. Arrays preserve order and are not traversed by ignore paths. The public canonicalize/fingerprint helpers enforce the same path syntax. No wildcards, expressions, indices, `constructor`, `prototype` or `__proto__`. Missing ignored paths have no effect. Literal dotted JSON keys do not match nested paths. IDs and timestamps are never automatically ignored.
|
|
8
|
+
|
|
9
|
+
## Fingerprint v1
|
|
10
|
+
|
|
11
|
+
awi-sha256-v1 hashes operation type, toolName (fallback name), argument presence and canonical argument JSON. Keys sort lexically by UTF-16 code unit. Arrays keep order, JSON types remain distinct, missing differs from null. Unicode has no normalization. Numbers use finite JavaScript double semantics: -0 becomes 0; large numeric integers may lose precision, so encode precision-sensitive integers as strings. Duplicate keys follow JSON.parse last-value-wins before hashing. Unsupported values (BigInt, undefined as a value, functions, class instances, sparse arrays, symbol keys, accessors), nonfinite numbers, unsafe keys and oversized structures fail. There is no executable canonicalizer.
|
|
12
|
+
|
|
13
|
+
Generated canonical JSON is not subject to the per-input-string limit; argument objects containing multiple individually valid strings remain supported within the content budget. The v1 byte contract is unchanged. Ignored paths are compiled into bounded maps, safely reused while unchanged, and never matched by repeatedly serializing full ancestor paths.
|
|
14
|
+
|
|
15
|
+
Identical fingerprints mean identical recorded values under these rules, not identical effects. Hashes are sensitive correlators, not anonymization, and are absent from ordinary reports.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Detection rules
|
|
2
|
+
|
|
3
|
+
Diagnostics separate observed evidence from heuristic explanation. High evidence means recorded equivalence/failure or policy counts are directly established. Moderate means pattern/association inference. Categories are not statistical probabilities; no rule establishes intent, actual defects or wasted money.
|
|
4
|
+
|
|
5
|
+
Identity rules require recorded tool/operation identity (not an adapter display placeholder), recorded arguments (null is present), positive-duration timed siblings under one parent, and non-overlap. Untimed/zero-duration groups cannot establish sequences and are excluded. Known model/generic spans are projected out of tool sequences, while their timing still constrains overlap and eligibility. Missing tool arguments/identity interrupt tool segments. All members of overlapping clusters are excluded, splitting runs; unrelated traces/parents never combine. Incomplete exports can therefore hide genuine problems.
|
|
6
|
+
|
|
7
|
+
| ID | Default criterion | Limitations |
|
|
8
|
+
| ------ | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
|
|
9
|
+
| AWI001 | Same fingerprint at least 4 times in disjoint 100-call window | Intentional polling matches; window boundaries can miss repetition |
|
|
10
|
+
| AWI002 | 3 same-fingerprint explicit errors with matching error object; nonblank code or message required | Different failure text splits equivalence; unknown is not failure |
|
|
11
|
+
| AWI003 | Contiguous cycle length 2–6 repeating 3 times, at least two distinct fingerprints | Shortest matching period wins; consumed cycles not rescanned |
|
|
12
|
+
| AWI004 | 5 equivalent attempts in 30 seconds from first attempt, every prior attempt explicitly failed | Retry intent inferred; successes, unknown status and slow attempts do not qualify |
|
|
13
|
+
| AWI005 | More than 100 total operations OR observed depth over 20 | One trace-wide finding, not a tool-count limit; complexity is not a defect |
|
|
14
|
+
| AWI006 | Recorded usage or cost on implicated leaf operations from AWI001–004 | Direct association only; no inferred price or unrelated model attribution |
|
|
15
|
+
|
|
16
|
+
AWI001/002/004 use disjoint 100-call windows after segmentation; AWI004 accepts disjoint time groups within each fingerprint group, ending a retry chain at its first non-error outcome. Later attempts cannot erase an earlier qualifying storm. Boundary-spanning patterns may be missed. AWI003 scans the full eligible segment, with configurable length capped at 16. Single-fingerprint repetition is AWI001/002 rather than a cycle.
|
|
17
|
+
|
|
18
|
+
## Interactions
|
|
19
|
+
|
|
20
|
+
AWI002 suppresses AWI001 for its exact covered failure operations. Remaining repeated operations can qualify separately. AWI004 can coexist with AWI002: rapid failed attempts and matching failure outcomes provide distinct evidence. AWI003 adds ordering evidence. One AWI006 per trace unions related IDs and counts each leaf once. AWI005 uses empty operationIds for trace-wide evidence.
|
|
21
|
+
|
|
22
|
+
## Resource accounting
|
|
23
|
+
|
|
24
|
+
Summaries sum recorded leaf usage, excluding parent aggregates. Input/output/total fields are independently summed and never added to each other. Cost sums remain separate per currency. AWI006 uses only implicated leaves, never nearby/enclosing model spans. Missing resources remain absent. Explicit zero is valid recorded evidence. Parent-only usage is omitted conservatively, so absence of AWI006 does not imply free execution. Wall duration is not CPU time or wasted time.
|
|
25
|
+
|
|
26
|
+
## Limits
|
|
27
|
+
|
|
28
|
+
16 MiB/file; 64 MiB aggregate CLI inputs; 256 files; 2,048 directory entries; directory depth 16; 50,000 total operations; 50,000 traces; JSON depth 64; 65,536 UTF-16 code units/string or key; 1,000,000 JSON values and a 64 MiB escaped-content budget per raw or normalized structure (shared values count per occurrence); 1,000 findings. Structural limits fail with exit 1. Default report data is subject to the same node/content budget before serialization; excessive evidence amplification fails with an explicit report-limit error rather than attempting unbounded output. Finding overflow returns complete:false, warning, exit 1 even with fail-on none. At most 100 fingerprint paths. Config schemas bound thresholds/windows/cycle lengths. A 50,000-span test uses a generous 15-second regression ceiling, not a cross-hardware benchmark claim.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
Install the Node/pnpm versions listed in README, then run `pnpm install --frozen-lockfile` and `pnpm check`. Only dependency installation needs network access.
|
|
4
|
+
|
|
5
|
+
Run `node dist/cli/index.js --help` and `node dist/cli/index.js inspect --help`. Try examples/healthy/basic.json for a clean result, examples/suspicious/retries.json for a finding, and examples/opentelemetry/tools.json for OTLP ingestion. AWI analyzes exports, not live executions.
|
|
6
|
+
|
|
7
|
+
A minimal canonical trace is:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"version": 1,
|
|
12
|
+
"traceId": "example",
|
|
13
|
+
"operations": [
|
|
14
|
+
{
|
|
15
|
+
"id": "one",
|
|
16
|
+
"type": "tool",
|
|
17
|
+
"name": "lookup",
|
|
18
|
+
"arguments": { "q": "demo" },
|
|
19
|
+
"status": "ok",
|
|
20
|
+
"startTime": "2026-01-01T00:00:00Z",
|
|
21
|
+
"endTime": "2026-01-01T00:00:01Z"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Supply several files or a directory. Files sharing traceId merge; duplicate operation IDs then fail validation. Distinct trace IDs never share sequence detection. Output is withheld on ordinary input failures.
|
|
28
|
+
|
|
29
|
+
Use --format json for tooling. --output must name a new file in an existing directory. Keep reports outside input directories, as they would be treated as traces on the next scan.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Limitations
|
|
2
|
+
|
|
3
|
+
Rules evaluate recorded facts and bounded heuristics, not intention or correctness. Polling/retries/cycles may be intentional. Missing arguments/timing, zero durations, overlaps, missing parent structure and instrumentation differences can hide patterns. Missing parents yield lower-bound depth. Neither graph structure nor temporal order establishes causality. Timing rules assume comparable recorded clocks; AWI does not correct distributed clock skew.
|
|
4
|
+
|
|
5
|
+
Disjoint 100-call windows can miss boundary-spanning repetition. Cycles are exact, contiguous and bounded. Different arguments do not semantically match. No LLM, embeddings, live tracing or distributed causal reconstruction. Failure comparison is exact recorded-object matching.
|
|
6
|
+
|
|
7
|
+
Only recorded leaf resources are counted. Parent aggregates and unrelated model usage are not attributed. Separate leaves can still duplicate values in poorly instrumented exports; absent accounting semantics cannot be recovered. Costs use finite JSON/JavaScript number arithmetic, are explicit, and never mix currencies; they are not a billing ledger. Wall duration is never labeled waste.
|
|
8
|
+
|
|
9
|
+
OTLP support is the documented subset, not every export/framework. Duplicate JSON keys are last-wins; numbers use double precision. Strings preserve large integers exactly. Timestamp ordering retains up to nanosecond precision; duration summaries use floating-point milliseconds.
|
|
10
|
+
|
|
11
|
+
Windows and Ubuntu WSL checks do not establish macOS or hosted-runner compatibility; the full CI matrix must run. Regression timing is not a universal speed promise. Trusted directories remain required against concurrent replacement of parent components. Legitimate symlink paths, including package-manager aliases and some OS temporary paths, must be resolved to real paths before inspection. Report publication requires hard-link support. Opt-in key-based redaction is not general anonymization.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Agent Workflow Inspector v1.0.0 — Deterministic AI Agent Trace Diagnostics
|
|
2
|
+
|
|
3
|
+
Release notes prepared for approval. This version has not been published.
|
|
4
|
+
|
|
5
|
+
Agent Workflow Inspector is a local-first, deterministic linter for AI agent execution traces. It highlights recorded patterns for investigation; it does not understand agent intentions or prove a workflow is stuck.
|
|
6
|
+
|
|
7
|
+
## Included in V1
|
|
8
|
+
|
|
9
|
+
- Six bounded rules covering repeated tool calls, repeated failures, execution cycles, rapid retry attempts, operation/depth limits and associated recorded leaf resource usage.
|
|
10
|
+
- Versioned AWI JSON and the [documented OTLP JSON subset](https://github.com/jigar48/awi/blob/v1.0.0/docs/trace-formats.md); no blanket framework compatibility claim.
|
|
11
|
+
- CLI and ESM/TypeScript library, configurable thresholds, human-readable and structured JSON reports, stable evidence references and CI exit codes.
|
|
12
|
+
- Local analysis without telemetry or an LLM. Input budgets, terminal escaping, symlink checks and exclusive output creation.
|
|
13
|
+
|
|
14
|
+
## Install after publication
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm install --global agent-workflow-inspector@1.0.0
|
|
18
|
+
awi --help
|
|
19
|
+
awi inspect trace.json --format json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Requires Node 22.18+ (22.x), 24.11+ (24.x), or 26.x. Exit 0: completed below threshold; 1: invalid input, operational failure or incomplete analysis; 2: completed with findings meeting the configured threshold (high by default).
|
|
23
|
+
|
|
24
|
+
The audited source passed 69 tests and nine hosted Windows/macOS/Linux × Node 22/24/26 jobs. The final release commit must pass a fresh matrix before publication; see the [verification record](https://github.com/jigar48/awi/blob/v1.0.0/docs/verification.md).
|
|
25
|
+
|
|
26
|
+
## Privacy and limitations
|
|
27
|
+
|
|
28
|
+
Default reports omit raw arguments, results, messages and metadata. Trace/operation identifiers remain visible and can themselves contain sensitive data. Opt-in content display uses limited key-based redaction and can expose secrets. Review reports before sharing.
|
|
29
|
+
|
|
30
|
+
Intentional polling/retries can trigger findings. Missing timestamps, concurrency, incomplete exports, bounded windows and leaf-only resource attribution limit detection and accounting. Resource estimates do not establish causality or financial waste.
|
|
31
|
+
|
|
32
|
+
See [getting started](https://github.com/jigar48/awi/blob/v1.0.0/docs/getting-started.md), [rules](https://github.com/jigar48/awi/blob/v1.0.0/docs/detection-rules.md), [configuration](https://github.com/jigar48/awi/blob/v1.0.0/docs/configuration.md), [security](https://github.com/jigar48/awi/blob/v1.0.0/docs/security.md), [limitations](https://github.com/jigar48/awi/blob/v1.0.0/docs/limitations.md) and [changelog](https://github.com/jigar48/awi/blob/v1.0.0/CHANGELOG.md). MIT licensed; maintained by [Jigar Prajapati](https://github.com/jigar48).
|