@vellumai/assistant 0.4.35 → 0.4.37
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 +5 -2
- 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 +29 -0
- 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 +814 -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 +494 -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} +134 -245
- 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 +175 -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 +135 -34
- 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 +11 -0
- package/src/memory/llm-usage-store.ts +186 -0
- 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/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 +2 -0
- 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/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/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
|
@@ -2,10 +2,7 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
4
4
|
import type { AssistantConfig } from "../config/schema.js";
|
|
5
|
-
import {
|
|
6
|
-
isSkillFeatureEnabled,
|
|
7
|
-
resolveSkillStates,
|
|
8
|
-
} from "../config/skill-state.js";
|
|
5
|
+
import { resolveSkillStates, skillFlagKey } from "../config/skill-state.js";
|
|
9
6
|
import type { SkillSummary } from "../config/skills.js";
|
|
10
7
|
|
|
11
8
|
const DECLARED_FLAG_KEY = "feature_flags.hatch-new-assistant.enabled";
|
|
@@ -55,27 +52,33 @@ function makeSkill(
|
|
|
55
52
|
}
|
|
56
53
|
|
|
57
54
|
// ---------------------------------------------------------------------------
|
|
58
|
-
//
|
|
55
|
+
// isAssistantFeatureFlagEnabled with skillFlagKey (canonical path)
|
|
59
56
|
// ---------------------------------------------------------------------------
|
|
60
57
|
|
|
61
|
-
describe("
|
|
58
|
+
describe("isAssistantFeatureFlagEnabled with skillFlagKey", () => {
|
|
62
59
|
test("returns false when no flag overrides (registry default is false)", () => {
|
|
63
60
|
const config = makeConfig();
|
|
64
|
-
expect(
|
|
61
|
+
expect(
|
|
62
|
+
isAssistantFeatureFlagEnabled(skillFlagKey(DECLARED_SKILL_ID), config),
|
|
63
|
+
).toBe(false);
|
|
65
64
|
});
|
|
66
65
|
|
|
67
66
|
test("returns true when skill key is explicitly true", () => {
|
|
68
67
|
const config = makeConfig({
|
|
69
68
|
assistantFeatureFlagValues: { [DECLARED_FLAG_KEY]: true },
|
|
70
69
|
});
|
|
71
|
-
expect(
|
|
70
|
+
expect(
|
|
71
|
+
isAssistantFeatureFlagEnabled(skillFlagKey(DECLARED_SKILL_ID), config),
|
|
72
|
+
).toBe(true);
|
|
72
73
|
});
|
|
73
74
|
|
|
74
75
|
test("returns false when skill key is explicitly false", () => {
|
|
75
76
|
const config = makeConfig({
|
|
76
77
|
assistantFeatureFlagValues: { [DECLARED_FLAG_KEY]: false },
|
|
77
78
|
});
|
|
78
|
-
expect(
|
|
79
|
+
expect(
|
|
80
|
+
isAssistantFeatureFlagEnabled(skillFlagKey(DECLARED_SKILL_ID), config),
|
|
81
|
+
).toBe(false);
|
|
79
82
|
});
|
|
80
83
|
});
|
|
81
84
|
|
|
@@ -51,18 +51,39 @@ const platformOverrides: Record<string, (...args: unknown[]) => unknown> = {
|
|
|
51
51
|
migrateToDataLayout: () => {},
|
|
52
52
|
removeSocketFile: () => {},
|
|
53
53
|
};
|
|
54
|
-
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
55
|
+
const realPlatform = require("../util/platform.js");
|
|
56
|
+
mock.module("../util/platform.js", () => ({
|
|
57
|
+
...realPlatform,
|
|
58
|
+
...platformOverrides,
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
const noopLogger = new Proxy({} as Record<string, unknown>, {
|
|
62
|
+
get: (_target, prop) => (prop === "child" ? () => noopLogger : () => {}),
|
|
63
|
+
});
|
|
55
64
|
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
66
|
+
const realLogger = require("../util/logger.js");
|
|
56
67
|
mock.module("../util/logger.js", () => ({
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}),
|
|
68
|
+
...realLogger,
|
|
69
|
+
getLogger: () => noopLogger,
|
|
70
|
+
getCliLogger: () => noopLogger,
|
|
61
71
|
isDebug: () => false,
|
|
72
|
+
truncateForLog: (value: string) => value,
|
|
73
|
+
initLogger: () => {},
|
|
74
|
+
pruneOldLogFiles: () => 0,
|
|
62
75
|
}));
|
|
63
76
|
|
|
64
77
|
mock.module("../config/loader.js", () => ({
|
|
65
78
|
getConfig: () => currentConfig,
|
|
79
|
+
loadConfig: () => currentConfig,
|
|
80
|
+
loadRawConfig: () => ({}),
|
|
81
|
+
saveConfig: () => {},
|
|
82
|
+
saveRawConfig: () => {},
|
|
83
|
+
invalidateConfigCache: () => {},
|
|
84
|
+
getNestedValue: () => undefined,
|
|
85
|
+
setNestedValue: () => {},
|
|
86
|
+
syncConfigToLockfile: () => {},
|
|
66
87
|
}));
|
|
67
88
|
|
|
68
89
|
await import("../tools/skills/load.js");
|
|
@@ -69,7 +69,6 @@ mock.module("../config/skills.js", () => ({
|
|
|
69
69
|
// only needs skillFlagKey and doesn't exercise resolveSkillStates.
|
|
70
70
|
mock.module("../config/skill-state.js", () => ({
|
|
71
71
|
skillFlagKey: (id: string) => `feature_flags.${id}.enabled`,
|
|
72
|
-
isSkillFeatureEnabled: () => true,
|
|
73
72
|
resolveSkillStates: () => [],
|
|
74
73
|
}));
|
|
75
74
|
|
|
@@ -12,33 +12,63 @@ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
12
12
|
|
|
13
13
|
const TEST_DIR = join(tmpdir(), `vellum-skills-test-${crypto.randomUUID()}`);
|
|
14
14
|
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
16
|
+
const realPlatform = require("../util/platform.js");
|
|
15
17
|
mock.module("../util/platform.js", () => ({
|
|
18
|
+
...realPlatform,
|
|
16
19
|
getRootDir: () => TEST_DIR,
|
|
17
20
|
getDataDir: () => TEST_DIR,
|
|
21
|
+
getIpcBlobDir: () => join(TEST_DIR, "ipc-blobs"),
|
|
22
|
+
getSandboxRootDir: () => join(TEST_DIR, "sandbox"),
|
|
23
|
+
getSandboxWorkingDir: () => TEST_DIR,
|
|
24
|
+
getInterfacesDir: () => join(TEST_DIR, "interfaces"),
|
|
18
25
|
ensureDataDir: () => {},
|
|
19
26
|
getSocketPath: () => join(TEST_DIR, "vellum.sock"),
|
|
20
27
|
getPidPath: () => join(TEST_DIR, "vellum.pid"),
|
|
21
28
|
getDbPath: () => join(TEST_DIR, "data", "assistant.db"),
|
|
22
29
|
getLogPath: () => join(TEST_DIR, "logs", "vellum.log"),
|
|
30
|
+
getHistoryPath: () => join(TEST_DIR, "history"),
|
|
23
31
|
isMacOS: () => process.platform === "darwin",
|
|
24
32
|
isLinux: () => process.platform === "linux",
|
|
25
33
|
isWindows: () => process.platform === "win32",
|
|
26
34
|
getPlatformName: () => process.platform,
|
|
35
|
+
getClipboardCommand: () => null,
|
|
27
36
|
getWorkspaceConfigPath: () => join(TEST_DIR, "config.json"),
|
|
28
37
|
getWorkspaceSkillsDir: () => join(TEST_DIR, "skills"),
|
|
38
|
+
getWorkspaceHooksDir: () => join(TEST_DIR, "hooks"),
|
|
39
|
+
getHooksDir: () => join(TEST_DIR, "hooks"),
|
|
29
40
|
getWorkspaceDir: () => TEST_DIR,
|
|
30
41
|
getWorkspacePromptPath: (file: string) => join(TEST_DIR, file),
|
|
31
42
|
migrateToDataLayout: () => {},
|
|
32
43
|
migrateToWorkspaceLayout: () => {},
|
|
44
|
+
migratePath: () => {},
|
|
45
|
+
readSessionToken: () => null,
|
|
46
|
+
removeSocketFile: () => {},
|
|
47
|
+
normalizeAssistantId: (id: string) => id,
|
|
48
|
+
readLockfile: () => null,
|
|
49
|
+
writeLockfile: () => {},
|
|
33
50
|
}));
|
|
34
51
|
|
|
52
|
+
const noopLogger = {
|
|
53
|
+
info: () => {},
|
|
54
|
+
warn: () => {},
|
|
55
|
+
error: () => {},
|
|
56
|
+
debug: () => {},
|
|
57
|
+
trace: () => {},
|
|
58
|
+
fatal: () => {},
|
|
59
|
+
child: () => noopLogger,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
63
|
+
const realLogger = require("../util/logger.js");
|
|
35
64
|
mock.module("../util/logger.js", () => ({
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}),
|
|
65
|
+
...realLogger,
|
|
66
|
+
getLogger: () => noopLogger,
|
|
67
|
+
getCliLogger: () => noopLogger,
|
|
40
68
|
isDebug: () => false,
|
|
41
69
|
truncateForLog: (v: string) => v,
|
|
70
|
+
initLogger: () => {},
|
|
71
|
+
pruneOldLogFiles: () => 0,
|
|
42
72
|
}));
|
|
43
73
|
|
|
44
74
|
const { loadSkillCatalog, loadSkillBySelector, resolveSkillSelector } =
|
|
@@ -61,9 +61,9 @@ mock.module("../security/secure-keys.js", () => ({
|
|
|
61
61
|
deleteSecureKey: (account: string) => {
|
|
62
62
|
if (account in secureKeyStore) {
|
|
63
63
|
delete secureKeyStore[account];
|
|
64
|
-
return
|
|
64
|
+
return "deleted";
|
|
65
65
|
}
|
|
66
|
-
return
|
|
66
|
+
return "not-found";
|
|
67
67
|
},
|
|
68
68
|
listSecureKeys: () => Object.keys(secureKeyStore),
|
|
69
69
|
getBackendType: () => "encrypted",
|
|
@@ -14,7 +14,10 @@ const TEST_DIR = join(tmpdir(), `vellum-sysprompt-test-${crypto.randomUUID()}`);
|
|
|
14
14
|
|
|
15
15
|
import { mock } from "bun:test";
|
|
16
16
|
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
18
|
+
const realPlatform = require("../util/platform.js");
|
|
17
19
|
mock.module("../util/platform.js", () => ({
|
|
20
|
+
...realPlatform,
|
|
18
21
|
getRootDir: () => TEST_DIR,
|
|
19
22
|
getDataDir: () => TEST_DIR,
|
|
20
23
|
getWorkspaceDir: () => TEST_DIR,
|
|
@@ -38,19 +41,27 @@ mock.module("../util/platform.js", () => ({
|
|
|
38
41
|
isWindows: () => process.platform === "win32",
|
|
39
42
|
getPlatformName: () => process.platform,
|
|
40
43
|
getClipboardCommand: () => null,
|
|
44
|
+
readSessionToken: () => null,
|
|
41
45
|
removeSocketFile: () => {},
|
|
42
46
|
migratePath: () => {},
|
|
43
47
|
migrateToWorkspaceLayout: () => {},
|
|
44
48
|
migrateToDataLayout: () => {},
|
|
45
49
|
}));
|
|
46
50
|
|
|
51
|
+
const noopLogger = new Proxy({} as Record<string, unknown>, {
|
|
52
|
+
get: (_target, prop) => (prop === "child" ? () => noopLogger : () => {}),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
56
|
+
const realLogger = require("../util/logger.js");
|
|
47
57
|
mock.module("../util/logger.js", () => ({
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}),
|
|
58
|
+
...realLogger,
|
|
59
|
+
getLogger: () => noopLogger,
|
|
60
|
+
getCliLogger: () => noopLogger,
|
|
52
61
|
isDebug: () => false,
|
|
53
62
|
truncateForLog: (v: string) => v,
|
|
63
|
+
initLogger: () => {},
|
|
64
|
+
pruneOldLogFiles: () => 0,
|
|
54
65
|
}));
|
|
55
66
|
|
|
56
67
|
mock.module("../config/loader.js", () => ({
|
|
@@ -59,9 +70,20 @@ mock.module("../config/loader.js", () => ({
|
|
|
59
70
|
|
|
60
71
|
sandbox: { enabled: true },
|
|
61
72
|
}),
|
|
73
|
+
loadConfig: () => ({}),
|
|
74
|
+
loadRawConfig: () => ({}),
|
|
75
|
+
saveConfig: () => {},
|
|
76
|
+
saveRawConfig: () => {},
|
|
77
|
+
invalidateConfigCache: () => {},
|
|
78
|
+
getNestedValue: () => undefined,
|
|
79
|
+
setNestedValue: () => {},
|
|
80
|
+
syncConfigToLockfile: () => {},
|
|
62
81
|
}));
|
|
63
82
|
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
84
|
+
const realUserReference = require("../config/user-reference.js");
|
|
64
85
|
mock.module("../config/user-reference.js", () => ({
|
|
86
|
+
...realUserReference,
|
|
65
87
|
resolveUserReference: () => "John",
|
|
66
88
|
resolveUserPronouns: () => null,
|
|
67
89
|
}));
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `ensureTelegramBotUsernameResolved()`.
|
|
3
|
+
*
|
|
4
|
+
* This function fills the bot-username gap when the token was configured
|
|
5
|
+
* without a `getMe` call (e.g. via `credential set` or ingress secret
|
|
6
|
+
* redirect). Each branch is exercised in isolation by controlling the
|
|
7
|
+
* mutable mock variables below.
|
|
8
|
+
*/
|
|
9
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Mutable mock state — tests toggle these before each call
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
let mockMetadata: { accountInfo?: string } | undefined;
|
|
16
|
+
let mockSecureKey: string | undefined;
|
|
17
|
+
let mockUpsertCalls: Array<{
|
|
18
|
+
service: string;
|
|
19
|
+
key: string;
|
|
20
|
+
patch: Record<string, unknown>;
|
|
21
|
+
}> = [];
|
|
22
|
+
|
|
23
|
+
mock.module("../tools/credentials/metadata-store.js", () => ({
|
|
24
|
+
getCredentialMetadata: (_service: string, _key: string) => mockMetadata,
|
|
25
|
+
upsertCredentialMetadata: (
|
|
26
|
+
service: string,
|
|
27
|
+
key: string,
|
|
28
|
+
patch: Record<string, unknown>,
|
|
29
|
+
) => {
|
|
30
|
+
mockUpsertCalls.push({ service, key, patch });
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
mock.module("../security/secure-keys.js", () => ({
|
|
35
|
+
getSecureKey: (_keyId: string) => mockSecureKey,
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
// Suppress logger output during tests
|
|
39
|
+
mock.module("../util/logger.js", () => ({
|
|
40
|
+
getLogger: () =>
|
|
41
|
+
new Proxy({} as Record<string, unknown>, {
|
|
42
|
+
get: () => () => {},
|
|
43
|
+
}),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Global fetch mock — swapped per test
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
let mockFetchResponse: {
|
|
51
|
+
ok: boolean;
|
|
52
|
+
status: number;
|
|
53
|
+
json: () => Promise<unknown>;
|
|
54
|
+
};
|
|
55
|
+
let mockFetchThrows: Error | undefined;
|
|
56
|
+
let fetchCallCount = 0;
|
|
57
|
+
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
mockMetadata = undefined;
|
|
60
|
+
mockSecureKey = undefined;
|
|
61
|
+
mockUpsertCalls = [];
|
|
62
|
+
mockFetchThrows = undefined;
|
|
63
|
+
mockFetchResponse = {
|
|
64
|
+
ok: true,
|
|
65
|
+
status: 200,
|
|
66
|
+
json: async () => ({ ok: true, result: { username: "ResolvedBot" } }),
|
|
67
|
+
};
|
|
68
|
+
fetchCallCount = 0;
|
|
69
|
+
|
|
70
|
+
globalThis.fetch = (async (..._args: unknown[]) => {
|
|
71
|
+
fetchCallCount++;
|
|
72
|
+
if (mockFetchThrows) throw mockFetchThrows;
|
|
73
|
+
return mockFetchResponse;
|
|
74
|
+
}) as typeof globalThis.fetch;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Import under test — AFTER mocks are registered
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
const { ensureTelegramBotUsernameResolved } =
|
|
82
|
+
await import("../runtime/channel-invite-transports/telegram.js");
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Tests
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
describe("ensureTelegramBotUsernameResolved", () => {
|
|
89
|
+
test("(a) early-returns when accountInfo is already cached", async () => {
|
|
90
|
+
mockMetadata = { accountInfo: "CachedBot" };
|
|
91
|
+
mockSecureKey = "some-token";
|
|
92
|
+
|
|
93
|
+
await ensureTelegramBotUsernameResolved();
|
|
94
|
+
|
|
95
|
+
expect(fetchCallCount).toBe(0);
|
|
96
|
+
expect(mockUpsertCalls).toHaveLength(0);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("(b) fetches getMe and caches username on success", async () => {
|
|
100
|
+
mockMetadata = undefined;
|
|
101
|
+
mockSecureKey = "bot-token-123";
|
|
102
|
+
mockFetchResponse = {
|
|
103
|
+
ok: true,
|
|
104
|
+
status: 200,
|
|
105
|
+
json: async () => ({ ok: true, result: { username: "MyNewBot" } }),
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
await ensureTelegramBotUsernameResolved();
|
|
109
|
+
|
|
110
|
+
expect(fetchCallCount).toBe(1);
|
|
111
|
+
expect(mockUpsertCalls).toEqual([
|
|
112
|
+
{
|
|
113
|
+
service: "telegram",
|
|
114
|
+
key: "bot_token",
|
|
115
|
+
patch: { accountInfo: "MyNewBot" },
|
|
116
|
+
},
|
|
117
|
+
]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("(c) handles non-200 response gracefully without caching", async () => {
|
|
121
|
+
mockMetadata = undefined;
|
|
122
|
+
mockSecureKey = "bot-token-123";
|
|
123
|
+
mockFetchResponse = {
|
|
124
|
+
ok: false,
|
|
125
|
+
status: 401,
|
|
126
|
+
json: async () => ({ ok: false, description: "Unauthorized" }),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
await ensureTelegramBotUsernameResolved();
|
|
130
|
+
|
|
131
|
+
expect(fetchCallCount).toBe(1);
|
|
132
|
+
expect(mockUpsertCalls).toHaveLength(0);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("(d) handles missing username in response gracefully", async () => {
|
|
136
|
+
mockMetadata = undefined;
|
|
137
|
+
mockSecureKey = "bot-token-123";
|
|
138
|
+
mockFetchResponse = {
|
|
139
|
+
ok: true,
|
|
140
|
+
status: 200,
|
|
141
|
+
json: async () => ({ ok: true, result: {} }),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
await ensureTelegramBotUsernameResolved();
|
|
145
|
+
|
|
146
|
+
expect(fetchCallCount).toBe(1);
|
|
147
|
+
expect(mockUpsertCalls).toHaveLength(0);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("(e) handles network errors (fetch throws) gracefully", async () => {
|
|
151
|
+
mockMetadata = undefined;
|
|
152
|
+
mockSecureKey = "bot-token-123";
|
|
153
|
+
mockFetchThrows = new Error("ECONNREFUSED");
|
|
154
|
+
|
|
155
|
+
await ensureTelegramBotUsernameResolved();
|
|
156
|
+
|
|
157
|
+
expect(fetchCallCount).toBe(1);
|
|
158
|
+
expect(mockUpsertCalls).toHaveLength(0);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("(f) no-ops when no bot token is configured", async () => {
|
|
162
|
+
mockMetadata = undefined;
|
|
163
|
+
mockSecureKey = undefined;
|
|
164
|
+
|
|
165
|
+
await ensureTelegramBotUsernameResolved();
|
|
166
|
+
|
|
167
|
+
expect(fetchCallCount).toBe(0);
|
|
168
|
+
expect(mockUpsertCalls).toHaveLength(0);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("treats whitespace-only accountInfo as uncached", async () => {
|
|
172
|
+
mockMetadata = { accountInfo: " " };
|
|
173
|
+
mockSecureKey = "bot-token-123";
|
|
174
|
+
mockFetchResponse = {
|
|
175
|
+
ok: true,
|
|
176
|
+
status: 200,
|
|
177
|
+
json: async () => ({ ok: true, result: { username: "FreshBot" } }),
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
await ensureTelegramBotUsernameResolved();
|
|
181
|
+
|
|
182
|
+
expect(fetchCallCount).toBe(1);
|
|
183
|
+
expect(mockUpsertCalls).toEqual([
|
|
184
|
+
{
|
|
185
|
+
service: "telegram",
|
|
186
|
+
key: "bot_token",
|
|
187
|
+
patch: { accountInfo: "FreshBot" },
|
|
188
|
+
},
|
|
189
|
+
]);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("treats empty-string accountInfo as uncached", async () => {
|
|
193
|
+
mockMetadata = { accountInfo: "" };
|
|
194
|
+
mockSecureKey = "bot-token-456";
|
|
195
|
+
mockFetchResponse = {
|
|
196
|
+
ok: true,
|
|
197
|
+
status: 200,
|
|
198
|
+
json: async () => ({ ok: true, result: { username: "AnotherBot" } }),
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
await ensureTelegramBotUsernameResolved();
|
|
202
|
+
|
|
203
|
+
expect(fetchCallCount).toBe(1);
|
|
204
|
+
expect(mockUpsertCalls).toEqual([
|
|
205
|
+
{
|
|
206
|
+
service: "telegram",
|
|
207
|
+
key: "bot_token",
|
|
208
|
+
patch: { accountInfo: "AnotherBot" },
|
|
209
|
+
},
|
|
210
|
+
]);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the Telegram channel invite adapter.
|
|
3
|
+
*
|
|
4
|
+
* Covers `buildShareLink`, `extractInboundToken`, and
|
|
5
|
+
* `resolveChannelHandle` on the real production adapter.
|
|
6
|
+
*/
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
8
|
+
|
|
9
|
+
import type { ChannelId } from "../channels/types.js";
|
|
10
|
+
import { telegramInviteAdapter } from "../runtime/channel-invite-transports/telegram.js";
|
|
11
|
+
|
|
12
|
+
// Mock credential metadata so tests don't depend on local persisted state.
|
|
13
|
+
mock.module("../tools/credentials/metadata-store.js", () => ({
|
|
14
|
+
getCredentialMetadata: () => undefined,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Helpers
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
const CHANNEL: ChannelId = "telegram" as ChannelId;
|
|
22
|
+
|
|
23
|
+
function setEnv(key: string, value: string | undefined) {
|
|
24
|
+
if (value === undefined) {
|
|
25
|
+
delete process.env[key];
|
|
26
|
+
} else {
|
|
27
|
+
process.env[key] = value;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// buildShareLink
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
describe("telegramInviteAdapter.buildShareLink", () => {
|
|
36
|
+
let originalBotUsername: string | undefined;
|
|
37
|
+
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
originalBotUsername = process.env.TELEGRAM_BOT_USERNAME;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
setEnv("TELEGRAM_BOT_USERNAME", originalBotUsername);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("builds a deep link with iv_ prefix", () => {
|
|
47
|
+
setEnv("TELEGRAM_BOT_USERNAME", "TestBot");
|
|
48
|
+
|
|
49
|
+
const link = telegramInviteAdapter.buildShareLink!({
|
|
50
|
+
rawToken: "abc123",
|
|
51
|
+
sourceChannel: CHANNEL,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(link.url).toBe("https://t.me/TestBot?start=iv_abc123");
|
|
55
|
+
expect(link.displayText).toContain("https://t.me/TestBot?start=iv_abc123");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("throws when bot username is not configured", () => {
|
|
59
|
+
setEnv("TELEGRAM_BOT_USERNAME", undefined);
|
|
60
|
+
|
|
61
|
+
expect(() =>
|
|
62
|
+
telegramInviteAdapter.buildShareLink!({
|
|
63
|
+
rawToken: "abc123",
|
|
64
|
+
sourceChannel: CHANNEL,
|
|
65
|
+
}),
|
|
66
|
+
).toThrow(/bot username/i);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// extractInboundToken
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
describe("telegramInviteAdapter.extractInboundToken", () => {
|
|
75
|
+
test("extracts token from structured commandIntent", () => {
|
|
76
|
+
const token = telegramInviteAdapter.extractInboundToken!({
|
|
77
|
+
commandIntent: { type: "start", payload: "iv_tok123" },
|
|
78
|
+
content: "/start iv_tok123",
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
expect(token).toBe("tok123");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("returns undefined for non-invite commandIntent payload", () => {
|
|
85
|
+
const token = telegramInviteAdapter.extractInboundToken!({
|
|
86
|
+
commandIntent: { type: "start", payload: "gv_guardian_token" },
|
|
87
|
+
content: "/start gv_guardian_token",
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(token).toBeUndefined();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("returns undefined for commandIntent with empty payload after prefix", () => {
|
|
94
|
+
const token = telegramInviteAdapter.extractInboundToken!({
|
|
95
|
+
commandIntent: { type: "start", payload: "iv_" },
|
|
96
|
+
content: "/start iv_",
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(token).toBeUndefined();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("falls back to raw content parsing when no commandIntent", () => {
|
|
103
|
+
const token = telegramInviteAdapter.extractInboundToken!({
|
|
104
|
+
content: "/start iv_fallback_token",
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(token).toBe("fallback_token");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("returns undefined for non-invite raw content", () => {
|
|
111
|
+
const token = telegramInviteAdapter.extractInboundToken!({
|
|
112
|
+
content: "/start gv_something",
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
expect(token).toBeUndefined();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("returns undefined for plain text content", () => {
|
|
119
|
+
const token = telegramInviteAdapter.extractInboundToken!({
|
|
120
|
+
content: "hello world",
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(token).toBeUndefined();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("returns undefined for commandIntent with non-start type", () => {
|
|
127
|
+
const token = telegramInviteAdapter.extractInboundToken!({
|
|
128
|
+
commandIntent: { type: "help", payload: "iv_tok" },
|
|
129
|
+
content: "/help iv_tok",
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
expect(token).toBeUndefined();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// resolveChannelHandle
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
describe("telegramInviteAdapter.resolveChannelHandle", () => {
|
|
141
|
+
let originalBotUsername: string | undefined;
|
|
142
|
+
|
|
143
|
+
beforeEach(() => {
|
|
144
|
+
originalBotUsername = process.env.TELEGRAM_BOT_USERNAME;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
afterEach(() => {
|
|
148
|
+
setEnv("TELEGRAM_BOT_USERNAME", originalBotUsername);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("returns @-prefixed bot username from env", () => {
|
|
152
|
+
setEnv("TELEGRAM_BOT_USERNAME", "MyBot");
|
|
153
|
+
|
|
154
|
+
const handle = telegramInviteAdapter.resolveChannelHandle!();
|
|
155
|
+
expect(handle).toBe("@MyBot");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("returns undefined when bot username is not configured", () => {
|
|
159
|
+
setEnv("TELEGRAM_BOT_USERNAME", undefined);
|
|
160
|
+
|
|
161
|
+
const handle = telegramInviteAdapter.resolveChannelHandle!();
|
|
162
|
+
expect(handle).toBeUndefined();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -63,7 +63,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
63
63
|
|
|
64
64
|
provider: "mock-provider",
|
|
65
65
|
timeouts: { permissionTimeoutSec: 5, toolExecutionTimeoutSec: 120 },
|
|
66
|
-
permissions: { mode: "
|
|
66
|
+
permissions: { mode: "workspace" },
|
|
67
67
|
skills: { load: { extraDirs: [] } },
|
|
68
68
|
secretDetection: { enabled: true, entropyThreshold: 4.0, action: "warn" },
|
|
69
69
|
sandbox: { enabled: false },
|
|
@@ -6,7 +6,10 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
6
6
|
|
|
7
7
|
const testDir = mkdtempSync(join(tmpdir(), "permsim-handler-test-"));
|
|
8
8
|
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
10
|
+
const realPlatform = require("../util/platform.js");
|
|
9
11
|
mock.module("../util/platform.js", () => ({
|
|
12
|
+
...realPlatform,
|
|
10
13
|
getRootDir: () => testDir,
|
|
11
14
|
getDataDir: () => join(testDir, "data"),
|
|
12
15
|
getWorkspaceSkillsDir: () => join(testDir, "skills"),
|
|
@@ -21,7 +24,10 @@ mock.module("../util/platform.js", () => ({
|
|
|
21
24
|
getIpcBlobDir: () => join(testDir, "ipc-blobs"),
|
|
22
25
|
}));
|
|
23
26
|
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
28
|
+
const realLogger = require("../util/logger.js");
|
|
24
29
|
mock.module("../util/logger.js", () => ({
|
|
30
|
+
...realLogger,
|
|
25
31
|
getLogger: () => ({
|
|
26
32
|
info: () => {},
|
|
27
33
|
warn: () => {},
|
|
@@ -39,7 +45,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
39
45
|
}));
|
|
40
46
|
|
|
41
47
|
const testConfig: Record<string, any> = {
|
|
42
|
-
permissions: { mode: "
|
|
48
|
+
permissions: { mode: "workspace" as "strict" | "workspace" },
|
|
43
49
|
skills: { load: { extraDirs: [] as string[] } },
|
|
44
50
|
sandbox: { enabled: true },
|
|
45
51
|
};
|
|
@@ -111,7 +117,7 @@ describe("tool_permission_simulate handler", () => {
|
|
|
111
117
|
beforeEach(() => {
|
|
112
118
|
clearAllRules();
|
|
113
119
|
clearCache();
|
|
114
|
-
testConfig.permissions.mode = "
|
|
120
|
+
testConfig.permissions.mode = "workspace";
|
|
115
121
|
});
|
|
116
122
|
|
|
117
123
|
test("validation: returns error when toolName is missing", async () => {
|