@vellumai/assistant 0.8.10-dev.202606092238.d04fd59 → 0.8.10
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/AGENTS.md +1 -1
- package/examples/plugins/echo/README.md +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +0 -2
- package/openapi.yaml +11 -37
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +14 -19
- package/src/__tests__/build-persisted-content.test.ts +1 -75
- package/src/__tests__/config-managed-gemini-defaults.test.ts +46 -2
- package/src/__tests__/context-overflow-reducer.test.ts +124 -264
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +22 -22
- package/src/__tests__/conversation-agent-loop.test.ts +37 -45
- package/src/__tests__/conversation-fork-crud.test.ts +0 -110
- package/src/__tests__/conversation-provider-retry-repair.test.ts +20 -16
- package/src/__tests__/conversation-runtime-assembly.test.ts +5 -118
- package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -4
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +142 -91
- package/src/__tests__/credential-execution-feature-gates.test.ts +5 -3
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +40 -6
- package/src/__tests__/external-plugin-loader.test.ts +1 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -91
- package/src/__tests__/history-repair-hook.test.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +0 -45
- package/src/__tests__/memory-retrieval-hook.test.ts +15 -29
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/persistence-secret-redaction.test.ts +0 -78
- package/src/__tests__/plugin-bootstrap.test.ts +73 -82
- package/src/__tests__/plugin-skill-contribution.test.ts +355 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +4 -7
- package/src/__tests__/plugin-types.test.ts +8 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +9 -1
- package/src/__tests__/server-history-render.test.ts +0 -39
- package/src/__tests__/skills.test.ts +0 -123
- package/src/__tests__/title-generate-hook.test.ts +0 -2
- package/src/__tests__/tool-audit-listener.test.ts +0 -51
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -32
- package/src/agent/loop.ts +23 -26
- package/src/api/events/tool-result.ts +0 -9
- package/src/api/events/tool-use-start.ts +0 -7
- package/src/api/responses/conversation-message.ts +14 -91
- package/src/approvals/guardian-request-resolvers.ts +0 -26
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/inference-providers.ts +8 -0
- package/src/cli/commands/plugins.ts +2 -2
- package/src/cli/lib/__tests__/install-from-github.test.ts +18 -174
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +1 -1
- package/src/cli/lib/__tests__/plugin-details.test.ts +1 -1
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +1 -1
- package/src/cli/lib/__tests__/search-plugins.test.ts +11 -11
- package/src/cli/lib/install-from-github.ts +19 -130
- package/src/cli/lib/plugin-catalog-cache.ts +1 -1
- package/src/cli/lib/plugin-details.ts +4 -4
- package/src/cli/lib/plugin-marketplace.ts +2 -2
- package/src/cli/lib/search-plugins.ts +6 -6
- package/src/config/bundled-skills/skill-management/SKILL.md +1 -89
- package/src/config/env-registry.ts +0 -12
- package/src/config/feature-flag-registry.json +80 -0
- package/src/config/loader.ts +22 -5
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/config/skills.ts +47 -101
- package/src/context/strip-injections.ts +4 -7
- package/src/daemon/conversation-agent-loop-handlers.ts +6 -102
- package/src/daemon/conversation-agent-loop.ts +232 -134
- package/src/daemon/conversation-runtime-assembly.ts +8 -44
- package/src/daemon/conversation-tool-setup.ts +1 -9
- package/src/daemon/external-plugins-bootstrap.ts +87 -33
- package/src/daemon/handlers/config-channels.ts +2 -11
- package/src/daemon/handlers/shared.ts +1 -9
- package/src/daemon/handlers/skills.ts +2 -6
- package/src/daemon/host-app-control-proxy.ts +57 -72
- package/src/daemon/message-protocol.ts +7 -0
- package/src/daemon/message-types/browser.ts +10 -0
- package/src/daemon/message-types/skills.ts +0 -4
- package/src/events/tool-audit-listener.ts +1 -7
- package/src/memory/conversation-crud.ts +1 -44
- package/src/memory/db-init.ts +0 -4
- package/src/memory/embedding-backend.ts +22 -5
- package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
- package/src/memory/migrations/index.ts +0 -2
- package/src/memory/schema/conversations.ts +0 -3
- package/src/memory/tool-usage-store.ts +3 -4
- package/src/memory/v2/activation-store.ts +1 -50
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +187 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +1 -2
- package/src/notifications/copy-composer.ts +267 -49
- package/src/notifications/decision-engine.ts +35 -16
- package/src/notifications/home-feed-side-effect.ts +6 -1
- package/src/platform/client.ts +3 -1
- package/src/platform/feature-gate.ts +12 -3
- package/src/plugin-api/constants.ts +0 -2
- package/src/plugin-api/index.ts +6 -34
- package/src/plugin-api/types.ts +0 -19
- package/src/plugins/defaults/compaction/compact.ts +1 -39
- package/src/plugins/defaults/compaction/context-overflow-reducer.ts +32 -240
- package/src/plugins/defaults/compaction/window-manager.ts +0 -136
- package/src/plugins/defaults/index.ts +0 -24
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +43 -68
- package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit.ts → user-prompt-submit-temp.ts} +67 -36
- package/src/plugins/external-api.ts +1 -1
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/types.ts +51 -5
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +2 -1
- package/src/providers/model-catalog.ts +1 -0
- package/src/providers/registry.ts +8 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +0 -151
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +62 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +4 -4
- package/src/runtime/routes/acp-routes.test.ts +0 -106
- package/src/runtime/routes/acp-routes.ts +2 -248
- package/src/runtime/routes/browser-tabs-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +5 -14
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +14 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +34 -33
- package/src/runtime/routes/inference-provider-connection-routes.ts +45 -0
- package/src/runtime/routes/plugins-routes.ts +3 -3
- package/src/runtime/verification-outbound-actions.ts +2 -147
- package/src/runtime/verification-templates.ts +3 -29
- package/src/telemetry/types.ts +1 -25
- package/src/telemetry/usage-telemetry-reporter.test.ts +7 -141
- package/src/telemetry/usage-telemetry-reporter.ts +7 -63
- package/src/tools/executor.ts +1 -4
- package/src/tools/skills/load.ts +6 -11
- package/src/tools/terminal/safe-env.ts +0 -1
- package/src/tools/types.ts +0 -10
- package/src/__tests__/context-window-manager-overflow-rung.test.ts +0 -289
- package/src/__tests__/conversation-tool-setup-skill-attribution.test.ts +0 -116
- package/src/__tests__/conversation-tool-setup-test-helpers.ts +0 -98
- package/src/__tests__/corrected-target.test.ts +0 -93
- package/src/__tests__/tool-start-timestamp.test.ts +0 -218
- package/src/memory/__tests__/tool-invocation-test-helpers.ts +0 -69
- package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +0 -81
- package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +0 -20
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +0 -68
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +0 -20
- package/src/memory/tool-execution-events-store.test.ts +0 -162
- package/src/memory/tool-execution-events-store.ts +0 -73
- package/src/memory/v2/__tests__/injected-block-slugs.test.ts +0 -51
- package/src/memory/v2/injected-block-slugs.ts +0 -30
- package/src/notifications/__tests__/copy-composer.test.ts +0 -244
- package/src/notifications/access-request-copy.ts +0 -298
- package/src/plugins/defaults/compaction/corrected-target.ts +0 -53
- package/src/plugins/defaults/memory-retrieval/package.json +0 -14
package/AGENTS.md
CHANGED
|
@@ -84,7 +84,7 @@ The rule exists because test machinery and production code have **inverted invar
|
|
|
84
84
|
|
|
85
85
|
Concretely:
|
|
86
86
|
|
|
87
|
-
- **Test helpers** (e.g. `src/__tests__/*-test-helpers.ts`) use only node stdlib, `bun:test`, and sibling helpers.
|
|
87
|
+
- **Test helpers** (e.g. `src/__tests__/*-test-helpers.ts`) use only node stdlib, `bun:test`, and sibling helpers. If they need to manipulate shared state that production code also reads, both sides declare a typed slot under `globalThis.vellumAssistant.*` and read/write that slot independently. The slot shape is duplicated by design — the helper and the production module both reference the namespace, neither imports the other.
|
|
88
88
|
- **The test preload** (`src/__tests__/test-preload.ts`) is the strictest: it must not import from `src/` at all. Its only static imports are node stdlib, `bun:test`, and helpers in `src/__tests__/`. Importing from a source module risks running its import-time side effects before the workspace override is set.
|
|
89
89
|
- **The preload verifier** (`src/__tests__/test-preload-verifier.ts`) runs after the main preload and asserts the override took effect (`VELLUM_WORKSPACE_DIR` must resolve under `os.tmpdir()`).
|
|
90
90
|
- **Destructive ops** (e.g. `rmSync(dbPath, ...)`) in tests must call `assertNotLiveDb(path)` from `src/__tests__/assert-not-live-db.js` immediately before the destructive call. The check is a per-callsite belt to the preload-verifier suspenders.
|
|
@@ -16,8 +16,8 @@ to eyeball which hooks fire during a conversation.
|
|
|
16
16
|
|
|
17
17
|
For the full plugin authoring guide — manifest shape, every contribution
|
|
18
18
|
surface, hook patterns, and conventions — see
|
|
19
|
-
[`plugins/README.md`](../../../../plugins/README.md).
|
|
20
|
-
[`simple-memory`](../../../../plugins/simple-memory/) is the
|
|
19
|
+
[`experimental/plugins/README.md`](../../../../experimental/plugins/README.md).
|
|
20
|
+
[`simple-memory`](../../../../experimental/plugins/simple-memory/) is the
|
|
21
21
|
canonical reference implementation that exercises every wired surface.
|
|
22
22
|
|
|
23
23
|
## What it does
|
|
@@ -115,7 +115,7 @@ vellum restart
|
|
|
115
115
|
|
|
116
116
|
## Next steps
|
|
117
117
|
|
|
118
|
-
- Read [`plugins/README.md`](../../../../plugins/README.md)
|
|
118
|
+
- Read [`experimental/plugins/README.md`](../../../../experimental/plugins/README.md)
|
|
119
119
|
for the full plugin authoring guide: manifest shape, every contribution
|
|
120
120
|
surface, hook patterns (observe / transform), tool contributions, and
|
|
121
121
|
conventions.
|
|
@@ -195,8 +195,6 @@ interface ToolLifecycleEventBase {
|
|
|
195
195
|
conversationId: string;
|
|
196
196
|
requestId?: string;
|
|
197
197
|
executionTarget?: ExecutionTarget;
|
|
198
|
-
/** Id of the skill whose `skill_execute` dispatch triggered this tool call. Absent for direct tool calls. */
|
|
199
|
-
skillId?: string;
|
|
200
198
|
}
|
|
201
199
|
|
|
202
200
|
export interface AllowlistOption {
|
package/openapi.yaml
CHANGED
|
@@ -96,11 +96,8 @@ paths:
|
|
|
96
96
|
operationId: acp_by_id_steer_post
|
|
97
97
|
summary: Steer ACP session
|
|
98
98
|
description:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
loading. Resuming a terminal session re-spawns the host agent, so it requires guardian approval: the route acks
|
|
102
|
-
immediately with approvalPending=true and performs the resume in the background once approved, streaming results
|
|
103
|
-
over SSE."
|
|
99
|
+
Send a steering instruction to an ACP session. Sessions no longer in memory (completed, or lost to a daemon
|
|
100
|
+
restart) are transparently resumed from persisted history first, when the agent supports ACP session loading.
|
|
104
101
|
tags:
|
|
105
102
|
- acp
|
|
106
103
|
responses:
|
|
@@ -118,11 +115,6 @@ paths:
|
|
|
118
115
|
resumed:
|
|
119
116
|
description: True when the session was resumed from persisted history before steering.
|
|
120
117
|
type: boolean
|
|
121
|
-
approvalPending:
|
|
122
|
-
description:
|
|
123
|
-
True when the steer triggered a guardian-approval-gated resume that will run asynchronously; watch SSE for
|
|
124
|
-
the outcome.
|
|
125
|
-
type: boolean
|
|
126
118
|
required:
|
|
127
119
|
- acpSessionId
|
|
128
120
|
- steered
|
|
@@ -15945,8 +15937,6 @@ paths:
|
|
|
15945
15937
|
- assistant
|
|
15946
15938
|
content:
|
|
15947
15939
|
type: string
|
|
15948
|
-
deprecated: true
|
|
15949
|
-
description: "Deprecated: superseded by contentBlocks. Flat plain-text body (joined text segments)."
|
|
15950
15940
|
timestamp:
|
|
15951
15941
|
type: string
|
|
15952
15942
|
attachments:
|
|
@@ -16251,8 +16241,6 @@ paths:
|
|
|
16251
16241
|
- name
|
|
16252
16242
|
- input
|
|
16253
16243
|
additionalProperties: false
|
|
16254
|
-
deprecated: true
|
|
16255
|
-
description: "Deprecated: superseded by contentBlocks (the tool_use variant). Flat list of tool calls."
|
|
16256
16244
|
surfaces:
|
|
16257
16245
|
type: array
|
|
16258
16246
|
items:
|
|
@@ -16304,28 +16292,18 @@ paths:
|
|
|
16304
16292
|
- surfaceType
|
|
16305
16293
|
- data
|
|
16306
16294
|
additionalProperties: false
|
|
16307
|
-
deprecated: true
|
|
16308
|
-
description: "Deprecated: superseded by contentBlocks (the surface variant). Flat list of surfaces."
|
|
16309
16295
|
textSegments:
|
|
16310
16296
|
type: array
|
|
16311
16297
|
items:
|
|
16312
16298
|
type: string
|
|
16313
|
-
deprecated: true
|
|
16314
|
-
description: "Deprecated: superseded by contentBlocks. Text segments split by tool-call boundaries."
|
|
16315
16299
|
thinkingSegments:
|
|
16316
16300
|
type: array
|
|
16317
16301
|
items:
|
|
16318
16302
|
type: string
|
|
16319
|
-
deprecated: true
|
|
16320
|
-
description: "Deprecated: superseded by contentBlocks. Reasoning text extracted from thinking blocks."
|
|
16321
16303
|
contentOrder:
|
|
16322
16304
|
type: array
|
|
16323
16305
|
items:
|
|
16324
16306
|
type: string
|
|
16325
|
-
deprecated: true
|
|
16326
|
-
description:
|
|
16327
|
-
'Deprecated: superseded by contentBlocks. Positional "<type>:<index>" content ordering (e.g. "text:0",
|
|
16328
|
-
"thinking:1").'
|
|
16329
16307
|
contentBlocks:
|
|
16330
16308
|
type: array
|
|
16331
16309
|
items:
|
|
@@ -16348,10 +16326,6 @@ paths:
|
|
|
16348
16326
|
const: thinking
|
|
16349
16327
|
thinking:
|
|
16350
16328
|
type: string
|
|
16351
|
-
startedAt:
|
|
16352
|
-
type: number
|
|
16353
|
-
completedAt:
|
|
16354
|
-
type: number
|
|
16355
16329
|
required:
|
|
16356
16330
|
- type
|
|
16357
16331
|
- thinking
|
|
@@ -19475,11 +19449,11 @@ paths:
|
|
|
19475
19449
|
operationId: plugins_install_post
|
|
19476
19450
|
summary: Install a plugin
|
|
19477
19451
|
description:
|
|
19478
|
-
"Install a plugin by name from the canonical source — a whitelisted `plugins/marketplace.json`
|
|
19479
|
-
the first-party `plugins/<name>/` convention. Always resolves against the curated
|
|
19480
|
-
caller-supplied ref): installing from an unreviewed revision would bypass the
|
|
19481
|
-
boundary and let attacker-controlled code be loaded. Materializes the plugin
|
|
19482
|
-
`<workspaceDir>/plugins/<name>/`; the assistant must be restarted to load it. Mirrors the CLI's `assistant
|
|
19452
|
+
"Install a plugin by name from the canonical source — a whitelisted `experimental/plugins/marketplace.json`
|
|
19453
|
+
entry, else the first-party `experimental/plugins/<name>/` convention. Always resolves against the curated
|
|
19454
|
+
default git ref (no caller-supplied ref): installing from an unreviewed revision would bypass the
|
|
19455
|
+
marketplace/first-party curation boundary and let attacker-controlled code be loaded. Materializes the plugin
|
|
19456
|
+
under `<workspaceDir>/plugins/<name>/`; the assistant must be restarted to load it. Mirrors the CLI's `assistant
|
|
19483
19457
|
plugins install <name>`. An already-installed name without `force` returns 409; a name that resolves to nothing
|
|
19484
19458
|
returns 404. Sibling to `POST /v1/skills/install`."
|
|
19485
19459
|
tags:
|
|
@@ -19541,9 +19515,9 @@ paths:
|
|
|
19541
19515
|
operationId: plugins_search_get
|
|
19542
19516
|
summary: Search the plugin catalog
|
|
19543
19517
|
description:
|
|
19544
|
-
List installable plugins from the canonical `vellum-ai/vellum-assistant` catalog at
|
|
19545
|
-
is an ECMAScript regex matched case-insensitively against the directory name
|
|
19546
|
-
query returns every entry. Mirrors the CLI's `assistant plugins search`.
|
|
19518
|
+
List installable plugins from the canonical `vellum-ai/vellum-assistant` catalog at
|
|
19519
|
+
`experimental/plugins/`. The query is an ECMAScript regex matched case-insensitively against the directory name
|
|
19520
|
+
(e.g. `memory`, `^simple`). Empty query returns every entry. Mirrors the CLI's `assistant plugins search`.
|
|
19547
19521
|
tags:
|
|
19548
19522
|
- plugins
|
|
19549
19523
|
responses:
|
|
@@ -19571,7 +19545,7 @@ paths:
|
|
|
19571
19545
|
path:
|
|
19572
19546
|
type: string
|
|
19573
19547
|
description:
|
|
19574
|
-
"Human-readable origin: a repo-relative path (`plugins/<name>`) for first-party plugins or a
|
|
19548
|
+
"Human-readable origin: a repo-relative path (`experimental/plugins/<name>`) for first-party plugins or a
|
|
19575
19549
|
`github:owner/repo@ref` locator for external ones."
|
|
19576
19550
|
description:
|
|
19577
19551
|
description: Short description, when known (external entries only today).
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* Sites not exercised here (`aborted_via_error`) require deeper provider
|
|
14
14
|
* fakery and are best covered by integration tests.
|
|
15
15
|
*/
|
|
16
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
16
|
+
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
17
17
|
|
|
18
18
|
import type {
|
|
19
19
|
AgentEvent,
|
|
@@ -23,14 +23,12 @@ import type {
|
|
|
23
23
|
import { AgentLoop, isMaxTokensStopReason } from "../agent/loop.js";
|
|
24
24
|
import type { ContextWindowConfig } from "../config/types.js";
|
|
25
25
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
26
|
-
import { HOOKS } from "../plugin-api/constants.js";
|
|
27
26
|
import {
|
|
28
27
|
createContextWindowManager,
|
|
29
28
|
disposeContextWindowManager,
|
|
30
29
|
getContextWindowManager,
|
|
31
30
|
} from "../plugins/defaults/compaction/manager-store.js";
|
|
32
31
|
import type { PostCompactContext } from "../plugins/defaults/memory-retrieval/hooks/post-compact.js";
|
|
33
|
-
import { registerPlugin } from "../plugins/registry.js";
|
|
34
32
|
import type {
|
|
35
33
|
Message,
|
|
36
34
|
Provider,
|
|
@@ -39,25 +37,22 @@ import type {
|
|
|
39
37
|
ToolDefinition,
|
|
40
38
|
} from "../providers/types.js";
|
|
41
39
|
|
|
42
|
-
// The agent loop
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// failure; when unset the hook leaves the history it was handed untouched.
|
|
40
|
+
// The agent loop invokes the default post-compaction re-injection hook directly
|
|
41
|
+
// when it compacts in place. Stub it so these unit tests can drive the
|
|
42
|
+
// re-injection result without the daemon-level injector chain. Tests assign
|
|
43
|
+
// `postCompactImpl` to observe the call or force a failure; when unset
|
|
44
|
+
// the hook is a no-op that returns the history it was handed.
|
|
48
45
|
let postCompactImpl:
|
|
49
46
|
| ((input: PostCompactContext) => Promise<Message[]>)
|
|
50
47
|
| null = null;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
input.history
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
},
|
|
60
|
-
});
|
|
48
|
+
mock.module(
|
|
49
|
+
"../plugins/defaults/memory-retrieval/hooks/post-compact.js",
|
|
50
|
+
() => ({
|
|
51
|
+
default: async (input: PostCompactContext) => ({
|
|
52
|
+
messages: postCompactImpl ? await postCompactImpl(input) : input.history,
|
|
53
|
+
}),
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
61
56
|
|
|
62
57
|
// ---------------------------------------------------------------------------
|
|
63
58
|
// Helpers (mirrored from agent-loop.test.ts so this file is self-contained)
|
|
@@ -54,10 +54,7 @@ mock.module("../memory/llm-request-log-store.js", () => ({
|
|
|
54
54
|
}));
|
|
55
55
|
|
|
56
56
|
// ── Imports (after mocks) ─────────────────────────────────────────────────────
|
|
57
|
-
import {
|
|
58
|
-
buildPersistedAssistantContent,
|
|
59
|
-
stampThinkingTiming,
|
|
60
|
-
} from "../daemon/conversation-agent-loop-handlers.js";
|
|
57
|
+
import { buildPersistedAssistantContent } from "../daemon/conversation-agent-loop-handlers.js";
|
|
61
58
|
import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
|
|
62
59
|
import type { ContentBlock } from "../providers/types.js";
|
|
63
60
|
|
|
@@ -185,74 +182,3 @@ describe("buildPersistedAssistantContent — native activityMetadata", () => {
|
|
|
185
182
|
expect(block._activityMetadata).toBeUndefined();
|
|
186
183
|
});
|
|
187
184
|
});
|
|
188
|
-
|
|
189
|
-
describe("stampThinkingTiming", () => {
|
|
190
|
-
test("stamps internal timing onto thinking blocks by position", () => {
|
|
191
|
-
// GIVEN a turn that interleaves text and two thinking blocks AND the
|
|
192
|
-
// per-block timing captured while streaming (one entry per thinking block,
|
|
193
|
-
// in stream order)
|
|
194
|
-
const content = [
|
|
195
|
-
{ type: "thinking", thinking: "first", signature: "s1" },
|
|
196
|
-
{ type: "text", text: "answer" },
|
|
197
|
-
{ type: "thinking", thinking: "second", signature: "s2" },
|
|
198
|
-
] as unknown as ContentBlock[];
|
|
199
|
-
const timings = [
|
|
200
|
-
{ startedAt: 100, completedAt: 250 },
|
|
201
|
-
{ startedAt: 400, completedAt: 480 },
|
|
202
|
-
];
|
|
203
|
-
|
|
204
|
-
// WHEN the content is stamped before persistence
|
|
205
|
-
const stamped = stampThinkingTiming(content, timings) as unknown as Array<
|
|
206
|
-
Record<string, unknown>
|
|
207
|
-
>;
|
|
208
|
-
|
|
209
|
-
// THEN each thinking block carries the `_`-prefixed timing for its position
|
|
210
|
-
expect(stamped[0]).toMatchObject({
|
|
211
|
-
type: "thinking",
|
|
212
|
-
_startedAt: 100,
|
|
213
|
-
_completedAt: 250,
|
|
214
|
-
});
|
|
215
|
-
expect(stamped[2]).toMatchObject({
|
|
216
|
-
type: "thinking",
|
|
217
|
-
_startedAt: 400,
|
|
218
|
-
_completedAt: 480,
|
|
219
|
-
});
|
|
220
|
-
// AND the interleaved text block is left untouched
|
|
221
|
-
expect(stamped[1]).toEqual({ type: "text", text: "answer" });
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
test("leaves thinking blocks unstamped when no timing was captured", () => {
|
|
225
|
-
// GIVEN thinking content but an empty timing list (thinking streaming was
|
|
226
|
-
// disabled, so no per-block timing was recorded this turn)
|
|
227
|
-
const content = [
|
|
228
|
-
{ type: "thinking", thinking: "first", signature: "s1" },
|
|
229
|
-
] as unknown as ContentBlock[];
|
|
230
|
-
|
|
231
|
-
// WHEN the content is stamped with no timing
|
|
232
|
-
const stamped = stampThinkingTiming(content, []);
|
|
233
|
-
|
|
234
|
-
// THEN the original content is returned unchanged so the UI hides duration,
|
|
235
|
-
// exactly as a tool call with no timing
|
|
236
|
-
expect(stamped).toBe(content);
|
|
237
|
-
expect(stamped[0]).not.toHaveProperty("_startedAt");
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
test("stamps only the thinking blocks that have a matching timing entry", () => {
|
|
241
|
-
// GIVEN two thinking blocks but only one captured timing entry (e.g. the
|
|
242
|
-
// second block opened after the timing array was already finalized)
|
|
243
|
-
const content = [
|
|
244
|
-
{ type: "thinking", thinking: "first", signature: "s1" },
|
|
245
|
-
{ type: "thinking", thinking: "second", signature: "s2" },
|
|
246
|
-
] as unknown as ContentBlock[];
|
|
247
|
-
const timings = [{ startedAt: 100, completedAt: 250 }];
|
|
248
|
-
|
|
249
|
-
// WHEN the content is stamped
|
|
250
|
-
const stamped = stampThinkingTiming(content, timings) as unknown as Array<
|
|
251
|
-
Record<string, unknown>
|
|
252
|
-
>;
|
|
253
|
-
|
|
254
|
-
// THEN the first block is stamped and the unmatched second block is left as-is
|
|
255
|
-
expect(stamped[0]).toMatchObject({ _startedAt: 100, _completedAt: 250 });
|
|
256
|
-
expect(stamped[1]).not.toHaveProperty("_startedAt");
|
|
257
|
-
});
|
|
258
|
-
});
|
|
@@ -57,8 +57,17 @@ mock.module("../util/logger.js", () => ({
|
|
|
57
57
|
getLogger: () => makeLoggerStub(),
|
|
58
58
|
}));
|
|
59
59
|
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Feature flag mock — controls whether managed-gemini-embeddings-enabled is on
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
let featureFlagEnabled = false;
|
|
65
|
+
|
|
60
66
|
mock.module("../config/assistant-feature-flags.js", () => ({
|
|
61
|
-
isAssistantFeatureFlagEnabled: () =>
|
|
67
|
+
isAssistantFeatureFlagEnabled: (key: string) => {
|
|
68
|
+
if (key === "managed-gemini-embeddings-enabled") return featureFlagEnabled;
|
|
69
|
+
return true;
|
|
70
|
+
},
|
|
62
71
|
clearFeatureFlagOverridesCache: () => {},
|
|
63
72
|
initFeatureFlagOverrides: async () => {},
|
|
64
73
|
getAssistantFeatureFlagDefaults: () => ({}),
|
|
@@ -110,6 +119,8 @@ describe("managed Gemini embedding defaults (via loadConfig)", () => {
|
|
|
110
119
|
setStorePathForTesting(join(WORKSPACE_DIR, "keys.enc"));
|
|
111
120
|
invalidateConfigCache();
|
|
112
121
|
|
|
122
|
+
// Reset mock state
|
|
123
|
+
featureFlagEnabled = false;
|
|
113
124
|
originalIsPlatform = process.env.IS_PLATFORM;
|
|
114
125
|
delete process.env.IS_PLATFORM;
|
|
115
126
|
});
|
|
@@ -126,9 +137,10 @@ describe("managed Gemini embedding defaults (via loadConfig)", () => {
|
|
|
126
137
|
}
|
|
127
138
|
});
|
|
128
139
|
|
|
129
|
-
test("applies managed Gemini defaults when IS_PLATFORM + provider auto", () => {
|
|
140
|
+
test("applies managed Gemini defaults when FF on + IS_PLATFORM + provider auto", () => {
|
|
130
141
|
writeConfig({});
|
|
131
142
|
|
|
143
|
+
featureFlagEnabled = true;
|
|
132
144
|
process.env.IS_PLATFORM = "true";
|
|
133
145
|
|
|
134
146
|
const config = loadConfig();
|
|
@@ -150,9 +162,22 @@ describe("managed Gemini embedding defaults (via loadConfig)", () => {
|
|
|
150
162
|
expect(qdrantRaw.vectorSize).toBe(3072);
|
|
151
163
|
});
|
|
152
164
|
|
|
165
|
+
test("does NOT apply when feature flag is OFF", () => {
|
|
166
|
+
writeConfig({});
|
|
167
|
+
|
|
168
|
+
featureFlagEnabled = false;
|
|
169
|
+
process.env.IS_PLATFORM = "true";
|
|
170
|
+
|
|
171
|
+
const config = loadConfig();
|
|
172
|
+
|
|
173
|
+
expect(config.memory.embeddings.provider).toBe("auto");
|
|
174
|
+
expect(config.memory.qdrant.vectorSize).toBe(384);
|
|
175
|
+
});
|
|
176
|
+
|
|
153
177
|
test("does NOT apply when IS_PLATFORM is not set", () => {
|
|
154
178
|
writeConfig({});
|
|
155
179
|
|
|
180
|
+
featureFlagEnabled = true;
|
|
156
181
|
delete process.env.IS_PLATFORM;
|
|
157
182
|
|
|
158
183
|
const config = loadConfig();
|
|
@@ -166,6 +191,7 @@ describe("managed Gemini embedding defaults (via loadConfig)", () => {
|
|
|
166
191
|
memory: { embeddings: { provider: "local" } },
|
|
167
192
|
});
|
|
168
193
|
|
|
194
|
+
featureFlagEnabled = true;
|
|
169
195
|
process.env.IS_PLATFORM = "true";
|
|
170
196
|
|
|
171
197
|
const config = loadConfig();
|
|
@@ -178,6 +204,7 @@ describe("managed Gemini embedding defaults (via loadConfig)", () => {
|
|
|
178
204
|
memory: { embeddings: { provider: "openai" } },
|
|
179
205
|
});
|
|
180
206
|
|
|
207
|
+
featureFlagEnabled = true;
|
|
181
208
|
process.env.IS_PLATFORM = "true";
|
|
182
209
|
|
|
183
210
|
const config = loadConfig();
|
|
@@ -192,6 +219,7 @@ describe("managed Gemini embedding defaults (via loadConfig)", () => {
|
|
|
192
219
|
},
|
|
193
220
|
});
|
|
194
221
|
|
|
222
|
+
featureFlagEnabled = true;
|
|
195
223
|
process.env.IS_PLATFORM = "true";
|
|
196
224
|
|
|
197
225
|
const config = loadConfig();
|
|
@@ -207,6 +235,7 @@ describe("managed Gemini embedding defaults (via loadConfig)", () => {
|
|
|
207
235
|
memory: { embeddings: { provider: "ollama" } },
|
|
208
236
|
});
|
|
209
237
|
|
|
238
|
+
featureFlagEnabled = true;
|
|
210
239
|
process.env.IS_PLATFORM = "true";
|
|
211
240
|
|
|
212
241
|
const config = loadConfig();
|
|
@@ -216,6 +245,7 @@ describe("managed Gemini embedding defaults (via loadConfig)", () => {
|
|
|
216
245
|
test("is idempotent — second loadConfig is a no-op after migration", () => {
|
|
217
246
|
writeConfig({});
|
|
218
247
|
|
|
248
|
+
featureFlagEnabled = true;
|
|
219
249
|
process.env.IS_PLATFORM = "true";
|
|
220
250
|
|
|
221
251
|
const config = loadConfig();
|
|
@@ -244,6 +274,7 @@ describe("managed Gemini embedding defaults (via loadConfig)", () => {
|
|
|
244
274
|
},
|
|
245
275
|
});
|
|
246
276
|
|
|
277
|
+
featureFlagEnabled = true;
|
|
247
278
|
process.env.IS_PLATFORM = "true";
|
|
248
279
|
|
|
249
280
|
const config = loadConfig();
|
|
@@ -264,9 +295,22 @@ describe("managed Gemini embedding defaults (via loadConfig)", () => {
|
|
|
264
295
|
expect(qdrantRaw.onDisk).toBe(false);
|
|
265
296
|
});
|
|
266
297
|
|
|
298
|
+
test("does NOT apply when both FF off and IS_PLATFORM not set", () => {
|
|
299
|
+
writeConfig({});
|
|
300
|
+
|
|
301
|
+
featureFlagEnabled = false;
|
|
302
|
+
delete process.env.IS_PLATFORM;
|
|
303
|
+
|
|
304
|
+
const config = loadConfig();
|
|
305
|
+
|
|
306
|
+
expect(config.memory.embeddings.provider).toBe("auto");
|
|
307
|
+
expect(config.memory.qdrant.vectorSize).toBe(384);
|
|
308
|
+
});
|
|
309
|
+
|
|
267
310
|
test("applies when IS_PLATFORM is '1'", () => {
|
|
268
311
|
writeConfig({});
|
|
269
312
|
|
|
313
|
+
featureFlagEnabled = true;
|
|
270
314
|
process.env.IS_PLATFORM = "1";
|
|
271
315
|
|
|
272
316
|
const config = loadConfig();
|