@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.
- package/bun.lock +62 -1
- package/docs/workspace-tools.md +196 -0
- package/examples/plugins/echo/README.md +3 -3
- package/knip.json +1 -0
- package/openapi.yaml +460 -128
- package/package.json +2 -1
- package/scripts/build-plugin-api.ts +299 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
- package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
- package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
- package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
- package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
- package/src/__tests__/agent-loop-thinking.test.ts +4 -0
- package/src/__tests__/agent-loop.test.ts +578 -5
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/catalog-install-normalize.test.ts +141 -0
- package/src/__tests__/ces-startup-timeout.test.ts +60 -0
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +264 -124
- package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
- package/src/__tests__/conversation-agent-loop.test.ts +131 -551
- package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-fork-crud.test.ts +259 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-lifecycle.test.ts +257 -1
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
- package/src/__tests__/conversation-queue.test.ts +1 -39
- package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
- package/src/__tests__/conversation-skill-tools.test.ts +491 -5
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-store.test.ts +74 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
- package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
- package/src/__tests__/credential-execution-tools.test.ts +23 -11
- package/src/__tests__/credential-security-invariants.test.ts +6 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
- package/src/__tests__/device-id.test.ts +70 -1
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +242 -66
- package/src/__tests__/external-plugin-loader.test.ts +0 -31
- package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
- package/src/__tests__/history-repair-hook.test.ts +228 -3
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-browser-proxy.test.ts +254 -9
- package/src/__tests__/identity-routes.test.ts +1 -0
- package/src/__tests__/image-recovery-hook.test.ts +387 -0
- package/src/__tests__/injector-chain.test.ts +5 -4
- package/src/__tests__/injector-v3-suppression.test.ts +373 -47
- package/src/__tests__/intent-routing.test.ts +7 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/oauth-store.test.ts +0 -85
- package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
- package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
- package/src/__tests__/persist-unsendable-image.test.ts +4 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-bootstrap.test.ts +82 -73
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -8
- package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
- package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
- package/src/__tests__/registry.test.ts +240 -1
- package/src/__tests__/require-fresh-approval.test.ts +3 -0
- package/src/__tests__/schedule-routes.test.ts +116 -1
- package/src/__tests__/schedule-store.test.ts +28 -0
- package/src/__tests__/schedule-tools.test.ts +94 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
- package/src/__tests__/skills.test.ts +202 -0
- package/src/__tests__/slim-skill-category.test.ts +195 -0
- package/src/__tests__/strip-memory-injections.test.ts +33 -39
- package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
- package/src/__tests__/title-generate-hook.test.ts +9 -7
- package/src/__tests__/tool-audit-listener.test.ts +264 -1
- package/src/__tests__/tool-error-hook.test.ts +4 -3
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/tools-get-route.test.ts +202 -0
- package/src/__tests__/workspace-tool-loader.test.ts +319 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +569 -319
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/index.ts +10 -0
- package/src/api/responses/conversation-message.ts +135 -27
- package/src/api/responses/memory-v3-selection-log.ts +4 -4
- package/src/approvals/guardian-request-resolvers.ts +26 -0
- package/src/browser-session/backends/host-bridge.ts +29 -0
- package/src/browser-session/index.ts +1 -0
- package/src/browser-session/types.ts +5 -1
- package/src/cli/commands/__tests__/schedules.test.ts +62 -4
- package/src/cli/commands/__tests__/skills.test.ts +53 -0
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/inference-providers.ts +0 -8
- package/src/cli/commands/plugins.ts +2 -2
- package/src/cli/commands/schedules.ts +27 -4
- package/src/cli/commands/skills.ts +187 -146
- package/src/cli/commands/tools.ts +106 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
- package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
- package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
- package/src/cli/lib/install-from-github.ts +187 -117
- package/src/cli/lib/plugin-catalog-cache.ts +9 -9
- package/src/cli/lib/plugin-details.ts +38 -68
- package/src/cli/lib/plugin-marketplace.ts +42 -14
- package/src/cli/lib/search-plugins.ts +29 -129
- package/src/cli/program.ts +2 -0
- package/src/config/bundled-skills/acp/SKILL.md +1 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
- package/src/config/bundled-skills/app-control/SKILL.md +1 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
- package/src/config/bundled-skills/contacts/SKILL.md +1 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
- package/src/config/bundled-skills/followups/SKILL.md +1 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +1 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
- package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
- package/src/config/bundled-skills/sequences/SKILL.md +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +1 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
- package/src/config/bundled-skills/subagent/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
- package/src/config/env-registry.ts +23 -0
- package/src/config/feature-flag-registry.json +17 -81
- package/src/config/loader.ts +5 -22
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
- package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
- package/src/config/schemas/compaction-logs.ts +79 -0
- package/src/config/schemas/memory-v2.ts +0 -8
- package/src/config/schemas/memory-v3.ts +104 -33
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/config/skills.ts +117 -47
- package/src/context/compactor.ts +11 -0
- package/src/context/strip-injections.ts +38 -4
- package/src/credential-execution/feature-gates.ts +0 -21
- package/src/credential-execution/startup-timeout.ts +32 -4
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
- package/src/daemon/conversation-agent-loop.ts +123 -658
- package/src/daemon/conversation-error.ts +6 -33
- package/src/daemon/conversation-lifecycle.ts +1 -1
- package/src/daemon/conversation-runtime-assembly.ts +183 -22
- package/src/daemon/conversation-skill-tools.ts +137 -8
- package/src/daemon/conversation-slash.ts +0 -14
- package/src/daemon/conversation-store.ts +2 -19
- package/src/daemon/conversation-tool-setup.ts +87 -1
- package/src/daemon/conversation.ts +119 -50
- package/src/daemon/external-plugins-bootstrap.ts +36 -95
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +10 -4
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-browser-proxy.ts +117 -22
- package/src/daemon/lifecycle.ts +34 -5
- package/src/daemon/message-protocol.ts +0 -7
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/skills.ts +17 -0
- package/src/daemon/providers-setup.ts +3 -0
- package/src/daemon/server.ts +3 -3
- package/src/daemon/tool-setup-types.ts +9 -3
- package/src/daemon/trust-context.ts +23 -0
- package/src/daemon/workspace-tools-watcher.ts +324 -0
- package/src/events/tool-audit-listener.ts +78 -16
- package/src/events/tool-metrics-listener.ts +2 -5
- package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
- package/src/memory/__tests__/conversation-queries.test.ts +176 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
- package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
- package/src/memory/conversation-crud.ts +134 -3
- package/src/memory/conversation-queries.ts +64 -7
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.test.ts +130 -1
- package/src/memory/embedding-backend.ts +79 -106
- package/src/memory/embedding-gemini.ts +5 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
- package/src/memory/graph/conversation-graph-memory.ts +36 -25
- package/src/memory/graph/tool-handlers.ts +3 -0
- package/src/memory/job-handlers/cleanup.ts +3 -1
- package/src/memory/jobs-store.ts +0 -28
- package/src/memory/jobs-worker.ts +10 -22
- package/src/memory/memory-marker.ts +29 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
- package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
- package/src/memory/migrations/270-schedule-description.ts +36 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
- package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
- package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
- package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
- package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
- package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +16 -0
- package/src/memory/schema/infrastructure.ts +26 -0
- package/src/memory/skill-loaded-events-store.test.ts +160 -0
- package/src/memory/skill-loaded-events-store.ts +95 -0
- package/src/memory/tool-executed-events-store.test.ts +219 -0
- package/src/memory/tool-executed-events-store.ts +102 -0
- package/src/memory/tool-usage-store.ts +15 -3
- package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
- package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
- package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
- package/src/memory/v2/__tests__/page-store.test.ts +33 -0
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
- package/src/memory/v2/activation-store.ts +50 -1
- package/src/memory/v2/consolidation-job.ts +113 -29
- package/src/memory/v2/injected-block-slugs.ts +79 -0
- package/src/memory/v2/injection.ts +6 -1
- package/src/memory/v2/prompts/consolidation.ts +414 -13
- package/src/memory/v2/router.ts +2 -28
- package/src/memory/v2/static-context.ts +1 -1
- package/src/memory/v2/types.ts +16 -0
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +2 -1
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/oauth/oauth-store.ts +0 -9
- package/src/permissions/checker.test.ts +83 -1
- package/src/permissions/checker.ts +25 -2
- package/src/permissions/gateway-threshold-reader.test.ts +182 -0
- package/src/permissions/gateway-threshold-reader.ts +80 -0
- package/src/platform/client.ts +1 -3
- package/src/platform/feature-gate.ts +3 -12
- package/src/plugin-api/constants.ts +4 -2
- package/src/plugin-api/index.ts +63 -11
- package/src/plugin-api/types.ts +236 -71
- package/src/plugins/defaults/compaction/compact.ts +66 -2
- package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +303 -1
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
- package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
- package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
- package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
- package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
- package/src/plugins/defaults/history-repair/terminal.ts +39 -2
- package/src/plugins/defaults/image-recovery/detect.ts +25 -0
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
- package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
- package/src/plugins/defaults/image-recovery/package.json +14 -0
- package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
- package/src/plugins/defaults/index.ts +71 -5
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
- package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
- package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
- package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
- package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
- package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
- package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
- package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
- package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
- package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
- package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
- package/src/plugins/pipeline.ts +8 -5
- package/src/plugins/types.ts +5 -51
- package/src/providers/cache-control.ts +26 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
- package/src/providers/model-catalog.ts +13 -1
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
- package/src/providers/openai/chat-completions-provider.ts +46 -0
- package/src/providers/openai/responses-provider.ts +45 -0
- package/src/providers/registry.ts +0 -8
- package/src/runtime/__tests__/agent-wake.test.ts +0 -1
- package/src/runtime/agent-wake.ts +13 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
- package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
- package/src/runtime/routes/acp-routes.test.ts +106 -0
- package/src/runtime/routes/acp-routes.ts +248 -2
- package/src/runtime/routes/browser-tabs-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
- package/src/runtime/routes/consolidation-routes.ts +6 -82
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +71 -0
- package/src/runtime/routes/identity-routes.ts +8 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
- package/src/runtime/routes/plugins-routes.ts +34 -45
- package/src/runtime/routes/schedule-routes.ts +43 -5
- package/src/runtime/routes/settings-routes.ts +140 -15
- package/src/runtime/routes/skills-routes.ts +18 -6
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
- package/src/runtime/services/conversation-serializer.ts +38 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/schedule/schedule-store.ts +19 -0
- package/src/skills/catalog-install.ts +77 -13
- package/src/tasks/task-scheduler.ts +1 -0
- package/src/telemetry/types.ts +66 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
- package/src/telemetry/usage-telemetry-reporter.ts +213 -20
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
- package/src/tools/browser/browser-execution.ts +27 -9
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
- package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
- package/src/tools/browser/cdp-client/factory.ts +217 -17
- package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
- package/src/tools/browser/cdp-client/types.ts +22 -2
- package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
- package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
- package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
- package/src/tools/executor.ts +39 -7
- package/src/tools/registry.ts +387 -5
- package/src/tools/schedule/create.ts +16 -0
- package/src/tools/schedule/list.ts +12 -4
- package/src/tools/schedule/update.ts +12 -0
- package/src/tools/skills/load.ts +11 -6
- package/src/tools/terminal/safe-env.ts +2 -0
- package/src/tools/types.ts +65 -9
- package/src/tools/workspace-tools/loader.ts +673 -0
- package/src/usage/attribution.ts +28 -0
- package/src/util/device-id.ts +17 -3
- package/src/util/platform.ts +16 -0
- package/tsconfig.plugin-api.json +13 -0
- package/src/__tests__/plugin-external-api.test.ts +0 -68
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
- package/src/daemon/message-types/browser.ts +0 -10
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
- package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
- package/src/plugins/external-api.ts +0 -114
- 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
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
44
|
-
result: redactSecrets(event.result.content).slice(
|
|
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
|
-
|
|
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
|
|
55
|
-
result
|
|
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:
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
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
|
}
|