@vellumai/assistant 0.4.45 → 0.4.48
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/ARCHITECTURE.md +6 -6
- package/docs/architecture/memory.md +1 -1
- package/docs/architecture/scheduling.md +2 -3
- package/docs/architecture/security.md +5 -5
- package/docs/trusted-contact-access.md +5 -6
- package/package.json +4 -1
- package/src/__tests__/avatar-e2e.test.ts +18 -219
- package/src/__tests__/avatar-generator.test.ts +5 -57
- package/src/__tests__/browser-fill-credential.test.ts +5 -2
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +2 -1
- package/src/__tests__/channel-readiness-routes.test.ts +20 -19
- package/src/__tests__/cli.test.ts +23 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +23 -22
- package/src/__tests__/credential-broker-server-use.test.ts +22 -21
- package/src/__tests__/credential-broker.test.ts +2 -1
- package/src/__tests__/credential-metadata-store.test.ts +240 -18
- package/src/__tests__/credential-resolve.test.ts +5 -4
- package/src/__tests__/credential-security-e2e.test.ts +8 -8
- package/src/__tests__/credential-security-invariants.test.ts +104 -7
- package/src/__tests__/credential-vault-unit.test.ts +22 -20
- package/src/__tests__/credential-vault.test.ts +284 -12
- package/src/__tests__/credentials-cli.test.ts +11 -6
- package/src/__tests__/gateway-only-enforcement.test.ts +4 -2
- package/src/__tests__/gemini-image-service.test.ts +75 -45
- package/src/__tests__/gemini-provider.test.ts +9 -6
- package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -33
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -20
- package/src/__tests__/guardian-action-followup-executor.test.ts +1 -28
- package/src/__tests__/guardian-action-followup-store.test.ts +1 -1
- package/src/__tests__/guardian-grant-minting.test.ts +35 -0
- package/src/__tests__/integration-status.test.ts +53 -21
- package/src/__tests__/managed-proxy-context.test.ts +5 -3
- package/src/__tests__/media-generate-image.test.ts +63 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -3
- package/src/__tests__/messaging-send-tool.test.ts +4 -6
- package/src/__tests__/provider-fail-open-selection.test.ts +3 -1
- package/src/__tests__/provider-managed-proxy-integration.test.ts +70 -6
- package/src/__tests__/schedule-store.test.ts +1 -1
- package/src/__tests__/schema-transforms.test.ts +226 -0
- package/src/__tests__/script-proxy-injection-runtime.test.ts +23 -13
- package/src/__tests__/script-proxy-policy-runtime.test.ts +1 -1
- package/src/__tests__/script-proxy-session-manager.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +5 -3
- package/src/__tests__/session-messaging-secret-redirect.test.ts +5 -4
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +0 -9
- package/src/__tests__/slack-channel-config.test.ts +9 -8
- package/src/__tests__/slack-share-routes.test.ts +11 -6
- package/src/__tests__/telegram-bot-username-resolution.test.ts +3 -0
- package/src/__tests__/twilio-config.test.ts +2 -1
- package/src/__tests__/twilio-provider.test.ts +4 -2
- package/src/__tests__/twilio-routes.test.ts +5 -4
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
- package/src/approvals/AGENTS.md +1 -1
- package/src/calls/call-domain.ts +7 -4
- package/src/calls/twilio-config.ts +2 -1
- package/src/calls/twilio-provider.ts +2 -1
- package/src/calls/twilio-rest.ts +2 -2
- package/src/cli/commands/browser-relay.ts +40 -15
- package/src/cli/commands/credentials.ts +9 -8
- package/src/cli/commands/oauth.ts +1 -1
- package/src/cli.ts +3 -2
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -4
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +29 -32
- package/src/config/bundled-skills/gmail/SKILL.md +4 -4
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +54 -61
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +25 -28
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +14 -17
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +39 -44
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +61 -58
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +50 -49
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +11 -13
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +148 -146
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +4 -7
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +175 -173
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +4 -7
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +71 -76
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +32 -38
- package/src/config/bundled-skills/google-calendar/SKILL.md +2 -2
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +70 -29
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +9 -10
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +5 -6
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +4 -5
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +14 -15
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +37 -37
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +4 -9
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +24 -3
- package/src/config/bundled-skills/messaging/SKILL.md +6 -6
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +62 -63
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +15 -16
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +6 -7
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +14 -15
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +128 -128
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +33 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +11 -11
- package/src/config/bundled-skills/notifications/SKILL.md +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +5 -5
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
- package/src/config/bundled-skills/slack/tools/shared.ts +4 -10
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +15 -16
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +95 -92
- package/src/config/loader.ts +6 -0
- package/src/daemon/computer-use-session.ts +7 -1
- package/src/daemon/guardian-action-generators.ts +4 -5
- package/src/daemon/handlers/config-slack-channel.ts +37 -20
- package/src/daemon/handlers/config-telegram.ts +33 -20
- package/src/daemon/lifecycle.ts +9 -1
- package/src/daemon/message-types/integrations.ts +1 -0
- package/src/daemon/ride-shotgun-handler.ts +3 -1
- package/src/daemon/session-messaging.ts +3 -1
- package/src/daemon/session-tool-setup.ts +18 -2
- package/src/daemon/session.ts +1 -1
- package/src/email/providers/index.ts +2 -1
- package/src/instrument.ts +15 -1
- package/src/media/app-icon-generator.ts +30 -4
- package/src/media/avatar-router.ts +28 -62
- package/src/media/gemini-image-service.ts +28 -2
- package/src/memory/canonical-guardian-store.ts +1 -1
- package/src/memory/guardian-action-store.ts +1 -1
- package/src/memory/schema/guardian.ts +1 -1
- package/src/messaging/provider.ts +16 -10
- package/src/messaging/providers/gmail/adapter.ts +40 -23
- package/src/messaging/providers/gmail/client.ts +203 -122
- package/src/messaging/providers/gmail/people-client.ts +26 -18
- package/src/messaging/providers/slack/adapter.ts +29 -19
- package/src/messaging/providers/slack/client.ts +265 -78
- package/src/messaging/providers/telegram-bot/adapter.ts +5 -4
- package/src/messaging/providers/whatsapp/adapter.ts +6 -3
- package/src/messaging/registry.ts +2 -1
- package/src/oauth/byo-connection.test.ts +436 -0
- package/src/oauth/byo-connection.ts +112 -0
- package/src/oauth/connect-orchestrator.ts +27 -0
- package/src/oauth/connection-resolver.ts +34 -0
- package/src/oauth/connection.ts +38 -0
- package/src/oauth/platform-connection.test.ts +163 -0
- package/src/oauth/platform-connection.ts +110 -0
- package/src/oauth/provider-base-urls.ts +21 -0
- package/src/oauth/provider-profiles.ts +1 -1
- package/src/oauth/token-persistence.ts +20 -20
- package/src/permissions/checker.ts +6 -1
- package/src/prompts/system-prompt.ts +52 -15
- package/src/prompts/templates/BOOTSTRAP.md +1 -1
- package/src/providers/gemini/client.ts +15 -6
- package/src/providers/managed-proxy/constants.ts +2 -2
- package/src/providers/managed-proxy/context.ts +5 -1
- package/src/providers/ratelimit.ts +17 -0
- package/src/providers/registry.ts +2 -2
- package/src/runtime/AGENTS.md +18 -1
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/channel-invite-transports/telegram.ts +2 -1
- package/src/runtime/channel-readiness-service.ts +168 -195
- package/src/runtime/channel-readiness-types.ts +4 -0
- package/src/runtime/guardian-action-conversation-turn.ts +1 -3
- package/src/runtime/guardian-action-followup-executor.ts +1 -2
- package/src/runtime/guardian-action-message-composer.ts +3 -23
- package/src/runtime/http-server.ts +9 -4
- package/src/runtime/http-types.ts +0 -1
- package/src/runtime/middleware/rate-limiter.ts +74 -20
- package/src/runtime/middleware/twilio-validation.ts +1 -3
- package/src/runtime/routes/channel-readiness-routes.ts +2 -0
- package/src/runtime/routes/diagnostics-routes.ts +11 -9
- package/src/runtime/routes/guardian-approval-interception.ts +20 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +71 -25
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -5
- package/src/runtime/routes/integrations/slack/share.ts +3 -2
- package/src/runtime/routes/integrations/twilio.ts +6 -5
- package/src/runtime/routes/secret-routes.ts +3 -2
- package/src/runtime/routes/settings-routes.ts +75 -17
- package/src/runtime/telegram-streaming-delivery.test.ts +132 -0
- package/src/runtime/telegram-streaming-delivery.ts +11 -1
- package/src/schedule/integration-status.ts +5 -4
- package/src/security/credential-key.ts +170 -0
- package/src/security/token-manager.ts +36 -7
- package/src/tools/apps/definitions.ts +0 -5
- package/src/tools/assets/materialize.ts +0 -5
- package/src/tools/assets/search.ts +0 -5
- package/src/tools/browser/headless-browser.ts +1 -67
- package/src/tools/claude-code/claude-code.ts +0 -5
- package/src/tools/computer-use/request-computer-control.ts +0 -5
- package/src/tools/credentials/broker.ts +6 -4
- package/src/tools/credentials/metadata-store.ts +72 -20
- package/src/tools/credentials/resolve.ts +2 -1
- package/src/tools/credentials/vault.ts +77 -16
- package/src/tools/filesystem/edit.ts +1 -6
- package/src/tools/filesystem/read.ts +0 -5
- package/src/tools/filesystem/write.ts +1 -6
- package/src/tools/host-filesystem/edit.ts +1 -6
- package/src/tools/host-filesystem/read.ts +1 -6
- package/src/tools/host-filesystem/write.ts +1 -6
- package/src/tools/mcp/mcp-tool-factory.ts +18 -1
- package/src/tools/memory/definitions.ts +0 -5
- package/src/tools/network/web-fetch.ts +0 -5
- package/src/tools/network/web-search.ts +0 -5
- package/src/tools/schema-transforms.ts +99 -0
- package/src/tools/skills/load.ts +0 -5
- package/src/tools/swarm/delegate.ts +0 -5
- package/src/tools/system/avatar-generator.ts +3 -44
- package/src/tools/ui-surface/definitions.ts +0 -15
- package/src/tools/watch/screen-watch.ts +0 -5
- package/src/version.ts +10 -0
- package/src/watcher/providers/github.ts +51 -52
- package/src/watcher/providers/gmail.ts +88 -80
- package/src/watcher/providers/google-calendar.ts +93 -86
- package/src/watcher/providers/linear.ts +87 -93
- package/src/__tests__/avatar-router.test.ts +0 -149
- package/src/__tests__/managed-avatar-client.test.ts +0 -337
- package/src/config/bundled-skills/doordash/SKILL.md +0 -170
- package/src/config/bundled-skills/doordash/__tests__/doordash-client.test.ts +0 -205
- package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -74
- package/src/config/bundled-skills/doordash/doordash-cli.ts +0 -1081
- package/src/config/bundled-skills/doordash/doordash-entry.ts +0 -22
- package/src/config/bundled-skills/doordash/lib/cart-queries.ts +0 -787
- package/src/config/bundled-skills/doordash/lib/client.ts +0 -1069
- package/src/config/bundled-skills/doordash/lib/order-queries.ts +0 -85
- package/src/config/bundled-skills/doordash/lib/queries.ts +0 -28
- package/src/config/bundled-skills/doordash/lib/query-extractor.ts +0 -94
- package/src/config/bundled-skills/doordash/lib/search-queries.ts +0 -203
- package/src/config/bundled-skills/doordash/lib/session.ts +0 -96
- package/src/config/bundled-skills/doordash/lib/shared/errors.ts +0 -61
- package/src/config/bundled-skills/doordash/lib/shared/network-recorder.ts +0 -380
- package/src/config/bundled-skills/doordash/lib/shared/platform.ts +0 -55
- package/src/config/bundled-skills/doordash/lib/shared/recording-store.ts +0 -43
- package/src/config/bundled-skills/doordash/lib/shared/recording-types.ts +0 -49
- package/src/config/bundled-skills/doordash/lib/shared/truncate.ts +0 -6
- package/src/config/bundled-skills/doordash/lib/store-queries.ts +0 -246
- package/src/config/bundled-skills/doordash/lib/types.ts +0 -367
- package/src/media/avatar-types.ts +0 -53
- package/src/media/managed-avatar-client.ts +0 -225
|
@@ -69,6 +69,8 @@ mock.module("./policy-validate.js", () => ({
|
|
|
69
69
|
}),
|
|
70
70
|
}));
|
|
71
71
|
|
|
72
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
73
|
+
|
|
72
74
|
const { credentialStoreTool } = await import("../tools/credentials/vault.js");
|
|
73
75
|
|
|
74
76
|
describe("one-time send override", () => {
|
|
@@ -96,7 +98,7 @@ describe("one-time send override", () => {
|
|
|
96
98
|
expect(result.isError).toBe(true);
|
|
97
99
|
expect(result.content).toContain("not enabled");
|
|
98
100
|
// Value must NOT be stored in keychain
|
|
99
|
-
expect(storedKeys.has("
|
|
101
|
+
expect(storedKeys.has(credentialKey("svc", "key"))).toBe(false);
|
|
100
102
|
});
|
|
101
103
|
|
|
102
104
|
test("transient_send succeeds when allowOneTimeSend is enabled", async () => {
|
|
@@ -119,7 +121,7 @@ describe("one-time send override", () => {
|
|
|
119
121
|
expect(result.isError).toBe(false);
|
|
120
122
|
expect(result.content).toContain("NOT saved");
|
|
121
123
|
// Value must NOT be stored in keychain
|
|
122
|
-
expect(storedKeys.has("
|
|
124
|
+
expect(storedKeys.has(credentialKey("svc", "key"))).toBe(false);
|
|
123
125
|
});
|
|
124
126
|
|
|
125
127
|
test("store delivery always persists to keychain regardless of allowOneTimeSend", async () => {
|
|
@@ -138,7 +140,7 @@ describe("one-time send override", () => {
|
|
|
138
140
|
);
|
|
139
141
|
expect(result.isError).toBe(false);
|
|
140
142
|
expect(result.content).toContain("stored");
|
|
141
|
-
expect(storedKeys.has("
|
|
143
|
+
expect(storedKeys.has(credentialKey("svc", "key"))).toBe(true);
|
|
142
144
|
});
|
|
143
145
|
|
|
144
146
|
test("transient_send response content never contains the secret value", async () => {
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
redirectToSecurePrompt,
|
|
6
6
|
} from "../daemon/session-messaging.js";
|
|
7
7
|
import type { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
8
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
8
9
|
|
|
9
10
|
const setSecureKeyMock = mock((_key?: string, _value?: string) => true);
|
|
10
11
|
const upsertCredentialMetadataMock = mock(
|
|
@@ -108,7 +109,7 @@ describe("session-messaging secret redirect", () => {
|
|
|
108
109
|
label: "Telegram Bot Token",
|
|
109
110
|
});
|
|
110
111
|
expect(setSecureKeyMock).toHaveBeenCalledWith(
|
|
111
|
-
"
|
|
112
|
+
credentialKey("telegram", "bot_token"),
|
|
112
113
|
"123456789:ABCDefGHIJklmnopQRSTuvwxyz012345678",
|
|
113
114
|
);
|
|
114
115
|
expect(upsertCredentialMetadataMock).toHaveBeenCalledWith(
|
|
@@ -158,7 +159,7 @@ describe("session-messaging secret redirect", () => {
|
|
|
158
159
|
label: "Telegram Bot Token",
|
|
159
160
|
});
|
|
160
161
|
expect(setSecureKeyMock).toHaveBeenCalledWith(
|
|
161
|
-
"
|
|
162
|
+
credentialKey("telegram", "bot_token"),
|
|
162
163
|
"123456789:ABCDefGHIJklmnopQRSTuvwxyz012345678",
|
|
163
164
|
);
|
|
164
165
|
});
|
|
@@ -197,7 +198,7 @@ describe("session-messaging secret redirect", () => {
|
|
|
197
198
|
label: "Telegram Bot Token",
|
|
198
199
|
});
|
|
199
200
|
expect(setSecureKeyMock).toHaveBeenCalledWith(
|
|
200
|
-
"
|
|
201
|
+
credentialKey("telegram", "bot_token"),
|
|
201
202
|
"123456789:ABCDefGHIJklmnopQRSTuvwxyz012345678",
|
|
202
203
|
);
|
|
203
204
|
});
|
|
@@ -231,7 +232,7 @@ describe("session-messaging secret redirect", () => {
|
|
|
231
232
|
label: "Secure Credential Entry",
|
|
232
233
|
});
|
|
233
234
|
expect(setSecureKeyMock).toHaveBeenCalledWith(
|
|
234
|
-
"
|
|
235
|
+
credentialKey("detected", "Some Unknown Secret"),
|
|
235
236
|
"opaque-secret",
|
|
236
237
|
);
|
|
237
238
|
expect(upsertCredentialMetadataMock).toHaveBeenCalledWith(
|
|
@@ -58,7 +58,7 @@ describe("assistant skills uninstall", () => {
|
|
|
58
58
|
|
|
59
59
|
// GIVEN a skill is installed locally
|
|
60
60
|
installFakeSkill("weather");
|
|
61
|
-
writeSkillsIndex("- weather\n- google-oauth-
|
|
61
|
+
writeSkillsIndex("- weather\n- google-oauth-applescript\n");
|
|
62
62
|
|
|
63
63
|
// WHEN we uninstall the skill
|
|
64
64
|
uninstallSkillLocally("weather");
|
|
@@ -71,7 +71,7 @@ describe("assistant skills uninstall", () => {
|
|
|
71
71
|
expect(index).not.toContain("weather");
|
|
72
72
|
|
|
73
73
|
// AND other skills should remain in the index
|
|
74
|
-
expect(index).toContain("google-oauth-
|
|
74
|
+
expect(index).toContain("google-oauth-applescript");
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
test("errors when skill is not installed", () => {
|
|
@@ -675,15 +675,6 @@ describe("ingress-dependent setup skills declare public-ingress", () => {
|
|
|
675
675
|
expect(includes).toContain("public-ingress");
|
|
676
676
|
});
|
|
677
677
|
|
|
678
|
-
test("google-oauth-setup includes public-ingress", () => {
|
|
679
|
-
const includes = readSkillIncludes(
|
|
680
|
-
FIRST_PARTY_SKILLS_DIR,
|
|
681
|
-
"google-oauth-setup",
|
|
682
|
-
);
|
|
683
|
-
expect(includes).toBeDefined();
|
|
684
|
-
expect(includes).toContain("public-ingress");
|
|
685
|
-
});
|
|
686
|
-
|
|
687
678
|
test("slack-oauth-setup includes browser", () => {
|
|
688
679
|
const includes = readSkillIncludes(
|
|
689
680
|
FIRST_PARTY_SKILLS_DIR,
|
|
@@ -172,6 +172,7 @@ import {
|
|
|
172
172
|
getSlackChannelConfig,
|
|
173
173
|
setSlackChannelConfig,
|
|
174
174
|
} from "../daemon/handlers/config-slack-channel.js";
|
|
175
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
175
176
|
|
|
176
177
|
afterAll(() => {
|
|
177
178
|
globalThis.fetch = originalFetch;
|
|
@@ -199,8 +200,8 @@ describe("Slack channel config handler", () => {
|
|
|
199
200
|
});
|
|
200
201
|
|
|
201
202
|
test("GET returns connected: true when both tokens are set", () => {
|
|
202
|
-
secureKeyStore["
|
|
203
|
-
secureKeyStore["
|
|
203
|
+
secureKeyStore[credentialKey("slack_channel", "bot_token")] = "xoxb-test";
|
|
204
|
+
secureKeyStore[credentialKey("slack_channel", "app_token")] = "xapp-test";
|
|
204
205
|
|
|
205
206
|
const result = getSlackChannelConfig();
|
|
206
207
|
expect(result.success).toBe(true);
|
|
@@ -210,7 +211,7 @@ describe("Slack channel config handler", () => {
|
|
|
210
211
|
});
|
|
211
212
|
|
|
212
213
|
test("GET returns metadata from config when available", () => {
|
|
213
|
-
secureKeyStore["
|
|
214
|
+
secureKeyStore[credentialKey("slack_channel", "bot_token")] = "xoxb-test";
|
|
214
215
|
configStore = {
|
|
215
216
|
slack: {
|
|
216
217
|
teamId: "T123",
|
|
@@ -240,7 +241,7 @@ describe("Slack channel config handler", () => {
|
|
|
240
241
|
);
|
|
241
242
|
expect(result.success).toBe(true);
|
|
242
243
|
expect(result.hasAppToken).toBe(true);
|
|
243
|
-
expect(secureKeyStore["
|
|
244
|
+
expect(secureKeyStore[credentialKey("slack_channel", "app_token")]).toBe(
|
|
244
245
|
"xapp-valid-token-123",
|
|
245
246
|
);
|
|
246
247
|
});
|
|
@@ -296,8 +297,8 @@ describe("Slack channel config handler", () => {
|
|
|
296
297
|
});
|
|
297
298
|
|
|
298
299
|
test("DELETE clears credentials and config", async () => {
|
|
299
|
-
secureKeyStore["
|
|
300
|
-
secureKeyStore["
|
|
300
|
+
secureKeyStore[credentialKey("slack_channel", "bot_token")] = "xoxb-test";
|
|
301
|
+
secureKeyStore[credentialKey("slack_channel", "app_token")] = "xapp-test";
|
|
301
302
|
credentialMetadataStore.push({
|
|
302
303
|
service: "slack_channel",
|
|
303
304
|
field: "bot_token",
|
|
@@ -322,10 +323,10 @@ describe("Slack channel config handler", () => {
|
|
|
322
323
|
expect(result.connected).toBe(false);
|
|
323
324
|
|
|
324
325
|
expect(
|
|
325
|
-
secureKeyStore["
|
|
326
|
+
secureKeyStore[credentialKey("slack_channel", "bot_token")],
|
|
326
327
|
).toBeUndefined();
|
|
327
328
|
expect(
|
|
328
|
-
secureKeyStore["
|
|
329
|
+
secureKeyStore[credentialKey("slack_channel", "app_token")],
|
|
329
330
|
).toBeUndefined();
|
|
330
331
|
expect(credentialMetadataStore).toHaveLength(0);
|
|
331
332
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
4
|
+
|
|
3
5
|
// ---------------------------------------------------------------------------
|
|
4
6
|
// Mocks — must be declared before any imports that pull in mocked modules
|
|
5
7
|
// ---------------------------------------------------------------------------
|
|
@@ -105,7 +107,7 @@ describe("handleListSlackChannels", () => {
|
|
|
105
107
|
|
|
106
108
|
test("returns channels sorted by type then name", async () => {
|
|
107
109
|
secureKeyValues.set(
|
|
108
|
-
"
|
|
110
|
+
credentialKey("integration:slack", "access_token"),
|
|
109
111
|
"xoxb-test",
|
|
110
112
|
);
|
|
111
113
|
|
|
@@ -176,7 +178,10 @@ describe("handleListSlackChannels", () => {
|
|
|
176
178
|
});
|
|
177
179
|
|
|
178
180
|
test("falls back to legacy bot token", async () => {
|
|
179
|
-
secureKeyValues.set(
|
|
181
|
+
secureKeyValues.set(
|
|
182
|
+
credentialKey("slack_channel", "bot_token"),
|
|
183
|
+
"xoxb-legacy",
|
|
184
|
+
);
|
|
180
185
|
|
|
181
186
|
listConversationsResult = { ok: true, channels: [] };
|
|
182
187
|
|
|
@@ -194,7 +199,7 @@ describe("handleShareToSlackChannel", () => {
|
|
|
194
199
|
|
|
195
200
|
test("returns 400 for malformed JSON", async () => {
|
|
196
201
|
secureKeyValues.set(
|
|
197
|
-
"
|
|
202
|
+
credentialKey("integration:slack", "access_token"),
|
|
198
203
|
"xoxb-test",
|
|
199
204
|
);
|
|
200
205
|
const req = new Request("http://localhost/v1/slack/share", {
|
|
@@ -208,7 +213,7 @@ describe("handleShareToSlackChannel", () => {
|
|
|
208
213
|
|
|
209
214
|
test("returns 400 when missing required fields", async () => {
|
|
210
215
|
secureKeyValues.set(
|
|
211
|
-
"
|
|
216
|
+
credentialKey("integration:slack", "access_token"),
|
|
212
217
|
"xoxb-test",
|
|
213
218
|
);
|
|
214
219
|
const req = makeRequest({ appId: "app1" });
|
|
@@ -220,7 +225,7 @@ describe("handleShareToSlackChannel", () => {
|
|
|
220
225
|
|
|
221
226
|
test("returns 404 when app not found", async () => {
|
|
222
227
|
secureKeyValues.set(
|
|
223
|
-
"
|
|
228
|
+
credentialKey("integration:slack", "access_token"),
|
|
224
229
|
"xoxb-test",
|
|
225
230
|
);
|
|
226
231
|
appStoreResult = null;
|
|
@@ -231,7 +236,7 @@ describe("handleShareToSlackChannel", () => {
|
|
|
231
236
|
|
|
232
237
|
test("posts message and returns success", async () => {
|
|
233
238
|
secureKeyValues.set(
|
|
234
|
-
"
|
|
239
|
+
credentialKey("integration:slack", "access_token"),
|
|
235
240
|
"xoxb-test",
|
|
236
241
|
);
|
|
237
242
|
appStoreResult = {
|
|
@@ -38,6 +38,9 @@ mock.module("../config/loader.js", () => ({
|
|
|
38
38
|
|
|
39
39
|
mock.module("../security/secure-keys.js", () => ({
|
|
40
40
|
getSecureKey: (_keyId: string) => mockSecureKey,
|
|
41
|
+
setSecureKey: (_account: string, _value: string) => true,
|
|
42
|
+
deleteSecureKey: (_account: string) => "deleted" as const,
|
|
43
|
+
listSecureKeys: () => [] as string[],
|
|
41
44
|
}));
|
|
42
45
|
|
|
43
46
|
// Suppress logger output during tests
|
|
@@ -26,11 +26,12 @@ mock.module("../inbound/public-ingress-urls.js", () => ({
|
|
|
26
26
|
}));
|
|
27
27
|
|
|
28
28
|
import { getTwilioConfig } from "../calls/twilio-config.js";
|
|
29
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
29
30
|
|
|
30
31
|
describe("twilio-config", () => {
|
|
31
32
|
beforeEach(() => {
|
|
32
33
|
mockSecureKeys = {
|
|
33
|
-
"
|
|
34
|
+
[credentialKey("twilio", "auth_token")]: "test_auth_token",
|
|
34
35
|
};
|
|
35
36
|
mockLoadConfigResult = {
|
|
36
37
|
twilio: {
|
|
@@ -8,6 +8,8 @@ import { tmpdir } from "node:os";
|
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
10
10
|
|
|
11
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
12
|
+
|
|
11
13
|
const testDir = mkdtempSync(join(tmpdir(), "twilio-provider-test-"));
|
|
12
14
|
|
|
13
15
|
mock.module("../util/platform.js", () => ({
|
|
@@ -40,8 +42,8 @@ mock.module("../config/loader.js", () => ({
|
|
|
40
42
|
|
|
41
43
|
mock.module("../security/secure-keys.js", () => ({
|
|
42
44
|
getSecureKey: (key: string) => {
|
|
43
|
-
if (key === "
|
|
44
|
-
if (key === "
|
|
45
|
+
if (key === credentialKey("twilio", "auth_token")) return mockAuthToken;
|
|
46
|
+
if (key === credentialKey("twilio", "account_sid")) return mockAccountSid;
|
|
45
47
|
return undefined;
|
|
46
48
|
},
|
|
47
49
|
}));
|
|
@@ -47,12 +47,12 @@ function readMockTwilioAccountSid(): string | undefined {
|
|
|
47
47
|
const twilio = (mockRawConfigStore.twilio ?? {}) as Record<string, unknown>;
|
|
48
48
|
return (
|
|
49
49
|
(twilio.accountSid as string | undefined) ??
|
|
50
|
-
mockSecureKeyStore["
|
|
50
|
+
mockSecureKeyStore[credentialKey("twilio", "account_sid")]
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
function readMockTwilioAuthToken(): string | undefined {
|
|
55
|
-
return mockSecureKeyStore["
|
|
55
|
+
return mockSecureKeyStore[credentialKey("twilio", "auth_token")];
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
function readMockTwilioPhoneNumber(): string | undefined {
|
|
@@ -333,6 +333,7 @@ import {
|
|
|
333
333
|
handleProvisionTwilioNumber,
|
|
334
334
|
handleSetTwilioCredentials,
|
|
335
335
|
} from "../runtime/routes/integrations/twilio.js";
|
|
336
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
336
337
|
|
|
337
338
|
initializeDb();
|
|
338
339
|
|
|
@@ -429,8 +430,8 @@ describe("twilio webhook routes", () => {
|
|
|
429
430
|
twilio: { accountSid: "AC_existing", phoneNumber: "+15550001111" },
|
|
430
431
|
};
|
|
431
432
|
mockSecureKeyStore = {
|
|
432
|
-
"
|
|
433
|
-
"
|
|
433
|
+
[credentialKey("twilio", "account_sid")]: "AC_existing",
|
|
434
|
+
[credentialKey("twilio", "auth_token")]: "test-auth-token",
|
|
434
435
|
};
|
|
435
436
|
mockAvailableNumbers = [{ phoneNumber: "+15556667777" }];
|
|
436
437
|
mockProvisionedNumber = { phoneNumber: "+15556667777" };
|
|
@@ -402,7 +402,7 @@ describe("isVerificationControlPlaneInvocation", () => {
|
|
|
402
402
|
test("detects endpoint despite malformed percent-encoding elsewhere in command", () => {
|
|
403
403
|
const result = isVerificationControlPlaneInvocation("bash", {
|
|
404
404
|
command:
|
|
405
|
-
'curl -H "X: %ZZ" http://localhost:3000/v1/channel-verification-sessions -d \'{"channel":"
|
|
405
|
+
'curl -H "X: %ZZ" http://localhost:3000/v1/channel-verification-sessions -d \'{"channel":"telegram"}\'',
|
|
406
406
|
});
|
|
407
407
|
expect(result).toBe(true);
|
|
408
408
|
});
|
package/src/approvals/AGENTS.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Approval Flow Resilience
|
|
4
4
|
|
|
5
5
|
- **Rich delivery failures must degrade gracefully.** If delivering a rich approval prompt (e.g., Telegram inline buttons) fails, fall back to plain text with instructions (e.g., `Reply "yes" to approve`) — never auto-deny.
|
|
6
|
-
- **Non-rich channels** (
|
|
6
|
+
- **Non-rich channels** (http-api) receive plain-text approval prompts. The conversational approval engine handles free-text responses.
|
|
7
7
|
- **Race conditions:** Always check whether a decision has already been resolved before delivering the engine's optimistic reply. If `handleChannelDecision` returns `applied: false`, deliver an "already resolved" notice and return `stale_ignored`.
|
|
8
8
|
- **Requester self-cancel:** A requester with a pending guardian approval must be able to cancel their own request (but not self-approve).
|
|
9
9
|
- **Unified guardian decision primitive:** All guardian decision paths (callback buttons, conversational engine, requester self-cancel) must route through `applyGuardianDecision()` in `assistant/src/approvals/guardian-decision-primitive.ts`. Do not inline decision logic (approve_always downgrade, approval record updates, grant minting) at individual callsites.
|
package/src/calls/call-domain.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { upsertBinding } from "../memory/external-conversation-store.js";
|
|
|
21
21
|
import { revokeScopedApprovalGrantsForContext } from "../memory/scoped-approval-grants.js";
|
|
22
22
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
23
23
|
import { isGuardian } from "../runtime/channel-verification-service.js";
|
|
24
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
24
25
|
import { getSecureKey } from "../security/secure-keys.js";
|
|
25
26
|
import { getLogger } from "../util/logger.js";
|
|
26
27
|
import { upsertActiveCallLease } from "./active-call-lease.js";
|
|
@@ -135,7 +136,7 @@ export type CallerIdentityResult =
|
|
|
135
136
|
* For `assistant_number`: uses the Twilio phone number from
|
|
136
137
|
* `getTwilioConfig()`. No eligibility check is performed — this is a fast path.
|
|
137
138
|
* For `user_number`: uses `config.calls.callerIdentity.userNumber` or the
|
|
138
|
-
* secure key `credential
|
|
139
|
+
* secure key `credential/twilio/user_phone_number`, then validates that the
|
|
139
140
|
* number is usable as an outbound caller ID via the Twilio API.
|
|
140
141
|
*/
|
|
141
142
|
export async function resolveCallerIdentity(
|
|
@@ -197,7 +198,9 @@ export async function resolveCallerIdentity(
|
|
|
197
198
|
userNumber = getTwilioUserPhoneNumber()!;
|
|
198
199
|
numberSource = "env_var";
|
|
199
200
|
} else {
|
|
200
|
-
const secureKeyValue = getSecureKey(
|
|
201
|
+
const secureKeyValue = getSecureKey(
|
|
202
|
+
credentialKey("twilio", "user_phone_number"),
|
|
203
|
+
);
|
|
201
204
|
if (secureKeyValue) {
|
|
202
205
|
userNumber = secureKeyValue;
|
|
203
206
|
numberSource = "secure_key";
|
|
@@ -212,7 +215,7 @@ export async function resolveCallerIdentity(
|
|
|
212
215
|
return {
|
|
213
216
|
ok: false,
|
|
214
217
|
error:
|
|
215
|
-
"user_number mode requires a user phone number. Set calls.callerIdentity.userNumber in config or store credential
|
|
218
|
+
"user_number mode requires a user phone number. Set calls.callerIdentity.userNumber in config or store credential/twilio/user_phone_number via the credential_store tool.",
|
|
216
219
|
};
|
|
217
220
|
}
|
|
218
221
|
|
|
@@ -223,7 +226,7 @@ export async function resolveCallerIdentity(
|
|
|
223
226
|
);
|
|
224
227
|
return {
|
|
225
228
|
ok: false,
|
|
226
|
-
error: `User phone number "${userNumber}" is not in E.164 format (must start with + followed by digits, e.g. +14155551234). Check calls.callerIdentity.userNumber in config or credential
|
|
229
|
+
error: `User phone number "${userNumber}" is not in E.164 format (must start with + followed by digits, e.g. +14155551234). Check calls.callerIdentity.userNumber in config or credential/twilio/user_phone_number.`,
|
|
227
230
|
};
|
|
228
231
|
}
|
|
229
232
|
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
getPublicBaseUrl,
|
|
5
5
|
getTwilioRelayUrl,
|
|
6
6
|
} from "../inbound/public-ingress-urls.js";
|
|
7
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
7
8
|
import { getSecureKey } from "../security/secure-keys.js";
|
|
8
9
|
import { ConfigError } from "../util/errors.js";
|
|
9
10
|
import { getLogger } from "../util/logger.js";
|
|
@@ -45,7 +46,7 @@ export function resolveTwilioPhoneNumber(): string {
|
|
|
45
46
|
export function getTwilioConfig(): TwilioConfig {
|
|
46
47
|
const config = loadConfig();
|
|
47
48
|
const accountSid = config.twilio?.accountSid || "";
|
|
48
|
-
const authToken = getSecureKey("
|
|
49
|
+
const authToken = getSecureKey(credentialKey("twilio", "auth_token")) || "";
|
|
49
50
|
const phoneNumber = resolveTwilioPhoneNumber();
|
|
50
51
|
const webhookBaseUrl = getPublicBaseUrl(config);
|
|
51
52
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
2
|
|
|
3
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
3
4
|
import { getSecureKey } from "../security/secure-keys.js";
|
|
4
5
|
import { ProviderError } from "../util/errors.js";
|
|
5
6
|
import { getLogger } from "../util/logger.js";
|
|
@@ -280,7 +281,7 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
|
|
|
280
281
|
* middleware) can check availability independently.
|
|
281
282
|
*/
|
|
282
283
|
static getAuthToken(): string | null {
|
|
283
|
-
return getSecureKey("
|
|
284
|
+
return getSecureKey(credentialKey("twilio", "auth_token")) || null;
|
|
284
285
|
}
|
|
285
286
|
|
|
286
287
|
/**
|
package/src/calls/twilio-rest.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { loadConfig } from "../config/loader.js";
|
|
10
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
10
11
|
import { getSecureKey } from "../security/secure-keys.js";
|
|
11
12
|
import { ConfigError, ProviderError } from "../util/errors.js";
|
|
12
13
|
|
|
@@ -28,7 +29,7 @@ function resolveAccountSid(): string | undefined {
|
|
|
28
29
|
|
|
29
30
|
/** Resolve the Twilio Auth Token from the credential store. */
|
|
30
31
|
function resolveAuthToken(): string | undefined {
|
|
31
|
-
return getSecureKey("
|
|
32
|
+
return getSecureKey(credentialKey("twilio", "auth_token")) || undefined;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/** Resolve Twilio credentials from config (SID) and credential store (token). Throws if not configured. */
|
|
@@ -124,7 +125,6 @@ export async function searchAvailableNumbers(
|
|
|
124
125
|
areaCode?: string,
|
|
125
126
|
): Promise<AvailablePhoneNumber[]> {
|
|
126
127
|
const params = new URLSearchParams({
|
|
127
|
-
SmsEnabled: "true",
|
|
128
128
|
VoiceEnabled: "true",
|
|
129
129
|
});
|
|
130
130
|
if (areaCode) params.set("AreaCode", areaCode);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
|
|
3
|
+
import type { ExtensionCommand } from "../../browser-extension-relay/protocol.js";
|
|
4
|
+
import { extensionRelayServer } from "../../browser-extension-relay/server.js";
|
|
3
5
|
import {
|
|
4
6
|
initAuthSigningKey,
|
|
5
7
|
isSigningKeyInitialized,
|
|
@@ -16,22 +18,36 @@ import {
|
|
|
16
18
|
} from "../../tools/browser/chrome-cdp.js";
|
|
17
19
|
|
|
18
20
|
// ---------------------------------------------------------------------------
|
|
19
|
-
// Shared relay helper
|
|
21
|
+
// Shared relay helper — dual-path: in-process first, gateway fallback
|
|
20
22
|
// ---------------------------------------------------------------------------
|
|
21
23
|
|
|
22
24
|
async function relayCommand(command: Record<string, unknown>): Promise<void> {
|
|
23
25
|
try {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
id: string;
|
|
26
|
+
// Try the in-process extensionRelayServer first (for when CLI runs
|
|
27
|
+
// within the daemon). Falls back to gateway HTTP for out-of-process
|
|
28
|
+
// CLI contexts.
|
|
29
|
+
let data: {
|
|
30
|
+
id?: string;
|
|
30
31
|
success: boolean;
|
|
31
32
|
result?: unknown;
|
|
32
33
|
error?: string;
|
|
33
34
|
tabId?: number;
|
|
34
|
-
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
data = await extensionRelayServer.sendCommand(
|
|
39
|
+
command as Omit<ExtensionCommand, "id">,
|
|
40
|
+
);
|
|
41
|
+
} catch {
|
|
42
|
+
// In-process relay unavailable — fall back to gateway HTTP
|
|
43
|
+
if (!isSigningKeyInitialized()) {
|
|
44
|
+
initAuthSigningKey(loadOrCreateSigningKey());
|
|
45
|
+
}
|
|
46
|
+
({ data } = await gatewayPost<typeof data>(
|
|
47
|
+
"/v1/browser-relay/command",
|
|
48
|
+
command,
|
|
49
|
+
));
|
|
50
|
+
}
|
|
35
51
|
|
|
36
52
|
if (data.success) {
|
|
37
53
|
process.stdout.write(
|
|
@@ -367,15 +383,24 @@ Examples:
|
|
|
367
383
|
)
|
|
368
384
|
.action(async () => {
|
|
369
385
|
try {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
const data = await gatewayGet<{
|
|
386
|
+
// Dual-path: try in-process first, fall back to gateway HTTP
|
|
387
|
+
let data: {
|
|
374
388
|
connected: boolean;
|
|
375
|
-
connectionId?: string;
|
|
376
|
-
lastHeartbeatAt?: number;
|
|
389
|
+
connectionId?: string | null;
|
|
390
|
+
lastHeartbeatAt?: number | null;
|
|
377
391
|
pendingCommandCount: number;
|
|
378
|
-
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
data = extensionRelayServer.getStatus();
|
|
396
|
+
} catch {
|
|
397
|
+
// In-process relay unavailable — fall back to gateway HTTP
|
|
398
|
+
if (!isSigningKeyInitialized()) {
|
|
399
|
+
initAuthSigningKey(loadOrCreateSigningKey());
|
|
400
|
+
}
|
|
401
|
+
data = await gatewayGet<typeof data>("/v1/browser-relay/status");
|
|
402
|
+
}
|
|
403
|
+
|
|
379
404
|
process.stdout.write(
|
|
380
405
|
JSON.stringify({
|
|
381
406
|
ok: true,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
|
|
3
|
+
import { credentialKey } from "../../security/credential-key.js";
|
|
3
4
|
import {
|
|
4
5
|
deleteSecureKeyAsync,
|
|
5
6
|
getSecureKey,
|
|
@@ -127,7 +128,7 @@ export function registerCredentialsCommand(program: Command): void {
|
|
|
127
128
|
"after",
|
|
128
129
|
`
|
|
129
130
|
Credentials are identified by name in service:field format, matching the
|
|
130
|
-
storage convention used internally (credential
|
|
131
|
+
storage convention used internally (credential/{service}/{field}):
|
|
131
132
|
|
|
132
133
|
twilio:account_sid Twilio account SID
|
|
133
134
|
twilio:auth_token Twilio auth token
|
|
@@ -198,7 +199,7 @@ Examples:
|
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
const credentials = allMetadata.map((m) => {
|
|
201
|
-
const secret = getSecureKey(
|
|
202
|
+
const secret = getSecureKey(credentialKey(m.service, m.field));
|
|
202
203
|
return buildCredentialOutput(m, secret);
|
|
203
204
|
});
|
|
204
205
|
|
|
@@ -273,7 +274,7 @@ Examples:
|
|
|
273
274
|
}
|
|
274
275
|
|
|
275
276
|
const { service, field } = parsed;
|
|
276
|
-
const storageKey =
|
|
277
|
+
const storageKey = credentialKey(service, field);
|
|
277
278
|
|
|
278
279
|
assertMetadataWritable();
|
|
279
280
|
|
|
@@ -350,7 +351,7 @@ Examples:
|
|
|
350
351
|
}
|
|
351
352
|
|
|
352
353
|
const { service, field } = parsed;
|
|
353
|
-
const storageKey =
|
|
354
|
+
const storageKey = credentialKey(service, field);
|
|
354
355
|
|
|
355
356
|
assertMetadataWritable();
|
|
356
357
|
|
|
@@ -424,11 +425,11 @@ Examples:
|
|
|
424
425
|
return;
|
|
425
426
|
}
|
|
426
427
|
metadata = getCredentialMetadata(parsed.service, parsed.field);
|
|
427
|
-
storageKey =
|
|
428
|
+
storageKey = credentialKey(parsed.service, parsed.field);
|
|
428
429
|
} else {
|
|
429
430
|
metadata = getCredentialMetadataById(name);
|
|
430
431
|
if (metadata) {
|
|
431
|
-
storageKey =
|
|
432
|
+
storageKey = credentialKey(metadata.service, metadata.field);
|
|
432
433
|
} else {
|
|
433
434
|
// No metadata found by UUID, and we can't determine the storage key
|
|
434
435
|
writeOutput(cmd, { ok: false, error: "Credential not found" });
|
|
@@ -529,11 +530,11 @@ Examples:
|
|
|
529
530
|
process.exitCode = 1;
|
|
530
531
|
return;
|
|
531
532
|
}
|
|
532
|
-
storageKey =
|
|
533
|
+
storageKey = credentialKey(parsed.service, parsed.field);
|
|
533
534
|
} else {
|
|
534
535
|
const metadata = getCredentialMetadataById(name);
|
|
535
536
|
if (metadata) {
|
|
536
|
-
storageKey =
|
|
537
|
+
storageKey = credentialKey(metadata.service, metadata.field);
|
|
537
538
|
} else {
|
|
538
539
|
writeOutput(cmd, { ok: false, error: "Credential not found" });
|
|
539
540
|
process.exitCode = 1;
|
|
@@ -17,7 +17,7 @@ guaranteed-valid access token, refreshing transparently if the stored token
|
|
|
17
17
|
is expired or near-expiry. Callers never need to handle refresh themselves.
|
|
18
18
|
|
|
19
19
|
The <service> argument is the short integration name (e.g. "twitter", "gmail",
|
|
20
|
-
"slack"). Internally this maps to credential
|
|
20
|
+
"slack"). Internally this maps to credential/integration:<service>/access_token.
|
|
21
21
|
|
|
22
22
|
Examples:
|
|
23
23
|
$ assistant oauth token twitter
|
package/src/cli.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
3
|
import { dirname } from "node:path";
|
|
3
4
|
import * as readline from "node:readline";
|
|
@@ -503,7 +504,7 @@ export async function startCli(): Promise<void> {
|
|
|
503
504
|
const trimmed = answer.trim().toLowerCase();
|
|
504
505
|
if (trimmed === "n") {
|
|
505
506
|
// Create a new conversation by using a unique key
|
|
506
|
-
conversationKey = `builtin-cli:${
|
|
507
|
+
conversationKey = `builtin-cli:${randomUUID()}`;
|
|
507
508
|
sessionId = "";
|
|
508
509
|
pendingSessionPick = false;
|
|
509
510
|
// Reconnect SSE with new conversation key
|
|
@@ -1147,7 +1148,7 @@ export async function startCli(): Promise<void> {
|
|
|
1147
1148
|
|
|
1148
1149
|
if (content === "/new") {
|
|
1149
1150
|
// Create a new conversation by using a unique key
|
|
1150
|
-
conversationKey = `builtin-cli:${
|
|
1151
|
+
conversationKey = `builtin-cli:${randomUUID()}`;
|
|
1151
1152
|
sessionId = "";
|
|
1152
1153
|
reconnectSse().then(() => {
|
|
1153
1154
|
process.stdout.write(
|
|
@@ -37,10 +37,6 @@
|
|
|
37
37
|
"type": "string",
|
|
38
38
|
"enum": ["general", "researcher", "coder", "reviewer"],
|
|
39
39
|
"description": "Worker profile that scopes tool access. Defaults to general (backward compatible)."
|
|
40
|
-
},
|
|
41
|
-
"reason": {
|
|
42
|
-
"type": "string",
|
|
43
|
-
"description": "Brief non-technical explanation of what you are delegating and why, shown to the user as a status update. Use simple language a non-technical person would understand."
|
|
44
40
|
}
|
|
45
41
|
}
|
|
46
42
|
},
|