@vellumai/assistant 0.12.0-staging.1 → 0.12.0
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/Dockerfile +5 -0
- package/docs/architecture/turn-actor.md +9 -0
- package/knip.json +1 -0
- package/node_modules/@vellumai/app-icons/package.json +18 -0
- package/node_modules/@vellumai/app-icons/src/index.test.ts +85 -0
- package/node_modules/@vellumai/app-icons/src/index.ts +387 -0
- package/node_modules/@vellumai/app-icons/tsconfig.json +20 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +31 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +28 -3
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +31 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +28 -3
- package/node_modules/@vellumai/gateway-client/src/__tests__/plugin-admission-denied-contract.test.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/plugin-admission-denied-contract.ts +15 -2
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +31 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +28 -3
- package/openapi.yaml +628 -0
- package/package.json +3 -1
- package/scripts/smoke-container-workspace-dependencies.ts +19 -0
- package/src/__tests__/app-builder-icon-names.test.ts +29 -0
- package/src/__tests__/assistant-attachment-directive.test.ts +4 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +2 -0
- package/src/__tests__/conversation-attachments.test.ts +142 -0
- package/src/__tests__/conversation-delete-activation-progress.test.ts +145 -0
- package/src/__tests__/conversation-event-sink.test.ts +50 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -2
- package/src/__tests__/conversation-queue.test.ts +297 -0
- package/src/__tests__/credential-routes.test.ts +185 -6
- package/src/__tests__/drain-kick-guard.test.ts +2 -0
- package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
- package/src/__tests__/messaging-send-tool.test.ts +42 -0
- package/src/__tests__/oauth-commands-routes.test.ts +47 -0
- package/src/__tests__/subagent-manager-notify.test.ts +25 -4
- package/src/activation/progress-store.test.ts +1564 -0
- package/src/activation/progress-store.ts +1296 -0
- package/src/activation/turn-hooks.test.ts +246 -0
- package/src/activation/turn-hooks.ts +126 -0
- package/src/api/events/subagent-status-changed.ts +7 -5
- package/src/api/responses/activation.ts +136 -0
- package/src/apps/app-store.ts +8 -0
- package/src/cli/__tests__/catalog-search-help.test.ts +7 -5
- package/src/cli/commands/__tests__/cli-test-harness.ts +12 -3
- package/src/cli/commands/channels/__tests__/channels.test.ts +2 -0
- package/src/cli/commands/channels/__tests__/request.test.ts +191 -0
- package/src/cli/commands/channels/index.help.ts +70 -18
- package/src/cli/commands/channels/index.ts +17 -6
- package/src/cli/commands/channels/request.ts +66 -0
- package/src/cli/commands/oauth/request.test.ts +2 -0
- package/src/cli/commands/oauth/request.ts +227 -186
- package/src/config/bundled-skills/app-builder/SKILL.md +3 -1
- package/src/config/bundled-skills/app-builder/TOOLS.json +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +8 -4
- package/src/config/feature-flag-registry.json +35 -5
- package/src/daemon/assistant-attachments.ts +11 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +5 -0
- package/src/daemon/conversation-agent-loop.ts +71 -3
- package/src/daemon/conversation-attachments.ts +42 -1
- package/src/daemon/conversation-event-sink.ts +25 -0
- package/src/daemon/conversation-process.ts +69 -21
- package/src/daemon/conversation-store.ts +4 -3
- package/src/daemon/conversation-surfaces.ts +19 -4
- package/src/daemon/message-types/sync.ts +2 -0
- package/src/ipc/assistant-server.ts +2 -0
- package/src/ipc/routes/__tests__/activation-sync-ipc-routes.test.ts +51 -0
- package/src/ipc/routes/activation-sync-ipc-routes.ts +42 -0
- package/src/notifications/AGENTS.md +1 -1
- package/src/notifications/__tests__/proactive-home-thread.test.ts +111 -0
- package/src/notifications/conversation-pairing.ts +47 -5
- package/src/notifications/delivered-post-record.ts +3 -0
- package/src/persistence/__tests__/slack-thread-root-evidence.test.ts +102 -0
- package/src/persistence/conversation-crud.ts +39 -0
- package/src/persistence/delivery-crud.ts +13 -0
- package/src/plugins/AGENTS.md +1 -0
- package/src/runtime/auth/__tests__/route-policy.test.ts +37 -0
- package/src/runtime/routes/__tests__/user-routes-notices.test.ts +248 -0
- package/src/runtime/routes/activation-routes.test.ts +415 -0
- package/src/runtime/routes/activation-routes.ts +172 -0
- package/src/runtime/routes/credential-routes.ts +46 -3
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/oauth-commands-routes.ts +21 -8
- package/src/runtime/routes/platform-managed-credentials.ts +48 -0
- package/src/runtime/routes/secret-routes.ts +3 -12
- package/src/runtime/routes/user-route-resolution.ts +21 -0
- package/src/runtime/routes/user-routes.ts +112 -9
- package/src/runtime/sync/activation-sidecar-publish.test.ts +78 -0
- package/src/runtime/sync/documents-sidecar-publish.test.ts +3 -0
- package/src/runtime/sync/resource-sync-events.ts +23 -0
- package/src/runtime/sync/worker-daemon-notify.test.ts +36 -0
- package/src/runtime/sync/worker-daemon-notify.ts +39 -1
- package/src/tools/apps/executors.ts +8 -8
|
@@ -93,9 +93,34 @@ export const CHANNEL_BOT_PROVIDER = {
|
|
|
93
93
|
* connected" question, for any provider key.
|
|
94
94
|
*/
|
|
95
95
|
export function isChannelBotProvider(providerKey: string): boolean {
|
|
96
|
-
return (
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
return channelForBotProvider(providerKey) !== undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The provider key holding a channel's bot credential, or `undefined` when
|
|
101
|
+
* the channel has none, or is not a channel at all. Walks the map's own
|
|
102
|
+
* entries, so an inherited object property name is never a channel.
|
|
103
|
+
*/
|
|
104
|
+
export function botProviderForChannel(channelId: string): string | undefined {
|
|
105
|
+
return Object.entries(CHANNEL_BOT_PROVIDER).find(
|
|
106
|
+
([id]) => id === channelId,
|
|
107
|
+
)?.[1];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The channel whose bot credential a provider key holds, or `undefined` when
|
|
112
|
+
* the key is not a bot's. The inverse of `botProviderForChannel`, for a
|
|
113
|
+
* surface that has only the provider key in hand and needs to speak in the
|
|
114
|
+
* channel's vocabulary.
|
|
115
|
+
*/
|
|
116
|
+
export function channelForBotProvider(
|
|
117
|
+
providerKey: string,
|
|
118
|
+
): keyof typeof CHANNEL_BOT_PROVIDER | undefined {
|
|
119
|
+
return (
|
|
120
|
+
Object.entries(CHANNEL_BOT_PROVIDER) as Array<
|
|
121
|
+
[keyof typeof CHANNEL_BOT_PROVIDER, string]
|
|
122
|
+
>
|
|
123
|
+
).find(([, key]) => key === providerKey)?.[0];
|
|
99
124
|
}
|
|
100
125
|
|
|
101
126
|
/**
|
package/node_modules/@vellumai/gateway-client/src/__tests__/plugin-admission-denied-contract.test.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { describe, expect, test } from "bun:test";
|
|
|
3
3
|
import {
|
|
4
4
|
ACCESS_DENIED_NOT_APPROVED_REPLY,
|
|
5
5
|
PLUGIN_ADMISSION_DENIED_NOTICE_PATH,
|
|
6
|
+
PLUGIN_NOTICES_ROUTE_PREFIX,
|
|
6
7
|
PluginAdmissionDeniedNoticeSchema,
|
|
7
8
|
} from "../plugin-admission-denied-contract.js";
|
|
8
9
|
|
|
@@ -19,6 +20,15 @@ describe("plugin admission-denied contract", () => {
|
|
|
19
20
|
);
|
|
20
21
|
});
|
|
21
22
|
|
|
23
|
+
test("the notice lives under the gateway-only notices prefix", () => {
|
|
24
|
+
// The runtime reserves this prefix for the gateway's service principal,
|
|
25
|
+
// so a notice posted anywhere else would be reachable by actor clients.
|
|
26
|
+
expect(PLUGIN_NOTICES_ROUTE_PREFIX).toBe("notices");
|
|
27
|
+
expect(PLUGIN_ADMISSION_DENIED_NOTICE_PATH).toStartWith(
|
|
28
|
+
`${PLUGIN_NOTICES_ROUTE_PREFIX}/`,
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
22
32
|
test("round-trips a floor-deny notice", () => {
|
|
23
33
|
const notice = {
|
|
24
34
|
reason: "admission_floor" as const,
|
|
@@ -22,10 +22,23 @@ export const ACCESS_DENIED_NOT_APPROVED_REPLY =
|
|
|
22
22
|
"Sorry, you haven't been approved to message this assistant.";
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* Prefix, under `/x/plugins/<plugin>/`, of the routes reserved for notices
|
|
26
|
+
* the gateway posts to a plugin.
|
|
27
|
+
*
|
|
28
|
+
* The runtime serves every path under it to the gateway's service principal
|
|
29
|
+
* only (`assistant/src/runtime/routes/user-routes.ts`). A notice is the
|
|
30
|
+
* gateway's own verified decision, and a plugin acts on it without running a
|
|
31
|
+
* turn, so an authenticated client must not be able to post one: a forged
|
|
32
|
+
* notice would have the plugin spend its vendor credentials on a recipient
|
|
33
|
+
* and a message the client chose.
|
|
34
|
+
*/
|
|
35
|
+
export const PLUGIN_NOTICES_ROUTE_PREFIX = "notices";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Plugin route the gateway posts the admission-denied notice to, under
|
|
26
39
|
* `/v1/x/plugins/<plugin>/`. Not a public ingress path.
|
|
27
40
|
*/
|
|
28
|
-
export const PLUGIN_ADMISSION_DENIED_NOTICE_PATH =
|
|
41
|
+
export const PLUGIN_ADMISSION_DENIED_NOTICE_PATH = `${PLUGIN_NOTICES_ROUTE_PREFIX}/admission-denied`;
|
|
29
42
|
|
|
30
43
|
export const PluginAdmissionDeniedNoticeSchema = z.object({
|
|
31
44
|
reason: z.literal("admission_floor"),
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
botProviderForChannel,
|
|
4
5
|
CHANNEL_BOT_PROVIDER,
|
|
5
6
|
CHANNEL_IDS,
|
|
7
|
+
channelForBotProvider,
|
|
6
8
|
isChannelBotProvider,
|
|
7
9
|
isChannelUserIntegration,
|
|
8
10
|
isChannelId,
|
|
@@ -41,6 +43,35 @@ describe("isChannelId", () => {
|
|
|
41
43
|
});
|
|
42
44
|
});
|
|
43
45
|
|
|
46
|
+
describe("botProviderForChannel and channelForBotProvider", () => {
|
|
47
|
+
test("are inverses over every entry of the map", () => {
|
|
48
|
+
for (const [channelId, botProviderKey] of Object.entries(
|
|
49
|
+
CHANNEL_BOT_PROVIDER,
|
|
50
|
+
)) {
|
|
51
|
+
expect(botProviderForChannel(channelId)).toBe(botProviderKey);
|
|
52
|
+
expect(channelForBotProvider(botProviderKey)).toBe(
|
|
53
|
+
channelId as keyof typeof CHANNEL_BOT_PROVIDER,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("answer nothing for a channel without a bot, a person's grant, or an unknown key", () => {
|
|
59
|
+
expect(botProviderForChannel("phone")).toBeUndefined();
|
|
60
|
+
expect(botProviderForChannel("vellum")).toBeUndefined();
|
|
61
|
+
expect(botProviderForChannel("not-a-channel")).toBeUndefined();
|
|
62
|
+
// `slack` is the person's integration standing beside the bot's key.
|
|
63
|
+
expect(channelForBotProvider("slack")).toBeUndefined();
|
|
64
|
+
expect(channelForBotProvider("google")).toBeUndefined();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("never resolve an inherited object property as a channel or a key", () => {
|
|
68
|
+
for (const name of ["constructor", "toString", "__proto__"]) {
|
|
69
|
+
expect(botProviderForChannel(name)).toBeUndefined();
|
|
70
|
+
expect(channelForBotProvider(name)).toBeUndefined();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
44
75
|
describe("isChannelUserIntegration", () => {
|
|
45
76
|
test("names the grant standing beside a bot of the same brand", () => {
|
|
46
77
|
expect(isChannelUserIntegration("slack")).toBe(true);
|
|
@@ -93,9 +93,34 @@ export const CHANNEL_BOT_PROVIDER = {
|
|
|
93
93
|
* connected" question, for any provider key.
|
|
94
94
|
*/
|
|
95
95
|
export function isChannelBotProvider(providerKey: string): boolean {
|
|
96
|
-
return (
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
return channelForBotProvider(providerKey) !== undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The provider key holding a channel's bot credential, or `undefined` when
|
|
101
|
+
* the channel has none, or is not a channel at all. Walks the map's own
|
|
102
|
+
* entries, so an inherited object property name is never a channel.
|
|
103
|
+
*/
|
|
104
|
+
export function botProviderForChannel(channelId: string): string | undefined {
|
|
105
|
+
return Object.entries(CHANNEL_BOT_PROVIDER).find(
|
|
106
|
+
([id]) => id === channelId,
|
|
107
|
+
)?.[1];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The channel whose bot credential a provider key holds, or `undefined` when
|
|
112
|
+
* the key is not a bot's. The inverse of `botProviderForChannel`, for a
|
|
113
|
+
* surface that has only the provider key in hand and needs to speak in the
|
|
114
|
+
* channel's vocabulary.
|
|
115
|
+
*/
|
|
116
|
+
export function channelForBotProvider(
|
|
117
|
+
providerKey: string,
|
|
118
|
+
): keyof typeof CHANNEL_BOT_PROVIDER | undefined {
|
|
119
|
+
return (
|
|
120
|
+
Object.entries(CHANNEL_BOT_PROVIDER) as Array<
|
|
121
|
+
[keyof typeof CHANNEL_BOT_PROVIDER, string]
|
|
122
|
+
>
|
|
123
|
+
).find(([, key]) => key === providerKey)?.[0];
|
|
99
124
|
}
|
|
100
125
|
|
|
101
126
|
/**
|