@vellumai/assistant 0.11.10 → 0.11.11-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/plan-credit.test.ts +51 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/plan-credit.ts +66 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/plan-credit.test.ts +51 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/plan-credit.ts +66 -0
- package/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/service-contracts/src/__tests__/plan-credit.test.ts +51 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/plan-credit.ts +66 -0
- package/openapi.yaml +152 -55
- package/package.json +1 -1
- package/src/__tests__/anthropic-provider.test.ts +60 -0
- package/src/__tests__/config-get-vision-flag.test.ts +36 -0
- package/src/__tests__/conversation-error.test.ts +38 -0
- package/src/__tests__/copy-composer-tc-templates.test.ts +2 -2
- package/src/__tests__/guardian-action-service-scope.test.ts +53 -0
- package/src/__tests__/headless-browser-mode.test.ts +63 -13
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +118 -27
- package/src/__tests__/host-app-control-proxy.test.ts +2 -2
- package/src/__tests__/host-app-control-routes.test.ts +41 -0
- package/src/__tests__/host-bash-routes.test.ts +30 -1
- package/src/__tests__/host-browser-routes.test.ts +34 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +29 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +29 -0
- package/src/__tests__/host-proxy-base.test.ts +1 -0
- package/src/__tests__/host-transfer-routes-targeted.test.ts +86 -0
- package/src/__tests__/llm-schema.test.ts +32 -0
- package/src/__tests__/mcp-config-secret-boundary.test.ts +2 -0
- package/src/__tests__/memory-config-file-parity.test.ts +18 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +15 -0
- package/src/__tests__/messaging-send-tool.test.ts +159 -13
- package/src/__tests__/messaging-shared.test.ts +19 -0
- package/src/__tests__/notification-events-source-context-probe.test.ts +211 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +40 -6
- package/src/__tests__/outlook-messaging-provider.test.ts +9 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +2 -2
- package/src/__tests__/schedule-routes.test.ts +30 -1
- package/src/__tests__/scheduler-result-notification.test.ts +209 -0
- package/src/__tests__/skills-install-extract.test.ts +26 -0
- package/src/__tests__/skills-install-staging.test.ts +11 -2
- package/src/api/constants/personality-sliders.test.ts +21 -0
- package/src/api/constants/personality-sliders.ts +64 -0
- package/src/api/index.ts +8 -0
- package/src/archive/ustar-size.test.ts +32 -0
- package/src/archive/ustar-size.ts +23 -0
- package/src/background-wake/next-wake.test.ts +13 -0
- package/src/background-wake/next-wake.ts +4 -2
- package/src/calls/voice-session-bridge.ts +26 -0
- package/src/cli/__tests__/catalog-search-help.test.ts +1 -0
- package/src/cli/commands/__tests__/plugins-search.test.ts +81 -0
- package/src/cli/commands/config.help.ts +3 -1
- package/src/cli/commands/memory/worker.ts +1 -1
- package/src/cli/commands/platform/__tests__/credits-format.test.ts +94 -5
- package/src/cli/commands/platform/__tests__/credits.test.ts +10 -0
- package/src/cli/commands/platform/credits-format.ts +57 -0
- package/src/cli/commands/platform/index.help.ts +19 -3
- package/src/cli/commands/plugins.help.ts +4 -1
- package/src/cli/commands/plugins.ts +2 -1
- package/src/cli/lib/bundled-marketplace.json +1 -1
- package/src/cli/lib/install-from-platform.ts +8 -10
- package/src/config/bundled-skills/messaging/SKILL.md +2 -0
- package/src/config/bundled-skills/messaging/TOOLS.json +4 -4
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +85 -1
- package/src/config/bundled-skills/messaging/tools/shared.ts +6 -0
- package/src/config/bundled-skills/schedule/SKILL.md +9 -1
- package/src/config/bundled-skills/screen-annotation/SKILL.md +87 -20
- package/src/config/bundled-skills/screen-annotation/TOOLS.json +21 -5
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +67 -0
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/input-modalities.test.ts +71 -0
- package/src/config/input-modalities.ts +95 -0
- package/src/config/schemas/heartbeat.ts +3 -3
- package/src/config/schemas/llm.ts +13 -3
- package/src/config/schemas/memory-storage.test.ts +39 -0
- package/src/config/schemas/memory-storage.ts +21 -0
- package/src/context/token-estimator.ts +2 -2
- package/src/daemon/__tests__/embedding-reconcile.test.ts +77 -0
- package/src/daemon/__tests__/lifecycle-embedding-reconcile.test.ts +3 -0
- package/src/daemon/conversation-error.ts +20 -7
- package/src/daemon/embedding-reconcile.ts +64 -1
- package/src/daemon/handlers/config-embeddings.ts +31 -0
- package/src/daemon/host-app-control-proxy.ts +2 -0
- package/src/daemon/host-bash-proxy.ts +1 -0
- package/src/daemon/host-cu-proxy.ts +6 -6
- package/src/daemon/host-file-proxy.ts +6 -6
- package/src/daemon/host-proxy-base.ts +10 -9
- package/src/daemon/host-transfer-proxy.ts +21 -24
- package/src/daemon/mcp-reload-service.ts +6 -0
- package/src/daemon/providers-setup.ts +5 -26
- package/src/heartbeat/heartbeat-service.ts +72 -13
- package/src/live-voice/__tests__/live-voice-photo.test.ts +72 -0
- package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +81 -0
- package/src/live-voice/live-voice-photo.ts +68 -4
- package/src/live-voice/live-voice-session.ts +36 -2
- package/src/live-voice/protocol.ts +98 -1
- package/src/mcp/__tests__/reload-signal-emission.test.ts +54 -0
- package/src/mcp/__tests__/reload-signal.test.ts +57 -0
- package/src/mcp/__tests__/startup.test.ts +150 -0
- package/src/mcp/reload-signal.ts +50 -0
- package/src/mcp/startup.ts +101 -0
- package/src/messaging/providers/outlook/adapter.ts +2 -13
- package/src/messaging/providers/outlook/client.ts +13 -1
- package/src/messaging/providers/outlook/types.ts +3 -0
- package/src/notifications/__tests__/decision-engine.test.ts +79 -6
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +27 -0
- package/src/notifications/__tests__/schedule-result-producer.test.ts +397 -0
- package/src/notifications/copy-composer.ts +33 -11
- package/src/notifications/decision-engine.ts +48 -11
- package/src/notifications/events-store.ts +68 -2
- package/src/notifications/home-feed-side-effect.ts +15 -3
- package/src/notifications/schedule-result-producer.ts +275 -0
- package/src/notifications/signal.ts +5 -0
- package/src/oauth/seed-providers.ts +3 -2
- package/src/persistence/conversation-crud.ts +38 -0
- package/src/persistence/embeddings/embedding-backend.test.ts +162 -0
- package/src/persistence/embeddings/embedding-backend.ts +148 -11
- package/src/persistence/embeddings/embedding-openai.test.ts +113 -0
- package/src/persistence/embeddings/embedding-openai.ts +55 -4
- package/src/persistence/embeddings/embedding-types.test.ts +24 -0
- package/src/persistence/embeddings/embedding-types.ts +25 -1
- package/src/persistence/job-utils.ts +6 -2
- package/src/plugin-api/index.ts +2 -1
- package/src/plugin-api/vision-support.test.ts +81 -0
- package/src/plugin-api/vision-support.ts +57 -10
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +8 -0
- package/src/plugins/defaults/memory/jobs/__tests__/embed-concept-page.test.ts +1 -0
- package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +11 -3
- package/src/plugins/defaults/memory/src/__tests__/memory-worker-routes.test.ts +20 -0
- package/src/plugins/defaults/memory/src/memory-worker-routes.ts +3 -1
- package/src/plugins/defaults/memory/substrate/__tests__/reembed-job.test.ts +1 -0
- package/src/plugins/defaults/memory/v2/__tests__/backfill-jobs.test.ts +1 -0
- package/src/plugins/defaults/memory/v3/__tests__/section-dense-store.test.ts +82 -0
- package/src/plugins/defaults/memory/v3/section-dense-store.ts +9 -27
- package/src/providers/__tests__/provider-secret-catalog.test.ts +4 -0
- package/src/providers/__tests__/xml-tool-call-salvage.test.ts +172 -0
- package/src/providers/anthropic/client.ts +9 -4
- package/src/providers/inline-audio-support.test.ts +34 -0
- package/src/providers/inline-audio-support.ts +27 -0
- package/src/providers/model-catalog.ts +22 -2
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +50 -0
- package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +51 -0
- package/src/providers/openai/__tests__/chat-completions-provider-xml-salvage.test.ts +291 -0
- package/src/providers/openai/api-error-normalization.ts +18 -3
- package/src/providers/openai/chat-completions-provider.ts +113 -13
- package/src/providers/openai/responses-provider.ts +10 -8
- package/src/providers/provider-secret-catalog.ts +11 -1
- package/src/providers/vellum/client.ts +14 -1
- package/src/providers/vellum/personality-directions.test.ts +163 -0
- package/src/providers/vellum/personality-directions.ts +120 -0
- package/src/providers/xml-tool-call-salvage.ts +212 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +42 -4
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -6
- package/src/runtime/auth/__tests__/route-policy.test.ts +65 -0
- package/src/runtime/auth/same-actor.ts +29 -5
- package/src/runtime/background-job-runner.ts +38 -29
- package/src/runtime/guardian-action-service.ts +20 -1
- package/src/runtime/migrations/vbundle-validator.ts +3 -3
- package/src/runtime/pending-interactions.ts +4 -5
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +24 -0
- package/src/runtime/routes/__tests__/platform-credits-routes.test.ts +99 -5
- package/src/runtime/routes/conversation-query-routes.ts +53 -11
- package/src/runtime/routes/document-comments-routes.ts +8 -2
- package/src/runtime/routes/guardian-action-routes.ts +7 -0
- package/src/runtime/routes/heartbeat-routes.ts +41 -53
- package/src/runtime/routes/host-app-control-routes.ts +10 -36
- package/src/runtime/routes/host-bash-routes.ts +11 -45
- package/src/runtime/routes/host-browser-routes.ts +24 -0
- package/src/runtime/routes/host-cu-routes.ts +11 -45
- package/src/runtime/routes/host-file-routes.ts +11 -44
- package/src/runtime/routes/host-proxy-result-binding.ts +57 -0
- package/src/runtime/routes/host-transfer-routes.ts +33 -25
- package/src/runtime/routes/index.ts +0 -2
- package/src/runtime/routes/integrations/a2a.ts +12 -3
- package/src/runtime/routes/integrations/vercel.ts +13 -3
- package/src/runtime/routes/platform-routes.ts +121 -24
- package/src/runtime/routes/schedule-routes.ts +15 -1
- package/src/schedule/__tests__/schedule-timezone.test.ts +21 -3
- package/src/schedule/__tests__/worker-mcp-tools.test.ts +88 -0
- package/src/schedule/schedule-timezone.ts +25 -0
- package/src/schedule/scheduler.ts +19 -0
- package/src/schedule/worker.ts +119 -2
- package/src/skills/catalog-install.ts +40 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +3 -0
- package/src/tools/browser/browser-execution.ts +117 -53
- package/src/tools/browser/browser-status-constants.ts +6 -6
- package/src/tools/capability-offer.test.ts +87 -0
- package/src/tools/capability-offer.ts +93 -0
- package/src/tools/terminal/__tests__/safe-env.test.ts +17 -0
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/util/provider-error-patterns.ts +34 -0
- package/src/watcher/__tests__/engine.test.ts +5 -2
- package/src/watcher/engine.ts +7 -4
- package/src/runtime/routes/__tests__/sight-frame-routes.test.ts +0 -487
- package/src/runtime/routes/sight-frame-routes.ts +0 -136
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { ClientOs, InterfaceId } from "../channels/types.js";
|
|
2
|
+
|
|
3
|
+
/** Public download page for the desktop apps. */
|
|
4
|
+
export const DESKTOP_APP_DOWNLOAD_URL = "https://www.vellum.ai/download";
|
|
5
|
+
|
|
6
|
+
/** Chrome Web Store listing for the Vellum Assistant browser extension. */
|
|
7
|
+
export const CHROME_WEB_STORE_INSTALL_URL =
|
|
8
|
+
"https://chromewebstore.google.com/detail/vellum-assistant-browser/hphbdmpffeigpcdjkckleobjmhhokpne";
|
|
9
|
+
|
|
10
|
+
export const DESKTOP_APP_INSTALL_HINT = `Install the Vellum desktop app: ${DESKTOP_APP_DOWNLOAD_URL}`;
|
|
11
|
+
|
|
12
|
+
/** User-facing install step for status userActions and command-failure hints. */
|
|
13
|
+
export const CHROME_EXTENSION_INSTALL_HINT = `Install the Vellum Assistant Chrome extension from the Chrome Web Store: ${CHROME_WEB_STORE_INSTALL_URL}`;
|
|
14
|
+
|
|
15
|
+
export interface CapabilityOfferSurface {
|
|
16
|
+
transportInterface?: InterfaceId;
|
|
17
|
+
clientOs?: ClientOs;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const LOGGED_IN_BROWSER_HELPFUL_RE =
|
|
21
|
+
/timed?\s*out|timeout|etimedout|econnrefused|enotfound|ehostunreach|unreachable|err_connection|err_name_not_resolved|err_timed_out|net::err_|login required|sign[- ]in required|\b401\b|\b403\b|authentication|captcha|verify you are human|access denied|forbidden/i;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* True on phone transports and mobile client OS values. Those surfaces have
|
|
25
|
+
* no in-app browser and cannot install the Chrome extension in-place.
|
|
26
|
+
*/
|
|
27
|
+
export function isPhoneSurface(surface?: CapabilityOfferSurface): boolean {
|
|
28
|
+
if (
|
|
29
|
+
surface?.transportInterface === "ios" ||
|
|
30
|
+
surface?.transportInterface === "phone"
|
|
31
|
+
) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return surface?.clientOs === "ios" || surface?.clientOs === "android";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* True when a logged-in browser session (desktop app or Chrome extension)
|
|
39
|
+
* would plausibly reach the page this failure could not.
|
|
40
|
+
*/
|
|
41
|
+
export function shouldOfferLoggedInBrowser(message: string): boolean {
|
|
42
|
+
return LOGGED_IN_BROWSER_HELPFUL_RE.test(message);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Offer the desktop app and Chrome extension as a capability the user can
|
|
47
|
+
* turn on. Screenshot-and-paste is the fallback after that offer, not the
|
|
48
|
+
* first suggestion.
|
|
49
|
+
*/
|
|
50
|
+
export function formatLoggedInBrowserOffer(
|
|
51
|
+
surface?: CapabilityOfferSurface,
|
|
52
|
+
): string {
|
|
53
|
+
const lines: string[] = [];
|
|
54
|
+
if (isPhoneSurface(surface)) {
|
|
55
|
+
lines.push(
|
|
56
|
+
"This page needs a logged-in browser on a computer. There is no in-app browser on this phone, and the Chrome extension cannot be installed here.",
|
|
57
|
+
);
|
|
58
|
+
lines.push(
|
|
59
|
+
`Install the desktop app on a Mac or Windows PC (${DESKTOP_APP_DOWNLOAD_URL}) or the Chrome extension in Chrome on that computer (${CHROME_WEB_STORE_INSTALL_URL}).`,
|
|
60
|
+
);
|
|
61
|
+
lines.push(
|
|
62
|
+
"Offer those first. Do not describe a browser panel here. Only ask for a screenshot or pasted page content if the user cannot install either.",
|
|
63
|
+
);
|
|
64
|
+
return lines.join("\n");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
lines.push(
|
|
68
|
+
"A connected desktop app or Chrome extension can open this page in a browser where you are already logged in.",
|
|
69
|
+
);
|
|
70
|
+
lines.push(`Desktop app: ${DESKTOP_APP_DOWNLOAD_URL}`);
|
|
71
|
+
lines.push(`Chrome extension: ${CHROME_WEB_STORE_INSTALL_URL}`);
|
|
72
|
+
lines.push(
|
|
73
|
+
"Offer those first. Only ask for a screenshot or pasted page content if the user cannot install either.",
|
|
74
|
+
);
|
|
75
|
+
return lines.join("\n");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Append {@link formatLoggedInBrowserOffer} when the message does not already
|
|
80
|
+
* carry the install URLs.
|
|
81
|
+
*/
|
|
82
|
+
export function appendLoggedInBrowserOffer(
|
|
83
|
+
message: string,
|
|
84
|
+
surface?: CapabilityOfferSurface,
|
|
85
|
+
): string {
|
|
86
|
+
if (
|
|
87
|
+
message.includes(DESKTOP_APP_DOWNLOAD_URL) &&
|
|
88
|
+
message.includes(CHROME_WEB_STORE_INSTALL_URL)
|
|
89
|
+
) {
|
|
90
|
+
return message;
|
|
91
|
+
}
|
|
92
|
+
return `${message}\n\n${formatLoggedInBrowserOffer(surface)}`;
|
|
93
|
+
}
|
|
@@ -50,6 +50,23 @@ describe("safe-env Qdrant forwarding", () => {
|
|
|
50
50
|
});
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
+
describe("safe-env CES vault isolation", () => {
|
|
54
|
+
test("strips the CES HTTP bearer and credential URL from child shells", () => {
|
|
55
|
+
expect(SAFE_ENV_VARS).not.toContain("CES_SERVICE_TOKEN");
|
|
56
|
+
expect(SAFE_ENV_VARS).not.toContain("CES_CREDENTIAL_URL");
|
|
57
|
+
expect(SAFE_ENV_VARS).toContain("CES_LOCAL_SOCKET");
|
|
58
|
+
|
|
59
|
+
const env = buildSanitizedEnv("linux", {
|
|
60
|
+
CES_SERVICE_TOKEN: "vault-bearer",
|
|
61
|
+
CES_CREDENTIAL_URL: "http://127.0.0.1:8090",
|
|
62
|
+
CES_LOCAL_SOCKET: "/tmp/ces.sock",
|
|
63
|
+
});
|
|
64
|
+
expect(env.CES_SERVICE_TOKEN).toBeUndefined();
|
|
65
|
+
expect(env.CES_CREDENTIAL_URL).toBeUndefined();
|
|
66
|
+
expect(env.CES_LOCAL_SOCKET).toBe("/tmp/ces.sock");
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
53
70
|
describe("safe-env Windows forwarding", () => {
|
|
54
71
|
test("forwards runtime paths only to Windows children", () => {
|
|
55
72
|
const keys = [
|
|
@@ -54,8 +54,10 @@ export const SAFE_ENV_VARS = [
|
|
|
54
54
|
"VELLUM_MARKETING_URL",
|
|
55
55
|
"VELLUM_MIGRATION_EXPORT_ALLOWED_HOSTS",
|
|
56
56
|
"VELLUM_MIGRATION_IMPORT_ALLOWED_HOSTS",
|
|
57
|
-
"CES_CREDENTIAL_URL",
|
|
58
57
|
"CES_MANAGED_MODE",
|
|
58
|
+
// Socket path only. The CES HTTP bearer (`CES_SERVICE_TOKEN`) and
|
|
59
|
+
// `CES_CREDENTIAL_URL` stay in the assistant process: child shells must
|
|
60
|
+
// not inherit a vault token they can printenv or log.
|
|
59
61
|
"CES_LOCAL_SOCKET",
|
|
60
62
|
// Per-instance port of the assistant-managed Qdrant sidecar, so skill and
|
|
61
63
|
// bash-tool subprocesses that use the vector helpers (e.g. embed/search over
|
|
@@ -67,7 +69,6 @@ export const SAFE_ENV_VARS = [
|
|
|
67
69
|
"IS_PLATFORM",
|
|
68
70
|
"VELLUM_CLOUD",
|
|
69
71
|
"VELLUM_SANDBOX_RUNTIME",
|
|
70
|
-
"CES_SERVICE_TOKEN",
|
|
71
72
|
"VELLUM_PROFILER_RUN_ID",
|
|
72
73
|
"VELLUM_PROFILER_MODE",
|
|
73
74
|
"VELLUM_PROFILER_MAX_BYTES",
|
|
@@ -45,6 +45,40 @@ export function isChatTemplateFailureError(message: string): boolean {
|
|
|
45
45
|
return CHAT_TEMPLATE_FAILURE_PATTERNS.some((re) => re.test(message));
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// Provider prose that indicates the selected model id is unknown to the
|
|
49
|
+
// endpoint. Distinct from OpenCode's `ModelError` / "is not supported"
|
|
50
|
+
// shape, which is only safe to treat as model-not-found on a 4xx.
|
|
51
|
+
export const MODEL_NOT_FOUND_PATTERNS = [
|
|
52
|
+
/model .*(?:not found|does not exist)/i,
|
|
53
|
+
/model_not_found/i,
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
// OpenCode zen returns HTTP 401 with `type=ModelError` and
|
|
57
|
+
// "Model <id> is not supported". Treating that as a rejected key sends
|
|
58
|
+
// users to update credentials instead of switching models. Do not apply
|
|
59
|
+
// these on 5xx: retry treats a stamped reason as authoritative, so a
|
|
60
|
+
// transient server error with this type would skip retries and may
|
|
61
|
+
// immediately switch profiles.
|
|
62
|
+
export const UNSUPPORTED_MODEL_ID_PATTERNS = [
|
|
63
|
+
/model .+ is not supported/i,
|
|
64
|
+
/\bModelError\b/,
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Whether a provider error message indicates the selected model is unknown
|
|
69
|
+
* to the endpoint. Providers wrap raw upstream rejections in their own
|
|
70
|
+
* prose, so the classifier matches the full {@link MODEL_NOT_FOUND_PATTERNS}
|
|
71
|
+
* and {@link UNSUPPORTED_MODEL_ID_PATTERNS} sets rather than any single
|
|
72
|
+
* normalized phrase. Callers that see HTTP 5xx must not use this helper
|
|
73
|
+
* to stamp `model_not_found`.
|
|
74
|
+
*/
|
|
75
|
+
export function isModelNotFoundError(message: string): boolean {
|
|
76
|
+
return (
|
|
77
|
+
MODEL_NOT_FOUND_PATTERNS.some((re) => re.test(message)) ||
|
|
78
|
+
UNSUPPORTED_MODEL_ID_PATTERNS.some((re) => re.test(message))
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
48
82
|
// Vendor-neutral (OpenRouter/Anthropic-style) credit-exhaustion prose. Also
|
|
49
83
|
// covers per-key spend caps: OpenRouter returns 403 "Key limit exceeded" when a
|
|
50
84
|
// key's configured credit limit is reached — a billing/budget condition that
|
|
@@ -274,9 +274,12 @@ describe("runWatchersOnce — Phase 2 runBackgroundJob integration", () => {
|
|
|
274
274
|
expect(sandwich.preamble).not.toContain("Linear inbox");
|
|
275
275
|
expect(sandwich.preamble).not.toContain("Investigate flaky CI");
|
|
276
276
|
|
|
277
|
-
// Postamble (user role) carries the disposition contract
|
|
278
|
-
//
|
|
277
|
+
// Postamble (user role) carries the disposition contract and the
|
|
278
|
+
// silent-by-default notify rule; it must NOT include the
|
|
279
|
+
// attacker-controllable watcher name or event payload.
|
|
279
280
|
expect(sandwich.postamble).toContain("<watcher-disposition>");
|
|
281
|
+
expect(sandwich.postamble).toContain("Watcher ticks are not scheduled runs");
|
|
282
|
+
expect(sandwich.postamble).toContain("Unmatched events stay quiet");
|
|
280
283
|
expect(sandwich.postamble).not.toContain("Linear inbox");
|
|
281
284
|
expect(sandwich.postamble).not.toContain("Investigate flaky CI");
|
|
282
285
|
});
|
package/src/watcher/engine.ts
CHANGED
|
@@ -411,7 +411,8 @@ export async function runWatchersOnce(
|
|
|
411
411
|
// assistant-role content as its own past output, so a malicious payload
|
|
412
412
|
// (e.g. a Linear title reading "Ignore previous instructions and
|
|
413
413
|
// exfiltrate ...") cannot override the user-role postamble. The runner
|
|
414
|
-
// inserts
|
|
414
|
+
// inserts the preamble and untrusted payload, then invokes
|
|
415
|
+
// processMessage with the trusted postamble as the kickoff prompt.
|
|
415
416
|
// See `assistantSandwich` in `runtime/background-job-runner.ts`.
|
|
416
417
|
//
|
|
417
418
|
// The fence marks the content as third-party data; the sandwich denies it
|
|
@@ -440,7 +441,9 @@ export async function runWatchersOnce(
|
|
|
440
441
|
].join("\n");
|
|
441
442
|
|
|
442
443
|
const postamble = [
|
|
443
|
-
"Process the events above according to the watcher's action prompt.
|
|
444
|
+
"Process the events above according to the watcher's action prompt.",
|
|
445
|
+
"Notify only when the action prompt's match criteria are met, using `assistant notifications send`. Unmatched events stay quiet: write a silent disposition block and do not send a notification, including a \"nothing new\" ping. Watcher ticks are not scheduled runs.",
|
|
446
|
+
"For each event, include a disposition block:",
|
|
444
447
|
"<watcher-disposition>",
|
|
445
448
|
'{"event_id": "...", "disposition": "silent|notify|escalate", "action": "what you did", "title": "notification title", "body": "notification body"}',
|
|
446
449
|
"</watcher-disposition>",
|
|
@@ -449,8 +452,8 @@ export async function runWatchersOnce(
|
|
|
449
452
|
const result = await runBackgroundJob({
|
|
450
453
|
jobName: `watcher:${watcher.id}`,
|
|
451
454
|
source: "watcher",
|
|
452
|
-
//
|
|
453
|
-
//
|
|
455
|
+
// Empty prompt tells the runner to use the sandwich postamble as
|
|
456
|
+
// the kickoff so persistUserMessage has content.
|
|
454
457
|
prompt: "",
|
|
455
458
|
systemHint: `Watcher: ${watcher.name}`,
|
|
456
459
|
trustContext: { sourceChannel: "vellum", trustClass: "guardian" },
|