@vellumai/assistant 0.8.10 → 0.8.11-staging.1

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.
Files changed (400) hide show
  1. package/bun.lock +62 -1
  2. package/docs/workspace-tools.md +196 -0
  3. package/examples/plugins/echo/README.md +3 -3
  4. package/knip.json +1 -0
  5. package/openapi.yaml +460 -128
  6. package/package.json +2 -1
  7. package/scripts/build-plugin-api.ts +299 -0
  8. package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
  9. package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
  10. package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
  11. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
  12. package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
  13. package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
  14. package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
  15. package/src/__tests__/agent-loop-thinking.test.ts +4 -0
  16. package/src/__tests__/agent-loop.test.ts +578 -5
  17. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
  20. package/src/__tests__/btw-routes.test.ts +0 -1
  21. package/src/__tests__/build-persisted-content.test.ts +75 -1
  22. package/src/__tests__/catalog-install-normalize.test.ts +141 -0
  23. package/src/__tests__/ces-startup-timeout.test.ts +60 -0
  24. package/src/__tests__/compaction-events.test.ts +1 -0
  25. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  26. package/src/__tests__/context-overflow-reducer.test.ts +264 -124
  27. package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
  28. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  29. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
  30. package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
  31. package/src/__tests__/conversation-agent-loop.test.ts +131 -551
  32. package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
  33. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  34. package/src/__tests__/conversation-fork-crud.test.ts +259 -0
  35. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  36. package/src/__tests__/conversation-lifecycle.test.ts +257 -1
  37. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  38. package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
  39. package/src/__tests__/conversation-queue.test.ts +1 -39
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
  41. package/src/__tests__/conversation-skill-tools.test.ts +491 -5
  42. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  43. package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
  44. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  45. package/src/__tests__/conversation-store.test.ts +74 -0
  46. package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
  47. package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
  48. package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
  49. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
  52. package/src/__tests__/corrected-target.test.ts +93 -0
  53. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
  54. package/src/__tests__/credential-execution-tools.test.ts +23 -11
  55. package/src/__tests__/credential-security-invariants.test.ts +6 -1
  56. package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
  57. package/src/__tests__/device-id.test.ts +70 -1
  58. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  59. package/src/__tests__/empty-response-hook.test.ts +242 -66
  60. package/src/__tests__/external-plugin-loader.test.ts +0 -31
  61. package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
  62. package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
  63. package/src/__tests__/history-repair-hook.test.ts +228 -3
  64. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  65. package/src/__tests__/host-browser-proxy.test.ts +254 -9
  66. package/src/__tests__/identity-routes.test.ts +1 -0
  67. package/src/__tests__/image-recovery-hook.test.ts +387 -0
  68. package/src/__tests__/injector-chain.test.ts +5 -4
  69. package/src/__tests__/injector-v3-suppression.test.ts +373 -47
  70. package/src/__tests__/intent-routing.test.ts +7 -0
  71. package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
  72. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  73. package/src/__tests__/oauth-store.test.ts +0 -85
  74. package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
  75. package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
  76. package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
  77. package/src/__tests__/persist-unsendable-image.test.ts +4 -4
  78. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  79. package/src/__tests__/plugin-bootstrap.test.ts +82 -73
  80. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  81. package/src/__tests__/plugin-types.test.ts +0 -8
  82. package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
  83. package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
  84. package/src/__tests__/registry.test.ts +240 -1
  85. package/src/__tests__/require-fresh-approval.test.ts +3 -0
  86. package/src/__tests__/schedule-routes.test.ts +116 -1
  87. package/src/__tests__/schedule-store.test.ts +28 -0
  88. package/src/__tests__/schedule-tools.test.ts +94 -1
  89. package/src/__tests__/server-history-render.test.ts +39 -0
  90. package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
  91. package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
  92. package/src/__tests__/skills.test.ts +202 -0
  93. package/src/__tests__/slim-skill-category.test.ts +195 -0
  94. package/src/__tests__/strip-memory-injections.test.ts +33 -39
  95. package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
  96. package/src/__tests__/title-generate-hook.test.ts +9 -7
  97. package/src/__tests__/tool-audit-listener.test.ts +264 -1
  98. package/src/__tests__/tool-error-hook.test.ts +4 -3
  99. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
  100. package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
  101. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
  102. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  103. package/src/__tests__/tools-get-route.test.ts +202 -0
  104. package/src/__tests__/workspace-tool-loader.test.ts +319 -0
  105. package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
  106. package/src/agent/loop.ts +569 -319
  107. package/src/api/events/tool-result.ts +9 -0
  108. package/src/api/events/tool-use-start.ts +7 -0
  109. package/src/api/index.ts +10 -0
  110. package/src/api/responses/conversation-message.ts +135 -27
  111. package/src/api/responses/memory-v3-selection-log.ts +4 -4
  112. package/src/approvals/guardian-request-resolvers.ts +26 -0
  113. package/src/browser-session/backends/host-bridge.ts +29 -0
  114. package/src/browser-session/index.ts +1 -0
  115. package/src/browser-session/types.ts +5 -1
  116. package/src/cli/commands/__tests__/schedules.test.ts +62 -4
  117. package/src/cli/commands/__tests__/skills.test.ts +53 -0
  118. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  119. package/src/cli/commands/inference-providers.ts +0 -8
  120. package/src/cli/commands/plugins.ts +2 -2
  121. package/src/cli/commands/schedules.ts +27 -4
  122. package/src/cli/commands/skills.ts +187 -146
  123. package/src/cli/commands/tools.ts +106 -0
  124. package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
  125. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
  126. package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
  127. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
  128. package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
  129. package/src/cli/lib/install-from-github.ts +187 -117
  130. package/src/cli/lib/plugin-catalog-cache.ts +9 -9
  131. package/src/cli/lib/plugin-details.ts +38 -68
  132. package/src/cli/lib/plugin-marketplace.ts +42 -14
  133. package/src/cli/lib/search-plugins.ts +29 -129
  134. package/src/cli/program.ts +2 -0
  135. package/src/config/bundled-skills/acp/SKILL.md +1 -0
  136. package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
  137. package/src/config/bundled-skills/app-control/SKILL.md +1 -0
  138. package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
  139. package/src/config/bundled-skills/contacts/SKILL.md +1 -0
  140. package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
  141. package/src/config/bundled-skills/followups/SKILL.md +1 -0
  142. package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
  143. package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
  144. package/src/config/bundled-skills/messaging/SKILL.md +1 -0
  145. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
  146. package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
  147. package/src/config/bundled-skills/schedule/SKILL.md +1 -0
  148. package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
  149. package/src/config/bundled-skills/sequences/SKILL.md +1 -0
  150. package/src/config/bundled-skills/settings/SKILL.md +1 -0
  151. package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
  152. package/src/config/bundled-skills/subagent/SKILL.md +1 -0
  153. package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
  154. package/src/config/env-registry.ts +23 -0
  155. package/src/config/feature-flag-registry.json +17 -81
  156. package/src/config/loader.ts +5 -22
  157. package/src/config/schema.ts +2 -0
  158. package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
  159. package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
  160. package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
  161. package/src/config/schemas/compaction-logs.ts +79 -0
  162. package/src/config/schemas/memory-v2.ts +0 -8
  163. package/src/config/schemas/memory-v3.ts +104 -33
  164. package/src/config/seed-inference-profiles.ts +1 -1
  165. package/src/config/skills.ts +117 -47
  166. package/src/context/compactor.ts +11 -0
  167. package/src/context/strip-injections.ts +38 -4
  168. package/src/credential-execution/feature-gates.ts +0 -21
  169. package/src/credential-execution/startup-timeout.ts +32 -4
  170. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
  171. package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
  172. package/src/daemon/conversation-agent-loop.ts +123 -658
  173. package/src/daemon/conversation-error.ts +6 -33
  174. package/src/daemon/conversation-lifecycle.ts +1 -1
  175. package/src/daemon/conversation-runtime-assembly.ts +183 -22
  176. package/src/daemon/conversation-skill-tools.ts +137 -8
  177. package/src/daemon/conversation-slash.ts +0 -14
  178. package/src/daemon/conversation-store.ts +2 -19
  179. package/src/daemon/conversation-tool-setup.ts +87 -1
  180. package/src/daemon/conversation.ts +119 -50
  181. package/src/daemon/external-plugins-bootstrap.ts +36 -95
  182. package/src/daemon/handlers/config-channels.ts +11 -2
  183. package/src/daemon/handlers/shared.ts +9 -1
  184. package/src/daemon/handlers/skills.ts +10 -4
  185. package/src/daemon/host-app-control-proxy.ts +72 -57
  186. package/src/daemon/host-browser-proxy.ts +117 -22
  187. package/src/daemon/lifecycle.ts +34 -5
  188. package/src/daemon/message-protocol.ts +0 -7
  189. package/src/daemon/message-types/schedules.ts +1 -0
  190. package/src/daemon/message-types/skills.ts +17 -0
  191. package/src/daemon/providers-setup.ts +3 -0
  192. package/src/daemon/server.ts +3 -3
  193. package/src/daemon/tool-setup-types.ts +9 -3
  194. package/src/daemon/trust-context.ts +23 -0
  195. package/src/daemon/workspace-tools-watcher.ts +324 -0
  196. package/src/events/tool-audit-listener.ts +78 -16
  197. package/src/events/tool-metrics-listener.ts +2 -5
  198. package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
  199. package/src/memory/__tests__/conversation-queries.test.ts +176 -0
  200. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
  201. package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
  202. package/src/memory/conversation-crud.ts +134 -3
  203. package/src/memory/conversation-queries.ts +64 -7
  204. package/src/memory/db-init.ts +14 -0
  205. package/src/memory/embedding-backend.test.ts +130 -1
  206. package/src/memory/embedding-backend.ts +79 -106
  207. package/src/memory/embedding-gemini.ts +5 -0
  208. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
  209. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
  210. package/src/memory/graph/conversation-graph-memory.ts +36 -25
  211. package/src/memory/graph/tool-handlers.ts +3 -0
  212. package/src/memory/job-handlers/cleanup.ts +3 -1
  213. package/src/memory/jobs-store.ts +0 -28
  214. package/src/memory/jobs-worker.ts +10 -22
  215. package/src/memory/memory-marker.ts +29 -0
  216. package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
  217. package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
  218. package/src/memory/migrations/270-schedule-description.ts +36 -0
  219. package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
  220. package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
  221. package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
  222. package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
  223. package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
  224. package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
  225. package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
  226. package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
  227. package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
  228. package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
  229. package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
  230. package/src/memory/migrations/index.ts +10 -0
  231. package/src/memory/migrations/registry.ts +8 -0
  232. package/src/memory/schema/conversations.ts +16 -0
  233. package/src/memory/schema/infrastructure.ts +26 -0
  234. package/src/memory/skill-loaded-events-store.test.ts +160 -0
  235. package/src/memory/skill-loaded-events-store.ts +95 -0
  236. package/src/memory/tool-executed-events-store.test.ts +219 -0
  237. package/src/memory/tool-executed-events-store.ts +102 -0
  238. package/src/memory/tool-usage-store.ts +15 -3
  239. package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
  240. package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
  241. package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
  242. package/src/memory/v2/__tests__/page-store.test.ts +33 -0
  243. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
  244. package/src/memory/v2/activation-store.ts +50 -1
  245. package/src/memory/v2/consolidation-job.ts +113 -29
  246. package/src/memory/v2/injected-block-slugs.ts +79 -0
  247. package/src/memory/v2/injection.ts +6 -1
  248. package/src/memory/v2/prompts/consolidation.ts +414 -13
  249. package/src/memory/v2/router.ts +2 -28
  250. package/src/memory/v2/static-context.ts +1 -1
  251. package/src/memory/v2/types.ts +16 -0
  252. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  253. package/src/notifications/access-request-copy.ts +298 -0
  254. package/src/notifications/adapters/slack.ts +3 -3
  255. package/src/notifications/adapters/telegram.ts +2 -1
  256. package/src/notifications/copy-composer.ts +49 -267
  257. package/src/notifications/decision-engine.ts +16 -35
  258. package/src/notifications/home-feed-side-effect.ts +1 -6
  259. package/src/oauth/oauth-store.ts +0 -9
  260. package/src/permissions/checker.test.ts +83 -1
  261. package/src/permissions/checker.ts +25 -2
  262. package/src/permissions/gateway-threshold-reader.test.ts +182 -0
  263. package/src/permissions/gateway-threshold-reader.ts +80 -0
  264. package/src/platform/client.ts +1 -3
  265. package/src/platform/feature-gate.ts +3 -12
  266. package/src/plugin-api/constants.ts +4 -2
  267. package/src/plugin-api/index.ts +63 -11
  268. package/src/plugin-api/types.ts +236 -71
  269. package/src/plugins/defaults/compaction/compact.ts +66 -2
  270. package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
  271. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  272. package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
  273. package/src/plugins/defaults/compaction/window-manager.ts +303 -1
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
  275. package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
  276. package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
  277. package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
  278. package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
  279. package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
  280. package/src/plugins/defaults/history-repair/terminal.ts +39 -2
  281. package/src/plugins/defaults/image-recovery/detect.ts +25 -0
  282. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
  283. package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
  284. package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
  285. package/src/plugins/defaults/image-recovery/package.json +14 -0
  286. package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
  287. package/src/plugins/defaults/index.ts +71 -5
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
  289. package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
  291. package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
  297. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
  304. package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
  305. package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
  306. package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
  307. package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
  308. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
  309. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
  310. package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
  311. package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
  312. package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
  313. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
  314. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
  315. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
  316. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
  317. package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
  318. package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
  319. package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
  320. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
  321. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
  322. package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
  323. package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
  324. package/src/plugins/pipeline.ts +8 -5
  325. package/src/plugins/types.ts +5 -51
  326. package/src/providers/cache-control.ts +26 -0
  327. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
  328. package/src/providers/model-catalog.ts +13 -1
  329. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
  330. package/src/providers/openai/chat-completions-provider.ts +46 -0
  331. package/src/providers/openai/responses-provider.ts +45 -0
  332. package/src/providers/registry.ts +0 -8
  333. package/src/runtime/__tests__/agent-wake.test.ts +0 -1
  334. package/src/runtime/agent-wake.ts +13 -0
  335. package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
  336. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
  337. package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
  338. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
  339. package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
  340. package/src/runtime/routes/acp-routes.test.ts +106 -0
  341. package/src/runtime/routes/acp-routes.ts +248 -2
  342. package/src/runtime/routes/browser-tabs-routes.ts +1 -1
  343. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  344. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
  345. package/src/runtime/routes/consolidation-routes.ts +6 -82
  346. package/src/runtime/routes/conversation-list-routes.ts +6 -0
  347. package/src/runtime/routes/conversation-management-routes.ts +71 -0
  348. package/src/runtime/routes/identity-routes.ts +8 -0
  349. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  350. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
  351. package/src/runtime/routes/plugins-routes.ts +34 -45
  352. package/src/runtime/routes/schedule-routes.ts +43 -5
  353. package/src/runtime/routes/settings-routes.ts +140 -15
  354. package/src/runtime/routes/skills-routes.ts +18 -6
  355. package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
  356. package/src/runtime/services/conversation-serializer.ts +38 -1
  357. package/src/runtime/verification-outbound-actions.ts +147 -2
  358. package/src/runtime/verification-templates.ts +29 -3
  359. package/src/schedule/schedule-store.ts +19 -0
  360. package/src/skills/catalog-install.ts +77 -13
  361. package/src/tasks/task-scheduler.ts +1 -0
  362. package/src/telemetry/types.ts +66 -1
  363. package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
  364. package/src/telemetry/usage-telemetry-reporter.ts +213 -20
  365. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
  366. package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
  367. package/src/tools/browser/browser-execution.ts +27 -9
  368. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
  369. package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
  370. package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
  371. package/src/tools/browser/cdp-client/factory.ts +217 -17
  372. package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
  373. package/src/tools/browser/cdp-client/types.ts +22 -2
  374. package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
  375. package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
  376. package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
  377. package/src/tools/executor.ts +39 -7
  378. package/src/tools/registry.ts +387 -5
  379. package/src/tools/schedule/create.ts +16 -0
  380. package/src/tools/schedule/list.ts +12 -4
  381. package/src/tools/schedule/update.ts +12 -0
  382. package/src/tools/skills/load.ts +11 -6
  383. package/src/tools/terminal/safe-env.ts +2 -0
  384. package/src/tools/types.ts +65 -9
  385. package/src/tools/workspace-tools/loader.ts +673 -0
  386. package/src/usage/attribution.ts +28 -0
  387. package/src/util/device-id.ts +17 -3
  388. package/src/util/platform.ts +16 -0
  389. package/tsconfig.plugin-api.json +13 -0
  390. package/src/__tests__/plugin-external-api.test.ts +0 -68
  391. package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
  392. package/src/daemon/message-types/browser.ts +0 -10
  393. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  394. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
  395. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
  396. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
  397. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
  398. package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
  399. package/src/plugins/external-api.ts +0 -114
  400. package/src/plugins/plugin-skill-contributions.ts +0 -292
