@vellumai/assistant 0.8.11 → 0.8.12-staging.2
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/ARCHITECTURE.md +15 -17
- package/README.md +0 -6
- package/bun.lock +6 -122
- package/node_modules/@vellumai/gateway-client/bun.lock +1 -0
- package/node_modules/@vellumai/gateway-client/package.json +3 -1
- package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +1 -1
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +87 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +3 -5
- package/openapi.yaml +633 -4
- package/package.json +1 -3
- package/src/__tests__/adaptive-thinking-repair.test.ts +185 -0
- package/src/__tests__/agent-loop-compaction-events.test.ts +7 -6
- package/src/__tests__/anthropic-provider.test.ts +129 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +4 -1
- package/src/__tests__/btw-routes.test.ts +7 -34
- package/src/__tests__/checker.test.ts +6 -12
- package/src/__tests__/config-loader-backfill.test.ts +4 -2
- package/src/__tests__/config-loader-quarantine-notice.test.ts +167 -0
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-error.test.ts +2 -6
- package/src/__tests__/conversation-history-web-search.test.ts +8 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -1
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/disk-pressure-tools.test.ts +1 -1
- package/src/__tests__/exploration-drift-hook.test.ts +692 -0
- package/src/__tests__/filing-service.test.ts +8 -3
- package/src/__tests__/guardian-action-store.test.ts +0 -167
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -1
- package/src/__tests__/heartbeat-service.test.ts +5 -2
- package/src/__tests__/identity-intro-cache.test.ts +12 -5
- package/src/__tests__/identity-routes.test.ts +16 -57
- package/src/__tests__/injector-chain.test.ts +8 -3
- package/src/__tests__/injector-config-quarantine-notice.test.ts +115 -0
- package/src/__tests__/llm-catalog-parity.test.ts +16 -0
- package/src/__tests__/llm-usage-store.test.ts +11 -0
- package/src/__tests__/log-export-workspace.test.ts +468 -3
- package/src/__tests__/memory-v2-static-injector.test.ts +22 -0
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +19 -8
- package/src/__tests__/openai-provider.test.ts +34 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +0 -1
- package/src/__tests__/recurrence-engine.test.ts +45 -0
- package/src/__tests__/schedule-routes.test.ts +34 -0
- package/src/__tests__/scheduler-disk-pressure.test.ts +1 -1
- package/src/__tests__/script-proxy-conversation-manager.test.ts +10 -5
- package/src/__tests__/secret-fixtures.ts +20 -0
- package/src/__tests__/skill-tool-factory.test.ts +49 -0
- package/src/__tests__/subagent-role-registry.test.ts +24 -1
- package/src/__tests__/subagent-tools.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +109 -11
- package/src/__tests__/tool-approval-handler.test.ts +85 -0
- package/src/__tests__/tool-audit-listener.test.ts +86 -0
- package/src/__tests__/tool-error-hook.test.ts +1 -0
- package/src/__tests__/tool-result-spool.test.ts +337 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
- package/src/__tests__/validate-input.test.ts +95 -1
- package/src/__tests__/workspace-migration-098-remove-stale-updates-bulletin-file.test.ts +65 -0
- package/src/__tests__/workspace-migration-099-disable-cache-one-shot-callsites.test.ts +139 -0
- package/src/__tests__/workspace-migration-100-upgrade-quality-profile-to-fable-5.test.ts +174 -0
- package/src/__tests__/workspace-migration-101-upgrade-balanced-economy-to-minimax-m3.test.ts +162 -0
- package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +45 -95
- package/src/acp/__tests__/agent-process.test.ts +315 -2
- package/src/acp/__tests__/prepare-agent-env.test.ts +79 -5
- package/src/acp/agent-process.ts +163 -34
- package/src/acp/prepare-agent-env.ts +55 -15
- package/src/agent/loop.ts +81 -24
- package/src/api/events/usage-progress.ts +28 -0
- package/src/api/index.ts +6 -0
- package/src/background-wake/wake-intent-hooks.test.ts +2 -0
- package/src/bundler/app-bundler.ts +25 -42
- package/src/bundler/app-compiler.ts +8 -0
- package/src/calls/call-controller.ts +1 -1
- package/src/cli/commands/plugins.ts +248 -15
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +318 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +16 -9
- package/src/cli/lib/__tests__/plugin-artifact.test.ts +183 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +158 -0
- package/src/cli/lib/__tests__/plugin-fingerprint.test.ts +245 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +307 -0
- package/src/cli/lib/inspect-plugin.ts +252 -0
- package/src/cli/lib/install-from-github.ts +214 -21
- package/src/cli/lib/list-installed-plugins.ts +17 -6
- package/src/cli/lib/plugin-artifact.ts +103 -0
- package/src/cli/lib/plugin-details.ts +18 -1
- package/src/cli/lib/plugin-fingerprint.ts +197 -0
- package/src/cli/lib/upgrade-plugin.ts +225 -0
- package/src/config/bundled-skills/subagent/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +8 -2
- package/src/config/call-site-defaults.ts +13 -2
- package/src/config/feature-flag-registry.json +8 -16
- package/src/config/loader.ts +52 -59
- package/src/config/schema.ts +0 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +10 -0
- package/src/config/schemas/llm.ts +10 -0
- package/src/config/schemas/memory-v2.ts +13 -0
- package/src/config/schemas/memory-v3.ts +92 -0
- package/src/config/seed-inference-profiles.ts +4 -8
- package/src/context/post-turn-tool-result-truncation.ts +32 -18
- package/src/context/tool-result-spool.ts +104 -0
- package/src/credential-execution/feature-gates.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +41 -16
- package/src/daemon/conversation-error.ts +6 -15
- package/src/daemon/conversation.ts +9 -0
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/lifecycle.ts +1 -20
- package/src/daemon/message-types/conversations.ts +2 -15
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +40 -9
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -1
- package/src/home/__tests__/home-content-refresh.test.ts +114 -0
- package/src/home/__tests__/suggested-prompts.test.ts +86 -5
- package/src/home/home-content-refresh.ts +43 -31
- package/src/home/home-greeting-cache.ts +8 -1
- package/src/home/home-greeting.ts +13 -9
- package/src/home/suggested-prompts.ts +77 -24
- package/src/ipc/routes/trust-rules.test.ts +66 -72
- package/src/media/image-credentials.ts +2 -2
- package/src/memory/__tests__/compaction-log-store-clickhouse.test.ts +432 -0
- package/src/memory/{compaction-log-writer-clickhouse.ts → compaction-log-store-clickhouse.ts} +264 -55
- package/src/memory/conversation-attention-store.ts +1 -0
- package/src/memory/conversation-bootstrap.ts +18 -9
- package/src/memory/conversation-crud.ts +12 -2
- package/src/memory/conversation-title-service.ts +53 -9
- package/src/memory/delivery-channels.ts +0 -69
- package/src/memory/graph/extraction-job.ts +0 -15
- package/src/memory/guardian-action-store.ts +1 -376
- package/src/memory/llm-usage-store.ts +5 -1
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +2 -2
- package/src/memory/v2/__tests__/consolidation-job.test.ts +183 -2
- package/src/memory/v2/__tests__/injection.test.ts +70 -0
- package/src/memory/v2/__tests__/static-context.test.ts +12 -0
- package/src/memory/v2/consolidation-job.ts +93 -9
- package/src/memory/v2/injection.ts +53 -0
- package/src/memory/v2/prompts/consolidation.ts +1 -0
- package/src/memory/v2/static-context.ts +13 -1
- package/src/memory/v2/sweep-job.ts +1 -1
- package/src/memory/v2/types.ts +5 -0
- package/src/plugin-api/types.ts +7 -0
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +300 -0
- package/src/plugins/defaults/exploration-drift/package.json +15 -0
- package/src/plugins/defaults/index.ts +25 -0
- package/src/plugins/defaults/memory-retrieval/injectors.ts +132 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/card.test.ts +92 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +2 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fresh-set.test.ts +52 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +2 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +136 -5
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +17 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +6 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +5 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +68 -4
- package/src/plugins/defaults/memory-v3-shadow/card.ts +49 -5
- package/src/plugins/defaults/memory-v3-shadow/fresh-set.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +4 -2
- package/src/plugins/defaults/memory-v3-shadow/learned-edges.test.ts +169 -0
- package/src/plugins/defaults/memory-v3-shadow/learned-edges.ts +178 -0
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +115 -26
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +13 -9
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +144 -22
- package/src/plugins/defaults/memory-v3-shadow/types.ts +24 -6
- package/src/plugins/defaults/title-generate/hooks/stop.ts +13 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +16 -0
- package/src/prompts/cache-boundary.ts +17 -0
- package/src/prompts/sections.ts +50 -17
- package/src/prompts/system-prompt.ts +12 -4
- package/src/prompts/templates/system-sections.ts +22 -0
- package/src/providers/__tests__/unparseable-tool-args.test.ts +53 -0
- package/src/providers/anthropic/client.ts +74 -28
- package/src/providers/gemini/client.ts +5 -1
- package/src/providers/minimax/client.ts +9 -0
- package/src/providers/model-catalog.ts +28 -0
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/chat-completions-provider.ts +4 -2
- package/src/providers/openai/responses-provider.ts +7 -2
- package/src/providers/retry.ts +8 -0
- package/src/providers/types.ts +11 -0
- package/src/providers/unparseable-tool-args.ts +56 -0
- package/src/runtime/AGENTS.md +6 -0
- package/src/runtime/__tests__/agent-wake.test.ts +2 -2
- package/src/runtime/agent-wake.ts +5 -5
- package/src/runtime/background-job-runner.ts +2 -2
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +150 -3
- package/src/runtime/migrations/vbundle-import-analyzer.ts +29 -6
- package/src/runtime/migrations/vbundle-import-policy.ts +23 -0
- package/src/runtime/migrations/vbundle-importer.ts +9 -4
- package/src/runtime/migrations/vbundle-streaming-importer.ts +8 -3
- package/src/runtime/pre-first-message-gate.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +241 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +132 -0
- package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +97 -185
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +348 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +3 -3
- package/src/runtime/routes/btw-routes.ts +0 -14
- package/src/runtime/routes/conversation-compaction-routes.ts +86 -19
- package/src/runtime/routes/conversation-list-routes.ts +77 -5
- package/src/runtime/routes/conversation-management-routes.ts +54 -0
- package/src/runtime/routes/conversation-query-routes.ts +79 -4
- package/src/runtime/routes/gateway-log-routes.ts +14 -64
- package/src/runtime/routes/home-feed-routes.ts +10 -0
- package/src/runtime/routes/identity-intro-cache.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +76 -20
- package/src/runtime/routes/inbound-message-handler.ts +0 -36
- package/src/runtime/routes/log-export-routes.ts +143 -96
- package/src/runtime/routes/plugins-routes.ts +380 -0
- package/src/runtime/routes/redact-staged-export.ts +259 -0
- package/src/runtime/routes/schedule-routes.ts +19 -2
- package/src/runtime/routes/trust-rules-routes.ts +14 -67
- package/src/schedule/recurrence-engine.ts +34 -0
- package/src/schedule/scheduler.ts +1 -0
- package/src/security/redact-json.ts +61 -0
- package/src/skills/validate-input.ts +41 -1
- package/src/subagent/types.ts +26 -1
- package/src/telemetry/types.ts +15 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +6 -1
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/apps/executors.ts +1 -1
- package/src/tools/skills/skill-tool-factory.ts +19 -8
- package/src/tools/tool-approval-handler.ts +31 -0
- package/src/usage/types.ts +8 -1
- package/src/util/platform.ts +16 -0
- package/src/watcher/engine.ts +1 -0
- package/src/workspace/adaptive-thinking-repair.ts +113 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +70 -67
- package/src/workspace/migrations/098-remove-stale-updates-bulletin-file.ts +31 -0
- package/src/workspace/migrations/099-disable-cache-one-shot-callsites.ts +81 -0
- package/src/workspace/migrations/100-upgrade-quality-profile-to-fable-5.ts +86 -0
- package/src/workspace/migrations/101-upgrade-balanced-economy-to-minimax-m3.ts +70 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +0 -202
- package/src/__tests__/conversation-starters-cadence.test.ts +0 -161
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -322
- package/src/__tests__/guardian-action-followup-store.test.ts +0 -373
- package/src/__tests__/guardian-action-late-reply.test.ts +0 -1083
- package/src/__tests__/update-bulletin-job.test.ts +0 -292
- package/src/config/schemas/updates.ts +0 -14
- package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +0 -227
- package/src/memory/conversation-starters-cadence.ts +0 -78
- package/src/prompts/update-bulletin-job.ts +0 -180
- package/src/runtime/guardian-action-followup-executor.ts +0 -306
|
@@ -34,6 +34,12 @@
|
|
|
34
34
|
|
|
35
35
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
36
36
|
|
|
37
|
+
import {
|
|
38
|
+
type InspectPluginDeps,
|
|
39
|
+
type InspectPluginOptions,
|
|
40
|
+
type PluginInspection,
|
|
41
|
+
PluginInspectNotFoundError,
|
|
42
|
+
} from "../../../cli/lib/inspect-plugin.js";
|
|
37
43
|
import {
|
|
38
44
|
type InstallPluginOptions,
|
|
39
45
|
type InstallPluginResult,
|
|
@@ -59,6 +65,12 @@ import {
|
|
|
59
65
|
type UninstallPluginOptions,
|
|
60
66
|
type UninstallPluginResult,
|
|
61
67
|
} from "../../../cli/lib/uninstall-plugin.js";
|
|
68
|
+
import {
|
|
69
|
+
PluginNotUpgradableError,
|
|
70
|
+
type PluginUpgradeResult,
|
|
71
|
+
type UpgradePluginDeps,
|
|
72
|
+
type UpgradePluginOptions,
|
|
73
|
+
} from "../../../cli/lib/upgrade-plugin.js";
|
|
62
74
|
|
|
63
75
|
// Mutable list returned by the mocked library function. Tests reassign
|
|
64
76
|
// `installedFixture` before invoking the handler.
|
|
@@ -133,6 +145,38 @@ mock.module("../../../cli/lib/plugin-details.js", () => ({
|
|
|
133
145
|
getPluginDetails: detailsSpy,
|
|
134
146
|
}));
|
|
135
147
|
|
|
148
|
+
// Mock inspectPlugin: the lib computes the local-vs-remote drift (covered by
|
|
149
|
+
// inspect-plugin.test.ts); the route only forwards the name and maps errors.
|
|
150
|
+
const inspectSpy = mock(
|
|
151
|
+
async (
|
|
152
|
+
_opts: InspectPluginOptions,
|
|
153
|
+
_deps: InspectPluginDeps,
|
|
154
|
+
): Promise<PluginInspection> => {
|
|
155
|
+
throw new Error("inspectSpy default impl not configured");
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
mock.module("../../../cli/lib/inspect-plugin.js", () => ({
|
|
160
|
+
PluginInspectNotFoundError,
|
|
161
|
+
inspectPlugin: inspectSpy,
|
|
162
|
+
}));
|
|
163
|
+
|
|
164
|
+
// Mock upgradePlugin: the lib performs the re-pin (covered by
|
|
165
|
+
// upgrade-plugin.test.ts); the route projects its result and maps errors.
|
|
166
|
+
const upgradeSpy = mock(
|
|
167
|
+
async (
|
|
168
|
+
_opts: UpgradePluginOptions,
|
|
169
|
+
_deps: UpgradePluginDeps,
|
|
170
|
+
): Promise<PluginUpgradeResult> => {
|
|
171
|
+
throw new Error("upgradeSpy default impl not configured");
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
mock.module("../../../cli/lib/upgrade-plugin.js", () => ({
|
|
176
|
+
PluginNotUpgradableError,
|
|
177
|
+
upgradePlugin: upgradeSpy,
|
|
178
|
+
}));
|
|
179
|
+
|
|
136
180
|
import {
|
|
137
181
|
BadRequestError,
|
|
138
182
|
ConflictError,
|
|
@@ -154,6 +198,8 @@ const searchHandler = findHandler("plugins_search");
|
|
|
154
198
|
const uninstallHandler = findHandler("plugins_uninstall");
|
|
155
199
|
const getHandler = findHandler("plugins_get");
|
|
156
200
|
const installHandler = findHandler("plugins_install");
|
|
201
|
+
const inspectHandler = findHandler("plugins_inspect");
|
|
202
|
+
const upgradeHandler = findHandler("plugins_upgrade");
|
|
157
203
|
|
|
158
204
|
function invoke(args: RouteHandlerArgs = {}): {
|
|
159
205
|
plugins: Array<Record<string, unknown>>;
|
|
@@ -643,6 +689,7 @@ function pluginDetails(overrides: Partial<PluginDetails> = {}): PluginDetails {
|
|
|
643
689
|
},
|
|
644
690
|
readme: overrides.readme ?? null,
|
|
645
691
|
ref: overrides.ref ?? "main",
|
|
692
|
+
artifact: overrides.artifact ?? null,
|
|
646
693
|
};
|
|
647
694
|
}
|
|
648
695
|
|
|
@@ -880,3 +927,304 @@ describe("POST /v1/plugins/install", () => {
|
|
|
880
927
|
expect((caught as Error).message).toContain("ECONNRESET");
|
|
881
928
|
});
|
|
882
929
|
});
|
|
930
|
+
|
|
931
|
+
function inspection(
|
|
932
|
+
overrides: Partial<PluginInspection> = {},
|
|
933
|
+
): PluginInspection {
|
|
934
|
+
return {
|
|
935
|
+
name: overrides.name ?? "level-up",
|
|
936
|
+
installed: overrides.installed ?? true,
|
|
937
|
+
status: overrides.status ?? "update-available",
|
|
938
|
+
local:
|
|
939
|
+
overrides.local === undefined
|
|
940
|
+
? {
|
|
941
|
+
target: "/workspace/.vellum/plugins/level-up",
|
|
942
|
+
commit: "60a392b0000000000000000000000000000000aa",
|
|
943
|
+
version: "0.1.0",
|
|
944
|
+
description: "Surfaces a Level Up diff card.",
|
|
945
|
+
installedAt: "2026-06-08T00:00:00.000Z",
|
|
946
|
+
source: {
|
|
947
|
+
kind: "github",
|
|
948
|
+
owner: "vellum-ai",
|
|
949
|
+
repo: "level-up",
|
|
950
|
+
ref: "60a392b0000000000000000000000000000000aa",
|
|
951
|
+
},
|
|
952
|
+
localChanges: {
|
|
953
|
+
modified: [],
|
|
954
|
+
added: [],
|
|
955
|
+
removed: [],
|
|
956
|
+
clean: true,
|
|
957
|
+
},
|
|
958
|
+
issues: [],
|
|
959
|
+
}
|
|
960
|
+
: overrides.local,
|
|
961
|
+
remote:
|
|
962
|
+
overrides.remote === undefined
|
|
963
|
+
? {
|
|
964
|
+
repo: "vellum-ai/level-up",
|
|
965
|
+
path: "",
|
|
966
|
+
commit: "3eae1820000000000000000000000000000000bb",
|
|
967
|
+
description: "Surfaces a Level Up diff card.",
|
|
968
|
+
homepage: "https://github.com/vellum-ai/level-up",
|
|
969
|
+
license: "MIT",
|
|
970
|
+
category: null,
|
|
971
|
+
marketplaceRef: "main",
|
|
972
|
+
}
|
|
973
|
+
: overrides.remote,
|
|
974
|
+
remoteError: overrides.remoteError ?? null,
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
async function invokeInspect(
|
|
979
|
+
args: RouteHandlerArgs = {},
|
|
980
|
+
): Promise<PluginInspection> {
|
|
981
|
+
return (await inspectHandler(args)) as PluginInspection;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
describe("GET /v1/plugins/:name/inspect", () => {
|
|
985
|
+
beforeEach(() => {
|
|
986
|
+
inspectSpy.mockReset();
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
test("forwards the name to inspectPlugin and returns the drift verbatim", async () => {
|
|
990
|
+
// GIVEN inspectPlugin reports an available update for an installed plugin
|
|
991
|
+
const view = inspection({ status: "update-available" });
|
|
992
|
+
inspectSpy.mockImplementation(async () => view);
|
|
993
|
+
|
|
994
|
+
// WHEN the route handler is invoked with the path name
|
|
995
|
+
const result = await invokeInspect({ pathParams: { name: "level-up" } });
|
|
996
|
+
|
|
997
|
+
// THEN the inspection is returned unchanged
|
|
998
|
+
expect(result).toEqual(view);
|
|
999
|
+
// AND the name is forwarded to the lib (ref is never caller-supplied)
|
|
1000
|
+
expect(inspectSpy.mock.calls[0]?.[0]).toEqual({ name: "level-up" });
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
test("a captured marketplace error is returned as 200 with remote-unavailable, not thrown", async () => {
|
|
1004
|
+
// GIVEN the catalog was unreachable but a local copy exists, so the lib
|
|
1005
|
+
// returns a status rather than throwing
|
|
1006
|
+
const view = inspection({
|
|
1007
|
+
status: "remote-unavailable",
|
|
1008
|
+
remote: null,
|
|
1009
|
+
remoteError: "ENOTFOUND raw.githubusercontent.com",
|
|
1010
|
+
});
|
|
1011
|
+
inspectSpy.mockImplementation(async () => view);
|
|
1012
|
+
|
|
1013
|
+
// WHEN the handler runs
|
|
1014
|
+
const result = await invokeInspect({ pathParams: { name: "level-up" } });
|
|
1015
|
+
|
|
1016
|
+
// THEN it resolves (does not throw) and surfaces the captured error
|
|
1017
|
+
expect(result.status).toBe("remote-unavailable");
|
|
1018
|
+
expect(result.remoteError).toContain("ENOTFOUND");
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
test("InvalidPluginNameError → BadRequestError (400)", async () => {
|
|
1022
|
+
inspectSpy.mockImplementation(async () => {
|
|
1023
|
+
throw new InvalidPluginNameError("../escape");
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
await expect(
|
|
1027
|
+
invokeInspect({ pathParams: { name: "../escape" } }),
|
|
1028
|
+
).rejects.toBeInstanceOf(BadRequestError);
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
test("PluginInspectNotFoundError → NotFoundError (404)", async () => {
|
|
1032
|
+
inspectSpy.mockImplementation(async () => {
|
|
1033
|
+
throw new PluginInspectNotFoundError("ghost");
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
await expect(
|
|
1037
|
+
invokeInspect({ pathParams: { name: "ghost" } }),
|
|
1038
|
+
).rejects.toBeInstanceOf(NotFoundError);
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
test("unknown errors → InternalError with original message preserved", async () => {
|
|
1042
|
+
inspectSpy.mockImplementation(async () => {
|
|
1043
|
+
throw new Error("EUNEXPECTED");
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
let caught: unknown;
|
|
1047
|
+
try {
|
|
1048
|
+
await invokeInspect({ pathParams: { name: "level-up" } });
|
|
1049
|
+
} catch (err) {
|
|
1050
|
+
caught = err;
|
|
1051
|
+
}
|
|
1052
|
+
expect(caught).toBeInstanceOf(InternalError);
|
|
1053
|
+
expect((caught as Error).message).toContain("EUNEXPECTED");
|
|
1054
|
+
});
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
function upgradeResult(
|
|
1058
|
+
overrides: Partial<PluginUpgradeResult> = {},
|
|
1059
|
+
): PluginUpgradeResult {
|
|
1060
|
+
return {
|
|
1061
|
+
name: overrides.name ?? "level-up",
|
|
1062
|
+
outcome: overrides.outcome ?? "upgraded",
|
|
1063
|
+
fromCommit:
|
|
1064
|
+
overrides.fromCommit === undefined
|
|
1065
|
+
? "60a392b0000000000000000000000000000000aa"
|
|
1066
|
+
: overrides.fromCommit,
|
|
1067
|
+
toCommit: overrides.toCommit ?? "3eae1820000000000000000000000000000000bb",
|
|
1068
|
+
target: overrides.target ?? "/workspace/.vellum/plugins/level-up",
|
|
1069
|
+
fileCount: overrides.fileCount === undefined ? 12 : overrides.fileCount,
|
|
1070
|
+
dryRun: overrides.dryRun ?? false,
|
|
1071
|
+
provenanceWasUnknown: overrides.provenanceWasUnknown ?? false,
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
async function invokeUpgrade(args: RouteHandlerArgs = {}): Promise<{
|
|
1076
|
+
name: string;
|
|
1077
|
+
outcome: string;
|
|
1078
|
+
fromCommit: string | null;
|
|
1079
|
+
toCommit: string;
|
|
1080
|
+
target: string;
|
|
1081
|
+
fileCount: number | null;
|
|
1082
|
+
dryRun: boolean;
|
|
1083
|
+
provenanceWasUnknown: boolean;
|
|
1084
|
+
}> {
|
|
1085
|
+
return (await upgradeHandler(args)) as {
|
|
1086
|
+
name: string;
|
|
1087
|
+
outcome: string;
|
|
1088
|
+
fromCommit: string | null;
|
|
1089
|
+
toCommit: string;
|
|
1090
|
+
target: string;
|
|
1091
|
+
fileCount: number | null;
|
|
1092
|
+
dryRun: boolean;
|
|
1093
|
+
provenanceWasUnknown: boolean;
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
describe("POST /v1/plugins/:name/upgrade", () => {
|
|
1098
|
+
beforeEach(() => {
|
|
1099
|
+
upgradeSpy.mockReset();
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
test("forwards name + dryRun and projects the upgrade result", async () => {
|
|
1103
|
+
// GIVEN upgradePlugin reports a successful re-pin
|
|
1104
|
+
upgradeSpy.mockImplementation(async () => upgradeResult());
|
|
1105
|
+
|
|
1106
|
+
// WHEN the handler runs with an explicit dryRun flag
|
|
1107
|
+
const result = await invokeUpgrade({
|
|
1108
|
+
pathParams: { name: "level-up" },
|
|
1109
|
+
body: { dryRun: false },
|
|
1110
|
+
});
|
|
1111
|
+
|
|
1112
|
+
// THEN the result is projected onto the wire shape
|
|
1113
|
+
expect(result).toEqual({
|
|
1114
|
+
name: "level-up",
|
|
1115
|
+
outcome: "upgraded",
|
|
1116
|
+
fromCommit: "60a392b0000000000000000000000000000000aa",
|
|
1117
|
+
toCommit: "3eae1820000000000000000000000000000000bb",
|
|
1118
|
+
target: "/workspace/.vellum/plugins/level-up",
|
|
1119
|
+
fileCount: 12,
|
|
1120
|
+
dryRun: false,
|
|
1121
|
+
provenanceWasUnknown: false,
|
|
1122
|
+
});
|
|
1123
|
+
// AND the name + dryRun are forwarded to the lib
|
|
1124
|
+
expect(upgradeSpy.mock.calls[0]?.[0]).toEqual({
|
|
1125
|
+
name: "level-up",
|
|
1126
|
+
dryRun: false,
|
|
1127
|
+
});
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
test("omits dryRun (passes undefined) when the body flag is absent", async () => {
|
|
1131
|
+
// GIVEN a no-op upgrade where the install already matches the pin
|
|
1132
|
+
upgradeSpy.mockImplementation(async () =>
|
|
1133
|
+
upgradeResult({
|
|
1134
|
+
outcome: "already-up-to-date",
|
|
1135
|
+
toCommit: "60a392b0000000000000000000000000000000aa",
|
|
1136
|
+
fileCount: null,
|
|
1137
|
+
}),
|
|
1138
|
+
);
|
|
1139
|
+
|
|
1140
|
+
// WHEN invoked without a body
|
|
1141
|
+
const result = await invokeUpgrade({ pathParams: { name: "level-up" } });
|
|
1142
|
+
|
|
1143
|
+
// THEN the no-op outcome is surfaced
|
|
1144
|
+
expect(result.outcome).toBe("already-up-to-date");
|
|
1145
|
+
expect(result.fileCount).toBeNull();
|
|
1146
|
+
// AND dryRun is passed through as undefined (not coerced to false)
|
|
1147
|
+
expect(upgradeSpy.mock.calls[0]?.[0]).toEqual({
|
|
1148
|
+
name: "level-up",
|
|
1149
|
+
dryRun: undefined,
|
|
1150
|
+
});
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
test("InvalidPluginNameError → BadRequestError (400)", async () => {
|
|
1154
|
+
upgradeSpy.mockImplementation(async () => {
|
|
1155
|
+
throw new InvalidPluginNameError("../escape");
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
await expect(
|
|
1159
|
+
invokeUpgrade({ pathParams: { name: "../escape" } }),
|
|
1160
|
+
).rejects.toBeInstanceOf(BadRequestError);
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
test("PluginNotInstalledError → NotFoundError (404)", async () => {
|
|
1164
|
+
upgradeSpy.mockImplementation(async () => {
|
|
1165
|
+
throw new PluginNotInstalledError(
|
|
1166
|
+
"ghost",
|
|
1167
|
+
"/workspace/.vellum/plugins/ghost",
|
|
1168
|
+
);
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
await expect(
|
|
1172
|
+
invokeUpgrade({ pathParams: { name: "ghost" } }),
|
|
1173
|
+
).rejects.toBeInstanceOf(NotFoundError);
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
test("PluginNotUpgradableError → ConflictError (409)", async () => {
|
|
1177
|
+
// The install exists but has no marketplace entry to advance to: a
|
|
1178
|
+
// well-formed request that isn't actionable in the current state.
|
|
1179
|
+
upgradeSpy.mockImplementation(async () => {
|
|
1180
|
+
throw new PluginNotUpgradableError(
|
|
1181
|
+
"level-up",
|
|
1182
|
+
"it has no marketplace entry to upgrade from",
|
|
1183
|
+
);
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
await expect(
|
|
1187
|
+
invokeUpgrade({ pathParams: { name: "level-up" } }),
|
|
1188
|
+
).rejects.toBeInstanceOf(ConflictError);
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
test("PluginNotFoundError → NotFoundError (404)", async () => {
|
|
1192
|
+
upgradeSpy.mockImplementation(async () => {
|
|
1193
|
+
throw new PluginNotFoundError("level-up", "main", "vellum-ai/level-up");
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
await expect(
|
|
1197
|
+
invokeUpgrade({ pathParams: { name: "level-up" } }),
|
|
1198
|
+
).rejects.toBeInstanceOf(NotFoundError);
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
test("PluginSourceUnavailableError → ServiceUnavailableError (503)", async () => {
|
|
1202
|
+
// The re-install fetch can hit a rate-limited GitHub; that is retryable,
|
|
1203
|
+
// so surface 503 rather than a misleading 500.
|
|
1204
|
+
upgradeSpy.mockImplementation(async () => {
|
|
1205
|
+
throw new PluginSourceUnavailableError(
|
|
1206
|
+
"GitHub tree listing for vellum-ai/level-up: HTTP 403",
|
|
1207
|
+
403,
|
|
1208
|
+
);
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
await expect(
|
|
1212
|
+
invokeUpgrade({ pathParams: { name: "level-up" } }),
|
|
1213
|
+
).rejects.toBeInstanceOf(ServiceUnavailableError);
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
test("unknown errors → InternalError with original message preserved", async () => {
|
|
1217
|
+
upgradeSpy.mockImplementation(async () => {
|
|
1218
|
+
throw new Error("ECONNRESET");
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
let caught: unknown;
|
|
1222
|
+
try {
|
|
1223
|
+
await invokeUpgrade({ pathParams: { name: "level-up" } });
|
|
1224
|
+
} catch (err) {
|
|
1225
|
+
caught = err;
|
|
1226
|
+
}
|
|
1227
|
+
expect(caught).toBeInstanceOf(InternalError);
|
|
1228
|
+
expect((caught as Error).message).toContain("ECONNRESET");
|
|
1229
|
+
});
|
|
1230
|
+
});
|
|
@@ -206,6 +206,8 @@ mock.module("../../../util/platform.js", () => {
|
|
|
206
206
|
getPlatformName: () => "linux",
|
|
207
207
|
normalizeAssistantId: (id: string) => id,
|
|
208
208
|
getDataDir: stub,
|
|
209
|
+
getConfigQuarantineNoticePath: () =>
|
|
210
|
+
"/mock/workspace/data/config-quarantine-notice.json",
|
|
209
211
|
getEmbeddingModelsDir: stub,
|
|
210
212
|
getSandboxRootDir: stub,
|
|
211
213
|
getSandboxWorkingDir: stub,
|
|
@@ -414,9 +416,7 @@ describe("task_delete route", () => {
|
|
|
414
416
|
|
|
415
417
|
test("throws Zod validation error for empty task_ids array", async () => {
|
|
416
418
|
const route = findRoute("task_delete");
|
|
417
|
-
await expect(
|
|
418
|
-
route.handler({ body: { task_ids: [] } }),
|
|
419
|
-
).rejects.toThrow();
|
|
419
|
+
await expect(route.handler({ body: { task_ids: [] } })).rejects.toThrow();
|
|
420
420
|
|
|
421
421
|
expect(mockTaskDeleteCalls).toHaveLength(0);
|
|
422
422
|
});
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
|
|
15
15
|
import { z } from "zod";
|
|
16
16
|
|
|
17
|
-
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
18
17
|
import { getConfig } from "../../config/loader.js";
|
|
19
18
|
import { getOrCreateConversation } from "../../daemon/conversation-store.js";
|
|
20
19
|
import { readNowScratchpad } from "../../daemon/now-scratchpad.js";
|
|
@@ -34,9 +33,6 @@ const IDENTITY_INTRO_KEY = "identity-intro";
|
|
|
34
33
|
|
|
35
34
|
/** Conversation key used by the client for empty-state greeting generation. */
|
|
36
35
|
const GREETING_KEY = "greeting";
|
|
37
|
-
const EMPTY_STATE_DYNAMIC_GREETINGS_FLAG =
|
|
38
|
-
"empty-state-dynamic-greetings" as const;
|
|
39
|
-
const STATIC_EMPTY_STATE_GREETING = "What are we working on?";
|
|
40
36
|
|
|
41
37
|
// ---------------------------------------------------------------------------
|
|
42
38
|
// SSE helpers
|
|
@@ -68,16 +64,6 @@ async function handleBtw({
|
|
|
68
64
|
|
|
69
65
|
const trimmedContent = content.trim();
|
|
70
66
|
|
|
71
|
-
if (
|
|
72
|
-
conversationKey === GREETING_KEY &&
|
|
73
|
-
!isAssistantFeatureFlagEnabled(
|
|
74
|
-
EMPTY_STATE_DYNAMIC_GREETINGS_FLAG,
|
|
75
|
-
getConfig(),
|
|
76
|
-
)
|
|
77
|
-
) {
|
|
78
|
-
return streamText(STATIC_EMPTY_STATE_GREETING);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
67
|
// ----- Cached identity intro fast-path -----
|
|
82
68
|
if (conversationKey === IDENTITY_INTRO_KEY) {
|
|
83
69
|
const fastText = getCachedIntro()?.greetings[0];
|
|
@@ -34,30 +34,37 @@
|
|
|
34
34
|
* which has no compaction immediately before it but had several
|
|
35
35
|
* earlier in the same turn. The turn is the right unit of analysis.
|
|
36
36
|
*
|
|
37
|
-
* # Data
|
|
37
|
+
* # Data sources
|
|
38
38
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* table. Until then, missing fields surface as `null` and the Compaction
|
|
46
|
-
* tab renders `"Unavailable"` for them.
|
|
39
|
+
* When `compactionLogs.destination = "clickhouse"` is configured, the
|
|
40
|
+
* trail is served from the first-class compaction log: the agent loop's
|
|
41
|
+
* start/end event pairs written by
|
|
42
|
+
* `memory/compaction-log-store-clickhouse.ts`. Those rows carry real
|
|
43
|
+
* durations, summary-model token totals, and trigger reasons — none of
|
|
44
|
+
* which exist on `llm_request_logs`.
|
|
47
45
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
46
|
+
* The legacy projection over `llm_request_logs` rows with
|
|
47
|
+
* `call_site = "compactionAgent"` remains as the fallback: it serves
|
|
48
|
+
* turns that predate the compaction log (the table is append-only from
|
|
49
|
+
* the moment the destination is configured), assistants that never opt
|
|
50
|
+
* in, and reads where the ClickHouse query fails. On the legacy path
|
|
51
|
+
* `durationMs` is always `null` — the column doesn't exist on
|
|
52
|
+
* `llm_request_logs`.
|
|
51
53
|
*/
|
|
52
54
|
|
|
53
55
|
import { z } from "zod";
|
|
54
56
|
|
|
57
|
+
import {
|
|
58
|
+
type CompactionLogEvent,
|
|
59
|
+
getCompactionLogStore,
|
|
60
|
+
} from "../../memory/compaction-log-store-clickhouse.js";
|
|
55
61
|
import {
|
|
56
62
|
getConversation,
|
|
57
63
|
getTurnTimeBounds,
|
|
58
64
|
} from "../../memory/conversation-crud.js";
|
|
59
65
|
import { getLlmRequestLogSource } from "../../memory/llm-request-log-source.js";
|
|
60
66
|
import type { LogRow } from "../../memory/llm-request-log-store.js";
|
|
67
|
+
import { getLogger } from "../../util/logger.js";
|
|
61
68
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
62
69
|
import { BadRequestError, NotFoundError } from "./errors.js";
|
|
63
70
|
import {
|
|
@@ -66,6 +73,8 @@ import {
|
|
|
66
73
|
} from "./llm-context-normalization.js";
|
|
67
74
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
68
75
|
|
|
76
|
+
const log = getLogger("conversation-compaction-routes");
|
|
77
|
+
|
|
69
78
|
/**
|
|
70
79
|
* Wire shape for a single compaction event. Mirrors the React Query
|
|
71
80
|
* response type at
|
|
@@ -81,12 +90,9 @@ export interface CompactionTrailEvent {
|
|
|
81
90
|
inputTokens: number | null;
|
|
82
91
|
outputTokens: number | null;
|
|
83
92
|
/**
|
|
84
|
-
* Per-
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* having latency here, that's a concrete signal to extend the row
|
|
88
|
-
* (either as a column on `llm_request_logs` or on a dedicated
|
|
89
|
-
* `compaction_logs` table per the open data-model decision).
|
|
93
|
+
* Per-attempt wall-clock latency, in milliseconds. Populated from the
|
|
94
|
+
* compaction log when configured; always `null` on the legacy
|
|
95
|
+
* `llm_request_logs` path, which has no duration column.
|
|
90
96
|
*/
|
|
91
97
|
durationMs: number | null;
|
|
92
98
|
responsePreview: string | null;
|
|
@@ -172,6 +178,33 @@ export function projectLogRowToCompactionTrailEvent(
|
|
|
172
178
|
};
|
|
173
179
|
}
|
|
174
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Project a paired compaction-log event into the trail wire shape. Token
|
|
183
|
+
* counts are the summarizer's own usage (what the compaction itself cost),
|
|
184
|
+
* matching what the legacy path derived from the compaction agent's
|
|
185
|
+
* request/response payloads.
|
|
186
|
+
*
|
|
187
|
+
* Exported only for unit tests; the route handler is the sole production
|
|
188
|
+
* caller.
|
|
189
|
+
*/
|
|
190
|
+
export function projectCompactionLogEventToTrailEvent(
|
|
191
|
+
event: CompactionLogEvent,
|
|
192
|
+
): CompactionTrailEvent {
|
|
193
|
+
return {
|
|
194
|
+
id: event.compactionId,
|
|
195
|
+
createdAt: event.startedAt,
|
|
196
|
+
model: event.summaryModel,
|
|
197
|
+
provider: null,
|
|
198
|
+
inputTokens: event.summaryInputTokens,
|
|
199
|
+
outputTokens: event.summaryOutputTokens,
|
|
200
|
+
durationMs: event.durationMs,
|
|
201
|
+
responsePreview: event.summaryText,
|
|
202
|
+
requestMessageCount: event.preMessageCount,
|
|
203
|
+
stopReason: event.reason,
|
|
204
|
+
estimatedCostUsd: null,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
175
208
|
// ---------------------------------------------------------------------------
|
|
176
209
|
// Handler
|
|
177
210
|
// ---------------------------------------------------------------------------
|
|
@@ -232,6 +265,40 @@ async function handleGetCompactionTrail({
|
|
|
232
265
|
turnBounds !== null ? turnBounds.startTime - 1 : null;
|
|
233
266
|
const beforeCreatedAt: number =
|
|
234
267
|
turnBounds !== null ? turnBounds.endTime + 1 : selectedCall.createdAt;
|
|
268
|
+
// Prefer the first-class compaction log when the assistant has opted
|
|
269
|
+
// in. Zero rows means the turn predates the log (it is append-only from
|
|
270
|
+
// the moment the destination is configured), so fall through to the
|
|
271
|
+
// legacy projection rather than returning an empty trail; same for a
|
|
272
|
+
// failed ClickHouse read. Writes are best-effort and the start/end rows
|
|
273
|
+
// land independently, so an event without its end row (no `finishedAt`)
|
|
274
|
+
// means the end write failed or is lagging — in that case the legacy
|
|
275
|
+
// projection may still have the summarizer call details, so fall back
|
|
276
|
+
// rather than serve a trail with null model/tokens/duration.
|
|
277
|
+
const compactionStore = getCompactionLogStore();
|
|
278
|
+
if (compactionStore) {
|
|
279
|
+
try {
|
|
280
|
+
const events = await compactionStore.getEventsBetween(
|
|
281
|
+
conversationId,
|
|
282
|
+
afterCreatedAt,
|
|
283
|
+
beforeCreatedAt,
|
|
284
|
+
);
|
|
285
|
+
if (
|
|
286
|
+
events.length > 0 &&
|
|
287
|
+
events.every((event) => event.finishedAt !== null)
|
|
288
|
+
) {
|
|
289
|
+
return {
|
|
290
|
+
conversationId,
|
|
291
|
+
events: events.map(projectCompactionLogEventToTrailEvent),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
} catch (err) {
|
|
295
|
+
log.warn(
|
|
296
|
+
{ err, conversationId, callId },
|
|
297
|
+
"Compaction log read failed; falling back to llm_request_logs projection",
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
235
302
|
const logs = await source.getCompactionLogsBetween(
|
|
236
303
|
conversationId,
|
|
237
304
|
afterCreatedAt,
|
|
@@ -259,7 +326,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
259
326
|
},
|
|
260
327
|
summary: "Get the compaction trail leading up to an LLM call",
|
|
261
328
|
description:
|
|
262
|
-
|
|
329
|
+
'Return the chronological list of compaction events that ran in the same agent turn as the call identified by `callId`. Turn bounds are walked from the `messages` table (real user messages — tool-result user messages are not boundaries). Served from the first-class compaction log when `compactionLogs.destination = "clickhouse"` is configured, falling back to the legacy projection over `llm_request_logs` rows where `call_site = "compactionAgent"`. When the conversation has no other messages around the selected call, every compaction strictly before the call\'s `createdAt` is in scope. Drives the Inspector\'s Compaction tab.',
|
|
263
330
|
tags: ["conversations"],
|
|
264
331
|
pathParams: [
|
|
265
332
|
{
|