(value: unknown, depth = 0)
| 240 | const TOOL_INPUT_MAX_DEPTH = 2 |
| 241 | |
| 242 | function truncateToolInputValue(value: unknown, depth = 0): unknown { |
| 243 | if (typeof value === 'string') { |
| 244 | if (value.length > TOOL_INPUT_STRING_TRUNCATE_AT) { |
| 245 | return `${value.slice(0, TOOL_INPUT_STRING_TRUNCATE_TO)}…[${value.length} chars]` |
| 246 | } |
| 247 | return value |
| 248 | } |
| 249 | if ( |
| 250 | typeof value === 'number' || |
| 251 | typeof value === 'boolean' || |
| 252 | value === null || |
| 253 | value === undefined |
| 254 | ) { |
| 255 | return value |
| 256 | } |
| 257 | if (depth >= TOOL_INPUT_MAX_DEPTH) { |
| 258 | return '<nested>' |
| 259 | } |
| 260 | if (Array.isArray(value)) { |
| 261 | const mapped = value |
| 262 | .slice(0, TOOL_INPUT_MAX_COLLECTION_ITEMS) |
| 263 | .map(v => truncateToolInputValue(v, depth + 1)) |
| 264 | if (value.length > TOOL_INPUT_MAX_COLLECTION_ITEMS) { |
| 265 | mapped.push(`…[${value.length} items]`) |
| 266 | } |
| 267 | return mapped |
| 268 | } |
| 269 | if (typeof value === 'object') { |
| 270 | const entries = Object.entries(value as Record<string, unknown>) |
| 271 | // Skip internal marker keys (e.g. _simulatedSedEdit re-introduced by |
| 272 | // SedEditPermissionRequest) so they don't leak into telemetry. |
| 273 | .filter(([k]) => !k.startsWith('_')) |
| 274 | const mapped = entries |
| 275 | .slice(0, TOOL_INPUT_MAX_COLLECTION_ITEMS) |
| 276 | .map(([k, v]) => [k, truncateToolInputValue(v, depth + 1)]) |
| 277 | if (entries.length > TOOL_INPUT_MAX_COLLECTION_ITEMS) { |
| 278 | mapped.push(['…', `${entries.length} keys`]) |
| 279 | } |
| 280 | return Object.fromEntries(mapped) |
| 281 | } |
| 282 | return String(value) |
| 283 | } |
| 284 | |
| 285 | /** |
| 286 | * Serialize a tool's input arguments for the OTel tool_result event. |
no test coverage detected