@vellumai/assistant 0.11.6-dev.202608252309.48526e7 → 0.11.6-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 +1 -1
- package/docs/flux-turn-detection-spike.md +7 -8
- package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +0 -32
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +0 -18
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +5 -0
- package/node_modules/@vellumai/gateway-client/src/channel-permission-contract.ts +1 -3
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +0 -7
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +0 -8
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +0 -11
- package/openapi.yaml +6 -74
- package/package.json +1 -1
- package/src/__tests__/compactor-call-site-logging.test.ts +0 -61
- package/src/__tests__/config-loader-backfill.test.ts +0 -69
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +0 -238
- package/src/__tests__/conversation-error.test.ts +0 -44
- package/src/__tests__/conversation-runtime-assembly.test.ts +0 -23
- package/src/__tests__/conversation-slash.test.ts +0 -9
- package/src/__tests__/conversation-title-service.test.ts +0 -97
- package/src/__tests__/credential-routes.test.ts +1 -17
- package/src/__tests__/llm-resolver.test.ts +0 -20
- package/src/__tests__/managed-profile-guard.test.ts +0 -28
- package/src/__tests__/notification-decision-fallback.test.ts +0 -106
- package/src/__tests__/provider-usage-tracking.test.ts +0 -56
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +2 -108
- package/src/__tests__/voice-config-update.test.ts +0 -43
- package/src/agent/loop.ts +2 -31
- package/src/backup/__tests__/paths.test.ts +2 -6
- package/src/backup/paths.ts +4 -6
- package/src/cli/commands/backup.help.ts +13 -10
- package/src/cli/commands/credentials.help.ts +1 -1
- package/src/cli/commands/memory/memory-retrospective.ts +0 -1
- package/src/cli/commands/stt.help.ts +2 -2
- package/src/config/__tests__/backup-schema.test.ts +1 -1
- package/src/config/__tests__/default-profile-catalog.test.ts +1 -77
- package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +5 -11
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +8 -33
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +3 -54
- package/src/config/default-profile-catalog.ts +10 -195
- package/src/config/default-profile-names.ts +0 -41
- package/src/config/inference-profile-validation.ts +2 -11
- package/src/config/llm-resolver.ts +0 -1
- package/src/config/loader.ts +0 -3
- package/src/config/managed-speech-defaults.ts +8 -53
- package/src/config/schemas/__tests__/live-voice.test.ts +3 -4
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/live-voice.ts +3 -5
- package/src/config/schemas/llm.ts +10 -256
- package/src/config/schemas/services.ts +0 -24
- package/src/config/schemas/stt.ts +5 -29
- package/src/config/seed-inference-profiles.ts +3 -32
- package/src/context/compactor.ts +1 -74
- package/src/daemon/conversation-agent-loop-handlers.ts +0 -15
- package/src/daemon/conversation-agent-loop.ts +3 -40
- package/src/daemon/conversation-error.ts +0 -25
- package/src/daemon/conversation-runtime-assembly.ts +7 -19
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +2 -5
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +11 -73
- package/src/live-voice/__tests__/live-voice-session-preflight.test.ts +2 -148
- package/src/live-voice/__tests__/live-voice-stt.test.ts +0 -3
- package/src/live-voice/__tests__/protocol.test.ts +0 -169
- package/src/live-voice/live-voice-session-manager.ts +2 -39
- package/src/live-voice/live-voice-session.ts +6 -188
- package/src/live-voice/protocol.ts +1 -157
- package/src/notifications/adapters/slack.ts +1 -6
- package/src/notifications/approval-card-data.ts +1 -94
- package/src/notifications/broadcaster.ts +9 -3
- package/src/notifications/decision-engine.ts +0 -55
- package/src/notifications/guardian-question-mode.ts +0 -57
- package/src/oauth/seed-providers.ts +0 -265
- package/src/persistence/conversation-title-service.ts +29 -43
- package/src/plugin-api/model-profiles.test.ts +2 -2
- package/src/plugin-api/model-profiles.ts +7 -7
- package/src/plugins/defaults/compaction/window-manager.ts +0 -4
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +33 -125
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +31 -21
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +9 -13
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +4 -31
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +107 -140
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +13 -13
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +2 -2
- package/src/providers/connection-resolution.ts +17 -2
- package/src/providers/inference/adapter-factory.ts +3 -225
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +0 -22
- package/src/providers/openai/api-error-normalization.ts +0 -13
- package/src/providers/openai/chat-completions-provider.ts +0 -80
- package/src/providers/retry.ts +68 -938
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +0 -42
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +0 -136
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +0 -120
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +12 -97
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +3 -78
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +4 -132
- package/src/providers/speech-to-text/provider-catalog.ts +3 -159
- package/src/providers/speech-to-text/resolve.ts +15 -66
- package/src/providers/types.ts +0 -31
- package/src/providers/usage-tracking.ts +1 -12
- package/src/runtime/__tests__/agent-wake.test.ts +3 -105
- package/src/runtime/agent-wake.ts +5 -44
- package/src/runtime/routes/__tests__/backup-routes.test.ts +5 -9
- package/src/runtime/routes/__tests__/contact-routes.test.ts +0 -7
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -96
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +0 -37
- package/src/runtime/routes/backup-routes.ts +2 -2
- package/src/runtime/routes/contact-routes.ts +15 -20
- package/src/runtime/routes/conversation-query-routes.ts +10 -98
- package/src/runtime/routes/conversation-routes.ts +2 -2
- package/src/runtime/routes/credential-routes.ts +1 -2
- package/src/runtime/routes/inference-profile-session-handler.ts +2 -2
- package/src/runtime/routes/inference-profiles-routes.ts +7 -12
- package/src/runtime/routes/inference-send-routes.ts +2 -2
- package/src/runtime/routes/llm-call-sites-routes.ts +2 -2
- package/src/runtime/routes/stt-routes.ts +1 -6
- package/src/runtime/routes/wake-conversation-routes.ts +1 -8
- package/src/stt/daemon-batch-transcriber.ts +1 -4
- package/src/stt/types.ts +3 -33
- package/src/telemetry/__tests__/live-voice-funnel.test.ts +0 -34
- package/src/telemetry/live-voice-funnel.ts +2 -12
- package/src/tools/workflows/manage-workflows.ts +2 -2
- package/src/tools/workflows/run-workflow.test.ts +1 -2
- package/src/util/errors.ts +0 -1
- package/src/util/provider-error-patterns.ts +0 -20
- package/src/workspace/migrations/registry.ts +0 -8
- package/src/__tests__/default-profile-catalog-fallback.test.ts +0 -289
- package/src/__tests__/fallback-breaker.test.ts +0 -1335
- package/src/__tests__/llm-schema-fallback-profile.test.ts +0 -359
- package/src/__tests__/managed-fallback-dispatch.test.ts +0 -430
- package/src/__tests__/retry-fallback-escalation.test.ts +0 -1093
- package/src/__tests__/workspace-migration-147-rename-colliding-backup-profile-names.test.ts +0 -480
- package/src/__tests__/workspace-migration-148-strip-unsupported-fallback-profiles.test.ts +0 -167
- package/src/__tests__/workspace-migration-149-repoint-backup-profile-selections.test.ts +0 -144
- package/src/config/bundled-skills/settings/tools/open-system-settings.test.ts +0 -85
- package/src/live-voice/__tests__/live-voice-text-turn.test.ts +0 -354
- package/src/providers/fallback-breaker.ts +0 -389
- package/src/providers/openai/__tests__/chat-template-flatten.test.ts +0 -198
- package/src/providers/provider-resolvability.ts +0 -20
- package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +0 -296
- package/src/workspace/migrations/147-rename-colliding-backup-profile-names.ts +0 -360
- package/src/workspace/migrations/148-strip-unsupported-fallback-profiles.ts +0 -117
- package/src/workspace/migrations/149-repoint-backup-profile-selections.ts +0 -204
- package/src/workspace/migrations/150-stt-flux-provider-to-model-family.ts +0 -139
- package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +0 -160
package/ARCHITECTURE.md
CHANGED
|
@@ -84,7 +84,7 @@ All HTTP API requests use a single `Authorization: Bearer <jwt>` header for auth
|
|
|
84
84
|
|
|
85
85
|
**Identity lifecycle:**
|
|
86
86
|
|
|
87
|
-
1. **Bootstrap (loopback-only,
|
|
87
|
+
1. **Bootstrap (loopback-only, macOS)** — On first launch, the macOS client calls `POST /v1/guardian/init` with `{ platform, deviceId }`. The endpoint is loopback-only and mints a JWT access token + refresh token pair. Returns `{ guardianPrincipalId, accessToken, accessTokenExpiresAt, refreshToken, refreshTokenExpiresAt, refreshAfter, isNew }`. The CLI obtains its bearer token during `hatch` and does not perform a separate bootstrap step.
|
|
88
88
|
|
|
89
89
|
2. **Refresh** — `POST /v1/guardian/refresh` accepts `{ refreshToken }` and returns a new access/refresh token pair. Single-use rotation with replay detection and family-based revocation.
|
|
90
90
|
|
|
@@ -59,7 +59,7 @@ If it does not, set the Deepgram key the ordinary way (client Settings, Speech-t
|
|
|
59
59
|
|
|
60
60
|
## 2. Enable Flux
|
|
61
61
|
|
|
62
|
-
> **
|
|
62
|
+
> **Setting `services.stt.provider` to `deepgram-flux` turns off batch transcription for the whole workspace, not just live voice.** `services.stt.provider` is the single source of truth for every STT route, and Flux is the only provider in the catalog with no `daemon-batch` boundary. For as long as it is set, these all stop working: voice-message and inbound-attachment transcription, the `transcribe` skill, the `media-processing` skill's audio segments, `POST /v1/stt/transcribe`, and phone-call transcription. Each reports a message naming Flux as the cause rather than failing silently, but they do not fall back to `deepgram` on their own. **Set `services.stt.provider` back to `deepgram` when the spike is over**, and do not run the spike on an assistant that is also taking calls or handling voice messages.
|
|
63
63
|
|
|
64
64
|
Two keys in `config.json`, which lives at `$VELLUM_WORKSPACE_DIR/config.json` (default `~/.vellum/workspace/config.json`):
|
|
65
65
|
|
|
@@ -67,8 +67,7 @@ Two keys in `config.json`, which lives at `$VELLUM_WORKSPACE_DIR/config.json` (d
|
|
|
67
67
|
{
|
|
68
68
|
"services": {
|
|
69
69
|
"stt": {
|
|
70
|
-
"provider": "deepgram"
|
|
71
|
-
"providers": { "deepgram": { "model": "flux" } }
|
|
70
|
+
"provider": "deepgram-flux"
|
|
72
71
|
}
|
|
73
72
|
},
|
|
74
73
|
"liveVoice": {
|
|
@@ -94,12 +93,12 @@ The rest of `liveVoice.flux` is optional and defaulted (`config/schemas/live-voi
|
|
|
94
93
|
|
|
95
94
|
## 3. Run the A/B
|
|
96
95
|
|
|
97
|
-
**Flip `turnEnd.enabled` between runs and leave
|
|
96
|
+
**Flip `turnEnd.enabled` between runs and leave `services.stt.provider` on `deepgram-flux` in both arms.** That holds the STT engine, the model, the socket, and the transcriber lifecycle constant, so the only thing that changes is which signal commits the turn.
|
|
98
97
|
|
|
99
|
-
- **Arm A (control):** `
|
|
100
|
-
- **Arm B (treatment):** `
|
|
98
|
+
- **Arm A (control):** `provider: "deepgram-flux"`, `turnEnd.enabled: false`. Flux transcribes; the four turn-detection events are ignored; the local silence boundary and the front-door hold path run exactly as they do today.
|
|
99
|
+
- **Arm B (treatment):** `provider: "deepgram-flux"`, `turnEnd.enabled: true`.
|
|
101
100
|
|
|
102
|
-
**Do not A/B by switching the
|
|
101
|
+
**Do not A/B by switching the provider between `deepgram` and `deepgram-flux`.** That confounds two changes at once:
|
|
103
102
|
|
|
104
103
|
1. It swaps the STT model. `deepgram` runs `nova-2` (`DEFAULT_MODEL`, `deepgram-realtime.ts`), so any transcript-quality or first-partial difference lands in your latency numbers.
|
|
105
104
|
2. It swaps the transcriber lifecycle. `deepgram` implements `finalizeUtterance`, so the session adopts it as a persistent stream shared across the whole session (`sharedTranscriber`) and never tears it down between turns. Flux implements no `finalizeUtterance`, so every utterance owns its own `/v2/listen` socket and every release closes it. That per-turn socket churn is inside `roundTripMs` and `totalMs` on the Flux side and absent on the `deepgram` side, so a naive provider-swap A/B attributes it to turn detection. The next section is what it costs and why it cannot be removed.
|
|
@@ -231,7 +230,7 @@ Check these if you port the spike anywhere else, because a run that violates eit
|
|
|
231
230
|
|
|
232
231
|
## 8. Falling back
|
|
233
232
|
|
|
234
|
-
Set `liveVoice.flux.turnEnd.enabled` back to `false` (or delete the key) and restart. That is the whole rollback: the latch goes down, the turn-detection events become no-ops, and the silence-boundary path runs unchanged. You can leave
|
|
233
|
+
Set `liveVoice.flux.turnEnd.enabled` back to `false` (or delete the key) and restart. That is the whole rollback: the latch goes down, the turn-detection events become no-ops, and the silence-boundary path runs unchanged. You can leave `services.stt.provider` on `deepgram-flux` or move it back to `deepgram`; either is a working configuration.
|
|
235
234
|
|
|
236
235
|
Runtime fallback needs no action. A Flux stream that never emits `turn-end` is caught by the fail-open deadline and the utterance replays onto the silence path, so an outage degrades to today's behavior rather than to a hung turn.
|
|
237
236
|
|
|
@@ -20,7 +20,6 @@ describe("contact read contracts", () => {
|
|
|
20
20
|
contactType: "human",
|
|
21
21
|
lastInteraction: 1700000000,
|
|
22
22
|
interactionCount: 12,
|
|
23
|
-
autoApproveThreshold: "medium",
|
|
24
23
|
createdAt: 1699000000,
|
|
25
24
|
updatedAt: 1700000000,
|
|
26
25
|
channels: [
|
|
@@ -47,7 +46,6 @@ describe("contact read contracts", () => {
|
|
|
47
46
|
const parsed = ContactReadSchema.parse(contact);
|
|
48
47
|
expect(parsed.createdAt).toBe(1699000000);
|
|
49
48
|
expect(parsed.updatedAt).toBe(1700000000);
|
|
50
|
-
expect(parsed.autoApproveThreshold).toBe("medium");
|
|
51
49
|
});
|
|
52
50
|
|
|
53
51
|
test("ContactReadSchema accepts null interactionCount (daemon-native gateway-telemetry outage fail-soft)", () => {
|
|
@@ -84,36 +82,6 @@ describe("contact read contracts", () => {
|
|
|
84
82
|
const parsed = ContactReadSchema.parse(contact);
|
|
85
83
|
expect(parsed.interactionCount).toBeNull();
|
|
86
84
|
expect(parsed.channels[0].interactionCount).toBeNull();
|
|
87
|
-
expect(parsed.autoApproveThreshold).toBeUndefined();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("ContactReadSchema accepts a null auto-approve threshold", () => {
|
|
91
|
-
const contact = {
|
|
92
|
-
id: "c1",
|
|
93
|
-
displayName: "Example User",
|
|
94
|
-
role: "contact",
|
|
95
|
-
interactionCount: 0,
|
|
96
|
-
autoApproveThreshold: null,
|
|
97
|
-
createdAt: 1699000000,
|
|
98
|
-
updatedAt: 1700000000,
|
|
99
|
-
channels: [],
|
|
100
|
-
};
|
|
101
|
-
expect(ContactReadSchema.parse(contact).autoApproveThreshold).toBeNull();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("ContactReadSchema rejects an unknown auto-approve threshold", () => {
|
|
105
|
-
expect(() =>
|
|
106
|
-
ContactReadSchema.parse({
|
|
107
|
-
id: "c1",
|
|
108
|
-
displayName: "Example User",
|
|
109
|
-
role: "contact",
|
|
110
|
-
interactionCount: 0,
|
|
111
|
-
autoApproveThreshold: "full",
|
|
112
|
-
createdAt: 1699000000,
|
|
113
|
-
updatedAt: 1700000000,
|
|
114
|
-
channels: [],
|
|
115
|
-
}),
|
|
116
|
-
).toThrow();
|
|
117
85
|
});
|
|
118
86
|
|
|
119
87
|
test("ContactReadSchema requires createdAt/updatedAt", () => {
|
|
@@ -31,7 +31,6 @@ const fullVerdict: TrustVerdict = {
|
|
|
31
31
|
verifiedAt: 1699999999,
|
|
32
32
|
interactionCount: 12,
|
|
33
33
|
memberDisplayName: "Member Name",
|
|
34
|
-
autoApproveThreshold: "high",
|
|
35
34
|
};
|
|
36
35
|
|
|
37
36
|
describe("TrustVerdictSchema", () => {
|
|
@@ -39,23 +38,6 @@ describe("TrustVerdictSchema", () => {
|
|
|
39
38
|
expect(TrustVerdictSchema.parse(fullVerdict)).toEqual(fullVerdict);
|
|
40
39
|
});
|
|
41
40
|
|
|
42
|
-
test("parses a verdict with a null contact auto-approve threshold", () => {
|
|
43
|
-
const verdict = {
|
|
44
|
-
...fullVerdict,
|
|
45
|
-
autoApproveThreshold: null,
|
|
46
|
-
} satisfies TrustVerdict;
|
|
47
|
-
expect(TrustVerdictSchema.parse(verdict)).toEqual(verdict);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("rejects an unknown contact auto-approve threshold", () => {
|
|
51
|
-
expect(() =>
|
|
52
|
-
TrustVerdictSchema.parse({
|
|
53
|
-
...fullVerdict,
|
|
54
|
-
autoApproveThreshold: "full",
|
|
55
|
-
}),
|
|
56
|
-
).toThrow();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
41
|
test("parses a minimal verdict", () => {
|
|
60
42
|
const minimal = {
|
|
61
43
|
trustClass: "unknown",
|
|
@@ -82,10 +82,15 @@ export function isAdmissionPolicyExemptChannel(channelType: string): boolean {
|
|
|
82
82
|
* `vellum` is the local desktop/web client surface; the guardian is always
|
|
83
83
|
* max-rank there, so the seed default admits them regardless of the floor.
|
|
84
84
|
*
|
|
85
|
+
* `discord` has no ingress implementation, so there is nothing for a floor to
|
|
86
|
+
* gate and nothing for the user to configure. Hiding keeps it pinned at the
|
|
87
|
+
* seed default rather than offering a Channel Trust Floors row for a channel
|
|
88
|
+
* that receives nothing.
|
|
85
89
|
*/
|
|
86
90
|
export const ADMISSION_POLICY_HIDDEN_CHANNELS: ReadonlySet<string> = new Set([
|
|
87
91
|
"vellum",
|
|
88
92
|
"whatsapp",
|
|
93
|
+
"discord",
|
|
89
94
|
]);
|
|
90
95
|
|
|
91
96
|
export function isAdmissionPolicyHiddenChannel(channelType: string): boolean {
|
|
@@ -10,9 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Storage lives in the gateway DB (`channel_permission_overrides`) so the
|
|
12
12
|
* assistant cannot tamper with it, matching `channel_admission_policy` and
|
|
13
|
-
* `auto_approve_thresholds`.
|
|
14
|
-
* `contacts.auto_approve_threshold` and is stamped on the trust verdict;
|
|
15
|
-
* that override is not a matrix cell.
|
|
13
|
+
* `auto_approve_thresholds`.
|
|
16
14
|
*/
|
|
17
15
|
|
|
18
16
|
import { z } from "zod";
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
import { CHANNEL_IDS } from "@vellumai/service-contracts/channels";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
|
|
8
|
-
import { RiskThresholdSchema } from "./channel-permission-contract.js";
|
|
9
|
-
|
|
10
8
|
export const GATEWAY_LOG_LEVEL_NAMES = [
|
|
11
9
|
"trace",
|
|
12
10
|
"debug",
|
|
@@ -277,11 +275,6 @@ export const ContactReadSchema = z.object({
|
|
|
277
275
|
contactType: z.string().nullable().optional(),
|
|
278
276
|
lastInteraction: z.number().nullable().optional(),
|
|
279
277
|
interactionCount: z.number().nullable(),
|
|
280
|
-
/**
|
|
281
|
-
* Per-contact auto-approve ceiling. Null when unset (inherit cascade).
|
|
282
|
-
* Optional on the wire for version skew; gateway reads always emit it.
|
|
283
|
-
*/
|
|
284
|
-
autoApproveThreshold: RiskThresholdSchema.nullable().optional(),
|
|
285
278
|
createdAt: z.number(),
|
|
286
279
|
updatedAt: z.number(),
|
|
287
280
|
channels: z.array(ContactReadChannelSchema),
|
|
@@ -61,14 +61,6 @@ export const ApprovalUIMetadataSchema = z.object({
|
|
|
61
61
|
actions: z.array(ApprovalActionOptionSchema),
|
|
62
62
|
plainTextFallback: z.string(),
|
|
63
63
|
permissionDetails: PermissionRequestDetailsSchema.optional(),
|
|
64
|
-
/**
|
|
65
|
-
* What the recipient is being asked to do, so a renderer can head the card
|
|
66
|
-
* with the right verb. Resolved once by the producer; a renderer that reads
|
|
67
|
-
* the actions to work it out would be parsing a payload it is meant only to
|
|
68
|
-
* draw. Absent means an approval, which is what every existing producer
|
|
69
|
-
* sends and what a renderer without this field already assumes.
|
|
70
|
-
*/
|
|
71
|
-
intent: z.enum(["approval", "question"]).optional(),
|
|
72
64
|
});
|
|
73
65
|
|
|
74
66
|
export type ApprovalUIMetadata = z.infer<typeof ApprovalUIMetadataSchema>;
|
|
@@ -78,17 +78,6 @@ export const TrustVerdictSchema = z.object({
|
|
|
78
78
|
policy: z.string().optional(),
|
|
79
79
|
verifiedAt: z.number().nullable().optional(),
|
|
80
80
|
memberDisplayName: z.string().optional(),
|
|
81
|
-
/**
|
|
82
|
-
* Contact-level auto-approve ceiling from the gateway `contacts` row.
|
|
83
|
-
* Present when a member channel resolves. Null means the contact has no
|
|
84
|
-
* explicit override (inherit the room / trust-class cascade).
|
|
85
|
-
* Same vocabulary as `RiskThreshold` (`none` | `low` | `medium` | `high`).
|
|
86
|
-
* Inlined here so this leaf contract does not import the permission matrix.
|
|
87
|
-
*/
|
|
88
|
-
autoApproveThreshold: z
|
|
89
|
-
.enum(["none", "low", "medium", "high"])
|
|
90
|
-
.nullable()
|
|
91
|
-
.optional(),
|
|
92
81
|
|
|
93
82
|
// Gateway-owned interaction telemetry (a trust signal, not an info field per
|
|
94
83
|
// the 2×2) — carried straight off the member `contact_channels` row.
|
package/openapi.yaml
CHANGED
|
@@ -2766,8 +2766,8 @@ paths:
|
|
|
2766
2766
|
operationId: backup_destinations_get
|
|
2767
2767
|
summary: List configured offsite backup destinations
|
|
2768
2768
|
description:
|
|
2769
|
-
Returns the current offsite destinations array
|
|
2770
|
-
|
|
2769
|
+
Returns the current offsite destinations array, materializing the iCloud Drive default when no explicit
|
|
2770
|
+
array is configured.
|
|
2771
2771
|
tags:
|
|
2772
2772
|
- backups
|
|
2773
2773
|
responses:
|
|
@@ -2799,8 +2799,8 @@ paths:
|
|
|
2799
2799
|
operationId: backup_destinations_add_post
|
|
2800
2800
|
summary: Add an offsite backup destination
|
|
2801
2801
|
description:
|
|
2802
|
-
Appends a new destination. Materializes the
|
|
2803
|
-
|
|
2802
|
+
Appends a new destination. Materializes the iCloud default first if destinations is currently null. Errors
|
|
2803
|
+
if the path already exists.
|
|
2804
2804
|
tags:
|
|
2805
2805
|
- backups
|
|
2806
2806
|
requestBody:
|
|
@@ -4342,15 +4342,6 @@ paths:
|
|
|
4342
4342
|
- type: "null"
|
|
4343
4343
|
memberDisplayName:
|
|
4344
4344
|
type: string
|
|
4345
|
-
autoApproveThreshold:
|
|
4346
|
-
anyOf:
|
|
4347
|
-
- type: string
|
|
4348
|
-
enum:
|
|
4349
|
-
- none
|
|
4350
|
-
- low
|
|
4351
|
-
- medium
|
|
4352
|
-
- high
|
|
4353
|
-
- type: "null"
|
|
4354
4345
|
interactionCount:
|
|
4355
4346
|
type: number
|
|
4356
4347
|
hasInterceptableVerificationSession:
|
|
@@ -5232,10 +5223,6 @@ paths:
|
|
|
5232
5223
|
required:
|
|
5233
5224
|
- profile
|
|
5234
5225
|
- weight
|
|
5235
|
-
fallbackProfile:
|
|
5236
|
-
readOnly: true
|
|
5237
|
-
type: string
|
|
5238
|
-
minLength: 1
|
|
5239
5226
|
responses:
|
|
5240
5227
|
"200":
|
|
5241
5228
|
description: Successful response
|
|
@@ -5606,15 +5593,6 @@ paths:
|
|
|
5606
5593
|
anyOf:
|
|
5607
5594
|
- type: number
|
|
5608
5595
|
- type: "null"
|
|
5609
|
-
autoApproveThreshold:
|
|
5610
|
-
anyOf:
|
|
5611
|
-
- type: string
|
|
5612
|
-
enum:
|
|
5613
|
-
- none
|
|
5614
|
-
- low
|
|
5615
|
-
- medium
|
|
5616
|
-
- high
|
|
5617
|
-
- type: "null"
|
|
5618
5596
|
createdAt:
|
|
5619
5597
|
type: number
|
|
5620
5598
|
updatedAt:
|
|
@@ -5779,15 +5757,6 @@ paths:
|
|
|
5779
5757
|
anyOf:
|
|
5780
5758
|
- type: number
|
|
5781
5759
|
- type: "null"
|
|
5782
|
-
autoApproveThreshold:
|
|
5783
|
-
anyOf:
|
|
5784
|
-
- type: string
|
|
5785
|
-
enum:
|
|
5786
|
-
- none
|
|
5787
|
-
- low
|
|
5788
|
-
- medium
|
|
5789
|
-
- high
|
|
5790
|
-
- type: "null"
|
|
5791
5760
|
createdAt:
|
|
5792
5761
|
type: number
|
|
5793
5762
|
updatedAt:
|
|
@@ -5921,15 +5890,6 @@ paths:
|
|
|
5921
5890
|
anyOf:
|
|
5922
5891
|
- type: number
|
|
5923
5892
|
- type: "null"
|
|
5924
|
-
autoApproveThreshold:
|
|
5925
|
-
anyOf:
|
|
5926
|
-
- type: string
|
|
5927
|
-
enum:
|
|
5928
|
-
- none
|
|
5929
|
-
- low
|
|
5930
|
-
- medium
|
|
5931
|
-
- high
|
|
5932
|
-
- type: "null"
|
|
5933
5893
|
createdAt:
|
|
5934
5894
|
type: number
|
|
5935
5895
|
updatedAt:
|
|
@@ -6334,15 +6294,6 @@ paths:
|
|
|
6334
6294
|
anyOf:
|
|
6335
6295
|
- type: number
|
|
6336
6296
|
- type: "null"
|
|
6337
|
-
autoApproveThreshold:
|
|
6338
|
-
anyOf:
|
|
6339
|
-
- type: string
|
|
6340
|
-
enum:
|
|
6341
|
-
- none
|
|
6342
|
-
- low
|
|
6343
|
-
- medium
|
|
6344
|
-
- high
|
|
6345
|
-
- type: "null"
|
|
6346
6297
|
createdAt:
|
|
6347
6298
|
type: number
|
|
6348
6299
|
updatedAt:
|
|
@@ -6542,15 +6493,6 @@ paths:
|
|
|
6542
6493
|
anyOf:
|
|
6543
6494
|
- type: number
|
|
6544
6495
|
- type: "null"
|
|
6545
|
-
autoApproveThreshold:
|
|
6546
|
-
anyOf:
|
|
6547
|
-
- type: string
|
|
6548
|
-
enum:
|
|
6549
|
-
- none
|
|
6550
|
-
- low
|
|
6551
|
-
- medium
|
|
6552
|
-
- high
|
|
6553
|
-
- type: "null"
|
|
6554
6496
|
createdAt:
|
|
6555
6497
|
type: number
|
|
6556
6498
|
updatedAt:
|
|
@@ -15046,8 +14988,8 @@ paths:
|
|
|
15046
14988
|
description: Profile not found
|
|
15047
14989
|
"409":
|
|
15048
14990
|
description:
|
|
15049
|
-
Profile is still referenced by activeProfile, advisorProfile, a call site, a default-tier override, a
|
|
15050
|
-
arm
|
|
14991
|
+
Profile is still referenced by activeProfile, advisorProfile, a call site, a default-tier override, or a
|
|
14992
|
+
mix arm
|
|
15051
14993
|
get:
|
|
15052
14994
|
operationId: inference_profiles_by_name_get
|
|
15053
14995
|
summary: Get an effective inference profile
|
|
@@ -34816,12 +34758,6 @@ components:
|
|
|
34816
34758
|
- profile
|
|
34817
34759
|
- weight
|
|
34818
34760
|
- type: "null"
|
|
34819
|
-
fallbackProfile:
|
|
34820
|
-
anyOf:
|
|
34821
|
-
- readOnly: true
|
|
34822
|
-
type: string
|
|
34823
|
-
minLength: 1
|
|
34824
|
-
- type: "null"
|
|
34825
34761
|
additionalProperties: {}
|
|
34826
34762
|
ProfileStatus:
|
|
34827
34763
|
type: string
|
|
@@ -35593,10 +35529,6 @@ components:
|
|
|
35593
35529
|
- profile
|
|
35594
35530
|
- weight
|
|
35595
35531
|
additionalProperties: false
|
|
35596
|
-
fallbackProfile:
|
|
35597
|
-
readOnly: true
|
|
35598
|
-
type: string
|
|
35599
|
-
minLength: 1
|
|
35600
35532
|
supportsVision:
|
|
35601
35533
|
type: boolean
|
|
35602
35534
|
invariant:
|
package/package.json
CHANGED
|
@@ -194,64 +194,3 @@ describe("compactor records llm_request_logs with call_site=compactionAgent", ()
|
|
|
194
194
|
expect(recordRequestLogCalls[0]!.callSite).toBe("compactionAgent");
|
|
195
195
|
});
|
|
196
196
|
});
|
|
197
|
-
|
|
198
|
-
describe("compaction result reports what actually served the summary", () => {
|
|
199
|
-
beforeEach(() => {
|
|
200
|
-
recordRequestLogCalls.length = 0;
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
test("rerouted summary carries the backup provider and profile", async () => {
|
|
204
|
-
// `RetryProvider` escalated the summary call to a backup profile on
|
|
205
|
-
// another provider and stamped the response, so the result must report
|
|
206
|
-
// the backup even though the compactor resolved `primaryProfile`.
|
|
207
|
-
const provider: Provider = {
|
|
208
|
-
name: "openai",
|
|
209
|
-
sendMessage: async () => ({
|
|
210
|
-
content: [{ type: "text", text: compactionResponse }],
|
|
211
|
-
model: "claude-sonnet-5",
|
|
212
|
-
actualProvider: "anthropic",
|
|
213
|
-
actualInferenceProfile: "backupProfile",
|
|
214
|
-
usage: { inputTokens: 100, outputTokens: 50 },
|
|
215
|
-
stopReason: "end_turn",
|
|
216
|
-
rawRequest: RAW_REQUEST,
|
|
217
|
-
rawResponse: RAW_RESPONSE,
|
|
218
|
-
}),
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
const result = await runAssistantDrivenCompaction({
|
|
222
|
-
...args(provider),
|
|
223
|
-
overrideProfile: "primaryProfile",
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
expect(result.summaryModel).toBe("claude-sonnet-5");
|
|
227
|
-
expect(result.summaryActualProvider).toBe("anthropic");
|
|
228
|
-
expect(result.summaryActualInferenceProfile).toBe("backupProfile");
|
|
229
|
-
// The primary's resolution is still reported, just outranked downstream.
|
|
230
|
-
expect(result.summaryOverrideProfile).toBe("primaryProfile");
|
|
231
|
-
// Same value the request log recorded: one source of truth, not two.
|
|
232
|
-
expect(recordRequestLogCalls[0]!.provider).toBe("anthropic");
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
test("non-rerouted summary carries the configured provider and no profile", async () => {
|
|
236
|
-
const provider: Provider = {
|
|
237
|
-
name: "openai",
|
|
238
|
-
sendMessage: async () => ({
|
|
239
|
-
content: [{ type: "text", text: compactionResponse }],
|
|
240
|
-
model: "mock-model",
|
|
241
|
-
usage: { inputTokens: 100, outputTokens: 50 },
|
|
242
|
-
stopReason: "end_turn",
|
|
243
|
-
rawRequest: RAW_REQUEST,
|
|
244
|
-
rawResponse: RAW_RESPONSE,
|
|
245
|
-
}),
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
const result = await runAssistantDrivenCompaction({
|
|
249
|
-
...args(provider),
|
|
250
|
-
overrideProfile: "primaryProfile",
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
expect(result.summaryActualProvider).toBe("openai");
|
|
254
|
-
expect(result.summaryActualInferenceProfile).toBeUndefined();
|
|
255
|
-
expect(result.summaryOverrideProfile).toBe("primaryProfile");
|
|
256
|
-
});
|
|
257
|
-
});
|
|
@@ -1411,71 +1411,6 @@ describe("loadConfig startup behavior", () => {
|
|
|
1411
1411
|
expect(effective.balanced?.provider_connection).toBe("anthropic-personal");
|
|
1412
1412
|
});
|
|
1413
1413
|
|
|
1414
|
-
test("an active managed backup profile survives a reload", () => {
|
|
1415
|
-
// A managed backup resolves from the code catalog without being
|
|
1416
|
-
// materialized in `llm.profiles`, so selecting one persists nothing but
|
|
1417
|
-
// the reference. If the schema did not treat backup keys as
|
|
1418
|
-
// always-available references, the strip-and-reparse in `loadConfig`
|
|
1419
|
-
// would drop `activeProfile` and the seeder would reset the user's
|
|
1420
|
-
// chat-model selection to `balanced` on the next boot.
|
|
1421
|
-
writeConfig({
|
|
1422
|
-
llm: {
|
|
1423
|
-
defaultProvider: { provider: "vellum" },
|
|
1424
|
-
profiles: {},
|
|
1425
|
-
activeProfile: "balanced-backup",
|
|
1426
|
-
},
|
|
1427
|
-
});
|
|
1428
|
-
|
|
1429
|
-
const config = loadConfig();
|
|
1430
|
-
expect(config.llm.activeProfile).toBe("balanced-backup");
|
|
1431
|
-
|
|
1432
|
-
mergeDefaultConfigAndSeedInferenceProfiles();
|
|
1433
|
-
|
|
1434
|
-
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
1435
|
-
expect(raw.llm.activeProfile).toBe("balanced-backup");
|
|
1436
|
-
expect(raw.llm.profiles["balanced-backup"]).toBeUndefined();
|
|
1437
|
-
});
|
|
1438
|
-
|
|
1439
|
-
test("an active managed backup profile is repaired on a BYOK install", () => {
|
|
1440
|
-
// Backups live on the managed column alone, so under a BYOK default
|
|
1441
|
-
// provider the selection names a profile that resolves to nothing. The
|
|
1442
|
-
// schema rejects the reference on load and the seeder, which resolves
|
|
1443
|
-
// availability through the same provider-aware view, repairs the stored
|
|
1444
|
-
// selection instead of preserving a dead pointer.
|
|
1445
|
-
writeConfig({
|
|
1446
|
-
llm: {
|
|
1447
|
-
defaultProvider: { provider: "anthropic" },
|
|
1448
|
-
profiles: {},
|
|
1449
|
-
activeProfile: "balanced-backup",
|
|
1450
|
-
},
|
|
1451
|
-
});
|
|
1452
|
-
|
|
1453
|
-
const config = loadConfig();
|
|
1454
|
-
expect(config.llm.activeProfile).toBeUndefined();
|
|
1455
|
-
|
|
1456
|
-
mergeDefaultConfigAndSeedInferenceProfiles();
|
|
1457
|
-
|
|
1458
|
-
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
1459
|
-
expect(raw.llm.activeProfile).toBe("balanced");
|
|
1460
|
-
});
|
|
1461
|
-
|
|
1462
|
-
test("a call site pinned to a managed backup profile survives a reload", () => {
|
|
1463
|
-
writeConfig({
|
|
1464
|
-
llm: {
|
|
1465
|
-
defaultProvider: { provider: "vellum" },
|
|
1466
|
-
profiles: {},
|
|
1467
|
-
activeProfile: "balanced",
|
|
1468
|
-
callSites: { recall: { profile: "cost-optimized-backup" } },
|
|
1469
|
-
},
|
|
1470
|
-
});
|
|
1471
|
-
|
|
1472
|
-
const config = loadConfig();
|
|
1473
|
-
expect(config.llm.callSites.recall?.profile).toBe("cost-optimized-backup");
|
|
1474
|
-
|
|
1475
|
-
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
1476
|
-
expect(raw.llm.callSites.recall.profile).toBe("cost-optimized-backup");
|
|
1477
|
-
});
|
|
1478
|
-
|
|
1479
1414
|
test("still quarantines corrupt JSON", () => {
|
|
1480
1415
|
// Corrupt-config quarantine is a recovery path: the broken file is
|
|
1481
1416
|
// renamed to `config.json.corrupt-<ts>.json` and the daemon proceeds
|
|
@@ -1865,10 +1800,6 @@ describe("seedInferenceProfiles BYOK-mode default profiles", () => {
|
|
|
1865
1800
|
"quality-optimized",
|
|
1866
1801
|
"cost-optimized",
|
|
1867
1802
|
"latency-optimized",
|
|
1868
|
-
"balanced-backup",
|
|
1869
|
-
"quality-optimized-backup",
|
|
1870
|
-
"cost-optimized-backup",
|
|
1871
|
-
"latency-optimized-backup",
|
|
1872
1803
|
"my-custom",
|
|
1873
1804
|
]);
|
|
1874
1805
|
});
|