@@ -0,0 +1,324 @@
1
+ /**
2
+ * Filesystem watcher for `<workspaceDir>/tools/`.
3
+ *
4
+ * Watches the workspace-tools directory non-recursively using fs.watch.
5
+ * On any add/change/delete event, debounces per filename stem (= tool
6
+ * name) and reconciles registry state against on-disk state: registers
7
+ * newly added tools, re-imports changed tools (cache-busting via the
8
+ * loader's per-import URL query string), unregisters deleted tools,
9
+ * strips core tools when a `.removed` sentinel appears, restores them
10
+ * when the sentinel disappears.
11
+ *
12
+ * No assistant restart is required — the file watcher closes the
13
+ * "edit a file, see the change" loop the same way the apps watcher and
14
+ * plugin source watcher do for their respective directories.
15
+ *
16
+ * ## Why per-stem reconciliation
17
+ *
18
+ * The watcher receives `(eventType, filename)` from fs.watch but the
19
+ * eventType ("rename" vs "change") is unreliable across editors and
20
+ * platforms — vim atomic-save shows as a rename of the original file
21
+ * plus an add of a new file, VS Code shows as a change in place, etc.
22
+ * Rather than route on eventType, we debounce per stem and re-derive
23
+ * the world: "given what's on disk right now for `<stem>.*`, what
24
+ * registry state should the assistant be in?"
25
+ *
26
+ * This is the same eventual-consistency pattern as
27
+ * `plugin-source-watcher.ts` — the watcher exists to KICK the
28
+ * reconciler, not to be the source of truth about what changed.
29
+ *
30
+ * ## Lifecycle position
31
+ *
32
+ * Started after the initial `loadWorkspaceTools()` scan completes
33
+ * during daemon startup, gated on the `workspace-tools-watcher` feature
34
+ * flag — when the flag is off the initial scan still runs but no watch
35
+ * loop is mounted, so workspace tools load from disk once and live edits
36
+ * need a restart. Stopped on assistant shutdown alongside the
37
+ * other long-lived watchers. Stoppage during shutdown does not
38
+ * unregister tools — those go away with the process; the watcher's
39
+ * only job is to keep the registry fresh while the assistant is up.
40
+ */
41
+
42
+ import { existsSync, type FSWatcher, watch } from "node:fs";
43
+
44
+ import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
45
+ import { getConfig } from "../config/loader.js";
46
+ import {
47
+ getCoreToolOverride,
48
+ getTool,
49
+ getToolOwner,
50
+ removeCoreToolViaWorkspace,
51
+ restoreStrippedCoreTool,
52
+ unregisterWorkspaceTool,
53
+ } from "../tools/registry.js";
54
+ import {
55
+ classifyWorkspaceToolEntry,
56
+ findWinningWorkspaceToolPath,
57
+ loadSingleWorkspaceTool,
58
+ } from "../tools/workspace-tools/loader.js";
59
+ import { DebouncerMap } from "../util/debounce.js";
60
+ import { getLogger } from "../util/logger.js";
61
+ import { getWorkspaceToolsDir } from "../util/platform.js";
62
+
63
+ const log = getLogger("workspace-tools-watcher");
64
+
65
+ /**
66
+ * Gates the dynamic hot-reload path. When disabled, workspace tools still
67
+ * load from disk once at daemon startup via {@link loadWorkspaceTools}; only
68
+ * the live watch → re-registration loop is suppressed. Read at its point of
69
+ * use in {@link WorkspaceToolsWatcher.start} so a daemon restart picks up a
70
+ * changed value.
71
+ */
72
+ const WORKSPACE_TOOLS_WATCHER_FLAG = "workspace-tools-watcher" as const;
73
+
74
+ /**
75
+ * Wait this long after the last fs event for a given stem before
76
+ * reconciling. Editor saves often emit a burst of 2–4 events; the
77
+ * debounce collapses them into a single load.
78
+ */
79
+ const RECONCILE_DEBOUNCE_MS = 250;
80
+
81
+ export class WorkspaceToolsWatcher {
82
+ /**
83
+ * Process-wide singleton. Callers reach the watcher via
84
+ * {@link WorkspaceToolsWatcher.getInstance} rather than instantiating
85
+ * directly so the daemon `start()`/`stop()` lifecycle and any future
86
+ * "trigger a manual reconcile" code paths share one watcher across the
87
+ * assistant process lifetime.
88
+ */
89
+ private static singleton: WorkspaceToolsWatcher | null = null;
90
+
91
+ static getInstance(): WorkspaceToolsWatcher {
92
+ WorkspaceToolsWatcher.singleton ??= new WorkspaceToolsWatcher();
93
+ return WorkspaceToolsWatcher.singleton;
94
+ }
95
+
96
+ /** Test-only — drops the singleton so the next `getInstance()` rebuilds. */
97
+ static resetForTests(): void {
98
+ WorkspaceToolsWatcher.singleton?.stop();
99
+ WorkspaceToolsWatcher.singleton = null;
100
+ }
101
+
102
+ /** Test-only — whether a live `fs.watch` loop is currently mounted. */
103
+ isWatchingForTests(): boolean {
104
+ return this.watcher !== null;
105
+ }
106
+
107
+ private watcher: FSWatcher | null = null;
108
+ private debouncer = new DebouncerMap({
109
+ defaultDelayMs: RECONCILE_DEBOUNCE_MS,
110
+ maxEntries: 100,
111
+ });
112
+ /**
113
+ * Promise queue per stem — guarantees that two events for the same
114
+ * stem can't run concurrently and corrupt the unregister/load
115
+ * sequence. The queue is single-deep (the chained promise simply
116
+ * awaits the in-flight one before running its own work), so the
117
+ * debouncer's collapsing already does most of the deduplication; this
118
+ * queue exists for the case where a second event lands during the
119
+ * in-flight reconcile's `await loadSingleWorkspaceTool`.
120
+ */
121
+ private inflight = new Map<string, Promise<void>>();
122
+
123
+ start(): void {
124
+ if (this.watcher) return;
125
+ if (
126
+ !isAssistantFeatureFlagEnabled(WORKSPACE_TOOLS_WATCHER_FLAG, getConfig())
127
+ ) {
128
+ log.debug(
129
+ "Workspace tools watcher disabled by feature flag; workspace tools load from disk at startup only (restart required to pick up edits)",
130
+ );
131
+ return;
132
+ }
133
+ const toolsDir = getWorkspaceToolsDir();
134
+ if (!existsSync(toolsDir)) {
135
+ log.debug(
136
+ { toolsDir },
137
+ "Workspace tools directory does not exist; watcher not started (will not auto-start on directory creation — restart required)",
138
+ );
139
+ return;
140
+ }
141
+ try {
142
+ this.watcher = watch(
143
+ toolsDir,
144
+ { recursive: false },
145
+ (_eventType, filename) => {
146
+ if (!filename) return;
147
+ const classified = classifyWorkspaceToolEntry(filename);
148
+ if (!classified) {
149
+ // Not a workspace-tool file — ignore (README.md, .DS_Store, etc.)
150
+ return;
151
+ }
152
+ this.debouncer.schedule(`stem:${classified.stem}`, () => {
153
+ this.scheduleReconcile(classified.stem);
154
+ });
155
+ },
156
+ );
157
+ log.info({ toolsDir }, "Workspace tools watcher started");
158
+ } catch (err) {
159
+ log.warn(
160
+ { err, toolsDir },
161
+ "Failed to start workspace tools watcher — workspace tools will only register at startup",
162
+ );
163
+ }
164
+ }
165
+
166
+ stop(): void {
167
+ this.debouncer.cancelAll();
168
+ if (this.watcher) {
169
+ this.watcher.close();
170
+ this.watcher = null;
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Chain `reconcileStem(stem)` after any in-flight reconcile for the
176
+ * same stem so we never run two `loadSingleWorkspaceTool` calls
177
+ * concurrently for the same name.
178
+ */
179
+ private scheduleReconcile(stem: string): void {
180
+ const prev = this.inflight.get(stem) ?? Promise.resolve();
181
+ const next = prev
182
+ .catch(() => {
183
+ /* swallow — error already logged in the prior tick */
184
+ })
185
+ .then(() => this.reconcileStem(stem))
186
+ .catch((err) => {
187
+ const message = err instanceof Error ? err.message : String(err);
188
+ log.error(
189
+ { err, stem },
190
+ `workspace-tools-watcher: reconcile for "${stem}" threw: ${message}`,
191
+ );
192
+ })
193
+ .finally(() => {
194
+ // Only clear if we're still the current in-flight promise.
195
+ if (this.inflight.get(stem) === next) {
196
+ this.inflight.delete(stem);
197
+ }
198
+ });
199
+ this.inflight.set(stem, next);
200
+ }
201
+
202
+ /**
203
+ * Reconcile a single stem (= tool name) with on-disk state.
204
+ *
205
+ * Possible (live, removed) tuples and their resolution:
206
+ *
207
+ * - `(present, absent)` → ensure workspace tool is registered using
208
+ * the winning live file; re-import if a previous registration
209
+ * pointed at a different path
210
+ * - `(absent, present)` → ensure core tool is stripped (and any
211
+ * previous workspace registration torn down first)
212
+ * - `(absent, absent)` → ensure neither stripped state nor live
213
+ * registration remains
214
+ * - `(present, present)` → ambiguous; tear down both so neither
215
+ * state survives (matches the initial-scan contract)
216
+ */
217
+ private async reconcileStem(stem: string): Promise<void> {
218
+ const toolsDir = getWorkspaceToolsDir();
219
+ if (!existsSync(toolsDir)) {
220
+ this.teardownStem(stem);
221
+ return;
222
+ }
223
+
224
+ const { livePath, hasRemovedSentinel } = findWinningWorkspaceToolPath(
225
+ toolsDir,
226
+ stem,
227
+ );
228
+
229
+ // Ambiguous: tear down everything for this stem.
230
+ if (hasRemovedSentinel && livePath !== null) {
231
+ log.error(
232
+ { stem, toolsDir, livePath },
233
+ `workspace-tools-watcher: "${stem}" has both a live file and a .removed sentinel — tearing down both`,
234
+ );
235
+ this.teardownStem(stem);
236
+ return;
237
+ }
238
+
239
+ // Neither: tear down anything we own.
240
+ if (!hasRemovedSentinel && livePath === null) {
241
+ this.teardownStem(stem);
242
+ return;
243
+ }
244
+
245
+ if (hasRemovedSentinel) {
246
+ // Strip the core tool. Unregister any prior workspace registration
247
+ // first so removeCoreToolViaWorkspace doesn't throw on the
248
+ // workspace-owned-name check.
249
+ if (getToolOwner(stem)?.kind === "workspace") {
250
+ unregisterWorkspaceTool(stem);
251
+ }
252
+ try {
253
+ removeCoreToolViaWorkspace(stem);
254
+ } catch (err) {
255
+ const message = err instanceof Error ? err.message : String(err);
256
+ log.error(
257
+ { err, stem },
258
+ `workspace-tools-watcher: failed to strip "${stem}": ${message}`,
259
+ );
260
+ }
261
+ return;
262
+ }
263
+
264
+ if (livePath !== null) {
265
+ // If we previously stripped this core tool, restore it before the
266
+ // workspace registration runs so the override path sees the
267
+ // expected baseline (core tool present → stash + replace).
268
+ if (getCoreToolOverride(stem) && !getTool(stem)) {
269
+ restoreStrippedCoreTool(stem);
270
+ }
271
+
272
+ // If a workspace tool is already registered under this name,
273
+ // unregister it so the loader can re-import cleanly. This covers
274
+ // file-change events (same path, new contents) as well as
275
+ // extension-precedence flips (e.g. user added foo.js next to
276
+ // foo.ts — now .js wins and the registration must update).
277
+ if (getToolOwner(stem)?.kind === "workspace") {
278
+ unregisterWorkspaceTool(stem);
279
+ }
280
+
281
+ const registered = await loadSingleWorkspaceTool(livePath);
282
+ if (registered) {
283
+ log.info(
284
+ { stem, livePath },
285
+ `Workspace tool "${stem}" registered via watcher`,
286
+ );
287
+ }
288
+ // loadSingleWorkspaceTool already logs failures with attribution.
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Tear down any workspace-tool state we own for `stem`: unregister a
294
+ * live workspace tool, restore a core tool we stripped. Both no-ops
295
+ * when there's nothing to undo.
296
+ */
297
+ private teardownStem(stem: string): void {
298
+ if (getToolOwner(stem)?.kind === "workspace") {
299
+ unregisterWorkspaceTool(stem);
300
+ log.info(
301
+ { stem },
302
+ `Workspace tool "${stem}" unregistered via watcher (file removed)`,
303
+ );
304
+ }
305
+ if (getCoreToolOverride(stem) && !getTool(stem)) {
306
+ restoreStrippedCoreTool(stem);
307
+ }
308
+ }
309
+
310
+ // ── Test affordances ────────────────────────────────────────────────
311
+ //
312
+ // These are intentionally narrow — exposed for the watcher tests so
313
+ // they can drive reconcile() deterministically without waiting on the
314
+ // debouncer, and inspect whether an in-flight promise is settled.
315
+ // Not part of the public lifecycle API.
316
+
317
+ /**
318
+ * Run a reconcile for `stem` synchronously (well, asynchronously, but
319
+ * without going through the debouncer). For tests.
320
+ */
321
+ async _testReconcile(stem: string): Promise<void> {
322
+ await this.reconcileStem(stem);
323
+ }
324
+ }
@@ -1,12 +1,18 @@
1
+ import { getConfig } from "../config/loader.js";
1
2
  import {
2
3
  recordToolInvocation,
3
4
  type ToolInvocationRecord,
4
5
  } from "../memory/tool-usage-store.js";
5
6
  import { redactSecrets } from "../security/secret-scanner.js";
6
- import type {
7
- ToolLifecycleEvent,
8
- ToolLifecycleEventHandler,
7
+ import {
8
+ stringifyToolInput,
9
+ type ToolLifecycleEvent,
10
+ type ToolLifecycleEventHandler,
9
11
  } from "../tools/types.js";
12
+ import {
13
+ toAttributionColumns,
14
+ type UsageAttributionColumns,
15
+ } from "../usage/attribution.js";
10
16
  import { getLogger } from "../util/logger.js";
11
17
 
12
18
  const RESULT_PREVIEW_LIMIT = 1000;
@@ -36,33 +42,57 @@ function toInvocationRecord(
36
42
  event: ToolLifecycleEvent,
37
43
  ): ToolInvocationRecord | null {
38
44
  switch (event.type) {
39
- case "executed":
45
+ case "executed": {
46
+ const input = stringifyToolInput(event.input);
40
47
  return {
41
48
  conversationId: event.conversationId,
42
49
  toolName: event.toolName,
43
- input: stringifyInput(event.input),
44
- result: redactSecrets(event.result.content).slice(0, RESULT_PREVIEW_LIMIT),
50
+ input,
51
+ result: redactSecrets(event.result.content).slice(
52
+ 0,
53
+ RESULT_PREVIEW_LIMIT,
54
+ ),
45
55
  decision: event.decision,
46
56
  riskLevel: event.riskLevel,
47
57
  matchedTrustRuleId: event.matchedTrustRuleId,
48
58
  durationMs: event.durationMs,
59
+ // Prefer the executor-stamped raw pre-sanitization size: by the
60
+ // time listeners run, sensitive-output extraction has already
61
+ // rewritten `result.content`, so sizing it here would undercount
62
+ // for sensitive-output tools. The fallback covers emitters that
63
+ // don't stamp.
64
+ ...telemetryColumns(
65
+ event,
66
+ input,
67
+ event.resultBytes ?? Buffer.byteLength(event.result.content, "utf8"),
68
+ ),
49
69
  };
50
- case "error":
70
+ }
71
+ case "error": {
72
+ const input = stringifyToolInput(event.input);
73
+ const result = `error: ${event.errorMessage}`;
51
74
  return {
52
75
  conversationId: event.conversationId,
53
76
  toolName: event.toolName,
54
- input: stringifyInput(event.input),
55
- result: `error: ${event.errorMessage}`,
77
+ input,
78
+ result,
56
79
  decision: "error",
57
80
  riskLevel: event.riskLevel,
58
81
  matchedTrustRuleId: event.matchedTrustRuleId,
59
82
  durationMs: event.durationMs,
83
+ // The error result string is built right here and never goes
84
+ // through sensitive-output sanitization, so sizing it directly is
85
+ // already raw — no executor stamp exists or is needed.
86
+ ...telemetryColumns(event, input, Buffer.byteLength(result, "utf8")),
60
87
  };
88
+ }
61
89
  case "permission_denied":
90
+ // No telemetry fields: the tool never executed, and denied rows are
91
+ // filtered out of the tool_executed projection anyway.
62
92
  return {
63
93
  conversationId: event.conversationId,
64
94
  toolName: event.toolName,
65
- input: stringifyInput(event.input),
95
+ input: stringifyToolInput(event.input),
66
96
  result: formatDeniedResult(event.reason),
67
97
  decision: "denied",
68
98
  riskLevel: event.riskLevel,
@@ -75,12 +105,44 @@ function toInvocationRecord(
75
105
  }
76
106
  }
77
107
 
78
- function stringifyInput(input: Record<string, unknown>): string {
79
- try {
80
- return JSON.stringify(input);
81
- } catch {
82
- return "[unserializable-input]";
83
- }
108
+ type TelemetryColumns = Pick<ToolInvocationRecord, "argBytes" | "resultBytes"> &
109
+ UsageAttributionColumns;
110
+
111
+ const NULL_TELEMETRY_COLUMNS: TelemetryColumns = {
112
+ argBytes: null,
113
+ resultBytes: null,
114
+ provider: null,
115
+ model: null,
116
+ inferenceProfile: null,
117
+ inferenceProfileSource: null,
118
+ };
119
+
120
+ /**
121
+ * Telemetry-only columns (payload sizes + model attribution) for an
122
+ * executed/error audit row. This is the single write-time privacy gate for
123
+ * `tool_executed` telemetry: when usage data collection is disabled, the
124
+ * columns persist as NULL, which the projection's `arg_bytes IS NOT NULL`
125
+ * filter excludes permanently — the same mechanism that excludes legacy
126
+ * pre-migration rows (see tool-executed-events-store.ts). That makes
127
+ * opted-out rows unreportable by construction: no later opt-in, crash, or
128
+ * watermark race can ship them. The audit fields themselves (tool name,
129
+ * decision, input/result previews, duration) are unaffected —
130
+ * `tool_invocations` is a local always-on audit log.
131
+ *
132
+ * When opted in, the non-null sizing keeps `argBytes` populated, which the
133
+ * projection's legacy-row filter relies on.
134
+ */
135
+ function telemetryColumns(
136
+ event: Extract<ToolLifecycleEvent, { type: "executed" | "error" }>,
137
+ input: string,
138
+ resultBytes: number,
139
+ ): TelemetryColumns {
140
+ if (!getConfig().collectUsageData) return NULL_TELEMETRY_COLUMNS;
141
+ return {
142
+ argBytes: event.inputBytes ?? Buffer.byteLength(input, "utf8"),
143
+ resultBytes,
144
+ ...toAttributionColumns(event.attribution),
145
+ };
84
146
  }
85
147
 
86
148
  function formatDeniedResult(reason: string): string {
@@ -1,3 +1,4 @@
1
+ import { stringifyToolInput } from "../tools/types.js";
1
2
  import { getLogger, truncateForLog } from "../util/logger.js";
2
3
  import type { EventBus, Subscription } from "./bus.js";
3
4
  import type { AssistantDomainEvents } from "./domain-events.js";
@@ -129,9 +130,5 @@ function formatInputForLog(
129
130
  input: Record<string, unknown>,
130
131
  truncate: (value: string, maxLen: number) => string,
131
132
  ): string {
132
- try {
133
- return truncate(JSON.stringify(input), INPUT_PREVIEW_LIMIT);
134
- } catch {
135
- return "[unserializable-input]";
136
- }
133
+ return truncate(stringifyToolInput(input), INPUT_PREVIEW_LIMIT);
137
134
  }