diff --git a/.changeset/agent-monitoring-controls.md b/.changeset/agent-monitoring-controls.md new file mode 100644 index 0000000000..a3c971ace6 --- /dev/null +++ b/.changeset/agent-monitoring-controls.md @@ -0,0 +1,4 @@ +--- +--- + +Agent monitoring controls: User-Agent headers on all automated outbound requests, outbound request log with dashboard visibility, and owner controls to pause or adjust check frequency. diff --git a/package-lock.json b/package-lock.json index 0bc28e90e4..95b898e212 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "adcontextprotocol", "version": "3.0.0-rc.3", "dependencies": { - "@adcp/client": "^4.19.0", + "@adcp/client": "^4.22.0", "@anthropic-ai/sdk": "^0.82.0", "@asteasolutions/zod-to-openapi": "^8.5.0", "@google/generative-ai": "^0.24.1", @@ -115,10 +115,13 @@ } }, "node_modules/@adcp/client": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@adcp/client/-/client-4.20.0.tgz", - "integrity": "sha512-VV7+ixNEEplQo3zk/SPGmjjlFC1jqUi6qB5ePO+yWDrFAndzLkURINh7YI6ETBbkY/LBd9RylrdU/CQHnCmBkQ==", + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@adcp/client/-/client-4.22.0.tgz", + "integrity": "sha512-Dkky5u2BNkNrwio7TMLqGfQE1QtI2S4iNngSwVRvF3hVRIZ5mdIhdkzq8dzn3VS+0ePT6372Wce8d5CHj+68Zw==", "license": "Apache-2.0", + "dependencies": { + "yaml": "^2.7.1" + }, "bin": { "adcp": "bin/adcp.js" }, @@ -214,32 +217,30 @@ "license": "MIT" }, "node_modules/@asamuzakjp/css-color": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.4.tgz", - "integrity": "sha512-503MoTEmPSyEJ7zQ+5vlkwPtkyxDhbDwR9ajk/jpPGrCLiUFHzgEG4iViUPKdGlZPRT1mWSPSbDL2qkOoLU4vg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.6.tgz", + "integrity": "sha512-BXWCh8dHs9GOfpo/fWGDJtDmleta2VePN9rn6WQt3GjEbxzutVF4t0x2pmH+7dbMCLtuv3MlwqRsAuxlzFXqFg==", "license": "MIT", "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0", - "lru-cache": "^11.2.7" + "@csstools/css-tokenizer": "^4.0.0" }, "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/@asamuzakjp/dom-selector": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.4.tgz", - "integrity": "sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.7.tgz", + "integrity": "sha512-d2BgqDUOS1Hfp4IzKUZqCNz+Kg3Y88AkaBvJK/ZVSQPU1f7OpPNi7nQTH6/oI47Dkdg+Z3e8Yp6ynOu4UMINAQ==", "license": "MIT", "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.2.1", - "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.7" + "is-potential-custom-element-name": "^1.0.1" }, "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" @@ -815,15 +816,14 @@ } }, "node_modules/@emnapi/core": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, @@ -833,8 +833,7 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD", - "optional": true, - "peer": true + "optional": true }, "node_modules/@emnapi/runtime": { "version": "1.9.2", @@ -854,13 +853,12 @@ "optional": true }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -871,8 +869,7 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD", - "optional": true, - "peer": true + "optional": true }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.7", @@ -1381,9 +1378,9 @@ } }, "node_modules/@hono/node-server": { - "version": "1.19.12", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.12.tgz", - "integrity": "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==", + "version": "1.19.13", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.13.tgz", + "integrity": "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==", "license": "MIT", "engines": { "node": ">=18.14.1" @@ -2431,18 +2428,18 @@ } }, "node_modules/@mintlify/cli": { - "version": "4.0.1090", - "resolved": "https://registry.npmjs.org/@mintlify/cli/-/cli-4.0.1090.tgz", - "integrity": "sha512-SDA+KrZcMBoatfXujr+JeDEJkJ4kMpFKeeWPnC/vFJ4ojrGXfD0hWdSIdJ5MjLKfs2QtLqbvQErp7yBrj5r/QA==", + "version": "4.0.1103", + "resolved": "https://registry.npmjs.org/@mintlify/cli/-/cli-4.0.1103.tgz", + "integrity": "sha512-/Tz4ydJp0eY4I5oKv4D4FYK0xPm9fpwCfnSye4UzjRU7bVUv34Qzi6px/1PQJbQtpUiISwF7tuWH6tyB5AWknw==", "dev": true, "license": "Elastic-2.0", "dependencies": { "@inquirer/prompts": "7.9.0", - "@mintlify/common": "1.0.835", - "@mintlify/link-rot": "3.0.1010", - "@mintlify/prebuild": "1.0.977", - "@mintlify/previewing": "4.0.1038", - "@mintlify/validation": "0.1.653", + "@mintlify/common": "1.0.844", + "@mintlify/link-rot": "3.0.1019", + "@mintlify/prebuild": "1.0.986", + "@mintlify/previewing": "4.0.1047", + "@mintlify/validation": "0.1.660", "adm-zip": "0.5.16", "chalk": "5.2.0", "color": "4.2.3", @@ -2622,18 +2619,18 @@ } }, "node_modules/@mintlify/common": { - "version": "1.0.835", - "resolved": "https://registry.npmjs.org/@mintlify/common/-/common-1.0.835.tgz", - "integrity": "sha512-JP73YjY85KmFyvh2xEkAzjoFVYXo6QdoXF1RtdDEZdOGph3hw06MxukiuvRDaXJbfpXVyLBWCkUuaUWVbcyqkA==", + "version": "1.0.844", + "resolved": "https://registry.npmjs.org/@mintlify/common/-/common-1.0.844.tgz", + "integrity": "sha512-uTQ5yGFNvP4wpc5FHvBEkJubg5VNW9R2LL9+IcSg/KraDzRn0vCD9YIdq2f2RdwYDYl6sWGMmYjDxUqrOOZVFg==", "dev": true, "license": "ISC", "dependencies": { "@asyncapi/parser": "3.4.0", "@asyncapi/specs": "6.8.1", "@mintlify/mdx": "^3.0.4", - "@mintlify/models": "0.0.287", + "@mintlify/models": "0.0.290", "@mintlify/openapi-parser": "^0.0.8", - "@mintlify/validation": "0.1.653", + "@mintlify/validation": "0.1.660", "@sindresorhus/slugify": "2.2.0", "@types/mdast": "4.0.4", "acorn": "8.11.2", @@ -2854,17 +2851,17 @@ } }, "node_modules/@mintlify/link-rot": { - "version": "3.0.1010", - "resolved": "https://registry.npmjs.org/@mintlify/link-rot/-/link-rot-3.0.1010.tgz", - "integrity": "sha512-XtIuDv/KTmRrMUNxlxke77+k163FLgm0I+e/U8PO1mlrBpnp2yiOi/mMvH4cfQpxFjjSJ6Apl5urH2nsircHKA==", + "version": "3.0.1019", + "resolved": "https://registry.npmjs.org/@mintlify/link-rot/-/link-rot-3.0.1019.tgz", + "integrity": "sha512-moUkUUcdfm/ivgavmrcgcnxhJ4XCDAbYPABhQbwo6hP3FHXyTB8jJdbjG/wJLZSzjH3KQpq/+DglMH5cCmSNJQ==", "dev": true, "license": "Elastic-2.0", "dependencies": { - "@mintlify/common": "1.0.835", - "@mintlify/prebuild": "1.0.977", - "@mintlify/previewing": "4.0.1038", + "@mintlify/common": "1.0.844", + "@mintlify/prebuild": "1.0.986", + "@mintlify/previewing": "4.0.1047", "@mintlify/scraping": "4.0.522", - "@mintlify/validation": "0.1.653", + "@mintlify/validation": "0.1.660", "fs-extra": "11.1.0", "unist-util-visit": "4.1.2" }, @@ -2963,9 +2960,9 @@ } }, "node_modules/@mintlify/models": { - "version": "0.0.287", - "resolved": "https://registry.npmjs.org/@mintlify/models/-/models-0.0.287.tgz", - "integrity": "sha512-3i1iateUUd21zVDEXIB0BUN0vcgD9WsfaXO/jB264LQCWXVI9nJEbfEXPeXVHUHwjKAExoRre5YXtb8BpWsmlA==", + "version": "0.0.290", + "resolved": "https://registry.npmjs.org/@mintlify/models/-/models-0.0.290.tgz", + "integrity": "sha512-dkUIepQOpyZmgdapL22wdQi7MXupLyqFWP/ebiP0NYLcRRYBLWFVcpHHfIDGC2mWOZxNCVVZDvg2rTzfccpj6A==", "dev": true, "license": "Elastic-2.0", "dependencies": { @@ -2995,16 +2992,16 @@ } }, "node_modules/@mintlify/prebuild": { - "version": "1.0.977", - "resolved": "https://registry.npmjs.org/@mintlify/prebuild/-/prebuild-1.0.977.tgz", - "integrity": "sha512-/lKXOZHIPe9/Nrm5ZSUiEk8tK0RecJUf6WoY6haSxnNWrCvgVGBP2N9/Q3q3akT3ZK0ISZNVfM1z9K5L3dffKg==", + "version": "1.0.986", + "resolved": "https://registry.npmjs.org/@mintlify/prebuild/-/prebuild-1.0.986.tgz", + "integrity": "sha512-HGQwegpiP0ZwAg/kpISdtad6t5om32HZ/OCWQGHh2G3+gv2Fjg3hGRttagU88oBT9oKC1N7lJPjhxK8FrvwX3w==", "dev": true, "license": "Elastic-2.0", "dependencies": { - "@mintlify/common": "1.0.835", + "@mintlify/common": "1.0.844", "@mintlify/openapi-parser": "^0.0.8", - "@mintlify/scraping": "4.0.699", - "@mintlify/validation": "0.1.653", + "@mintlify/scraping": "4.0.708", + "@mintlify/validation": "0.1.660", "chalk": "5.3.0", "favicons": "7.2.0", "front-matter": "4.0.2", @@ -3398,13 +3395,13 @@ } }, "node_modules/@mintlify/prebuild/node_modules/@mintlify/scraping": { - "version": "4.0.699", - "resolved": "https://registry.npmjs.org/@mintlify/scraping/-/scraping-4.0.699.tgz", - "integrity": "sha512-vcn+W57lvMK82jEZ2s6v1uGXLZC2VBlpmbZ0cyJbZFbCTytgWFTJiB7a+9B+wMFto5zk2IRiwHeWxWagqiFVfQ==", + "version": "4.0.708", + "resolved": "https://registry.npmjs.org/@mintlify/scraping/-/scraping-4.0.708.tgz", + "integrity": "sha512-6GDxVKM7B0NqxXvg4Mm8nVhtybAzkVRZcMGtsp5OoHZrnATZ/C4wv2B82ZnwZvdhzLDATWoSoe3W14IXgYYcCQ==", "dev": true, "license": "Elastic-2.0", "dependencies": { - "@mintlify/common": "1.0.835", + "@mintlify/common": "1.0.844", "@mintlify/openapi-parser": "^0.0.8", "fs-extra": "11.1.1", "hast-util-to-mdast": "10.1.0", @@ -3825,15 +3822,15 @@ } }, "node_modules/@mintlify/previewing": { - "version": "4.0.1038", - "resolved": "https://registry.npmjs.org/@mintlify/previewing/-/previewing-4.0.1038.tgz", - "integrity": "sha512-TzxrVZN96dpCSYeJ9JDklawL+schE0/Rdef3WmNSoMpRkutvnjhbR2biUpEyn9/X2wV7IWN0V9BGMemP9Tvhxg==", + "version": "4.0.1047", + "resolved": "https://registry.npmjs.org/@mintlify/previewing/-/previewing-4.0.1047.tgz", + "integrity": "sha512-4/k7a/kXkD8LK7nHvRGEPCvigpeunFk2Ku07wlXLR4tB8OEG6v5ZjLFKVHArd+UuRmjHB/oBcCht3DARaizPOw==", "dev": true, "license": "Elastic-2.0", "dependencies": { - "@mintlify/common": "1.0.835", - "@mintlify/prebuild": "1.0.977", - "@mintlify/validation": "0.1.653", + "@mintlify/common": "1.0.844", + "@mintlify/prebuild": "1.0.986", + "@mintlify/validation": "0.1.660", "adm-zip": "0.5.16", "better-opn": "3.0.2", "chalk": "5.2.0", @@ -5034,14 +5031,14 @@ } }, "node_modules/@mintlify/validation": { - "version": "0.1.653", - "resolved": "https://registry.npmjs.org/@mintlify/validation/-/validation-0.1.653.tgz", - "integrity": "sha512-TK9dntGkxRQlnaFmsUSt7znd1QjGlS0nvEZymZfJICYxCaQbEkENEtKeex0gU8ot3XVJtImQhORelvzyjtEfEQ==", + "version": "0.1.660", + "resolved": "https://registry.npmjs.org/@mintlify/validation/-/validation-0.1.660.tgz", + "integrity": "sha512-IHlea3t9ZZcQMOfext3fZuG6/hXXTZPBFJkgeHA9lbG2OkdAVRbSMDY9FvC07sEEX1VQJX+bPimRaXUz/ujyYg==", "dev": true, "license": "Elastic-2.0", "dependencies": { "@mintlify/mdx": "^3.0.4", - "@mintlify/models": "0.0.287", + "@mintlify/models": "0.0.290", "arktype": "2.1.27", "js-yaml": "4.1.0", "lcm": "0.0.3", @@ -5702,9 +5699,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.122.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", - "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "version": "0.123.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.123.0.tgz", + "integrity": "sha512-YtECP/y8Mj1lSHiUWGSRzy/C6teUKlS87dEfuVKT09LgQbUsBW1rNg+MiJ4buGu3yuADV60gbIvo9/HplA56Ew==", "dev": true, "license": "MIT", "funding": { @@ -5728,9 +5725,9 @@ "license": "MIT" }, "node_modules/@posthog/core": { - "version": "1.24.6", - "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.24.6.tgz", - "integrity": "sha512-9WkcRKqmXSWIJcca6m3VwA9YbFd4HiG2hKEtDq6FcwEHlvfDhQQUZ5/sJZ47Fw8OtyNMHQ6rW4+COttk4Bg5NQ==", + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.25.1.tgz", + "integrity": "sha512-76enEGwLVtcCTbfAr1wJ6zi4tYzVkGsqJx+H9JiX0K8/VxuKdbOtVShsOJhTD9uvFfTeW3DX+PSCelcqWrRRkw==", "license": "MIT" }, "node_modules/@protobufjs/aspromise": { @@ -7389,9 +7386,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.13.tgz", + "integrity": "sha512-5ZiiecKH2DXAVJTNN13gNMUcCDg4Jy8ZjbXEsPnqa248wgOVeYRX0iqXXD5Jz4bI9BFHgKsI2qmyJynstbmr+g==", "cpu": [ "arm64" ], @@ -7406,9 +7403,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.13.tgz", + "integrity": "sha512-tz/v/8G77seu8zAB3A5sK3UFoOl06zcshEzhUO62sAEtrEuW/H1CcyoupOrD+NbQJytYgA4CppXPzlrmp4JZKA==", "cpu": [ "arm64" ], @@ -7423,9 +7420,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.13.tgz", + "integrity": "sha512-8DakphqOz8JrMYWTJmWA+vDJxut6LijZ8Xcdc4flOlAhU7PNVwo2MaWBF9iXjJAPo5rC/IxEFZDhJ3GC7NHvug==", "cpu": [ "x64" ], @@ -7440,9 +7437,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.13.tgz", + "integrity": "sha512-4wBQFfjDuXYN/SVI8inBF3Aa+isq40rc6VMFbk5jcpolUBTe5cYnMsHZ51nFWsx3PVyyNN3vgoESki0Hmr/4BA==", "cpu": [ "x64" ], @@ -7457,9 +7454,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", - "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.13.tgz", + "integrity": "sha512-JW/e4yPIXLms+jmnbwwy5LA/LxVwZUWLN8xug+V200wzaVi5TEGIWQlh8o91gWYFxW609euI98OCCemmWGuPrw==", "cpu": [ "arm" ], @@ -7474,9 +7471,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-ZfKWpXiUymDnavepCaM6KG/uGydJ4l2nBmMxg60Ci4CbeefpqjPWpfaZM7PThOhk2dssqBAcwLc6rAyr0uTdXg==", "cpu": [ "arm64" ], @@ -7491,9 +7488,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.13.tgz", + "integrity": "sha512-bmRg3O6Z0gq9yodKKWCIpnlH051sEfdVwt+6m5UDffAQMUUqU0xjnQqqAUm+Gu7ofAAly9DqiQDtKu2nPDEABA==", "cpu": [ "arm64" ], @@ -7508,9 +7505,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-8Wtnbw4k7pMYN9B/mOEAsQ8HOiq7AZ31Ig4M9BKn2So4xRaFEhtCSa4ZJaOutOWq50zpgR4N5+L/opnlaCx8wQ==", "cpu": [ "ppc64" ], @@ -7525,9 +7522,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-D/0Nlo8mQuxSMohNJUF2lDXWRsFDsHldfRRgD9bRgktj+EndGPj4DOV37LqDKPYS+osdyhZEH7fTakTAEcW7qg==", "cpu": [ "s390x" ], @@ -7542,9 +7539,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-eRrPvat2YaVQcwwKi/JzOP6MKf1WRnOCr+VaI3cTWz3ZoLcP/654z90lVCJ4dAuMEpPdke0n+qyAqXDZdIC4rA==", "cpu": [ "x64" ], @@ -7559,9 +7556,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.13.tgz", + "integrity": "sha512-PsdONiFRp8hR8KgVjTWjZ9s7uA3uueWL0t74/cKHfM4dR5zXYv4AjB8BvA+QDToqxAFg4ZkcVEqeu5F7inoz5w==", "cpu": [ "x64" ], @@ -7576,9 +7573,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.13.tgz", + "integrity": "sha512-hCNXgC5dI3TVOLrPT++PKFNZ+1EtS0mLQwfXXXSUD/+rGlB65gZDwN/IDuxLpQP4x8RYYHqGomlUXzpO8aVI2w==", "cpu": [ "arm64" ], @@ -7593,9 +7590,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", - "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.13.tgz", + "integrity": "sha512-viLS5C5et8NFtLWw9Sw3M/w4vvnVkbWkO7wSNh3C+7G1+uCkGpr6PcjNDSFcNtmXY/4trjPBqUfcOL+P3sWy/g==", "cpu": [ "wasm32" ], @@ -7603,16 +7600,37 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" + "@emnapi/core": "1.9.1", + "@emnapi/runtime": "1.9.1", + "@napi-rs/wasm-runtime": "^1.1.2" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.13.tgz", + "integrity": "sha512-Fqa3Tlt1xL4wzmAYxGNFV36Hb+VfPc9PYU+E25DAnswXv3ODDu/yyWjQDbXMo5AGWkQVjLgQExuVu8I/UaZhPQ==", "cpu": [ "arm64" ], @@ -7627,9 +7645,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.13.tgz", + "integrity": "sha512-/pLI5kPkGEi44TDlnbio3St/5gUFeN51YWNAk/Gnv6mEQBOahRBh52qVFVBpmrnU01n2yysvBML9Ynu7K4kGAQ==", "cpu": [ "x64" ], @@ -7644,9 +7662,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", - "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.13.tgz", + "integrity": "sha512-3ngTAv6F/Py35BsYbeeLeecvhMKdsKm4AoOETVhAA+Qc8nrA2I0kF7oa93mE9qnIurngOSpMnQ0x2nQY2FPviA==", "dev": true, "license": "MIT" }, @@ -7821,16 +7839,16 @@ } }, "node_modules/@slack/bolt": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@slack/bolt/-/bolt-4.6.0.tgz", - "integrity": "sha512-xPgfUs2+OXSugz54Ky07pA890+Qydk22SYToi8uGpXeHSt1JWwFJkRyd/9Vlg5I1AdfdpGXExDpwnbuN9Q/2dQ==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@slack/bolt/-/bolt-4.7.0.tgz", + "integrity": "sha512-Xpf+gKegNvkHpft1z4YiuqZdciJ3tUp1bIRQxylW30Ovf+hzjb0M1zTHVtJsRw9jsjPxHTPoyanEXVvG6qVE1g==", "license": "MIT", "dependencies": { - "@slack/logger": "^4.0.0", - "@slack/oauth": "^3.0.4", - "@slack/socket-mode": "^2.0.5", - "@slack/types": "^2.18.0", - "@slack/web-api": "^7.12.0", + "@slack/logger": "^4.0.1", + "@slack/oauth": "^3.0.5", + "@slack/socket-mode": "^2.0.6", + "@slack/types": "^2.20.1", + "@slack/web-api": "^7.15.0", "axios": "^1.12.0", "express": "^5.0.0", "path-to-regexp": "^8.1.0", @@ -8987,16 +9005,16 @@ "license": "ISC" }, "node_modules/@vitest/expect": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", - "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.3.tgz", + "integrity": "sha512-CW8Q9KMtXDGHj0vCsqui0M5KqRsu0zm0GNDW7Gd3U7nZ2RFpPKSCpeCXoT+/+5zr1TNlsoQRDEz+LzZUyq6gnQ==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.2", - "@vitest/utils": "4.1.2", + "@vitest/spy": "4.1.3", + "@vitest/utils": "4.1.3", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -9005,13 +9023,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz", - "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.3.tgz", + "integrity": "sha512-XN3TrycitDQSzGRnec/YWgoofkYRhouyVQj4YNsJ5r/STCUFqMrP4+oxEv3e7ZbLi4og5kIHrZwekDJgw6hcjw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.2", + "@vitest/spy": "4.1.3", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -9032,9 +9050,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz", - "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.3.tgz", + "integrity": "sha512-hYqqwuMbpkkBodpRh4k4cQSOELxXky1NfMmQvOfKvV8zQHz8x8Dla+2wzElkMkBvSAJX5TRGHJAQvK0TcOafwg==", "dev": true, "license": "MIT", "dependencies": { @@ -9045,13 +9063,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz", - "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.3.tgz", + "integrity": "sha512-VwgOz5MmT0KhlUj40h02LWDpUBVpflZ/b7xZFA25F29AJzIrE+SMuwzFf0b7t4EXdwRNX61C3B6auIXQTR3ttA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.2", + "@vitest/utils": "4.1.3", "pathe": "^2.0.3" }, "funding": { @@ -9059,14 +9077,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz", - "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.3.tgz", + "integrity": "sha512-9l+k/J9KG5wPJDX9BcFFzhhwNjwkRb8RsnYhaT1vPY7OufxmQFc9sZzScRCPTiETzl37mrIWVY9zxzmdVeJwDQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.2", - "@vitest/utils": "4.1.2", + "@vitest/pretty-format": "4.1.3", + "@vitest/utils": "4.1.3", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -9075,9 +9093,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz", - "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.3.tgz", + "integrity": "sha512-ujj5Uwxagg4XUIfAUyRQxAg631BP6e9joRiN99mr48Bg9fRs+5mdUElhOoZ6rP5mBr8Bs3lmrREnkrQWkrsTCw==", "dev": true, "license": "MIT", "funding": { @@ -9085,13 +9103,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz", - "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.3.tgz", + "integrity": "sha512-Pc/Oexse/khOWsGB+w3q4yzA4te7W4gpZZAvk+fr8qXfTURZUMj5i7kuxsNK5mP/dEB6ao3jfr0rs17fHhbHdw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.2", + "@vitest/pretty-format": "4.1.3", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -9100,9 +9118,9 @@ } }, "node_modules/@workos-inc/node": { - "version": "8.11.1", - "resolved": "https://registry.npmjs.org/@workos-inc/node/-/node-8.11.1.tgz", - "integrity": "sha512-kfuyMZBrbNgT8HSQo/Wz3v/OBh0J4M/KCrWu7We30xMdpd0hD4anvZrrLbuRowx93SSytzaKHRvVn88Ads1naA==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/@workos-inc/node/-/node-8.12.0.tgz", + "integrity": "sha512-y3WGK+A1dxUEsxV6jGXK4q5yVQLj47ZF5qphpEq+51axOhcbiInS28o8XDixVrtsDBCPDcQT+XCG909JmNgpbA==", "license": "MIT", "dependencies": { "eventemitter3": "^5.0.4" @@ -10762,9 +10780,9 @@ } }, "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", "engines": { "node": ">=18" @@ -11487,9 +11505,9 @@ } }, "node_modules/dotenv": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.0.tgz", - "integrity": "sha512-kCKF62fwtzwYm0IGBNjRUjtJgMfGapII+FslMHIjMR5KTnwEmBmWLDRSnc3XSNP8bNy34tekgQyDT0hr7pERRQ==", + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz", + "integrity": "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -11740,9 +11758,9 @@ } }, "node_modules/es-abstract": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", - "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", "dev": true, "license": "MIT", "dependencies": { @@ -14176,9 +14194,9 @@ } }, "node_modules/hono": { - "version": "4.12.10", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.10.tgz", - "integrity": "sha512-mx/p18PLy5og9ufies2GOSUqep98Td9q4i/EF6X7yJgAiIopxqdfIO3jbqsi3jRgTgw88jMDEzVKi+V2EF+27w==", + "version": "4.12.12", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.12.tgz", + "integrity": "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -15487,13 +15505,13 @@ } }, "node_modules/jsdom": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.1.tgz", - "integrity": "sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==", + "version": "29.0.2", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.2.tgz", + "integrity": "sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==", "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^5.0.1", - "@asamuzakjp/dom-selector": "^7.0.3", + "@asamuzakjp/css-color": "^5.1.5", + "@asamuzakjp/dom-selector": "^7.0.6", "@bramus/specificity": "^2.4.2", "@csstools/css-syntax-patches-for-csstree": "^1.1.1", "@exodus/bytes": "^1.15.0", @@ -15686,9 +15704,9 @@ } }, "node_modules/katex": { - "version": "0.16.44", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.44.tgz", - "integrity": "sha512-EkxoDTk8ufHqHlf9QxGwcxeLkWRR3iOuYfRpfORgYfqc8s13bgb+YtRY59NK5ZpRaCwq1kqA6a5lpX8C/eLphQ==", + "version": "0.16.45", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.45.tgz", + "integrity": "sha512-pQpZbdBu7wCTmQUh7ufPmLr0pFoObnGUoL/yhtwJDgmmQpbkg/0HSVti25Fu4rmd1oCR6NGWe9vqTWuWv3GcNA==", "dev": true, "funding": [ "https://opencollective.com/katex", @@ -16200,9 +16218,9 @@ } }, "node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.2.tgz", + "integrity": "sha512-wgWa6FWQ3QRRJbIjbsldRJZxdxYngT/dO0I5Ynmlnin8qy7tC6xYzbcJjtN4wHLXtkbVwHzk0C+OejVw1XM+DQ==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -17629,13 +17647,13 @@ } }, "node_modules/mintlify": { - "version": "4.2.487", - "resolved": "https://registry.npmjs.org/mintlify/-/mintlify-4.2.487.tgz", - "integrity": "sha512-H+tjbdji/D6GyWAKygcMYoZI/tYu8bLzi8EWs3E8thKdrjNM/5Vs97O3YO1j4vARg+ZkmqmkI8t/qYds7S6S9g==", + "version": "4.2.500", + "resolved": "https://registry.npmjs.org/mintlify/-/mintlify-4.2.500.tgz", + "integrity": "sha512-pVuzf4F+JRmVCuQZLQebIlggCzWQyHsnPiAbuUoJ8aofsKbbs30woRQznoeCmzgmzDxBk25xPay9yy4GRPRlOw==", "dev": true, "license": "Elastic-2.0", "dependencies": { - "@mintlify/cli": "4.0.1090" + "@mintlify/cli": "4.0.1103" }, "bin": { "mintlify": "index.js" @@ -17689,9 +17707,9 @@ "license": "MIT" }, "node_modules/msw": { - "version": "2.12.14", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.12.14.tgz", - "integrity": "sha512-4KXa4nVBIBjbDbd7vfQNuQ25eFxug0aropCQFoI0JdOBuJWamkT1yLVIWReFI8SiTRc+H1hKzaNk+cLk2N9rtQ==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.13.0.tgz", + "integrity": "sha512-5PPWf7I7DBHb4ZUZ0NUI+/VBDk/eiNYDNJZGt/jZ7+rbCSIK5hRcNTGqWMnn0vT6NrHiQlb0nfpenVGz1vrqpg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -17887,9 +17905,9 @@ } }, "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.0.tgz", + "integrity": "sha512-z9sZrk6wyf8/NDKKqe+Tyl58XtgkYrV4kgt1O8xrzYvpl1LvPacPo0imMLHfpStk3kgCIq1ksJ2bmJn9hue2lQ==", "dev": true, "license": "MIT", "engines": { @@ -19172,12 +19190,12 @@ } }, "node_modules/posthog-node": { - "version": "5.28.11", - "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.28.11.tgz", - "integrity": "sha512-H4FOiqKUBO8SVXyXlU5tyifeS11hyTGVwBirFPR5rPtw8X6OFs5xVLx38YL7ZBLjaa9u8is+nIWXKBwWsZ2vlw==", + "version": "5.29.1", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.29.1.tgz", + "integrity": "sha512-XA1OGrE8SAmO3JkfWjeVg7XxEN/6M6ZXzYmBJKl3uXv/xxk7ru0oeoqi/rcWJ6z5+nFP9wUFVoS37//qaPxwFg==", "license": "MIT", "dependencies": { - "@posthog/core": "1.24.6" + "@posthog/core": "1.25.1" }, "engines": { "node": "^20.20.0 || >=22.22.0" @@ -20524,14 +20542,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", - "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.13.tgz", + "integrity": "sha512-bvVj8YJmf0rq4pSFmH7laLa6pYrhghv3PRzrCdRAr23g66zOKVJ4wkvFtgohtPLWmthgg8/rkaqRHrpUEh0Zbw==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.12" + "@oxc-project/types": "=0.123.0", + "@rolldown/pluginutils": "1.0.0-rc.13" }, "bin": { "rolldown": "bin/cli.mjs" @@ -20540,21 +20558,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-x64": "1.0.0-rc.12", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + "@rolldown/binding-android-arm64": "1.0.0-rc.13", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.13", + "@rolldown/binding-darwin-x64": "1.0.0-rc.13", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.13", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.13", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.13", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.13", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.13", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.13", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.13", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.13" } }, "node_modules/router": { @@ -22270,14 +22288,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -22328,21 +22346,21 @@ } }, "node_modules/tldts": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", - "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", + "version": "7.0.28", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.28.tgz", + "integrity": "sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==", "license": "MIT", "dependencies": { - "tldts-core": "^7.0.27" + "tldts-core": "^7.0.28" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", - "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", + "version": "7.0.28", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.28.tgz", + "integrity": "sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==", "license": "MIT" }, "node_modules/to-data-view": { @@ -23203,16 +23221,16 @@ } }, "node_modules/vite": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", - "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.6.tgz", + "integrity": "sha512-jeOXoY6N8rOfit/mZADMd0misLqjRdWBB3/S23ZQNuPcbVsfMBJutWD8b4ftdczMOsNyMBnKro0Z1Kt0HIqq5Q==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.12", + "rolldown": "1.0.0-rc.13", "tinyglobby": "^0.2.15" }, "bin": { @@ -23230,7 +23248,7 @@ "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", @@ -23294,9 +23312,9 @@ } }, "node_modules/vite/node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", "dev": true, "funding": [ { @@ -23323,19 +23341,19 @@ } }, "node_modules/vitest": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz", - "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.3.tgz", + "integrity": "sha512-DBc4Tx0MPNsqb9isoyOq00lHftVx/KIU44QOm2q59npZyLUkENn8TMFsuzuO+4U2FUa9rgbbPt3udrP25GcjXw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.2", - "@vitest/mocker": "4.1.2", - "@vitest/pretty-format": "4.1.2", - "@vitest/runner": "4.1.2", - "@vitest/snapshot": "4.1.2", - "@vitest/spy": "4.1.2", - "@vitest/utils": "4.1.2", + "@vitest/expect": "4.1.3", + "@vitest/mocker": "4.1.3", + "@vitest/pretty-format": "4.1.3", + "@vitest/runner": "4.1.3", + "@vitest/snapshot": "4.1.3", + "@vitest/spy": "4.1.3", + "@vitest/utils": "4.1.3", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -23363,10 +23381,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.2", - "@vitest/browser-preview": "4.1.2", - "@vitest/browser-webdriverio": "4.1.2", - "@vitest/ui": "4.1.2", + "@vitest/browser-playwright": "4.1.3", + "@vitest/browser-preview": "4.1.3", + "@vitest/browser-webdriverio": "4.1.3", + "@vitest/coverage-istanbul": "4.1.3", + "@vitest/coverage-v8": "4.1.3", + "@vitest/ui": "4.1.3", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -23390,6 +23410,12 @@ "@vitest/browser-webdriverio": { "optional": true }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, "@vitest/ui": { "optional": true }, diff --git a/package.json b/package.json index d905a6a5bf..01ce3cae55 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "check:images": "bash scripts/check-image-quality.sh" }, "dependencies": { - "@adcp/client": "^4.19.0", + "@adcp/client": "^4.22.0", "@anthropic-ai/sdk": "^0.82.0", "@asteasolutions/zod-to-openapi": "^8.5.0", "@google/generative-ai": "^0.24.1", diff --git a/server/public/dashboard-agents.html b/server/public/dashboard-agents.html index a13dd66f22..caeeee6b1f 100644 --- a/server/public/dashboard-agents.html +++ b/server/public/dashboard-agents.html @@ -626,16 +626,36 @@

