@vellumai/assistant 0.4.43 → 0.4.44

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.
Files changed (88) hide show
  1. package/ARCHITECTURE.md +13 -14
  2. package/README.md +11 -12
  3. package/docs/architecture/integrations.md +75 -93
  4. package/package.json +1 -1
  5. package/src/__tests__/approval-routes-http.test.ts +0 -2
  6. package/src/__tests__/bundled-asset.test.ts +1 -1
  7. package/src/__tests__/checker.test.ts +31 -28
  8. package/src/__tests__/conversation-routes-guardian-reply.test.ts +6 -6
  9. package/src/__tests__/credential-security-invariants.test.ts +2 -1
  10. package/src/__tests__/error-handler-friendly-messages.test.ts +46 -0
  11. package/src/__tests__/managed-twitter-guardrails.test.ts +5 -1
  12. package/src/__tests__/onboarding-template-contract.test.ts +0 -10
  13. package/src/__tests__/provider-fail-open-selection.test.ts +12 -2
  14. package/src/__tests__/send-endpoint-busy.test.ts +0 -3
  15. package/src/__tests__/session-confirmation-signals.test.ts +7 -45
  16. package/src/__tests__/starter-task-flow.test.ts +9 -19
  17. package/src/__tests__/system-prompt.test.ts +3 -4
  18. package/src/__tests__/trust-store.test.ts +4 -4
  19. package/src/__tests__/twitter-platform-proxy-client.test.ts +43 -18
  20. package/src/cli/commands/amazon/index.ts +4 -39
  21. package/src/cli/commands/amazon/session.ts +18 -26
  22. package/src/cli/commands/twitter/__tests__/cli-read-routing.test.ts +58 -196
  23. package/src/cli/commands/twitter/__tests__/cli-routing.test.ts +26 -186
  24. package/src/cli/commands/twitter/__tests__/oauth-client.test.ts +1 -47
  25. package/src/cli/commands/twitter/index.ts +95 -835
  26. package/src/cli/commands/twitter/oauth-client.ts +1 -35
  27. package/src/cli/commands/twitter/router.ts +70 -115
  28. package/src/cli/commands/twitter/types.ts +30 -0
  29. package/src/cli/reference.ts +2 -2
  30. package/src/config/bundled-skills/amazon/SKILL.md +0 -1
  31. package/src/config/bundled-skills/app-builder/SKILL.md +0 -6
  32. package/src/config/bundled-skills/app-builder/TOOLS.json +0 -4
  33. package/src/config/bundled-skills/doordash/SKILL.md +0 -1
  34. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +1 -82
  35. package/src/config/bundled-skills/doordash/doordash-cli.ts +17 -28
  36. package/src/config/bundled-skills/doordash/lib/session.ts +21 -17
  37. package/src/config/bundled-skills/twitter/SKILL.md +53 -166
  38. package/src/config/feature-flag-registry.json +8 -0
  39. package/src/daemon/handlers/session-history.ts +41 -9
  40. package/src/daemon/lifecycle.ts +4 -17
  41. package/src/daemon/message-types/apps.ts +0 -25
  42. package/src/daemon/message-types/integrations.ts +1 -7
  43. package/src/daemon/message-types/sessions.ts +6 -1
  44. package/src/daemon/message-types/surfaces.ts +2 -0
  45. package/src/daemon/ride-shotgun-handler.ts +33 -1
  46. package/src/daemon/seed-files.ts +3 -27
  47. package/src/daemon/server.ts +2 -18
  48. package/src/daemon/session-agent-loop-handlers.ts +24 -2
  49. package/src/daemon/session-runtime-assembly.ts +0 -7
  50. package/src/daemon/session-surfaces.ts +185 -33
  51. package/src/daemon/session.ts +2 -28
  52. package/src/memory/app-store.ts +0 -18
  53. package/src/memory/schema/infrastructure.ts +0 -8
  54. package/src/permissions/defaults.ts +3 -3
  55. package/src/prompts/system-prompt.ts +4 -5
  56. package/src/prompts/templates/BOOTSTRAP.md +0 -3
  57. package/src/providers/registry.ts +2 -4
  58. package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
  59. package/src/runtime/auth/__tests__/scopes.test.ts +2 -1
  60. package/src/runtime/auth/route-policy.ts +0 -4
  61. package/src/runtime/auth/scopes.ts +1 -0
  62. package/src/runtime/auth/token-service.ts +1 -1
  63. package/src/runtime/http-types.ts +10 -0
  64. package/src/runtime/middleware/error-handler.ts +14 -1
  65. package/src/runtime/routes/app-management-routes.ts +61 -64
  66. package/src/runtime/routes/brain-graph/brain-graph.html +1845 -0
  67. package/src/runtime/routes/brain-graph-routes.ts +4 -42
  68. package/src/runtime/routes/conversation-routes.ts +9 -6
  69. package/src/runtime/routes/diagnostics-routes.ts +91 -14
  70. package/src/runtime/routes/settings-routes.ts +3 -93
  71. package/src/tools/AGENTS.md +38 -0
  72. package/src/tools/apps/executors.ts +0 -6
  73. package/src/tools/document/editor-template.ts +10 -8
  74. package/src/twitter/platform-proxy-client.ts +6 -3
  75. package/src/util/errors.ts +12 -0
  76. package/src/__tests__/home-base-bootstrap.test.ts +0 -84
  77. package/src/__tests__/prebuilt-home-base-seed.test.ts +0 -79
  78. package/src/cli/commands/twitter/__tests__/cli-error-shaping.test.ts +0 -265
  79. package/src/cli/commands/twitter/client.ts +0 -989
  80. package/src/cli/commands/twitter/session.ts +0 -121
  81. package/src/home-base/app-link-store.ts +0 -78
  82. package/src/home-base/bootstrap.ts +0 -74
  83. package/src/home-base/prebuilt/brain-graph.html +0 -1483
  84. package/src/home-base/prebuilt/index.html +0 -702
  85. package/src/home-base/prebuilt/seed-metadata.json +0 -21
  86. package/src/home-base/prebuilt/seed.ts +0 -122
  87. package/src/home-base/prebuilt-home-base-updater.ts +0 -36
  88. package/src/util/cookie-session.ts +0 -98
