@vellumai/assistant 0.11.5 → 0.11.6-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +5 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +118 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +103 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +118 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +103 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +70 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +16 -1
- package/node_modules/@vellumai/gateway-client/src/index.ts +6 -2
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +121 -61
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +118 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +103 -0
- package/openapi.yaml +421 -15
- package/package.json +1 -1
- package/scripts/sync-web-search-catalog.ts +6 -0
- package/src/__tests__/app-pin-store.test.ts +149 -0
- package/src/__tests__/channel-availability-routes.test.ts +23 -1
- package/src/__tests__/channel-readiness-discord.test.ts +231 -0
- package/src/__tests__/channel-readiness-service.test.ts +126 -0
- package/src/__tests__/channel-readiness-slack-remote.test.ts +141 -0
- package/src/__tests__/channel-reply-delivery.test.ts +4 -4
- package/src/__tests__/client-os-metadata-persistence.test.ts +23 -10
- package/src/__tests__/conversation-delete-watch-timeline.test.ts +231 -0
- package/src/__tests__/conversation-error.test.ts +17 -0
- package/src/__tests__/conversation-seed-composer.test.ts +8 -0
- package/src/__tests__/conversation-slash-commands.test.ts +8 -0
- package/src/__tests__/disk-pressure-policy.test.ts +6 -0
- package/src/__tests__/gemini-provider.test.ts +138 -0
- package/src/__tests__/history-repair.test.ts +105 -3
- package/src/__tests__/identity-routes.test.ts +1 -0
- package/src/__tests__/llm-catalog-parity.test.ts +45 -0
- package/src/__tests__/migration-import-from-path.test.ts +349 -0
- package/src/__tests__/notification-telegram-adapter.test.ts +102 -0
- package/src/__tests__/oauth-commands-routes.test.ts +89 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +7 -6
- package/src/__tests__/openai-provider.test.ts +18 -0
- package/src/__tests__/openai-responses-provider.test.ts +18 -0
- package/src/__tests__/platform-callback-registration.test.ts +184 -0
- package/src/__tests__/plugin-api-store-credential.test.ts +71 -3
- package/src/__tests__/pricing.test.ts +2 -2
- package/src/__tests__/public-ingress-urls.test.ts +36 -0
- package/src/__tests__/resolve-trust-class.test.ts +0 -48
- package/src/__tests__/sanitize-config-for-transfer.test.ts +28 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +49 -0
- package/src/__tests__/settings-routes.test.ts +85 -3
- package/src/__tests__/web-search-catalog-parity.test.ts +8 -0
- package/src/agent/history-repair/history-repair.ts +45 -14
- package/src/agent/loop.ts +4 -1
- package/src/api/constants/profile-config-validation.ts +60 -0
- package/src/api/events/tool-result.ts +6 -1
- package/src/api/events/watch-retro-completed.ts +52 -0
- package/src/api/index.ts +11 -0
- package/src/apps/app-pin-reconciler.ts +92 -0
- package/src/apps/app-pin-store.ts +125 -0
- package/src/channels/gateway-channel-socket-health.ts +32 -0
- package/src/channels/gateway-discord-admission.ts +32 -0
- package/src/channels/types.ts +20 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -1
- package/src/cli/commands/__tests__/inference-profiles.test.ts +16 -4
- package/src/cli/commands/__tests__/inference-providers.test.ts +67 -2
- package/src/cli/commands/channels/__tests__/channels.test.ts +85 -0
- package/src/cli/commands/channels/index.ts +45 -31
- package/src/cli/commands/inference-profiles.ts +56 -3
- package/src/cli/commands/inference-providers.ts +28 -2
- package/src/cli/commands/oauth/index.help.ts +7 -1
- package/src/cli/commands/oauth/request.test.ts +290 -0
- package/src/cli/commands/oauth/request.ts +57 -41
- package/src/cli/lib/bundled-marketplace.json +14 -1
- package/src/cli/lib/open-browser.test.ts +67 -0
- package/src/cli/lib/open-browser.ts +24 -5
- package/src/config/__tests__/profile-materialization.test.ts +26 -0
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +6 -0
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/profile-materialization.ts +29 -0
- package/src/config/sanitize-for-transfer.ts +16 -0
- package/src/config/schemas/llm.ts +7 -0
- package/src/config/schemas/services.ts +6 -0
- package/src/context/outbound-sanitize.ts +6 -0
- package/src/daemon/__tests__/lifecycle-watch-timeline-sweep.test.ts +98 -0
- package/src/daemon/conversation-error.ts +24 -2
- package/src/daemon/conversation-slash.ts +6 -15
- package/src/daemon/daemon-control.ts +1 -0
- package/src/daemon/disk-pressure-policy.ts +7 -1
- package/src/daemon/handlers/__tests__/config-ingress-tunnel-records.test.ts +208 -0
- package/src/daemon/handlers/config-ingress.ts +115 -5
- package/src/daemon/lifecycle.ts +26 -0
- package/src/daemon/message-types/web-activity.ts +3 -2
- package/src/daemon/trust-context.ts +0 -37
- package/src/inbound/__tests__/tunnel-probe.test.ts +448 -0
- package/src/inbound/platform-callback-registration.ts +28 -2
- package/src/inbound/public-ingress-urls.ts +12 -0
- package/src/inbound/tunnel-probe.ts +261 -0
- package/src/live-voice/__tests__/live-voice-connection.test.ts +25 -0
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +8 -2
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +212 -10
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +5 -2
- package/src/live-voice/live-voice-connection.ts +46 -8
- package/src/live-voice/live-voice-manager.ts +25 -0
- package/src/live-voice/live-voice-session-manager.ts +318 -2
- package/src/live-voice/live-voice-session.ts +52 -2
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +126 -68
- package/src/messaging/providers/channel-transport.ts +64 -47
- package/src/messaging/providers/discord/send.test.ts +46 -1
- package/src/messaging/providers/discord/send.ts +51 -0
- package/src/messaging/providers/discord/transport.ts +26 -3
- package/src/messaging/providers/index.ts +22 -47
- package/src/messaging/providers/slack/send.test.ts +83 -26
- package/src/messaging/providers/slack/send.ts +120 -51
- package/src/messaging/providers/slack/stream-tasks.test.ts +26 -0
- package/src/messaging/providers/slack/stream-tasks.ts +39 -0
- package/src/messaging/providers/slack/transport.ts +24 -22
- package/src/messaging/providers/telegram-bot/send.test.ts +109 -12
- package/src/messaging/providers/telegram-bot/send.ts +43 -0
- package/src/messaging/providers/telegram-bot/transport.ts +25 -8
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +30 -7
- package/src/notifications/adapters/telegram.ts +48 -1
- package/src/notifications/assistant-reply-producer.ts +7 -7
- package/src/notifications/conversation-seed-composer.ts +7 -2
- package/src/oauth/byo-connection.test.ts +63 -0
- package/src/oauth/byo-connection.ts +16 -15
- package/src/oauth/connection.test.ts +111 -0
- package/src/oauth/connection.ts +142 -1
- package/src/oauth/platform-connection.test.ts +34 -0
- package/src/oauth/platform-connection.ts +28 -5
- package/src/oauth/seed-providers.ts +15 -1
- package/src/permissions/types.ts +3 -1
- package/src/persistence/conversation-crud.ts +46 -0
- package/src/persistence/conversation-types.ts +11 -9
- package/src/persistence/db-async-query.ts +2 -1
- package/src/persistence/db-maintenance.ts +15 -0
- package/src/persistence/embeddings/qdrant-manager.ts +1 -0
- package/src/persistence/migrations/367-create-watch-timeline-entries.ts +46 -0
- package/src/persistence/migrations/368-watch-timeline-screenshot-blob.ts +33 -0
- package/src/persistence/migrations/369-create-app-pins.ts +37 -0
- package/src/persistence/migrations/__tests__/367-create-watch-timeline-entries.test.ts +98 -0
- package/src/persistence/migrations/__tests__/368-watch-timeline-screenshot-blob.test.ts +98 -0
- package/src/persistence/schema/index.ts +1 -0
- package/src/persistence/schema/infrastructure.ts +17 -0
- package/src/persistence/schema/watch.ts +29 -0
- package/src/persistence/steps.ts +6 -0
- package/src/plugins/mtime-cache.ts +11 -0
- package/src/providers/__tests__/retry-network-error.test.ts +84 -0
- package/src/providers/connection-resolution.ts +23 -1
- package/src/providers/content-blocks.ts +9 -0
- package/src/providers/fetch-provider-catalog.ts +19 -0
- package/src/providers/gemini/client.ts +13 -5
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +92 -0
- package/src/providers/inference/__tests__/profile-config-validation.test.ts +39 -0
- package/src/providers/inference/__tests__/profile-probe-classify.test.ts +66 -0
- package/src/providers/inference/adapter-factory.ts +0 -9
- package/src/providers/inference/credential-rotation.ts +61 -0
- package/src/providers/inference/endpoint-probe.ts +115 -0
- package/src/providers/inference/profile-probe.ts +256 -0
- package/src/providers/model-catalog.ts +170 -125
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +17 -1
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +42 -60
- package/src/providers/openai/__tests__/connection-error-wrap.test.ts +44 -0
- package/src/providers/openai/__tests__/orphan-tool-result-guard.test.ts +34 -2
- package/src/providers/openai/api-error-normalization.ts +16 -2
- package/src/providers/openai/chat-completions-provider.ts +75 -29
- package/src/providers/openai/responses-provider.ts +5 -2
- package/src/providers/openrouter/client.ts +0 -1
- package/src/providers/provider-send-message.ts +11 -0
- package/src/providers/retry.ts +6 -0
- package/src/providers/search-provider-catalog.ts +20 -0
- package/src/providers/vercel-ai-gateway/client.ts +0 -1
- package/src/runtime/AGENTS.md +1 -0
- package/src/runtime/__tests__/desktop-presence.test.ts +27 -4
- package/src/runtime/__tests__/host-observe.test.ts +302 -0
- package/src/runtime/channel-readiness-service.ts +214 -14
- package/src/runtime/channel-readiness-types.ts +49 -2
- package/src/runtime/channel-reply-delivery.ts +2 -2
- package/src/runtime/desktop-presence.ts +24 -21
- package/src/runtime/host-observe.ts +246 -0
- package/src/runtime/http-server.ts +181 -1
- package/src/runtime/migrations/__tests__/staged-import-path.test.ts +104 -0
- package/src/runtime/migrations/staged-import-path.ts +116 -0
- package/src/runtime/routes/__tests__/app-pin-routes.test.ts +383 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +80 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +118 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +20 -0
- package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +508 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +35 -56
- package/src/runtime/routes/__tests__/watch-routes-guardian-cache.test.ts +139 -0
- package/src/runtime/routes/__tests__/watch-routes.test.ts +598 -0
- package/src/runtime/routes/app-management-routes.ts +140 -29
- package/src/runtime/routes/channel-availability-routes.ts +1 -0
- package/src/runtime/routes/channel-readiness-routes.ts +14 -2
- package/src/runtime/routes/conversation-query-routes.ts +10 -0
- package/src/runtime/routes/guardian-approval-interception.ts +24 -33
- package/src/runtime/routes/host-cu-routes.ts +18 -0
- package/src/runtime/routes/identity-routes.ts +2 -0
- package/src/runtime/routes/inbound-message-handler.ts +10 -7
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +166 -308
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +158 -335
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-profiles-routes.ts +232 -31
- package/src/runtime/routes/inference-provider-connection-routes.ts +24 -4
- package/src/runtime/routes/ingress-status-routes.ts +180 -0
- package/src/runtime/routes/live-voice-routes.test.ts +40 -1
- package/src/runtime/routes/live-voice-routes.ts +34 -0
- package/src/runtime/routes/migration-routes.ts +218 -10
- package/src/runtime/routes/oauth-commands-routes.ts +23 -16
- package/src/runtime/routes/plugins-routes.ts +12 -28
- package/src/runtime/routes/question-routes.ts +6 -0
- package/src/runtime/routes/secret-routes.ts +7 -27
- package/src/runtime/routes/settings-routes.ts +9 -6
- package/src/runtime/routes/watch-routes.ts +807 -0
- package/src/runtime/slack-reply-session.test.ts +230 -121
- package/src/runtime/slack-reply-session.ts +113 -81
- package/src/runtime/{slack-task-progress.test.ts → task-progress.test.ts} +1 -28
- package/src/runtime/{slack-task-progress.ts → task-progress.ts} +30 -51
- package/src/security/__tests__/untrusted-content.test.ts +42 -0
- package/src/security/untrusted-content.ts +28 -9
- package/src/telemetry/__tests__/live-voice-funnel.test.ts +108 -0
- package/src/telemetry/live-voice-funnel.ts +75 -8
- package/src/tools/credentials/store.ts +18 -6
- package/src/tools/network/__tests__/firecrawl-compat.test.ts +77 -0
- package/src/tools/network/__tests__/web-fetch-fastcrw.test.ts +169 -0
- package/src/tools/network/__tests__/web-search.test.ts +97 -2
- package/src/tools/network/firecrawl-compat.ts +90 -0
- package/src/tools/network/web-fetch.ts +142 -62
- package/src/tools/network/web-search.ts +141 -55
- package/src/tools/types.ts +2 -1
- package/src/util/oauth-request-body.test.ts +74 -0
- package/src/util/oauth-request-body.ts +60 -0
- package/src/util/worker-process.ts +1 -0
- package/src/watch/__tests__/watch-retro.test.ts +665 -0
- package/src/watch/__tests__/watch-session-manager.test.ts +566 -0
- package/src/watch/__tests__/watch-timeline.test.ts +670 -0
- package/src/watch/watch-retro.ts +480 -0
- package/src/watch/watch-session-manager.ts +575 -0
- package/src/watch/watch-timeline.ts +848 -0
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
getTwilioMediaStreamUrl,
|
|
13
13
|
getTwilioStatusCallbackUrl,
|
|
14
14
|
getTwilioVoiceWebhookUrl,
|
|
15
|
+
tryGetPublicBaseUrl,
|
|
15
16
|
} from "../inbound/public-ingress-urls.js";
|
|
16
17
|
|
|
17
18
|
// ---------------------------------------------------------------------------
|
|
@@ -163,6 +164,41 @@ describe("getPublicBaseUrl", () => {
|
|
|
163
164
|
});
|
|
164
165
|
});
|
|
165
166
|
|
|
167
|
+
describe("tryGetPublicBaseUrl", () => {
|
|
168
|
+
beforeEach(() => {
|
|
169
|
+
setIngressPublicBaseUrl(undefined);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
afterEach(() => {
|
|
173
|
+
setIngressPublicBaseUrl(undefined);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("returns the configured public base URL", () => {
|
|
177
|
+
expect(
|
|
178
|
+
tryGetPublicBaseUrl({
|
|
179
|
+
ingress: { publicBaseUrl: "https://ingress.example.com/" },
|
|
180
|
+
}),
|
|
181
|
+
).toBe("https://ingress.example.com");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("returns the detected module-level ingress URL", () => {
|
|
185
|
+
setIngressPublicBaseUrl("https://detected.example.com/");
|
|
186
|
+
expect(tryGetPublicBaseUrl({})).toBe("https://detected.example.com");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("returns undefined when no URL is configured", () => {
|
|
190
|
+
expect(tryGetPublicBaseUrl({})).toBeUndefined();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("returns undefined when ingress is explicitly disabled", () => {
|
|
194
|
+
expect(
|
|
195
|
+
tryGetPublicBaseUrl({
|
|
196
|
+
ingress: { enabled: false, publicBaseUrl: "https://example.com" },
|
|
197
|
+
}),
|
|
198
|
+
).toBeUndefined();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
166
202
|
// ---------------------------------------------------------------------------
|
|
167
203
|
// Twilio-specific public base URL selection
|
|
168
204
|
// ---------------------------------------------------------------------------
|
|
@@ -13,7 +13,6 @@ mock.module("../config/env.js", () => ({
|
|
|
13
13
|
|
|
14
14
|
import {
|
|
15
15
|
FALLBACK_TURN_TRUST,
|
|
16
|
-
mapChatTypeToConversationType,
|
|
17
16
|
resolveTrustClass,
|
|
18
17
|
} from "../daemon/trust-context.js";
|
|
19
18
|
import type { TrustContext } from "../daemon/trust-context-types.js";
|
|
@@ -73,50 +72,3 @@ describe("resolveTrustClass", () => {
|
|
|
73
72
|
expect(resolveTrustClass(FALLBACK_TURN_TRUST)).toBe("unknown");
|
|
74
73
|
});
|
|
75
74
|
});
|
|
76
|
-
|
|
77
|
-
describe("mapChatTypeToConversationType", () => {
|
|
78
|
-
test("maps DM-shaped chat types to dm", () => {
|
|
79
|
-
expect(mapChatTypeToConversationType("im")).toBe("dm"); // Slack DM
|
|
80
|
-
expect(mapChatTypeToConversationType("private")).toBe("dm"); // Telegram DM
|
|
81
|
-
expect(mapChatTypeToConversationType("dm")).toBe("dm"); // Discord DM
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("every DM-capable channel's own word for a DM lands on the axis", () => {
|
|
85
|
-
// A missing arm is silent rather than loud: the permission cell stays
|
|
86
|
-
// settable, because the adapter validates against the channel registry,
|
|
87
|
-
// and simply never matches. The guardian sets a rule for DMs on that
|
|
88
|
-
// channel and it does nothing. Stated as a sweep so adding a DM-capable
|
|
89
|
-
// channel without its arm fails here.
|
|
90
|
-
const dmChatTypeByChannel = {
|
|
91
|
-
slack: "im",
|
|
92
|
-
telegram: "private",
|
|
93
|
-
whatsapp: "private",
|
|
94
|
-
discord: "dm",
|
|
95
|
-
};
|
|
96
|
-
for (const [channel, chatType] of Object.entries(dmChatTypeByChannel)) {
|
|
97
|
-
expect(`${channel}:${mapChatTypeToConversationType(chatType)}`).toBe(
|
|
98
|
-
`${channel}:dm`,
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test("maps closed-group chat types to private", () => {
|
|
104
|
-
expect(mapChatTypeToConversationType("mpim")).toBe("private"); // Slack multi-party DM
|
|
105
|
-
expect(mapChatTypeToConversationType("group")).toBe("private");
|
|
106
|
-
expect(mapChatTypeToConversationType("supergroup")).toBe("private");
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test("maps Slack 'channel' to undefined — public and private are indistinguishable", () => {
|
|
110
|
-
// The gateway forwards every non-DM Slack conversation as "channel"
|
|
111
|
-
// without a public/private distinction. Mapping it to "public" would let
|
|
112
|
-
// a permissive public-channel matrix cell govern private channels, so
|
|
113
|
-
// the channel-type tier must not match at all.
|
|
114
|
-
expect(mapChatTypeToConversationType("channel")).toBeUndefined();
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test("maps unknown or absent chat types to undefined", () => {
|
|
118
|
-
expect(mapChatTypeToConversationType(undefined)).toBeUndefined();
|
|
119
|
-
expect(mapChatTypeToConversationType("")).toBeUndefined();
|
|
120
|
-
expect(mapChatTypeToConversationType("broadcast")).toBeUndefined();
|
|
121
|
-
});
|
|
122
|
-
});
|
|
@@ -155,6 +155,34 @@ describe("sanitizeConfigForTransfer", () => {
|
|
|
155
155
|
});
|
|
156
156
|
});
|
|
157
157
|
|
|
158
|
+
test("strips the remembered tunnels and the assistant they fronted", () => {
|
|
159
|
+
// Every record describes the source host's tunnels; a destination that
|
|
160
|
+
// inherited them would report the old deployment's tunnel as its own, and
|
|
161
|
+
// advertise an address on the source's tailnet for pairing.
|
|
162
|
+
const input = {
|
|
163
|
+
ingress: {
|
|
164
|
+
publicBaseUrl: "https://source.ngrok.app",
|
|
165
|
+
lastTunnel: {
|
|
166
|
+
provider: "ngrok",
|
|
167
|
+
publicBaseUrl: "https://source.ngrok.app",
|
|
168
|
+
},
|
|
169
|
+
pairingTunnel: {
|
|
170
|
+
provider: "tailscale",
|
|
171
|
+
publicBaseUrl: "https://source-host.example.ts.net",
|
|
172
|
+
},
|
|
173
|
+
assistantId: "source-assistant",
|
|
174
|
+
webhook: { path: "/webhook" },
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const result = JSON.parse(sanitizeConfigForTransfer(JSON.stringify(input)));
|
|
179
|
+
|
|
180
|
+
expect(result.ingress).toEqual({
|
|
181
|
+
publicBaseUrl: "",
|
|
182
|
+
webhook: { path: "/webhook" },
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
158
186
|
test("preserves nested ingress fields other than environment-specific URL state", () => {
|
|
159
187
|
const input = {
|
|
160
188
|
ingress: {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import { eq } from "drizzle-orm";
|
|
4
|
+
|
|
3
5
|
import { getConfig } from "../config/loader.js";
|
|
6
|
+
import { getDb } from "../persistence/db-connection.js";
|
|
7
|
+
import { providerConnections } from "../persistence/schema/inference.js";
|
|
4
8
|
import { credentialKey } from "../security/credential-key.js";
|
|
5
9
|
|
|
6
10
|
let lastGeminiConstructorOpts: Record<string, unknown> | null = null;
|
|
@@ -160,6 +164,10 @@ describe("secret routes managed proxy registry sync", () => {
|
|
|
160
164
|
lastGeminiConstructorOpts = null;
|
|
161
165
|
platformBaseUrlOverride = undefined;
|
|
162
166
|
providerRefreshCalls = 0;
|
|
167
|
+
getDb()
|
|
168
|
+
.delete(providerConnections)
|
|
169
|
+
.where(eq(providerConnections.name, "openrouter-connection"))
|
|
170
|
+
.run();
|
|
163
171
|
await initializeProviders(getConfig());
|
|
164
172
|
});
|
|
165
173
|
|
|
@@ -252,6 +260,47 @@ describe("secret routes managed proxy registry sync", () => {
|
|
|
252
260
|
expect(providerRefreshCalls).toBe(2);
|
|
253
261
|
});
|
|
254
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Verifies that rotating a credential refreshes providers for dependent connections.
|
|
265
|
+
*/
|
|
266
|
+
test("non-managed credentials refresh providers when a connection uses them", async () => {
|
|
267
|
+
// GIVEN a non-managed credential.
|
|
268
|
+
// AND a provider connection references its canonical credential key.
|
|
269
|
+
const now = Date.now();
|
|
270
|
+
getDb()
|
|
271
|
+
.insert(providerConnections)
|
|
272
|
+
.values({
|
|
273
|
+
name: "openrouter-connection",
|
|
274
|
+
provider: "openai",
|
|
275
|
+
auth: JSON.stringify({
|
|
276
|
+
type: "api_key",
|
|
277
|
+
credential: credentialKey("openrouter", "api_key"),
|
|
278
|
+
}),
|
|
279
|
+
createdAt: now,
|
|
280
|
+
updatedAt: now,
|
|
281
|
+
})
|
|
282
|
+
.run();
|
|
283
|
+
|
|
284
|
+
// WHEN the credential is rotated.
|
|
285
|
+
await addCredential("openrouter:api_key", "openrouter-key");
|
|
286
|
+
|
|
287
|
+
// THEN the provider refresh runs once.
|
|
288
|
+
expect(providerRefreshCalls).toBe(1);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Verifies that rotating an unused credential does not refresh providers.
|
|
293
|
+
*/
|
|
294
|
+
test("non-managed credentials do not refresh providers when no connection uses them", async () => {
|
|
295
|
+
// GIVEN a non-managed credential.
|
|
296
|
+
// AND no provider connection references its credential key.
|
|
297
|
+
// WHEN the credential is rotated.
|
|
298
|
+
await addCredential("openrouter:api_key", "openrouter-key");
|
|
299
|
+
|
|
300
|
+
// THEN the provider refresh does not run.
|
|
301
|
+
expect(providerRefreshCalls).toBe(0);
|
|
302
|
+
});
|
|
303
|
+
|
|
255
304
|
test("storing vellum:platform_base_url sets override and triggers initializeProviders", async () => {
|
|
256
305
|
await addCredential(
|
|
257
306
|
"vellum:platform_base_url",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tests for the `workspace-files` list/read endpoints in
|
|
2
|
+
* Tests for the `workspace-files` list/read and ingress-config endpoints in
|
|
3
|
+
* settings-routes.
|
|
3
4
|
*
|
|
4
5
|
* Focus: the list includes the guardian's per-user persona file
|
|
5
|
-
* (`users/<slug>.md`) whenever a guardian exists,
|
|
6
|
-
*
|
|
6
|
+
* (`users/<slug>.md`) whenever a guardian exists, the read endpoint accepts
|
|
7
|
+
* paths under `users/`, and writing a different ingress URL drops the tunnel
|
|
8
|
+
* records that described the old one.
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
@@ -194,3 +196,83 @@ describe("GET /workspace-files/read", () => {
|
|
|
194
196
|
);
|
|
195
197
|
});
|
|
196
198
|
});
|
|
199
|
+
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// PUT /integrations/ingress/config
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
describe("PUT integrations/ingress/config", () => {
|
|
205
|
+
const handler = getHandler("integrations/ingress/config", "PUT");
|
|
206
|
+
const configPath = join(testWorkspaceDir, "config.json");
|
|
207
|
+
|
|
208
|
+
const TUNNEL_URL = "https://assistant-1.example.ts.net";
|
|
209
|
+
const LAST_TUNNEL = { provider: "tailscale", publicBaseUrl: TUNNEL_URL };
|
|
210
|
+
|
|
211
|
+
function readConfig(): Record<string, unknown> {
|
|
212
|
+
return existsSync(configPath)
|
|
213
|
+
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
214
|
+
: {};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function seedIngress(ingress: Record<string, unknown>): void {
|
|
218
|
+
writeFileSync(
|
|
219
|
+
configPath,
|
|
220
|
+
JSON.stringify({ ...readConfig(), ingress }, null, 2),
|
|
221
|
+
"utf-8",
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function readIngress(): Record<string, unknown> {
|
|
226
|
+
return readConfig().ingress as Record<string, unknown>;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
beforeEach(() => {
|
|
230
|
+
seedIngress({
|
|
231
|
+
publicBaseUrl: TUNNEL_URL,
|
|
232
|
+
enabled: true,
|
|
233
|
+
assistantId: "assistant-1",
|
|
234
|
+
lastTunnel: LAST_TUNNEL,
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("keeps the records when the URL is unchanged", async () => {
|
|
239
|
+
await handler(makeArgs({}, { publicBaseUrl: TUNNEL_URL, enabled: false }));
|
|
240
|
+
|
|
241
|
+
const ingress = readIngress();
|
|
242
|
+
expect(ingress.assistantId).toBe("assistant-1");
|
|
243
|
+
expect(ingress.lastTunnel).toEqual(LAST_TUNNEL);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test("ignores trailing slashes and whitespace when comparing", async () => {
|
|
247
|
+
await handler(
|
|
248
|
+
makeArgs({}, { publicBaseUrl: ` ${TUNNEL_URL}/ `, enabled: true }),
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
const ingress = readIngress();
|
|
252
|
+
expect(ingress.assistantId).toBe("assistant-1");
|
|
253
|
+
expect(ingress.lastTunnel).toEqual(LAST_TUNNEL);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("drops the records when the URL is retargeted", async () => {
|
|
257
|
+
await handler(
|
|
258
|
+
makeArgs(
|
|
259
|
+
{},
|
|
260
|
+
{ publicBaseUrl: "https://other.example.ts.net", enabled: true },
|
|
261
|
+
),
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const ingress = readIngress();
|
|
265
|
+
expect(ingress.publicBaseUrl).toBe("https://other.example.ts.net");
|
|
266
|
+
expect(ingress.assistantId).toBeUndefined();
|
|
267
|
+
expect(ingress.lastTunnel).toBeUndefined();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("drops the records when the URL is cleared", async () => {
|
|
271
|
+
await handler(makeArgs({}, { publicBaseUrl: "", enabled: false }));
|
|
272
|
+
|
|
273
|
+
const ingress = readIngress();
|
|
274
|
+
expect(ingress.publicBaseUrl).toBeUndefined();
|
|
275
|
+
expect(ingress.assistantId).toBeUndefined();
|
|
276
|
+
expect(ingress.lastTunnel).toBeUndefined();
|
|
277
|
+
});
|
|
278
|
+
});
|
|
@@ -29,6 +29,8 @@ interface JsonCatalogEntry {
|
|
|
29
29
|
secretKey?: string;
|
|
30
30
|
fallbackOrder?: number;
|
|
31
31
|
privacyPolicyUrl?: string;
|
|
32
|
+
supportsApiBase?: boolean;
|
|
33
|
+
defaultApiBase?: string;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
interface JsonCatalog {
|
|
@@ -75,6 +77,12 @@ function entryToPlain(
|
|
|
75
77
|
if (entry.privacyPolicyUrl !== undefined) {
|
|
76
78
|
out.privacyPolicyUrl = entry.privacyPolicyUrl;
|
|
77
79
|
}
|
|
80
|
+
if (entry.supportsApiBase !== undefined) {
|
|
81
|
+
out.supportsApiBase = entry.supportsApiBase;
|
|
82
|
+
}
|
|
83
|
+
if (entry.defaultApiBase !== undefined) {
|
|
84
|
+
out.defaultApiBase = entry.defaultApiBase;
|
|
85
|
+
}
|
|
78
86
|
return out;
|
|
79
87
|
}
|
|
80
88
|
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* recover — so detection and repair of ordering drift live together as one unit.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import { isEffectivelyEmptyContent } from "../../providers/content-blocks.js";
|
|
18
19
|
import { analyzeServerToolPairing } from "../../providers/server-tool-pairing.js";
|
|
19
20
|
import type {
|
|
20
21
|
ContentBlock,
|
|
@@ -391,7 +392,7 @@ function formatWebSearchContent(content: unknown): string {
|
|
|
391
392
|
|
|
392
393
|
/**
|
|
393
394
|
* Provider rejection messages indicating a tool_use/tool_result pairing or
|
|
394
|
-
* message-ordering violation
|
|
395
|
+
* message-ordering violation. The structural drift `deepRepairHistory`
|
|
395
396
|
* re-normalizes.
|
|
396
397
|
*/
|
|
397
398
|
export const ORDERING_ERROR_PATTERNS: readonly RegExp[] = [
|
|
@@ -420,6 +421,19 @@ export const WEB_SEARCH_ORDERING_PATTERNS: readonly RegExp[] = [
|
|
|
420
421
|
/web_search.*tool_result/i,
|
|
421
422
|
];
|
|
422
423
|
|
|
424
|
+
/** Provider rejections for APIs that require the last history turn to be a user or function turn. */
|
|
425
|
+
export const USER_TERMINAL_HISTORY_PATTERNS: readonly RegExp[] = [
|
|
426
|
+
/ending with a model turn/i,
|
|
427
|
+
/must end with a user (turn|message)/i,
|
|
428
|
+
];
|
|
429
|
+
|
|
430
|
+
/** Whether a provider error message requires history to end with a user turn. */
|
|
431
|
+
export function isUserTerminalHistoryError(message: string): boolean {
|
|
432
|
+
return USER_TERMINAL_HISTORY_PATTERNS.some((pattern) =>
|
|
433
|
+
pattern.test(message),
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
423
437
|
/**
|
|
424
438
|
* Whether a provider error message denotes an ordering/pairing violation that
|
|
425
439
|
* `deepRepairHistory` can recover by re-normalizing the history. Detection and
|
|
@@ -429,21 +443,22 @@ export const WEB_SEARCH_ORDERING_PATTERNS: readonly RegExp[] = [
|
|
|
429
443
|
export function isRepairableOrderingError(message: string): boolean {
|
|
430
444
|
return (
|
|
431
445
|
ORDERING_ERROR_PATTERNS.some((pattern) => pattern.test(message)) ||
|
|
432
|
-
WEB_SEARCH_ORDERING_PATTERNS.some((pattern) => pattern.test(message))
|
|
446
|
+
WEB_SEARCH_ORDERING_PATTERNS.some((pattern) => pattern.test(message)) ||
|
|
447
|
+
isUserTerminalHistoryError(message)
|
|
433
448
|
);
|
|
434
449
|
}
|
|
435
450
|
|
|
436
451
|
/**
|
|
437
|
-
*
|
|
438
|
-
* -
|
|
439
|
-
*
|
|
440
|
-
* - Merges consecutive same-role messages (before tool-use/result repair)
|
|
441
|
-
* Then applies the standard repairHistory on top (which also merges any
|
|
442
|
-
* consecutive same-role messages introduced by tool-use/result repair).
|
|
452
|
+
* Removes effectively empty messages, ensures the first message is from the
|
|
453
|
+
* user, merges consecutive same-role messages, optionally ensures the history
|
|
454
|
+
* ends with a user message, and applies standard tool-use/result repair.
|
|
443
455
|
*/
|
|
444
|
-
export function deepRepairHistory(
|
|
445
|
-
|
|
446
|
-
|
|
456
|
+
export function deepRepairHistory(
|
|
457
|
+
messages: Message[],
|
|
458
|
+
options: { requireUserTerminal?: boolean } = {},
|
|
459
|
+
): RepairResult {
|
|
460
|
+
// 1. Remove messages with no serializable content
|
|
461
|
+
let cleaned = messages.filter((m) => !isEffectivelyEmptyContent(m.content));
|
|
447
462
|
|
|
448
463
|
// 2. Strip leading assistant messages (provider requires user-first)
|
|
449
464
|
while (cleaned.length > 0 && cleaned[0].role === "assistant") {
|
|
@@ -461,9 +476,25 @@ export function deepRepairHistory(messages: Message[]): RepairResult {
|
|
|
461
476
|
}
|
|
462
477
|
}
|
|
463
478
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
479
|
+
if (options.requireUserTerminal) {
|
|
480
|
+
let droppedCount = 0;
|
|
481
|
+
while (
|
|
482
|
+
merged.length > 0 &&
|
|
483
|
+
merged[merged.length - 1]?.role === "assistant"
|
|
484
|
+
) {
|
|
485
|
+
merged.pop();
|
|
486
|
+
droppedCount++;
|
|
487
|
+
}
|
|
488
|
+
if (droppedCount > 0) {
|
|
489
|
+
log.warn(
|
|
490
|
+
{ droppedCount },
|
|
491
|
+
"Dropped trailing assistant turns because the provider requires history to end with a user turn",
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Apply standard tool-use/tool-result repair on top. The inner mapping is
|
|
497
|
+
// keyed to `merged`'s indices, not the caller's input, so it is not returned.
|
|
467
498
|
const { messages: repaired, stats } = repairHistory(merged);
|
|
468
499
|
return { messages: repaired, stats };
|
|
469
500
|
}
|
package/src/agent/loop.ts
CHANGED
|
@@ -56,6 +56,7 @@ import { CompactionCircuit } from "./compaction-circuit.js";
|
|
|
56
56
|
import {
|
|
57
57
|
deepRepairHistory,
|
|
58
58
|
isRepairableOrderingError,
|
|
59
|
+
isUserTerminalHistoryError,
|
|
59
60
|
} from "./history-repair/history-repair.js";
|
|
60
61
|
|
|
61
62
|
const log = getLogger("agent-loop");
|
|
@@ -2538,7 +2539,9 @@ export class AgentLoop {
|
|
|
2538
2539
|
) {
|
|
2539
2540
|
orderingRepairAttempted = true;
|
|
2540
2541
|
postModelCallContinues++;
|
|
2541
|
-
history = deepRepairHistory(errorOutcome.messages
|
|
2542
|
+
history = deepRepairHistory(errorOutcome.messages, {
|
|
2543
|
+
requireUserTerminal: isUserTerminalHistoryError(err.message),
|
|
2544
|
+
}).messages;
|
|
2542
2545
|
// Deep repair merges and drops messages, so the prior input
|
|
2543
2546
|
// boundary no longer maps onto the new array; the repaired history
|
|
2544
2547
|
// is the base the retry's output appends after.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static sanity check for a profile's explicit token budget against the
|
|
3
|
+
* model's catalog limits. Catches configurations that can never dispatch
|
|
4
|
+
* (the output budget consumes the model's entire context window, or exceeds
|
|
5
|
+
* its output cap) at save time instead of on the first chat message.
|
|
6
|
+
*
|
|
7
|
+
* Shared surface: the daemon's profile write routes enforce it as a blocking
|
|
8
|
+
* error, and the web profile editor mirrors the same judgment before its
|
|
9
|
+
* generic config PATCH, so the two paths cannot drift. Only fires when both
|
|
10
|
+
* sides of a judgment are known; unlisted models are covered by the live
|
|
11
|
+
* save-time probe instead.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Input room a profile must leave when its output budget is judged against
|
|
16
|
+
* the model's total context window.
|
|
17
|
+
*/
|
|
18
|
+
export const MIN_INPUT_RESERVE_TOKENS = 4096;
|
|
19
|
+
|
|
20
|
+
export interface ProfileConfigIssue {
|
|
21
|
+
field: "maxTokens";
|
|
22
|
+
/** Which judgment fired, so clients can compose surface-specific copy. */
|
|
23
|
+
code: "over_output_cap" | "no_input_room";
|
|
24
|
+
message: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function validateInferenceProfileConfig(args: {
|
|
28
|
+
maxTokens?: number;
|
|
29
|
+
modelMaxOutputTokens?: number;
|
|
30
|
+
modelContextWindowTokens?: number;
|
|
31
|
+
}): ProfileConfigIssue | null {
|
|
32
|
+
const { maxTokens, modelMaxOutputTokens, modelContextWindowTokens } = args;
|
|
33
|
+
if (maxTokens === undefined) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (modelMaxOutputTokens !== undefined && maxTokens > modelMaxOutputTokens) {
|
|
37
|
+
return {
|
|
38
|
+
field: "maxTokens",
|
|
39
|
+
code: "over_output_cap",
|
|
40
|
+
message:
|
|
41
|
+
`maxTokens (${maxTokens}) exceeds the model's maximum output of ` +
|
|
42
|
+
`${modelMaxOutputTokens} tokens. Requests would be rejected upstream; ` +
|
|
43
|
+
`reduce maxTokens to ${modelMaxOutputTokens} or less.`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (
|
|
47
|
+
modelContextWindowTokens !== undefined &&
|
|
48
|
+
maxTokens >= modelContextWindowTokens - MIN_INPUT_RESERVE_TOKENS
|
|
49
|
+
) {
|
|
50
|
+
return {
|
|
51
|
+
field: "maxTokens",
|
|
52
|
+
code: "no_input_room",
|
|
53
|
+
message:
|
|
54
|
+
`maxTokens (${maxTokens}) reserves the entire ${modelContextWindowTokens}-token ` +
|
|
55
|
+
`context window for output, leaving no room for your messages. ` +
|
|
56
|
+
`Reduce maxTokens (e.g. 8000) so input fits.`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
@@ -50,11 +50,16 @@ export const WebSearchProviderIdSchema = z.enum([
|
|
|
50
50
|
"tavily",
|
|
51
51
|
"keenable",
|
|
52
52
|
"firecrawl",
|
|
53
|
+
"fastcrw",
|
|
53
54
|
]);
|
|
54
55
|
|
|
55
56
|
export type WebSearchProviderId = z.infer<typeof WebSearchProviderIdSchema>;
|
|
56
57
|
|
|
57
|
-
export const WebFetchProviderIdSchema = z.enum([
|
|
58
|
+
export const WebFetchProviderIdSchema = z.enum([
|
|
59
|
+
"default",
|
|
60
|
+
"firecrawl",
|
|
61
|
+
"fastcrw",
|
|
62
|
+
]);
|
|
58
63
|
|
|
59
64
|
export type WebFetchProviderId = z.infer<typeof WebFetchProviderIdSchema>;
|
|
60
65
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `watch_retro_completed` SSE event.
|
|
3
|
+
*
|
|
4
|
+
* Server → client broadcast marking the end of a watch session's
|
|
5
|
+
* retrospective, the turn `runWatchRetro` dispatches once the recording socket
|
|
6
|
+
* is gone (`assistant/src/watch/watch-retro.ts`).
|
|
7
|
+
*
|
|
8
|
+
* **The watch stream cannot carry this.** A session's socket sends `closed`
|
|
9
|
+
* and is torn down before the retro is dispatched, so by the time there is
|
|
10
|
+
* anything to report the transport the user pressed stop on no longer exists.
|
|
11
|
+
* Every client that draws a watch session is already subscribed to `/v1/events`
|
|
12
|
+
* for the assistant, which is the one channel still open at that point.
|
|
13
|
+
*
|
|
14
|
+
* **Global, not conversation-scoped.** `conversationId` names the thread the
|
|
15
|
+
* report was written into, which is a background conversation the user is
|
|
16
|
+
* almost never sitting in: a session is narrated while working in another app
|
|
17
|
+
* entirely. Clients route this by `sessionId` rather than against whichever
|
|
18
|
+
* conversation they have open, the same way `notification_conversation_created`
|
|
19
|
+
* announces a conversation other than the active one.
|
|
20
|
+
*
|
|
21
|
+
* **Emitted on every outcome, including the empty ones.** A client that told
|
|
22
|
+
* the user their session is being summarized has to be told when it is not
|
|
23
|
+
* coming, or it draws a progress affordance over nothing until it gives up on
|
|
24
|
+
* its own. `reportReady` is the whole of that distinction: true when the turn
|
|
25
|
+
* left an account of the session behind and the conversation has been surfaced
|
|
26
|
+
* for the user to open, false when the session recorded nothing or the turn
|
|
27
|
+
* failed to produce a report.
|
|
28
|
+
*
|
|
29
|
+
* Canonical wire-contract source. Daemon code imports the type directly from
|
|
30
|
+
* this file; external consumers import via `@vellumai/assistant-api`.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { z } from "zod";
|
|
34
|
+
|
|
35
|
+
export const WatchRetroCompletedEventSchema = z.object({
|
|
36
|
+
type: z.literal("watch_retro_completed"),
|
|
37
|
+
/**
|
|
38
|
+
* The watch session the retrospective was run for, as the `ready` frame on
|
|
39
|
+
* the watch stream named it. Clients match on this rather than on the
|
|
40
|
+
* conversation, because a session can be started against a conversation it
|
|
41
|
+
* did not mint and two sessions can therefore share one.
|
|
42
|
+
*/
|
|
43
|
+
sessionId: z.string(),
|
|
44
|
+
/** The conversation holding the report, and the one to open on a yes. */
|
|
45
|
+
conversationId: z.string(),
|
|
46
|
+
/** Whether there is a report in that conversation to read. */
|
|
47
|
+
reportReady: z.boolean(),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export type WatchRetroCompletedEvent = z.infer<
|
|
51
|
+
typeof WatchRetroCompletedEventSchema
|
|
52
|
+
>;
|
package/src/api/index.ts
CHANGED
|
@@ -129,6 +129,7 @@ import { UISurfaceUpdateEventSchema } from "./events/ui-surface-update.js";
|
|
|
129
129
|
import { UsageProgressEventSchema } from "./events/usage-progress.js";
|
|
130
130
|
import { UsageUpdateEventSchema } from "./events/usage-update.js";
|
|
131
131
|
import { UserMessageEchoEventSchema } from "./events/user-message-echo.js";
|
|
132
|
+
import { WatchRetroCompletedEventSchema } from "./events/watch-retro-completed.js";
|
|
132
133
|
import { WorkflowCompletedEventSchema } from "./events/workflow-completed.js";
|
|
133
134
|
import { WorkflowLeafFinishedEventSchema } from "./events/workflow-leaf-finished.js";
|
|
134
135
|
import { WorkflowLeafStartedEventSchema } from "./events/workflow-leaf-started.js";
|
|
@@ -151,6 +152,11 @@ export {
|
|
|
151
152
|
type DocumentMutationTool,
|
|
152
153
|
REOPENABLE_DOCUMENT_MUTATION_TOOL_NAMES,
|
|
153
154
|
} from "./constants/document-tools.js";
|
|
155
|
+
export {
|
|
156
|
+
MIN_INPUT_RESERVE_TOKENS,
|
|
157
|
+
type ProfileConfigIssue,
|
|
158
|
+
validateInferenceProfileConfig,
|
|
159
|
+
} from "./constants/profile-config-validation.js";
|
|
154
160
|
export {
|
|
155
161
|
SSE_REPLAY_RING_AGE_LIMIT_MS,
|
|
156
162
|
SSE_REPLAY_RING_COUNT_LIMIT,
|
|
@@ -676,6 +682,10 @@ export {
|
|
|
676
682
|
type UserMessageEchoEvent,
|
|
677
683
|
UserMessageEchoEventSchema,
|
|
678
684
|
} from "./events/user-message-echo.js";
|
|
685
|
+
export {
|
|
686
|
+
type WatchRetroCompletedEvent,
|
|
687
|
+
WatchRetroCompletedEventSchema,
|
|
688
|
+
} from "./events/watch-retro-completed.js";
|
|
679
689
|
export {
|
|
680
690
|
type WorkflowCompletedEvent,
|
|
681
691
|
WorkflowCompletedEventSchema,
|
|
@@ -1032,6 +1042,7 @@ export const AssistantEventSchema = z.discriminatedUnion("type", [
|
|
|
1032
1042
|
UsageProgressEventSchema,
|
|
1033
1043
|
UsageUpdateEventSchema,
|
|
1034
1044
|
UserMessageEchoEventSchema,
|
|
1045
|
+
WatchRetroCompletedEventSchema,
|
|
1035
1046
|
WorkflowCompletedEventSchema,
|
|
1036
1047
|
WorkflowLeafFinishedEventSchema,
|
|
1037
1048
|
WorkflowLeafStartedEventSchema,
|