Agents

${clickableTrackPills ? '
' + clickableTrackPills + '
' : ''} ${sparkline} +
+ + + ${cs.monitoring_paused ? 'Compliance status will not update while paused' : ''} +
Last checked: ${escapeHtml(lastChecked)}
${!hasAuth ? 'Connect agent' : ''} +
+ `; }).join(''); @@ -673,6 +693,136 @@

Agents

} }); + // Monitoring pause toggle + document.addEventListener('change', async function(e) { + const toggle = e.target.closest('.monitoring-pause-toggle'); + if (toggle) { + const agentUrl = toggle.dataset.agentUrl; + const controlsRow = toggle.closest('.agent-monitoring-controls'); + const intervalSelect = controlsRow ? controlsRow.querySelector('.monitoring-interval-select') : null; + + try { + const res = await fetch(`/api/registry/agents/${encodeURIComponent(agentUrl)}/monitoring/pause`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + credentials: 'include', + body: JSON.stringify({ paused: toggle.checked }), + }); + if (!res.ok) { + toggle.checked = !toggle.checked; + } else { + // Update interval dropdown disabled state + if (intervalSelect) { + intervalSelect.disabled = toggle.checked; + intervalSelect.style.opacity = toggle.checked ? '0.4' : '1'; + } + // Show/hide pause warning + if (controlsRow) { + let warning = controlsRow.querySelector('.pause-warning'); + if (toggle.checked && !warning) { + warning = document.createElement('span'); + warning.className = 'pause-warning'; + warning.style.cssText = 'font-size: var(--text-xs); color: var(--color-warning-700, #b45309);'; + warning.textContent = 'Compliance status will not update while paused'; + controlsRow.appendChild(warning); + } else if (!toggle.checked && warning) { + warning.remove(); + } + } + } + } catch { + toggle.checked = !toggle.checked; + } + } + }); + + // Capture initial value for interval selects on focus (before change fires) + document.addEventListener('focus', function(e) { + const select = e.target.closest('.monitoring-interval-select'); + if (select) { + select.dataset.previousValue = select.value; + } + }, true); + + // Monitoring interval change + document.addEventListener('change', async function(e) { + const select = e.target.closest('.monitoring-interval-select'); + if (select) { + const agentUrl = select.dataset.agentUrl; + const previousValue = select.dataset.previousValue; + select.dataset.previousValue = select.value; + try { + const res = await fetch(`/api/registry/agents/${encodeURIComponent(agentUrl)}/monitoring/interval`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + credentials: 'include', + body: JSON.stringify({ interval_hours: parseInt(select.value, 10) }), + }); + if (!res.ok) { + select.value = previousValue; + } + } catch { + select.value = previousValue; + } + } + }); + + // View outbound requests log + document.addEventListener('click', async function(e) { + const btn = e.target.closest('.agent-requests-btn'); + if (!btn) return; + + const agentUrl = btn.dataset.agentUrl; + const cardId = btn.dataset.cardId; + const panel = document.getElementById(cardId + '-requests'); + if (!panel) return; + + if (panel.style.display !== 'none') { + panel.style.display = 'none'; + return; + } + + panel.style.display = 'block'; + panel.innerHTML = '
Loading requests...
'; + + try { + const res = await fetch(`/api/registry/agents/${encodeURIComponent(agentUrl)}/monitoring/requests?limit=50`, { + credentials: 'include', + }); + if (!res.ok) throw new Error('Failed to load'); + const data = await res.json(); + + if (data.requests.length === 0) { + panel.innerHTML = '
No outbound requests recorded yet.
'; + return; + } + + const rows = data.requests.map(function(r) { + const time = new Date(r.created_at).toLocaleString(); + const status = r.success + ? 'OK' + : 'ERR'; + const duration = r.response_time_ms != null ? r.response_time_ms + 'ms' : '-'; + const ua = r.user_agent ? r.user_agent.split(' ')[0] : '-'; + return '' + + '' + escapeHtml(time) + '' + + '' + escapeHtml(r.request_type) + '' + + '' + escapeHtml(ua) + '' + + '' + status + '' + + '' + escapeHtml(duration) + '' + + (r.error_message ? '' + escapeHtml(r.error_message) + '' : '') + + ''; + }).join(''); + + panel.innerHTML = '
Outbound Requests (' + escapeHtml(String(data.total)) + ' total)
' + + '' + + '' + + '' + rows + '
TimeTypeUser-AgentStatusDurationError
'; + } catch { + panel.innerHTML = '
Failed to load requests.
'; + } + }); + // Track pill click — fetch history and show scenario details for that track document.addEventListener('click', async function(e) { const trackBtn = e.target.closest('.agent-track[data-track]'); diff --git a/server/src/adagents-manager.ts b/server/src/adagents-manager.ts index 0952ae6dea..0d5337f3f5 100644 --- a/server/src/adagents-manager.ts +++ b/server/src/adagents-manager.ts @@ -1,5 +1,6 @@ import axios from 'axios'; import { PropertyDefinition, PlacementDefinition } from './types.js'; +import { AAO_UA_VALIDATOR } from './config/user-agents.js'; export interface ValidationError { field: string; @@ -175,7 +176,7 @@ export class AdAgentsManager { timeout: 10000, headers: { 'Accept': 'application/json', - 'User-Agent': 'AdCP-Testing-Framework/1.0' + 'User-Agent': AAO_UA_VALIDATOR }, validateStatus: () => true, // Don't throw on non-2xx status codes responseType: 'arraybuffer', @@ -314,7 +315,7 @@ export class AdAgentsManager { timeout: 10000, headers: { 'Accept': 'application/json', - 'User-Agent': 'AdCP-Testing-Framework/1.0' + 'User-Agent': AAO_UA_VALIDATOR }, validateStatus: () => true, responseType: 'arraybuffer', @@ -1229,7 +1230,7 @@ export class AdAgentsManager { timeout: 3000, // Keep short for responsive UX headers: { 'Accept': 'application/json', - 'User-Agent': 'AdCP-Testing-Framework/1.0' + 'User-Agent': AAO_UA_VALIDATOR }, validateStatus: () => true }); diff --git a/server/src/addie/jobs/compliance-heartbeat.ts b/server/src/addie/jobs/compliance-heartbeat.ts index 0fb050979f..7eaf41169e 100644 --- a/server/src/addie/jobs/compliance-heartbeat.ts +++ b/server/src/addie/jobs/compliance-heartbeat.ts @@ -11,6 +11,8 @@ import { query } from '../../db/client.js'; import { notifyComplianceChange } from '../../notifications/compliance.js'; import { notifySystemError } from '../error-notifier.js'; import { logger as baseLogger } from '../../logger.js'; +import { logOutboundRequest } from '../../db/outbound-log-db.js'; +import { AAO_UA_COMPLIANCE } from '../../config/user-agents.js'; const logger = baseLogger.child({ module: 'compliance-heartbeat' }); const complianceDb = new ComplianceDatabase(); @@ -48,6 +50,7 @@ export async function runComplianceHeartbeatJob(options: HeartbeatOptions = {}): ); for (const agent of agentsDue) { + const startTime = Date.now(); try { // Use the owning org's saved credentials from agent_contexts. // These are credentials the owner saved when connecting through Addie. @@ -58,10 +61,19 @@ export async function runComplianceHeartbeatJob(options: HeartbeatOptions = {}): dry_run: true, timeout_ms: 60_000, auth, + userAgent: AAO_UA_COMPLIANCE, }; const complianceResult = await comply(agent.agent_url, complyOptions); + logOutboundRequest({ + agent_url: agent.agent_url, + request_type: 'compliance', + user_agent: AAO_UA_COMPLIANCE, + response_time_ms: Date.now() - startTime, + success: true, + }); + // Map track results to storage format const tracksJson: TrackSummaryEntry[] = complianceResult.tracks.map(t => ({ track: t.track, @@ -127,6 +139,15 @@ export async function runComplianceHeartbeatJob(options: HeartbeatOptions = {}): } catch (error) { logger.error({ error, agentUrl: agent.agent_url }, 'Compliance check failed for agent'); + logOutboundRequest({ + agent_url: agent.agent_url, + request_type: 'compliance', + user_agent: AAO_UA_COMPLIANCE, + response_time_ms: Date.now() - startTime, + success: false, + error_message: error instanceof Error ? error.message : 'Unknown error', + }); + // Record failure so stale passing data doesn't persist try { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; diff --git a/server/src/addie/jobs/job-definitions.ts b/server/src/addie/jobs/job-definitions.ts index 64825d7a52..2a9977f401 100644 --- a/server/src/addie/jobs/job-definitions.ts +++ b/server/src/addie/jobs/job-definitions.ts @@ -378,6 +378,20 @@ export function registerAllJobs(): void { shouldLogResult: (r) => r.checked > 0, }); + // Outbound request log cleanup - retain 30 days + jobScheduler.register({ + name: 'outbound-log-cleanup', + description: 'Clean up old outbound request logs', + interval: { value: 24, unit: 'hours' }, + initialDelay: { value: 60, unit: 'minutes' }, + runner: async () => { + const { cleanupOldRequests } = await import('../../db/outbound-log-db.js'); + const deleted = await cleanupOldRequests(30); + return { deleted }; + }, + shouldLogResult: (r: { deleted: number }) => r.deleted > 0, + }); + // Shadow evaluator - generates what Addie would have said and compares with human answers jobScheduler.register({ name: 'shadow-evaluator', @@ -536,4 +550,5 @@ export const JOB_NAMES = { SHADOW_EVALUATOR: 'shadow-evaluator', KNOWLEDGE_GAP_CLOSER: 'knowledge-gap-closer', BRAND_REGISTRY_SWEEP: 'brand-registry-sweep', + OUTBOUND_LOG_CLEANUP: 'outbound-log-cleanup', } as const; diff --git a/server/src/brand-manager.ts b/server/src/brand-manager.ts index 03dabe24bc..c3e3bf7c95 100644 --- a/server/src/brand-manager.ts +++ b/server/src/brand-manager.ts @@ -9,6 +9,7 @@ import type { ResolvedBrand, KellerType, } from './types'; +import { AAO_UA_VALIDATOR } from './config/user-agents.js'; export interface BrandValidationError { field: string; @@ -168,7 +169,7 @@ export class BrandManager { timeout: 10000, headers: { Accept: 'application/json', - 'User-Agent': 'AdCP-Brand-Validator/1.0', + 'User-Agent': AAO_UA_VALIDATOR, }, validateStatus: () => true, responseType: 'arraybuffer', diff --git a/server/src/capabilities.ts b/server/src/capabilities.ts index c698457975..876f36567d 100644 --- a/server/src/capabilities.ts +++ b/server/src/capabilities.ts @@ -2,6 +2,8 @@ import type { Agent } from "./types.js"; import { FormatsService } from "./formats.js"; import { createLogger } from "./logger.js"; import { is401Error, AuthenticationRequiredError } from "@adcp/client"; +import { AAO_UA_DISCOVERY } from "./config/user-agents.js"; +import { logOutboundRequest } from "./db/outbound-log-db.js"; const logger = createLogger('capabilities'); @@ -66,10 +68,19 @@ export class CapabilityDiscovery { return cached; } + const startTime = Date.now(); try { const protocol = agent.protocol || "mcp"; const tools = await this.discoverTools(agent.url, protocol); + logOutboundRequest({ + agent_url: agent.url, + request_type: 'discovery', + user_agent: AAO_UA_DISCOVERY, + response_time_ms: Date.now() - startTime, + success: true, + }); + const profile: AgentCapabilityProfile = { agent_url: agent.url, protocol, @@ -93,6 +104,15 @@ export class CapabilityDiscovery { this.cache.set(agent.url, profile); return profile; } catch (error: any) { + logOutboundRequest({ + agent_url: agent.url, + request_type: 'discovery', + user_agent: AAO_UA_DISCOVERY, + response_time_ms: Date.now() - startTime, + success: false, + error_message: error.message, + }); + const isOAuthError = error instanceof AuthenticationRequiredError; const errorProfile: AgentCapabilityProfile = { agent_url: agent.url, @@ -127,7 +147,7 @@ export class CapabilityDiscovery { name: "Discovery Client", agent_uri: url, protocol: "mcp", - }]); + }], { userAgent: AAO_UA_DISCOVERY }); const client = multiClient.agent("discovery"); const agentInfo = await client.getAgentInfo(); @@ -164,7 +184,7 @@ export class CapabilityDiscovery { name: "Discovery Client", agent_uri: url, protocol: "a2a", - }]); + }], { userAgent: AAO_UA_DISCOVERY }); const client = multiClient.agent("discovery"); const agentInfo = await client.getAgentInfo(); diff --git a/server/src/config/user-agents.ts b/server/src/config/user-agents.ts new file mode 100644 index 0000000000..40604bc5eb --- /dev/null +++ b/server/src/config/user-agents.ts @@ -0,0 +1,11 @@ +/** + * User-Agent strings for automated outbound requests to agent endpoints. + * Follows RFC 9110 convention with a +URL suffix pointing to documentation. + */ + +const INFO_URL = 'https://agenticadvertising.org/docs/monitoring'; + +export const AAO_UA_HEALTH_CHECK = `AAO-HealthCheck/1.0 (+${INFO_URL})`; +export const AAO_UA_DISCOVERY = `AAO-Discovery/1.0 (+${INFO_URL})`; +export const AAO_UA_COMPLIANCE = `AAO-ComplianceCheck/1.0 (+${INFO_URL})`; +export const AAO_UA_VALIDATOR = `AAO-Validator/1.0 (+${INFO_URL})`; diff --git a/server/src/crawler.ts b/server/src/crawler.ts index fb0d44778d..fba1fc7cce 100644 --- a/server/src/crawler.ts +++ b/server/src/crawler.ts @@ -7,6 +7,7 @@ import { BrandDatabase } from "./db/brand-db.js"; import { MemberDatabase } from "./db/member-db.js"; import { CapabilityDiscovery } from "./capabilities.js"; import { AAO_HOST } from "./config/aao.js"; +import { AAO_UA_DISCOVERY } from "./config/user-agents.js"; import { createLogger } from "./logger.js"; import type { CatalogEventsDatabase, WriteEventInput } from "./db/catalog-events-db.js"; import type { AgentInventoryProfilesDatabase, ProfileUpsertInput } from "./db/agent-inventory-profiles-db.js"; @@ -30,7 +31,7 @@ export class CrawlerService { private profilesDb?: AgentInventoryProfilesDatabase; constructor(options?: { eventsDb?: CatalogEventsDatabase; profilesDb?: AgentInventoryProfilesDatabase }) { - this.crawler = new PropertyCrawler({ logLevel: (process.env.LOG_LEVEL as 'debug' | 'info' | 'warn' | 'error') || 'error' }); + this.crawler = new PropertyCrawler({ logLevel: (process.env.LOG_LEVEL as 'debug' | 'info' | 'warn' | 'error') || 'error', userAgent: AAO_UA_DISCOVERY }); this.federatedIndex = new FederatedIndexService(); this.adAgentsManager = new AdAgentsManager(); this.brandManager = new BrandManager(); @@ -48,10 +49,17 @@ export class CrawlerService { } this.crawling = true; - log.info({ agentCount: agents.length }, 'Starting crawl'); + + // Filter out agents whose owners have paused monitoring + const pausedUrls = await this.getPausedAgentUrls(); + const activeAgents = agents.filter(a => !pausedUrls.has(a.url)); + if (activeAgents.length < agents.length) { + log.info({ paused: agents.length - activeAgents.length, active: activeAgents.length }, 'Skipping paused agents'); + } + log.info({ agentCount: activeAgents.length }, 'Starting crawl'); // Convert our Agent type to AgentInfo for the crawler - const agentInfos: AgentInfo[] = agents.map((agent) => ({ + const agentInfos: AgentInfo[] = activeAgents.map((agent) => ({ agent_url: agent.url, protocol: agent.protocol || "mcp", // Use agent's protocol, default to MCP publisher_domain: this.extractDomain(agent.url), @@ -143,6 +151,18 @@ export class CrawlerService { }; } + private async getPausedAgentUrls(): Promise> { + try { + const result = await query( + `SELECT agent_url FROM agent_registry_metadata WHERE monitoring_paused = TRUE`, + ); + return new Set(result.rows.map((r: { agent_url: string }) => r.agent_url)); + } catch (err) { + log.warn({ err }, 'Failed to fetch paused agents, treating all as active'); + return new Set(); + } + } + private extractDomain(url: string): string { try { const parsed = new URL(url); @@ -400,8 +420,9 @@ export class CrawlerService { ...allAgents.map(a => a.url), ]); - // Filter out agents that already have a type - const urlsToProbe = Array.from(agentUrls).filter(url => !knownTypes.has(url)); + // Filter out agents that already have a type or are paused + const pausedUrls = await this.getPausedAgentUrls(); + const urlsToProbe = Array.from(agentUrls).filter(url => !knownTypes.has(url) && !pausedUrls.has(url)); if (urlsToProbe.length === 0) { log.debug('All agents already typed, skipping probe'); diff --git a/server/src/db/compliance-db.ts b/server/src/db/compliance-db.ts index c4aa97332f..752c3428ca 100644 --- a/server/src/db/compliance-db.ts +++ b/server/src/db/compliance-db.ts @@ -19,6 +19,9 @@ export interface AgentRegistryMetadata { lifecycle_stage: LifecycleStage; platform_type: string | null; compliance_opt_out: boolean; + monitoring_paused: boolean; + check_interval_hours: number; + monitoring_paused_at: Date | null; created_at: Date; updated_at: Date; } @@ -326,6 +329,7 @@ export class ComplianceDatabase { /** * Find agents that are due for a compliance check based on their lifecycle stage. * Joins federated agents (from discovered_agents + member profiles) with metadata and status. + * Respects owner-configured check_interval_hours and monitoring_paused. */ async getAgentsDueForCheck(limit: number = 10): Promise COALESCE(m.check_interval_hours, + CASE WHEN COALESCE(m.lifecycle_stage, 'production') = 'testing' THEN 24 ELSE 12 END + )) ) ORDER BY s.last_checked_at ASC NULLS FIRST LIMIT $1`, @@ -366,6 +366,47 @@ export class ComplianceDatabase { return result.rows; } + // ----- Monitoring Settings ----- + + async getMonitoringSettings(agentUrl: string): Promise<{ + monitoring_paused: boolean; + check_interval_hours: number; + monitoring_paused_at: Date | null; + }> { + const result = await query( + `SELECT monitoring_paused, check_interval_hours, monitoring_paused_at + FROM agent_registry_metadata WHERE agent_url = $1`, + [agentUrl], + ); + if (result.rows.length === 0) { + return { monitoring_paused: false, check_interval_hours: 12, monitoring_paused_at: null }; + } + return result.rows[0]; + } + + async updateMonitoringPaused(agentUrl: string, paused: boolean): Promise { + await query( + `INSERT INTO agent_registry_metadata (agent_url, monitoring_paused, monitoring_paused_at) + VALUES ($1, $2, CASE WHEN $2 THEN NOW() ELSE NULL END) + ON CONFLICT (agent_url) DO UPDATE SET + monitoring_paused = $2, + monitoring_paused_at = CASE WHEN $2 THEN NOW() ELSE NULL END, + updated_at = NOW()`, + [agentUrl, paused], + ); + } + + async updateCheckInterval(agentUrl: string, intervalHours: number): Promise { + await query( + `INSERT INTO agent_registry_metadata (agent_url, check_interval_hours) + VALUES ($1, $2) + ON CONFLICT (agent_url) DO UPDATE SET + check_interval_hours = $2, + updated_at = NOW()`, + [agentUrl, intervalHours], + ); + } + // ----- Helpers ----- /** diff --git a/server/src/db/migrations/378_agent_monitoring_controls.sql b/server/src/db/migrations/378_agent_monitoring_controls.sql new file mode 100644 index 0000000000..07913116ce --- /dev/null +++ b/server/src/db/migrations/378_agent_monitoring_controls.sql @@ -0,0 +1,41 @@ +-- Agent monitoring controls: outbound request logging and owner-configurable check frequency. + +-- Log of automated outbound requests AAO makes to agent endpoints. +CREATE TABLE IF NOT EXISTS agent_outbound_requests ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + agent_url TEXT NOT NULL, + request_type TEXT NOT NULL, + user_agent TEXT NOT NULL, + response_time_ms INTEGER, + success BOOLEAN NOT NULL DEFAULT TRUE, + error_message TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + + CONSTRAINT valid_request_type CHECK ( + request_type IN ('health_check', 'discovery', 'compliance', 'crawl', 'validation') + ) +); + +CREATE INDEX IF NOT EXISTS idx_outbound_requests_agent_time + ON agent_outbound_requests(agent_url, created_at DESC); + +-- For retention cleanup +CREATE INDEX IF NOT EXISTS idx_outbound_requests_created + ON agent_outbound_requests(created_at); + +-- Owner-configurable monitoring controls on existing metadata table. +ALTER TABLE agent_registry_metadata + ADD COLUMN IF NOT EXISTS monitoring_paused BOOLEAN NOT NULL DEFAULT FALSE; + +ALTER TABLE agent_registry_metadata + ADD COLUMN IF NOT EXISTS check_interval_hours INTEGER NOT NULL DEFAULT 12; + +-- Runs after the column is added above; safe since migration runs exactly once. +DO $$ BEGIN + ALTER TABLE agent_registry_metadata + ADD CONSTRAINT valid_check_interval CHECK (check_interval_hours BETWEEN 6 AND 168); +EXCEPTION WHEN duplicate_object THEN NULL; +END $$; + +ALTER TABLE agent_registry_metadata + ADD COLUMN IF NOT EXISTS monitoring_paused_at TIMESTAMPTZ; diff --git a/server/src/db/outbound-log-db.ts b/server/src/db/outbound-log-db.ts new file mode 100644 index 0000000000..3a96c4ec39 --- /dev/null +++ b/server/src/db/outbound-log-db.ts @@ -0,0 +1,87 @@ +import { query } from './client.js'; +import { logger as baseLogger } from '../logger.js'; + +const logger = baseLogger.child({ module: 'outbound-log' }); + +export type OutboundRequestType = 'health_check' | 'discovery' | 'compliance' | 'crawl' | 'validation'; + +export interface OutboundRequestEntry { + agent_url: string; + request_type: OutboundRequestType; + user_agent: string; + response_time_ms?: number; + success: boolean; + error_message?: string; +} + +export interface OutboundRequestRow extends OutboundRequestEntry { + id: string; + created_at: string; +} + +/** + * Fire-and-forget insert. Failures are logged but never propagated + * so monitoring never blocks the request path. + */ +export function logOutboundRequest(entry: OutboundRequestEntry): void { + query( + `INSERT INTO agent_outbound_requests + (agent_url, request_type, user_agent, response_time_ms, success, error_message) + VALUES ($1, $2, $3, $4, $5, $6)`, + [ + entry.agent_url, + entry.request_type, + entry.user_agent, + entry.response_time_ms ?? null, + entry.success, + entry.error_message ?? null, + ], + ).catch(err => { + logger.warn({ err, agentUrl: entry.agent_url }, 'Failed to log outbound request'); + }); +} + +export async function getRequestLog( + agentUrl: string, + options: { limit?: number; since?: string } = {}, +): Promise { + const limit = Math.max(1, Math.min(options.limit ?? 50, 200)); + const params: unknown[] = [agentUrl, limit]; + let whereClause = 'WHERE agent_url = $1'; + + if (options.since) { + const sinceDate = new Date(options.since); + if (!isNaN(sinceDate.getTime())) { + whereClause += ' AND created_at >= $3'; + params.push(sinceDate.toISOString()); + } + } + + const result = await query( + `SELECT id, agent_url, request_type, user_agent, response_time_ms, + success, error_message, created_at + FROM agent_outbound_requests + ${whereClause} + ORDER BY created_at DESC + LIMIT $2`, + params, + ); + return result.rows; +} + +export async function getRequestCount(agentUrl: string): Promise { + const result = await query( + 'SELECT COUNT(*) AS count FROM agent_outbound_requests WHERE agent_url = $1', + [agentUrl], + ); + return parseInt(result.rows[0]?.count ?? '0', 10); +} + +export async function cleanupOldRequests(retentionDays: number = 30): Promise { + const days = Math.max(1, Math.floor(retentionDays)); + const result = await query( + `DELETE FROM agent_outbound_requests WHERE created_at < NOW() - make_interval(days => $1)`, + [days], + ); + return result.rowCount ?? 0; +} diff --git a/server/src/formats.ts b/server/src/formats.ts index edcaf76eef..b0cb90b1a9 100644 --- a/server/src/formats.ts +++ b/server/src/formats.ts @@ -1,5 +1,6 @@ import { AdCPClient } from "@adcp/client"; import type { Agent, FormatInfo } from "./types.js"; +import { AAO_UA_DISCOVERY } from "./config/user-agents.js"; export interface AgentFormatsProfile { agent_url: string; @@ -29,7 +30,7 @@ export class FormatsService { agent_uri: agent.url, protocol: (agent.protocol || "mcp") as "mcp" | "a2a", }; - const multiClient = new AdCPClient([agentConfig]); + const multiClient = new AdCPClient([agentConfig], { userAgent: AAO_UA_DISCOVERY }); const client = multiClient.agent(agent.name); const result = await client.executeTask("list_creative_formats", {}); diff --git a/server/src/health.ts b/server/src/health.ts index 3e9f41a320..f3da821ef2 100644 --- a/server/src/health.ts +++ b/server/src/health.ts @@ -2,6 +2,8 @@ import type { Agent, AgentHealth, AgentStats } from "./types.js"; import { Cache } from "./cache.js"; import { getPropertyIndex } from "@adcp/client"; import { FormatsService } from "./formats.js"; +import { AAO_UA_HEALTH_CHECK } from "./config/user-agents.js"; +import { logOutboundRequest } from "./db/outbound-log-db.js"; export class HealthChecker { private healthCache: Cache; @@ -28,11 +30,20 @@ export class HealthChecker { const protocol = agent.protocol || "mcp"; // Only try the protocol the agent declares - if (protocol === "a2a") { - return await this.tryA2A(agent, startTime); - } else { - return await this.tryMCP(agent, startTime); - } + const health = protocol === "a2a" + ? await this.tryA2A(agent, startTime) + : await this.tryMCP(agent, startTime); + + logOutboundRequest({ + agent_url: agent.url, + request_type: 'health_check', + user_agent: AAO_UA_HEALTH_CHECK, + response_time_ms: health.response_time_ms ?? (Date.now() - startTime), + success: health.online, + error_message: health.error, + }); + + return health; } private async tryMCP(agent: Agent, startTime: number): Promise { @@ -44,7 +55,7 @@ export class HealthChecker { name: "Health Checker", agent_uri: agent.url, protocol: "mcp", - }]); + }], { userAgent: AAO_UA_HEALTH_CHECK }); const client = multiClient.agent("health-check"); const agentInfo = await client.getAgentInfo(); @@ -71,6 +82,7 @@ export class HealthChecker { // Check for A2A agent card at /.well-known/agent.json const agentCardUrl = `${agent.url.replace(/\/$/, "")}/.well-known/agent.json`; const response = await fetch(agentCardUrl, { + headers: { 'User-Agent': AAO_UA_HEALTH_CHECK }, signal: AbortSignal.timeout(5000), }); diff --git a/server/src/properties.ts b/server/src/properties.ts index c7d8a8a655..fa7a420bb7 100644 --- a/server/src/properties.ts +++ b/server/src/properties.ts @@ -1,5 +1,6 @@ import { AdCPClient } from "@adcp/client"; import type { Agent } from "./types.js"; +import { AAO_UA_DISCOVERY } from "./config/user-agents.js"; import { AgentValidator } from "./validator.js"; export interface PropertyInfo { @@ -48,7 +49,7 @@ export class PropertiesService { agent_uri: agent.url, protocol: (agent.protocol || "mcp") as "mcp" | "a2a", }; - const multiClient = new AdCPClient([agentConfig]); + const multiClient = new AdCPClient([agentConfig], { userAgent: AAO_UA_DISCOVERY }); const client = multiClient.agent(agent.name); const result = await client.executeTask("list_authorized_properties", {}); diff --git a/server/src/routes/registry-api.ts b/server/src/routes/registry-api.ts index b2b60683e4..4179480d27 100644 --- a/server/src/routes/registry-api.ts +++ b/server/src/routes/registry-api.ts @@ -54,6 +54,7 @@ import { PropertyCheckService } from "../services/property-check.js"; import { PropertyCheckDatabase } from "../db/property-check-db.js"; import { BulkPropertyCheckService } from "../services/bulk-property-check.js"; import { ComplianceDatabase, type LifecycleStage } from "../db/compliance-db.js"; +import { getRequestLog, getRequestCount } from "../db/outbound-log-db.js"; const logger = createLogger("registry-api"); const propertyCheckService = new PropertyCheckService(); @@ -2222,6 +2223,8 @@ export function createRegistryApiRouter(config: RegistryApiConfig): Router { streak_days: cs.streak_days, last_checked_at: cs.last_checked_at?.toISOString() || null, headline: cs.headline, + monitoring_paused: meta?.monitoring_paused ?? false, + check_interval_hours: meta?.check_interval_hours ?? 12, }; } } @@ -2427,7 +2430,114 @@ export function createRegistryApiRouter(config: RegistryApiConfig): Router { } }); + // ── Agent Monitoring Controls ────────────────────────────────── + router.get("/registry/agents/:encodedUrl/monitoring/settings", ...complianceWriteMiddleware, async (req, res) => { + try { + const agentUrl = decodeURIComponent(req.params.encodedUrl); + if (!validateAgentUrlParam(agentUrl)) { + return res.status(400).json({ error: "Invalid agent URL" }); + } + if (!req.user) { + return res.status(401).json({ error: "Authentication required" }); + } + const isOwner = await verifyAgentOwnership(req.user.id, agentUrl); + if (!isOwner) { + return res.status(403).json({ error: "You do not have permission to view this agent" }); + } + + const settings = await complianceDb.getMonitoringSettings(agentUrl); + res.json(settings); + } catch (error) { + logger.error({ err: error, path: req.path }, "Failed to get monitoring settings"); + res.status(500).json({ error: "Failed to get monitoring settings" }); + } + }); + + router.put("/registry/agents/:encodedUrl/monitoring/pause", ...complianceWriteMiddleware, async (req, res) => { + try { + const agentUrl = decodeURIComponent(req.params.encodedUrl); + if (!validateAgentUrlParam(agentUrl)) { + return res.status(400).json({ error: "Invalid agent URL" }); + } + if (!req.user) { + return res.status(401).json({ error: "Authentication required" }); + } + const isOwner = await verifyAgentOwnership(req.user.id, agentUrl); + if (!isOwner) { + return res.status(403).json({ error: "You do not have permission to modify this agent" }); + } + + const { paused } = req.body; + if (typeof paused !== "boolean") { + return res.status(400).json({ error: "paused must be a boolean" }); + } + + await complianceDb.updateMonitoringPaused(agentUrl, paused); + const settings = await complianceDb.getMonitoringSettings(agentUrl); + res.json(settings); + } catch (error) { + logger.error({ err: error, path: req.path }, "Failed to update monitoring pause"); + res.status(500).json({ error: "Failed to update monitoring pause" }); + } + }); + + router.put("/registry/agents/:encodedUrl/monitoring/interval", ...complianceWriteMiddleware, async (req, res) => { + try { + const agentUrl = decodeURIComponent(req.params.encodedUrl); + if (!validateAgentUrlParam(agentUrl)) { + return res.status(400).json({ error: "Invalid agent URL" }); + } + if (!req.user) { + return res.status(401).json({ error: "Authentication required" }); + } + const isOwner = await verifyAgentOwnership(req.user.id, agentUrl); + if (!isOwner) { + return res.status(403).json({ error: "You do not have permission to modify this agent" }); + } + + const { interval_hours } = req.body; + if (typeof interval_hours !== "number" || !Number.isInteger(interval_hours) || interval_hours < 6 || interval_hours > 168) { + return res.status(400).json({ error: "interval_hours must be an integer between 6 and 168" }); + } + + await complianceDb.updateCheckInterval(agentUrl, interval_hours); + const settings = await complianceDb.getMonitoringSettings(agentUrl); + res.json(settings); + } catch (error) { + logger.error({ err: error, path: req.path }, "Failed to update check interval"); + res.status(500).json({ error: "Failed to update check interval" }); + } + }); + + router.get("/registry/agents/:encodedUrl/monitoring/requests", ...complianceWriteMiddleware, async (req, res) => { + try { + const agentUrl = decodeURIComponent(req.params.encodedUrl); + if (!validateAgentUrlParam(agentUrl)) { + return res.status(400).json({ error: "Invalid agent URL" }); + } + if (!req.user) { + return res.status(401).json({ error: "Authentication required" }); + } + const isOwner = await verifyAgentOwnership(req.user.id, agentUrl); + if (!isOwner) { + return res.status(403).json({ error: "You do not have permission to view this agent" }); + } + + const limit = Math.min(parseInt(req.query.limit as string) || 50, 200); + const since = typeof req.query.since === "string" ? req.query.since : undefined; + + const [requests, total] = await Promise.all([ + getRequestLog(agentUrl, { limit, since }), + getRequestCount(agentUrl), + ]); + + res.json({ agent_url: agentUrl, requests, count: requests.length, total }); + } catch (error) { + logger.error({ err: error, path: req.path }, "Failed to get monitoring requests"); + res.status(500).json({ error: "Failed to get monitoring requests" }); + } + }); // ── Storyboards ──────────────────────────────────────────────── diff --git a/server/src/types.ts b/server/src/types.ts index 723a93833c..d6569433d7 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -962,6 +962,8 @@ export interface AgentCompliance { streak_days: number; last_checked_at: string | null; headline: string | null; + monitoring_paused: boolean; + check_interval_hours: number; } // Federated Discovery Types diff --git a/server/src/validator.ts b/server/src/validator.ts index 4469c22ee1..1911ac50ac 100644 --- a/server/src/validator.ts +++ b/server/src/validator.ts @@ -12,6 +12,7 @@ import type { PublisherPropertySelector, } from "./types.js"; import { Cache } from "./cache.js"; +import { AAO_UA_VALIDATOR } from "./config/user-agents.js"; interface FetchResult { data?: AdAgentsJson; @@ -164,7 +165,7 @@ export class AgentValidator { // lgtm[js/request-forgery] -- fetch target is restricted to HTTPS and public internet hosts only. const response = await fetch(url, { - headers: { "User-Agent": "AdCP-Registry/1.0" }, + headers: { "User-Agent": AAO_UA_VALIDATOR }, signal: AbortSignal.timeout(5000), });