@@ -1,79 +0,0 @@
1
- import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
5
-
6
- const testDir = mkdtempSync(join(tmpdir(), "home-base-seed-test-"));
7
-
8
- mock.module("../util/platform.js", () => ({
9
- getDataDir: () => testDir,
10
- isMacOS: () => process.platform === "darwin",
11
- isLinux: () => process.platform === "linux",
12
- isWindows: () => process.platform === "win32",
13
- getSocketPath: () => join(testDir, "test.sock"),
14
- getPidPath: () => join(testDir, "test.pid"),
15
- getDbPath: () => join(testDir, "test.db"),
16
- getLogPath: () => join(testDir, "test.log"),
17
- ensureDataDir: () => {},
18
- }));
19
-
20
- mock.module("../util/logger.js", () => ({
21
- getLogger: () =>
22
- new Proxy({} as Record<string, unknown>, {
23
- get: () => () => {},
24
- }),
25
- }));
26
-
27
- import {
28
- ensurePrebuiltHomeBaseSeeded,
29
- findSeededHomeBaseApp,
30
- } from "../home-base/prebuilt/seed.js";
31
- import { getApp, listApps, updateApp } from "../memory/app-store.js";
32
-
33
- describe("prebuilt home base seed", () => {
34
- beforeEach(() => {
35
- rmSync(testDir, { recursive: true, force: true });
36
- mkdirSync(testDir, { recursive: true });
37
- });
38
-
39
- afterAll(() => {
40
- rmSync(testDir, { recursive: true, force: true });
41
- });
42
-
43
- test("seeds a prebuilt Home Base app and is idempotent", () => {
44
- const first = ensurePrebuiltHomeBaseSeeded();
45
- const second = ensurePrebuiltHomeBaseSeeded();
46
-
47
- expect(first).not.toBeNull();
48
- expect(second).not.toBeNull();
49
- expect(first!.created).toBe(true);
50
- expect(second!.created).toBe(false);
51
- expect(second!.appId).toBe(first!.appId);
52
- expect(listApps().filter((app) => app.name === "Home Base").length).toBe(1);
53
- });
54
-
55
- test("findSeededHomeBaseApp resolves the seeded app", () => {
56
- const seeded = ensurePrebuiltHomeBaseSeeded();
57
- expect(seeded).not.toBeNull();
58
- const found = findSeededHomeBaseApp();
59
-
60
- expect(found).not.toBeNull();
61
- expect(found?.id).toBe(seeded!.appId);
62
- // listApps() (used by findSeededHomeBaseApp) no longer stores htmlDefinition
63
- // in the JSON file — it is persisted as index.html on disk.
64
- // Use getApp() to load the full definition including htmlDefinition.
65
- const fullApp = getApp(found!.id);
66
- expect(fullApp?.htmlDefinition).toContain("home-base-starter-lane");
67
- });
68
-
69
- test("rejects updates that remove required Home Base anchors", () => {
70
- const seeded = ensurePrebuiltHomeBaseSeeded();
71
- expect(seeded).not.toBeNull();
72
-
73
- expect(() => {
74
- updateApp(seeded!.appId, {
75
- htmlDefinition: '<main id="home-base-root"></main>',
76
- });
77
- }).toThrow("missing required anchors");
78
- });
79
- });
@@ -1,265 +0,0 @@
1
- /**
2
- * Tests for CLI error-shaping logic in the `run()` helper of twitter.ts.
3
- *
4
- * These tests verify that structured router metadata (pathUsed,
5
- * suggestAlternative, oauthError) is preserved in CLI output while
6
- * maintaining backward-compatible error codes.
7
- */
8
- import { describe, expect, test } from "bun:test";
9
-
10
- import { SessionExpiredError } from "../client.js";
11
-
12
- // ---------------------------------------------------------------------------
13
- // We test the error-shaping logic directly by reproducing the branching in
14
- // the CLI `run()` function. The actual `run()` function writes to stdout and
15
- // sets process.exitCode, which makes it awkward to test in isolation. Instead
16
- // we extract the payload-building logic into a pure helper and verify its
17
- // output here.
18
- // ---------------------------------------------------------------------------
19
-
20
- const SESSION_EXPIRED_MSG =
21
- "Your Twitter session has expired. Please sign in to Twitter in Chrome — " +
22
- "run `assistant twitter refresh` to capture your session automatically.";
23
-
24
- /**
25
- * Replicates the error-to-payload logic from `run()` in twitter.ts.
26
- * Returns the JSON payload that would be written to stdout.
27
- */
28
- function buildErrorPayload(err: unknown): Record<string, unknown> | null {
29
- const meta = err as Record<string, unknown>;
30
-
31
- if (err instanceof SessionExpiredError) {
32
- const payload: Record<string, unknown> = {
33
- ok: false,
34
- error: "session_expired",
35
- message: SESSION_EXPIRED_MSG,
36
- };
37
- if (meta.pathUsed !== undefined) payload.pathUsed = meta.pathUsed;
38
- if (meta.suggestAlternative !== undefined)
39
- payload.suggestAlternative = meta.suggestAlternative;
40
- if (meta.oauthError !== undefined) payload.oauthError = meta.oauthError;
41
- return payload;
42
- }
43
-
44
- if (
45
- err instanceof Error &&
46
- (meta.pathUsed !== undefined ||
47
- meta.suggestAlternative !== undefined ||
48
- meta.oauthError !== undefined ||
49
- meta.proxyErrorCode !== undefined)
50
- ) {
51
- const payload: Record<string, unknown> = {
52
- ok: false,
53
- error: err.message,
54
- };
55
- if (meta.pathUsed !== undefined) payload.pathUsed = meta.pathUsed;
56
- if (meta.suggestAlternative !== undefined)
57
- payload.suggestAlternative = meta.suggestAlternative;
58
- if (meta.oauthError !== undefined) payload.oauthError = meta.oauthError;
59
- if (meta.proxyErrorCode !== undefined)
60
- payload.proxyErrorCode = meta.proxyErrorCode;
61
- if (meta.retryable !== undefined) payload.retryable = meta.retryable;
62
- return payload;
63
- }
64
-
65
- // Generic fallback
66
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
67
- }
68
-
69
- describe("CLI error shaping", () => {
70
- test("plain SessionExpiredError preserves backward-compatible error code", () => {
71
- const err = new SessionExpiredError("No Twitter session found.");
72
- const payload = buildErrorPayload(err);
73
-
74
- expect(payload).toEqual({
75
- ok: false,
76
- error: "session_expired",
77
- message: SESSION_EXPIRED_MSG,
78
- });
79
- });
80
-
81
- test("SessionExpiredError from browser path preserves pathUsed and suggestAlternative", () => {
82
- const err = Object.assign(
83
- new SessionExpiredError("Session cookies expired"),
84
- {
85
- pathUsed: "browser" as const,
86
- suggestAlternative: "oauth" as const,
87
- },
88
- );
89
- const payload = buildErrorPayload(err);
90
-
91
- expect(payload).toEqual({
92
- ok: false,
93
- error: "session_expired",
94
- message: SESSION_EXPIRED_MSG,
95
- pathUsed: "browser",
96
- suggestAlternative: "oauth",
97
- });
98
- });
99
-
100
- test("SessionExpiredError from auto path preserves pathUsed and oauthError", () => {
101
- const err = Object.assign(
102
- new SessionExpiredError("Session cookies expired"),
103
- {
104
- pathUsed: "auto" as const,
105
- oauthError: "Token revoked",
106
- },
107
- );
108
- const payload = buildErrorPayload(err);
109
-
110
- expect(payload).toEqual({
111
- ok: false,
112
- error: "session_expired",
113
- message: SESSION_EXPIRED_MSG,
114
- pathUsed: "auto",
115
- oauthError: "Token revoked",
116
- });
117
- });
118
-
119
- test("routed non-session error with suggestAlternative emits structured JSON", () => {
120
- const err = Object.assign(
121
- new Error(
122
- "OAuth is not configured. Provide your X developer credentials here in the chat to set up OAuth, or switch to browser strategy.",
123
- ),
124
- {
125
- pathUsed: "oauth" as const,
126
- suggestAlternative: "browser" as const,
127
- },
128
- );
129
- const payload = buildErrorPayload(err);
130
-
131
- expect(payload).toEqual({
132
- ok: false,
133
- error:
134
- "OAuth is not configured. Provide your X developer credentials here in the chat to set up OAuth, or switch to browser strategy.",
135
- pathUsed: "oauth",
136
- suggestAlternative: "browser",
137
- });
138
- });
139
-
140
- test("routed auto-mode error with oauthError and suggestAlternative", () => {
141
- const err = Object.assign(
142
- new Error("Both OAuth and browser paths failed"),
143
- {
144
- pathUsed: "auto" as const,
145
- suggestAlternative: "browser" as const,
146
- oauthError: "Twitter API error (401)",
147
- },
148
- );
149
- const payload = buildErrorPayload(err);
150
-
151
- expect(payload).toEqual({
152
- ok: false,
153
- error: "Both OAuth and browser paths failed",
154
- pathUsed: "auto",
155
- suggestAlternative: "browser",
156
- oauthError: "Twitter API error (401)",
157
- });
158
- });
159
-
160
- test("auto-mode error with pathUsed and oauthError but no suggestAlternative preserves metadata", () => {
161
- // This is the scenario flagged by Codex: routedPostTweet in auto mode tries
162
- // OAuth (fails), then browser (fails with non-SessionExpiredError). The thrown
163
- // error has pathUsed and oauthError but no suggestAlternative.
164
- const err = Object.assign(
165
- new Error("Browser automation failed: element not found"),
166
- {
167
- pathUsed: "auto" as const,
168
- oauthError: "Twitter API error (401)",
169
- },
170
- );
171
- const payload = buildErrorPayload(err);
172
-
173
- expect(payload).toEqual({
174
- ok: false,
175
- error: "Browser automation failed: element not found",
176
- pathUsed: "auto",
177
- oauthError: "Twitter API error (401)",
178
- });
179
- });
180
-
181
- test("error with only pathUsed (no oauthError or suggestAlternative) preserves metadata", () => {
182
- const err = Object.assign(new Error("Something went wrong"), {
183
- pathUsed: "browser" as const,
184
- });
185
- const payload = buildErrorPayload(err);
186
-
187
- expect(payload).toEqual({
188
- ok: false,
189
- error: "Something went wrong",
190
- pathUsed: "browser",
191
- });
192
- });
193
-
194
- test("generic error without router metadata falls back to plain error", () => {
195
- const err = new Error("Network connection failed");
196
- const payload = buildErrorPayload(err);
197
-
198
- expect(payload).toEqual({
199
- ok: false,
200
- error: "Network connection failed",
201
- });
202
- });
203
-
204
- test("non-Error value falls back to stringified error", () => {
205
- const payload = buildErrorPayload("some string error");
206
-
207
- expect(payload).toEqual({
208
- ok: false,
209
- error: "some string error",
210
- });
211
- });
212
-
213
- test("managed proxy error preserves proxyErrorCode and retryable metadata", () => {
214
- const err = Object.assign(
215
- new Error("Connect Twitter in Settings as the assistant owner"),
216
- {
217
- pathUsed: "managed" as const,
218
- proxyErrorCode: "owner_credential_required",
219
- retryable: false,
220
- },
221
- );
222
- const payload = buildErrorPayload(err);
223
-
224
- expect(payload).toEqual({
225
- ok: false,
226
- error: "Connect Twitter in Settings as the assistant owner",
227
- pathUsed: "managed",
228
- proxyErrorCode: "owner_credential_required",
229
- retryable: false,
230
- });
231
- });
232
-
233
- test("managed proxy retryable error preserves metadata", () => {
234
- const err = Object.assign(new Error("Reconnect Twitter or retry"), {
235
- pathUsed: "managed" as const,
236
- proxyErrorCode: "auth_failure",
237
- retryable: true,
238
- });
239
- const payload = buildErrorPayload(err);
240
-
241
- expect(payload).toEqual({
242
- ok: false,
243
- error: "Reconnect Twitter or retry",
244
- pathUsed: "managed",
245
- proxyErrorCode: "auth_failure",
246
- retryable: true,
247
- });
248
- });
249
-
250
- test("backward compatibility: session_expired error code is always preserved", () => {
251
- // Even with metadata, the error code stays 'session_expired'
252
- const err = Object.assign(new SessionExpiredError("expired"), {
253
- pathUsed: "auto" as const,
254
- suggestAlternative: "oauth" as const,
255
- oauthError: "token expired",
256
- });
257
- const payload = buildErrorPayload(err);
258
-
259
- expect(payload!.error).toBe("session_expired");
260
- expect(payload!.ok).toBe(false);
261
- expect(payload!.pathUsed).toBe("auto");
262
- expect(payload!.suggestAlternative).toBe("oauth");
263
- expect(payload!.oauthError).toBe("token expired");
264
- });
265
- });