@vellumai/assistant 0.4.34 → 0.4.36
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/ARCHITECTURE.md +44 -49
- package/README.md +32 -20
- package/docs/architecture/keychain-broker.md +186 -0
- package/docs/architecture/security.md +110 -116
- package/docs/runbook-trusted-contacts.md +2 -2
- package/docs/skills.md +25 -25
- package/package.json +4 -1
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +11 -2
- package/src/__tests__/actor-token-service.test.ts +1 -0
- package/src/__tests__/amazon-cdp-integration.test.ts +74 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +38 -9
- package/src/__tests__/assistant-id-boundary-guard.test.ts +91 -43
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/bundle-scanner.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +102 -102
- package/src/__tests__/channel-invite-transport.test.ts +155 -256
- package/src/__tests__/channel-readiness-routes.test.ts +336 -0
- package/src/__tests__/checker.test.ts +6 -6
- package/src/__tests__/chrome-cdp.test.ts +350 -0
- package/src/__tests__/computer-use-session-lifecycle.test.ts +3 -3
- package/src/__tests__/computer-use-session-working-dir.test.ts +86 -52
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
- package/src/__tests__/config-loader-migration.test.ts +85 -0
- package/src/__tests__/conversation-pairing.test.ts +370 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +1 -10
- package/src/__tests__/credential-broker-server-use.test.ts +1 -10
- package/src/__tests__/credential-security-e2e.test.ts +7 -1
- package/src/__tests__/credential-security-invariants.test.ts +14 -20
- package/src/__tests__/credential-vault-unit.test.ts +1 -11
- package/src/__tests__/credential-vault.test.ts +5 -19
- package/src/__tests__/credentials-cli.test.ts +806 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +23 -4
- package/src/__tests__/email-invite-adapter.test.ts +78 -0
- package/src/__tests__/email-service-config-fallback.test.ts +102 -0
- package/src/__tests__/encrypted-store.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +5 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +70 -12
- package/src/__tests__/guardian-outbound-http.test.ts +53 -47
- package/src/__tests__/handle-user-message-secret-resume.test.ts +23 -0
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +32 -23
- package/src/__tests__/handlers-telegram-config.test.ts +8 -2
- package/src/__tests__/handlers-twitter-config.test.ts +2 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +108 -7
- package/src/__tests__/ingress-reconcile.test.ts +6 -0
- package/src/__tests__/intent-routing.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -0
- package/src/__tests__/ipc-snapshot.test.ts +8 -2
- package/src/__tests__/keychain-broker-client.test.ts +543 -0
- package/src/__tests__/llm-usage-store.test.ts +344 -0
- package/src/__tests__/mcp-client-auth.test.ts +2 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/migration-transport.test.ts +49 -0
- package/src/__tests__/notification-broadcaster.test.ts +205 -5
- package/src/__tests__/notification-deep-link.test.ts +365 -1
- package/src/__tests__/oauth-connect-handler.test.ts +2 -2
- package/src/__tests__/onboarding-starter-tasks.test.ts +17 -4
- package/src/__tests__/proxy-approval-callback.test.ts +1 -1
- package/src/__tests__/recording-handler.test.ts +1 -1
- package/src/__tests__/recording-intent-handler.test.ts +6 -1
- package/src/__tests__/recording-state-machine.test.ts +1 -1
- package/src/__tests__/relay-server.test.ts +9 -1
- package/src/__tests__/ride-shotgun-handler.test.ts +499 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +160 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +299 -2
- package/src/__tests__/script-proxy-profile-template-fallback.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +8 -2
- package/src/__tests__/secure-keys.test.ts +175 -216
- package/src/__tests__/session-confirmation-signals.test.ts +1 -1
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -1
- package/src/__tests__/session-queue.test.ts +2 -1
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +2 -2
- package/src/__tests__/skill-feature-flags-integration.test.ts +29 -4
- package/src/__tests__/skill-feature-flags.test.ts +12 -9
- package/src/__tests__/skill-load-feature-flag.test.ts +26 -5
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skills.test.ts +34 -4
- package/src/__tests__/slack-channel-config.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +26 -4
- package/src/__tests__/telegram-bot-username-resolution.test.ts +212 -0
- package/src/__tests__/telegram-invite-adapter.test.ts +164 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-permission-simulate-handler.test.ts +8 -2
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +9 -1
- package/src/__tests__/twitter-auth-handler.test.ts +2 -2
- package/src/__tests__/twitter-oauth-client.test.ts +1 -1
- package/src/__tests__/usage-routes.test.ts +339 -0
- package/src/__tests__/whatsapp-invite-adapter.test.ts +94 -0
- package/src/agent/loop.ts +3 -0
- package/src/amazon/checkout.ts +0 -1
- package/src/approvals/guardian-request-resolvers.ts +9 -1
- package/src/bundler/app-bundler.ts +28 -12
- package/src/bundler/bundle-scanner.ts +1 -1
- package/src/bundler/bundle-signer.ts +3 -3
- package/src/bundler/manifest.ts +1 -1
- package/src/bundler/signature-verifier.ts +3 -3
- package/src/channels/config.ts +1 -1
- package/src/cli/AGENTS.md +63 -0
- package/src/cli/__tests__/notifications.test.ts +470 -0
- package/src/cli/amazon.ts +344 -167
- package/src/cli/audit.ts +85 -0
- package/src/cli/autonomy.ts +369 -0
- package/src/cli/channels.ts +51 -0
- package/src/cli/completions.ts +208 -0
- package/src/cli/config.ts +220 -0
- package/src/cli/contacts.ts +471 -0
- package/src/cli/credentials.ts +564 -0
- package/src/cli/default-action.ts +14 -0
- package/src/cli/dev.ts +131 -0
- package/src/cli/doctor.ts +398 -0
- package/src/cli/email.ts +491 -0
- package/src/cli/influencer.ts +72 -0
- package/src/cli/integrations.ts +248 -57
- package/src/cli/keys.ts +114 -0
- package/src/cli/map.ts +46 -54
- package/src/cli/mcp.ts +111 -3
- package/src/cli/{config-commands.ts → memory.ts} +133 -242
- package/src/cli/notifications.ts +407 -0
- package/src/cli/program.ts +65 -0
- package/src/cli/reference.ts +48 -0
- package/src/cli/sequence.ts +154 -0
- package/src/cli/sessions.ts +262 -0
- package/src/cli/trust.ts +177 -0
- package/src/cli/twitter.ts +323 -106
- package/src/config/__tests__/build-cli-reference-section.test.ts +49 -0
- package/src/config/bundled-skills/amazon/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/TOOLS.json +26 -0
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +13 -0
- package/src/config/bundled-skills/contacts/SKILL.md +178 -10
- package/src/config/bundled-skills/doordash/doordash-cli.ts +23 -168
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +175 -145
- package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
- package/src/config/bundled-skills/twilio-setup/SKILL.md +70 -17
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/core-schema.ts +7 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +26 -0
- package/src/config/schema.ts +4 -0
- package/src/config/skill-state.ts +0 -13
- package/src/config/system-prompt.ts +27 -0
- package/src/contacts/contact-store.ts +25 -0
- package/src/daemon/computer-use-session.ts +1 -1
- package/src/daemon/handlers/apps.ts +1 -0
- package/src/daemon/handlers/config-channels.ts +3 -3
- package/src/daemon/handlers/config-dispatch.ts +29 -0
- package/src/daemon/handlers/config-inbox.ts +4 -3
- package/src/daemon/handlers/config.ts +3 -43
- package/src/daemon/handlers/contacts.ts +34 -0
- package/src/daemon/handlers/index.ts +17 -3
- package/src/daemon/handlers/session-user-message.ts +7 -0
- package/src/daemon/handlers/sessions.ts +21 -2
- package/src/daemon/handlers/shared.ts +17 -0
- package/src/daemon/ipc-contract/apps.ts +2 -0
- package/src/daemon/ipc-contract/computer-use.ts +9 -0
- package/src/daemon/ipc-contract/contacts.ts +3 -3
- package/src/daemon/ipc-contract/inbox.ts +2 -0
- package/src/daemon/ipc-contract/messages.ts +4 -0
- package/src/daemon/ipc-contract/sessions.ts +8 -0
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/ride-shotgun-handler.ts +139 -25
- package/src/daemon/session-agent-loop-handlers.ts +100 -0
- package/src/daemon/session-agent-loop.ts +72 -0
- package/src/daemon/session-tool-setup.ts +7 -0
- package/src/daemon/session.ts +23 -1
- package/src/daemon/tool-side-effects.ts +39 -1
- package/src/email/service.ts +59 -2
- package/src/index.ts +2 -60
- package/src/mcp/mcp-oauth-provider.ts +90 -8
- package/src/media/app-icon-generator.ts +86 -0
- package/src/memory/db-init.ts +12 -1
- package/src/memory/llm-usage-store.ts +186 -0
- package/src/memory/migrations/026-guardian-verification-sessions.ts +28 -9
- package/src/memory/migrations/027a-guardian-bootstrap-token.ts +16 -3
- package/src/memory/migrations/038-actor-token-records.ts +8 -1
- package/src/memory/migrations/039-actor-refresh-token-records.ts +11 -2
- package/src/memory/migrations/110-channel-guardian.ts +27 -6
- package/src/memory/migrations/112-assistant-inbox.ts +39 -15
- package/src/memory/migrations/114-notifications.ts +37 -15
- package/src/memory/migrations/117-conversation-attention.ts +33 -9
- package/src/memory/migrations/137-usage-dashboard-indexes.ts +26 -0
- package/src/memory/migrations/139-drop-usage-composite-indexes.ts +30 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/migrations/schema-introspection.ts +18 -0
- package/src/memory/schema-migration.ts +1 -0
- package/src/memory/shared-app-links-store.ts +1 -1
- package/src/messaging/registry.ts +27 -0
- package/src/notifications/README.md +79 -70
- package/src/notifications/broadcaster.ts +2 -1
- package/src/notifications/conversation-pairing.ts +147 -13
- package/src/notifications/copy-composer.ts +7 -3
- package/src/notifications/destination-resolver.ts +14 -1
- package/src/notifications/emit-signal.ts +3 -2
- package/src/notifications/signal.ts +105 -1
- package/src/notifications/types.ts +16 -0
- package/src/permissions/checker.ts +29 -3
- package/src/permissions/prompter.ts +11 -3
- package/src/runtime/access-request-helper.ts +2 -1
- package/src/runtime/auth/route-policy.ts +7 -1
- package/src/runtime/channel-invite-transport.ts +40 -63
- package/src/runtime/channel-invite-transports/email.ts +13 -39
- package/src/runtime/channel-invite-transports/slack.ts +5 -34
- package/src/runtime/channel-invite-transports/sms.ts +8 -29
- package/src/runtime/channel-invite-transports/telegram.ts +69 -28
- package/src/runtime/channel-invite-transports/voice.ts +0 -7
- package/src/runtime/channel-invite-transports/whatsapp.ts +43 -0
- package/src/runtime/channel-readiness-service.ts +202 -45
- package/src/runtime/confirmation-request-guardian-bridge.ts +2 -1
- package/src/runtime/guardian-outbound-actions.ts +8 -5
- package/src/runtime/http-server.ts +5 -9
- package/src/runtime/http-types.ts +13 -1
- package/src/runtime/invite-instruction-generator.ts +178 -0
- package/src/runtime/invite-service.ts +22 -25
- package/src/runtime/migrations/migration-transport.ts +13 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +8 -7
- package/src/runtime/routes/channel-readiness-routes.ts +30 -11
- package/src/runtime/routes/contact-routes.ts +54 -26
- package/src/runtime/routes/guardian-bootstrap-routes.ts +1 -1
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +2 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +2 -1
- package/src/runtime/routes/integration-routes.ts +1 -1
- package/src/runtime/routes/invite-routes.ts +1 -1
- package/src/runtime/routes/secret-routes.ts +31 -7
- package/src/runtime/routes/surface-content-routes.ts +104 -0
- package/src/runtime/routes/twilio-routes.ts +32 -1
- package/src/runtime/routes/usage-routes.ts +114 -0
- package/src/runtime/tool-grant-request-helper.ts +2 -1
- package/src/security/encrypted-store.ts +9 -5
- package/src/security/keychain-broker-client.ts +393 -0
- package/src/security/secure-keys.ts +106 -321
- package/src/tools/apps/executors.ts +73 -0
- package/src/tools/browser/auto-navigate.ts +15 -6
- package/src/tools/browser/chrome-cdp.ts +211 -0
- package/src/tools/browser/network-recorder.test.ts +83 -0
- package/src/tools/browser/network-recorder.ts +8 -7
- package/src/tools/browser/x-auto-navigate.ts +12 -6
- package/src/tools/credentials/policy-types.ts +24 -0
- package/src/tools/credentials/vault.ts +22 -27
- package/src/tools/network/script-proxy/session-manager.ts +47 -3
- package/src/tools/permission-checker.ts +1 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/ui-surface/definitions.ts +1 -2
- package/src/tools/watch/watch-state.ts +2 -0
- package/src/__tests__/key-migration.test.ts +0 -240
- package/src/__tests__/keychain.test.ts +0 -286
- package/src/cli/core-commands.ts +0 -899
- package/src/security/keychain-to-encrypted-migration.ts +0 -66
- package/src/security/keychain.ts +0 -490
|
@@ -35,7 +35,10 @@ let currentConfig: Record<string, unknown> = {
|
|
|
35
35
|
const DECLARED_FLAG_KEY = "feature_flags.hatch-new-assistant.enabled";
|
|
36
36
|
const DECLARED_SKILL_ID = "hatch-new-assistant";
|
|
37
37
|
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
39
|
+
const realPlatform = require("../util/platform.js");
|
|
38
40
|
mock.module("../util/platform.js", () => ({
|
|
41
|
+
...realPlatform,
|
|
39
42
|
getRootDir: () => TEST_DIR,
|
|
40
43
|
getDataDir: () => TEST_DIR,
|
|
41
44
|
getWorkspaceDir: () => TEST_DIR,
|
|
@@ -59,38 +62,60 @@ mock.module("../util/platform.js", () => ({
|
|
|
59
62
|
isWindows: () => false,
|
|
60
63
|
getPlatformName: () => "linux",
|
|
61
64
|
getClipboardCommand: () => null,
|
|
65
|
+
readSessionToken: () => null,
|
|
62
66
|
removeSocketFile: () => {},
|
|
63
67
|
migratePath: () => {},
|
|
64
68
|
migrateToWorkspaceLayout: () => {},
|
|
65
69
|
migrateToDataLayout: () => {},
|
|
66
70
|
}));
|
|
67
71
|
|
|
72
|
+
const noopLogger = new Proxy({} as Record<string, unknown>, {
|
|
73
|
+
get: (_target, prop) => (prop === "child" ? () => noopLogger : () => {}),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
77
|
+
const realLogger = require("../util/logger.js");
|
|
68
78
|
mock.module("../util/logger.js", () => ({
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}),
|
|
79
|
+
...realLogger,
|
|
80
|
+
getLogger: () => noopLogger,
|
|
81
|
+
getCliLogger: () => noopLogger,
|
|
73
82
|
isDebug: () => false,
|
|
74
83
|
truncateForLog: (v: string) => v,
|
|
84
|
+
initLogger: () => {},
|
|
85
|
+
pruneOldLogFiles: () => 0,
|
|
75
86
|
}));
|
|
76
87
|
|
|
77
88
|
mock.module("../config/loader.js", () => ({
|
|
78
89
|
getConfig: () => currentConfig,
|
|
90
|
+
loadConfig: () => currentConfig,
|
|
91
|
+
loadRawConfig: () => ({}),
|
|
92
|
+
saveConfig: () => {},
|
|
93
|
+
saveRawConfig: () => {},
|
|
94
|
+
invalidateConfigCache: () => {},
|
|
95
|
+
getNestedValue: () => undefined,
|
|
96
|
+
setNestedValue: () => {},
|
|
97
|
+
syncConfigToLockfile: () => {},
|
|
79
98
|
}));
|
|
80
99
|
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
101
|
+
const realUserReference = require("../config/user-reference.js");
|
|
81
102
|
mock.module("../config/user-reference.js", () => ({
|
|
103
|
+
...realUserReference,
|
|
82
104
|
resolveUserReference: () => "TestUser",
|
|
83
105
|
resolveUserPronouns: () => null,
|
|
84
106
|
}));
|
|
85
107
|
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
109
|
+
const realCredentialMetadataStore = require("../tools/credentials/metadata-store.js");
|
|
86
110
|
mock.module("../tools/credentials/metadata-store.js", () => ({
|
|
111
|
+
...realCredentialMetadataStore,
|
|
87
112
|
listCredentialMetadata: () => [],
|
|
88
113
|
}));
|
|
89
114
|
|
|
90
115
|
const { buildSystemPrompt } = await import("../config/system-prompt.js");
|
|
91
116
|
const { isAssistantFeatureFlagEnabled } =
|
|
92
117
|
await import("../config/assistant-feature-flags.js");
|
|
93
|
-
const {
|
|
118
|
+
const { skillFlagKey } = await import("../config/skill-state.js");
|
|
94
119
|
|
|
95
120
|
// ---------------------------------------------------------------------------
|
|
96
121
|
// Setup / Teardown
|
|
@@ -301,18 +326,22 @@ describe("isAssistantFeatureFlagEnabled", () => {
|
|
|
301
326
|
});
|
|
302
327
|
});
|
|
303
328
|
|
|
304
|
-
describe("
|
|
305
|
-
test("
|
|
329
|
+
describe("isAssistantFeatureFlagEnabled with skillFlagKey", () => {
|
|
330
|
+
test("resolves skill flag via canonical path", () => {
|
|
306
331
|
const config = {
|
|
307
332
|
assistantFeatureFlagValues: { [DECLARED_FLAG_KEY]: false },
|
|
308
333
|
} as any;
|
|
309
334
|
|
|
310
|
-
expect(
|
|
335
|
+
expect(
|
|
336
|
+
isAssistantFeatureFlagEnabled(skillFlagKey(DECLARED_SKILL_ID), config),
|
|
337
|
+
).toBe(false);
|
|
311
338
|
});
|
|
312
339
|
|
|
313
340
|
test("disabled when no override set (registry default is false)", () => {
|
|
314
341
|
const config = {} as any;
|
|
315
342
|
|
|
316
|
-
expect(
|
|
343
|
+
expect(
|
|
344
|
+
isAssistantFeatureFlagEnabled(skillFlagKey(DECLARED_SKILL_ID), config),
|
|
345
|
+
).toBe(false);
|
|
317
346
|
});
|
|
318
347
|
});
|
|
@@ -481,8 +481,8 @@ describe("assistant ID boundary", () => {
|
|
|
481
481
|
const repoRoot = getRepoRoot();
|
|
482
482
|
|
|
483
483
|
// Scan all Drizzle schema files for assistantId column definitions.
|
|
484
|
-
//
|
|
485
|
-
//
|
|
484
|
+
// Match `assistantId:` followed by any Drizzle column builder (text(,
|
|
485
|
+
// integer(, blob(, real(, etc.) — not just text(.
|
|
486
486
|
const schemaGlobs = [
|
|
487
487
|
"assistant/src/memory/schema/*.ts",
|
|
488
488
|
"assistant/src/memory/schema/**/*.ts",
|
|
@@ -492,7 +492,7 @@ describe("assistant ID boundary", () => {
|
|
|
492
492
|
try {
|
|
493
493
|
grepOutput = execFileSync(
|
|
494
494
|
"git",
|
|
495
|
-
["grep", "-nE", "assistantId\\s
|
|
495
|
+
["grep", "-nE", "assistantId\\s*:", "--", ...schemaGlobs],
|
|
496
496
|
{ encoding: "utf-8", cwd: repoRoot },
|
|
497
497
|
).trim();
|
|
498
498
|
} catch (err) {
|
|
@@ -546,6 +546,9 @@ describe("assistant ID boundary", () => {
|
|
|
546
546
|
// Scan store files for exported function signatures that include
|
|
547
547
|
// assistantId as a parameter. This covers memory stores, contact stores,
|
|
548
548
|
// notification stores, credential/token stores, and call stores.
|
|
549
|
+
//
|
|
550
|
+
// We read each file and extract full parameter lists (which may span
|
|
551
|
+
// multiple lines) from exported functions to catch multiline signatures.
|
|
549
552
|
const storeGlobs = [
|
|
550
553
|
"assistant/src/memory/*.ts",
|
|
551
554
|
"assistant/src/contacts/*.ts",
|
|
@@ -556,52 +559,97 @@ describe("assistant ID boundary", () => {
|
|
|
556
559
|
"assistant/src/calls/call-store.ts",
|
|
557
560
|
];
|
|
558
561
|
|
|
559
|
-
//
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
try {
|
|
573
|
-
grepOutput = execFileSync(
|
|
574
|
-
"git",
|
|
575
|
-
["grep", "-nE", pattern, "--", ...storeGlobs],
|
|
576
|
-
{ encoding: "utf-8", cwd: repoRoot },
|
|
577
|
-
).trim();
|
|
578
|
-
} catch (err) {
|
|
579
|
-
// Exit code 1 means no matches — happy path
|
|
580
|
-
if ((err as { status?: number }).status === 1) {
|
|
581
|
-
return;
|
|
562
|
+
// Find matching files using git ls-files with each glob
|
|
563
|
+
const matchedFiles: string[] = [];
|
|
564
|
+
for (const glob of storeGlobs) {
|
|
565
|
+
try {
|
|
566
|
+
const output = execFileSync("git", ["ls-files", "--", glob], {
|
|
567
|
+
encoding: "utf-8",
|
|
568
|
+
cwd: repoRoot,
|
|
569
|
+
}).trim();
|
|
570
|
+
if (output) {
|
|
571
|
+
matchedFiles.push(...output.split("\n").filter((f) => f.length > 0));
|
|
572
|
+
}
|
|
573
|
+
} catch {
|
|
574
|
+
// Ignore errors — glob may not match anything
|
|
582
575
|
}
|
|
583
|
-
throw err;
|
|
584
576
|
}
|
|
585
577
|
|
|
586
|
-
const
|
|
587
|
-
const violations = allLines.filter((line) => {
|
|
588
|
-
const filePath = line.split(":")[0];
|
|
589
|
-
if (isTestFile(filePath)) return false;
|
|
590
|
-
if (isMigrationFile(filePath)) return false;
|
|
578
|
+
const violations: string[] = [];
|
|
591
579
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
580
|
+
// Regex to find the start of an exported function declaration or
|
|
581
|
+
// arrow-function expression. We capture everything from `export` up to
|
|
582
|
+
// and including the opening parenthesis of the parameter list.
|
|
583
|
+
const exportFnStartRegex =
|
|
584
|
+
/export\s+(?:async\s+)?function\s+\w+\s*\(|export\s+const\s+\w+\s*=\s*(?:async\s+)?\(/g;
|
|
585
|
+
|
|
586
|
+
for (const relPath of matchedFiles) {
|
|
587
|
+
if (isTestFile(relPath) || isMigrationFile(relPath)) continue;
|
|
588
|
+
|
|
589
|
+
const content = readFileSync(join(repoRoot, relPath), "utf-8");
|
|
590
|
+
|
|
591
|
+
exportFnStartRegex.lastIndex = 0;
|
|
592
|
+
for (
|
|
593
|
+
let match = exportFnStartRegex.exec(content);
|
|
594
|
+
match;
|
|
595
|
+
match = exportFnStartRegex.exec(content)
|
|
599
596
|
) {
|
|
600
|
-
|
|
601
|
-
|
|
597
|
+
// Skip matches that fall inside comments. Find the beginning of
|
|
598
|
+
// the line containing the match and check for comment prefixes.
|
|
599
|
+
const lineStart = content.lastIndexOf("\n", match.index) + 1;
|
|
600
|
+
const linePrefix = content.slice(lineStart, match.index).trim();
|
|
601
|
+
if (linePrefix.startsWith("//")) {
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
// For block comments: check if the match is inside an unclosed
|
|
605
|
+
// block comment. A prefix starting with `*` (continuation line)
|
|
606
|
+
// or `/*` only counts if there is no closing `*/` between the
|
|
607
|
+
// last `/*` opener and the match position — otherwise the
|
|
608
|
+
// comment was already closed (e.g. `/** docs */ export …`).
|
|
609
|
+
if (linePrefix.startsWith("*") || linePrefix.startsWith("/*")) {
|
|
610
|
+
const textBeforeMatch = content.slice(lineStart, match.index);
|
|
611
|
+
const lastOpen = textBeforeMatch.lastIndexOf("/*");
|
|
612
|
+
if (lastOpen === -1) {
|
|
613
|
+
// No block-comment opener on this line but starts with `*`,
|
|
614
|
+
// so it's a continuation line inside a multi-line comment.
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
const closeBetween = textBeforeMatch.indexOf("*/", lastOpen + 2);
|
|
618
|
+
if (closeBetween === -1) {
|
|
619
|
+
// The block comment is still open at the match position.
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
// The block comment was closed before the match — fall through
|
|
623
|
+
// and evaluate the match normally.
|
|
624
|
+
}
|
|
602
625
|
|
|
603
|
-
|
|
604
|
-
|
|
626
|
+
// Find the matching closing paren to extract the full parameter list,
|
|
627
|
+
// which may span multiple lines.
|
|
628
|
+
const parenStart = match.index + match[0].length - 1; // index of '('
|
|
629
|
+
let depth = 1;
|
|
630
|
+
let paramEnd = parenStart + 1;
|
|
631
|
+
for (let i = parenStart + 1; i < content.length && depth > 0; i++) {
|
|
632
|
+
if (content[i] === "(") depth++;
|
|
633
|
+
if (content[i] === ")") depth--;
|
|
634
|
+
if (depth === 0) {
|
|
635
|
+
paramEnd = i;
|
|
636
|
+
break;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const paramList = content.slice(parenStart + 1, paramEnd);
|
|
641
|
+
|
|
642
|
+
// Check if the parameter list contains assistantId as a word boundary
|
|
643
|
+
if (/\bassistantId\b/.test(paramList)) {
|
|
644
|
+
// Determine the line number of the export keyword for reporting
|
|
645
|
+
const lineNum = content.slice(0, match.index).split("\n").length;
|
|
646
|
+
const firstLine = content
|
|
647
|
+
.slice(match.index, match.index + match[0].length)
|
|
648
|
+
.trim();
|
|
649
|
+
violations.push(`${relPath}:${lineNum}: ${firstLine}...`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
605
653
|
|
|
606
654
|
if (violations.length > 0) {
|
|
607
655
|
const message = [
|
|
@@ -60,7 +60,7 @@ let mockGetCredentialMetadata: ReturnType<typeof mock>;
|
|
|
60
60
|
mock.module("../security/secure-keys.js", () => ({
|
|
61
61
|
getSecureKey: (...args: unknown[]) => mockGetSecureKey(...args),
|
|
62
62
|
setSecureKey: () => true,
|
|
63
|
-
deleteSecureKey: () =>
|
|
63
|
+
deleteSecureKey: () => "deleted",
|
|
64
64
|
listSecureKeys: () => [],
|
|
65
65
|
getBackendType: () => "encrypted",
|
|
66
66
|
_resetBackend: () => {},
|
|
@@ -49,7 +49,7 @@ async function createBundle(
|
|
|
49
49
|
const data = await zip.generateAsync({ type: "uint8array" });
|
|
50
50
|
const path = join(
|
|
51
51
|
tempDir,
|
|
52
|
-
`test-${Date.now()}-${Math.random().toString(36).slice(2)}.
|
|
52
|
+
`test-${Date.now()}-${Math.random().toString(36).slice(2)}.vellum`,
|
|
53
53
|
);
|
|
54
54
|
await Bun.write(path, data);
|
|
55
55
|
return path;
|