@vellumai/assistant 0.8.2 → 0.8.3
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 +11 -12
- package/docker-entrypoint.sh +13 -1
- package/docker-init-apt-root.sh +79 -6
- package/openapi.yaml +336 -21
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/context-token-estimator.test.ts +30 -65
- package/src/__tests__/conversation-agent-loop.test.ts +57 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-runtime-assembly.test.ts +26 -4
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +5 -0
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
- package/src/__tests__/llm-resolver.test.ts +255 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/openai-provider.test.ts +218 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +6 -73
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/agent/loop.ts +167 -18
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/notifications.ts +65 -35
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/call-site-defaults.ts +105 -0
- package/src/config/feature-flag-registry.json +21 -29
- package/src/config/llm-resolver.ts +52 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +1 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +4 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +72 -12
- package/src/context/token-estimator.ts +32 -34
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
- package/src/daemon/conversation-agent-loop.ts +29 -2
- package/src/daemon/conversation-runtime-assembly.ts +9 -0
- package/src/daemon/conversation.ts +0 -7
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +289 -0
- package/src/daemon/handlers/conversations.ts +1 -0
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/lifecycle.ts +49 -61
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/process-message.ts +3 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -0
- package/src/heartbeat/heartbeat-service.ts +34 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/feed-types.ts +14 -2
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +6 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-enqueue.ts +1 -20
- package/src/memory/memory-retrospective-job.ts +33 -6
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection.test.ts +190 -3
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection.ts +49 -20
- package/src/memory/v2/page-index.ts +38 -13
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +11 -2
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +1 -0
- package/src/notifications/home-feed-side-effect.ts +85 -6
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/plugins/defaults/injectors.ts +38 -19
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +6 -51
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/system-prompt.ts +0 -8
- package/src/prompts/templates/BOOTSTRAP.md +5 -5
- package/src/prompts/templates/system-sections.ts +0 -9
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +9 -20
- package/src/providers/inference/auth.ts +12 -0
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +2 -0
- package/src/providers/model-catalog.ts +199 -244
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +159 -6
- package/src/providers/openrouter/client.ts +42 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/runtime/agent-wake.ts +61 -1
- package/src/runtime/auth/route-policy.ts +13 -0
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +0 -47
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +66 -4
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/conversation-query-routes.ts +70 -11
- package/src/runtime/routes/conversation-routes.ts +7 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +134 -1
- package/src/runtime/routes/integrations/a2a.ts +235 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/subagent/manager.ts +2 -0
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/registry.ts +2 -2
- package/src/tools/types.ts +37 -2
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
|
@@ -4,7 +4,7 @@ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
4
4
|
// Test isolation: mock logger and IPC client
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
6
6
|
|
|
7
|
-
mock.module("
|
|
7
|
+
mock.module("../../../util/logger.js", () => ({
|
|
8
8
|
getLogger: () =>
|
|
9
9
|
new Proxy({} as Record<string, unknown>, {
|
|
10
10
|
get: () => () => {},
|
|
@@ -18,21 +18,43 @@ mock.module("../../util/logger.js", () => ({
|
|
|
18
18
|
// Track cliIpcCall invocations and control responses
|
|
19
19
|
const ipcCalls: Array<{ method: string; params?: Record<string, unknown> }> =
|
|
20
20
|
[];
|
|
21
|
-
let ipcResponse: {
|
|
21
|
+
let ipcResponse: {
|
|
22
|
+
ok: boolean;
|
|
23
|
+
result?: unknown;
|
|
24
|
+
error?: string;
|
|
25
|
+
statusCode?: number;
|
|
26
|
+
} = {
|
|
22
27
|
ok: true,
|
|
23
28
|
result: {},
|
|
24
29
|
};
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
const exitCodeFromIpcResult = (r: { statusCode?: number }): number => {
|
|
32
|
+
if (r.statusCode === undefined) return 10;
|
|
33
|
+
if (r.statusCode >= 500) return 3;
|
|
34
|
+
if (r.statusCode >= 400) return 2;
|
|
35
|
+
return 1;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
mock.module("../../../ipc/cli-client.js", () => ({
|
|
27
39
|
cliIpcCall: async (method: string, params?: Record<string, unknown>) => {
|
|
28
40
|
ipcCalls.push({ method, params });
|
|
29
41
|
return ipcResponse;
|
|
30
42
|
},
|
|
43
|
+
exitFromIpcResult: (r: {
|
|
44
|
+
ok: false;
|
|
45
|
+
error?: string;
|
|
46
|
+
statusCode?: number;
|
|
47
|
+
}) => {
|
|
48
|
+
process.stderr.write((r.error ?? "Unknown error") + "\n");
|
|
49
|
+
process.exitCode = exitCodeFromIpcResult(r);
|
|
50
|
+
return undefined as never;
|
|
51
|
+
},
|
|
52
|
+
exitCodeFromIpcResult,
|
|
31
53
|
}));
|
|
32
54
|
|
|
33
55
|
import { Command } from "commander";
|
|
34
56
|
|
|
35
|
-
import { registerNotificationsCommand } from "../
|
|
57
|
+
import { registerNotificationsCommand } from "../notifications.js";
|
|
36
58
|
|
|
37
59
|
// ---------------------------------------------------------------------------
|
|
38
60
|
// Helpers
|
|
@@ -40,6 +62,7 @@ import { registerNotificationsCommand } from "../commands/notifications.js";
|
|
|
40
62
|
|
|
41
63
|
interface CommandResult {
|
|
42
64
|
parsed: Record<string, unknown>;
|
|
65
|
+
stderr: string;
|
|
43
66
|
exitCode: number;
|
|
44
67
|
}
|
|
45
68
|
|
|
@@ -52,7 +75,9 @@ interface CommandResult {
|
|
|
52
75
|
*/
|
|
53
76
|
async function runCommand(args: string[]): Promise<CommandResult> {
|
|
54
77
|
const chunks: string[] = [];
|
|
78
|
+
const stderrChunks: string[] = [];
|
|
55
79
|
const originalWrite = process.stdout.write;
|
|
80
|
+
const originalStderrWrite = process.stderr.write;
|
|
56
81
|
|
|
57
82
|
process.exitCode = 0;
|
|
58
83
|
|
|
@@ -61,6 +86,11 @@ async function runCommand(args: string[]): Promise<CommandResult> {
|
|
|
61
86
|
return true;
|
|
62
87
|
}) as typeof process.stdout.write;
|
|
63
88
|
|
|
89
|
+
process.stderr.write = ((chunk: string | Buffer) => {
|
|
90
|
+
stderrChunks.push(typeof chunk === "string" ? chunk : chunk.toString());
|
|
91
|
+
return true;
|
|
92
|
+
}) as typeof process.stderr.write;
|
|
93
|
+
|
|
64
94
|
try {
|
|
65
95
|
const program = new Command();
|
|
66
96
|
program.exitOverride();
|
|
@@ -76,6 +106,7 @@ async function runCommand(args: string[]): Promise<CommandResult> {
|
|
|
76
106
|
// Commander throws on .exitOverride() for --help/errors; ignore
|
|
77
107
|
} finally {
|
|
78
108
|
process.stdout.write = originalWrite;
|
|
109
|
+
process.stderr.write = originalStderrWrite;
|
|
79
110
|
}
|
|
80
111
|
|
|
81
112
|
const exitCode = process.exitCode ?? 0;
|
|
@@ -87,7 +118,13 @@ async function runCommand(args: string[]): Promise<CommandResult> {
|
|
|
87
118
|
? (JSON.parse(firstLine) as Record<string, unknown>)
|
|
88
119
|
: {};
|
|
89
120
|
|
|
90
|
-
return { parsed, exitCode };
|
|
121
|
+
return { parsed, stderr: stderrChunks.join(""), exitCode };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function lastSendBody(): Record<string, unknown> {
|
|
125
|
+
expect(ipcCalls).toHaveLength(1);
|
|
126
|
+
expect(ipcCalls[0].method).toBe("emit_notification_signal");
|
|
127
|
+
return ipcCalls[0].params?.body as Record<string, unknown>;
|
|
91
128
|
}
|
|
92
129
|
|
|
93
130
|
// ---------------------------------------------------------------------------
|
|
@@ -132,13 +169,10 @@ describe("notifications send", () => {
|
|
|
132
169
|
expect(parsed.ok).toBe(true);
|
|
133
170
|
expect(parsed.signalId).toBe("mock-id");
|
|
134
171
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
expect(
|
|
138
|
-
const
|
|
139
|
-
expect(callBody?.sourceChannel).toBe("assistant_tool");
|
|
140
|
-
expect(callBody?.sourceEventName).toBe("user.send_notification");
|
|
141
|
-
const payload = callBody?.contextPayload as Record<string, unknown>;
|
|
172
|
+
const body = lastSendBody();
|
|
173
|
+
expect(body.sourceChannel).toBe("assistant_tool");
|
|
174
|
+
expect(body.sourceEventName).toBe("user.send_notification");
|
|
175
|
+
const payload = body.contextPayload as Record<string, unknown>;
|
|
142
176
|
expect(payload.requestedMessage).toBe("Hello");
|
|
143
177
|
});
|
|
144
178
|
|
|
@@ -160,9 +194,7 @@ describe("notifications send", () => {
|
|
|
160
194
|
expect(exitCode).toBe(0);
|
|
161
195
|
expect(parsed.ok).toBe(true);
|
|
162
196
|
|
|
163
|
-
|
|
164
|
-
const emitBody = ipcCalls[0].params?.body as Record<string, unknown>;
|
|
165
|
-
const hints = emitBody?.attentionHints as Record<string, unknown>;
|
|
197
|
+
const hints = lastSendBody().attentionHints as Record<string, unknown>;
|
|
166
198
|
expect(hints.urgency).toBe("high");
|
|
167
199
|
expect(hints.requiresAction).toBe(true);
|
|
168
200
|
expect(hints.isAsyncBackground).toBe(true);
|
|
@@ -184,9 +216,7 @@ describe("notifications send", () => {
|
|
|
184
216
|
expect(exitCode).toBe(0);
|
|
185
217
|
expect(parsed.ok).toBe(true);
|
|
186
218
|
|
|
187
|
-
|
|
188
|
-
const dlBody = ipcCalls[0].params?.body as Record<string, unknown>;
|
|
189
|
-
const payload = dlBody?.contextPayload as Record<string, unknown>;
|
|
219
|
+
const payload = lastSendBody().contextPayload as Record<string, unknown>;
|
|
190
220
|
expect(payload.preferredChannels).toEqual(["telegram", "slack"]);
|
|
191
221
|
});
|
|
192
222
|
|
|
@@ -230,8 +260,8 @@ describe("notifications send", () => {
|
|
|
230
260
|
expect(exitCode).toBe(0);
|
|
231
261
|
expect(parsed.ok).toBe(true);
|
|
232
262
|
|
|
233
|
-
const
|
|
234
|
-
expect(
|
|
263
|
+
const body = lastSendBody();
|
|
264
|
+
expect(body.conversationAffinityHint).toEqual({ vellum: "conv-123" });
|
|
235
265
|
});
|
|
236
266
|
|
|
237
267
|
test("send omits conversationAffinityHint when --conversation-id not passed", async () => {
|
|
@@ -245,8 +275,8 @@ describe("notifications send", () => {
|
|
|
245
275
|
"Hi",
|
|
246
276
|
]);
|
|
247
277
|
|
|
248
|
-
const
|
|
249
|
-
expect(
|
|
278
|
+
const body = lastSendBody();
|
|
279
|
+
expect(body.conversationAffinityHint).toBeUndefined();
|
|
250
280
|
});
|
|
251
281
|
|
|
252
282
|
test("send rejects empty --conversation-id", async () => {
|
|
@@ -268,10 +298,35 @@ describe("notifications send", () => {
|
|
|
268
298
|
expect(ipcCalls).toHaveLength(0);
|
|
269
299
|
});
|
|
270
300
|
|
|
271
|
-
test("send surfaces IPC error response", async () => {
|
|
301
|
+
test("send surfaces IPC error response as JSON envelope in --json mode", async () => {
|
|
272
302
|
ipcResponse = {
|
|
273
303
|
ok: false,
|
|
274
|
-
error: "
|
|
304
|
+
error: "Could not connect to assistant daemon. Is it running?",
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const { parsed, stderr, exitCode } = await runCommand([
|
|
308
|
+
"send",
|
|
309
|
+
"--source-channel",
|
|
310
|
+
"assistant_tool",
|
|
311
|
+
"--source-event-name",
|
|
312
|
+
"user.send_notification",
|
|
313
|
+
"--message",
|
|
314
|
+
"Hello",
|
|
315
|
+
]);
|
|
316
|
+
|
|
317
|
+
// Transport failure (no statusCode) maps to exit 10 per exitFromIpcResult.
|
|
318
|
+
expect(exitCode).toBe(10);
|
|
319
|
+
expect(parsed.ok).toBe(false);
|
|
320
|
+
expect(parsed.error).toContain("Could not connect");
|
|
321
|
+
// --json mode keeps error on stdout envelope, not stderr.
|
|
322
|
+
expect(stderr).toBe("");
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("send maps daemon 4xx to exit 2 while preserving --json envelope", async () => {
|
|
326
|
+
ipcResponse = {
|
|
327
|
+
ok: false,
|
|
328
|
+
error: "Invalid signal payload",
|
|
329
|
+
statusCode: 422,
|
|
275
330
|
};
|
|
276
331
|
|
|
277
332
|
const { parsed, exitCode } = await runCommand([
|
|
@@ -284,9 +339,96 @@ describe("notifications send", () => {
|
|
|
284
339
|
"Hello",
|
|
285
340
|
]);
|
|
286
341
|
|
|
287
|
-
expect(exitCode).toBe(
|
|
342
|
+
expect(exitCode).toBe(2);
|
|
288
343
|
expect(parsed.ok).toBe(false);
|
|
289
|
-
expect(parsed.error).toBe("
|
|
344
|
+
expect(parsed.error).toBe("Invalid signal payload");
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// ---------------------------------------------------------------------------
|
|
349
|
+
// send — minimal-surface ergonomics (--urgent and source defaults)
|
|
350
|
+
// ---------------------------------------------------------------------------
|
|
351
|
+
|
|
352
|
+
describe("notifications send — minimal-surface ergonomics", () => {
|
|
353
|
+
test("--urgent maps to urgency=critical + requiresAction=true", async () => {
|
|
354
|
+
const { exitCode } = await runCommand([
|
|
355
|
+
"send",
|
|
356
|
+
"--message",
|
|
357
|
+
"Pager: prod is down",
|
|
358
|
+
"--urgent",
|
|
359
|
+
]);
|
|
360
|
+
|
|
361
|
+
expect(exitCode).toBe(0);
|
|
362
|
+
const hints = lastSendBody().attentionHints as Record<string, unknown>;
|
|
363
|
+
expect(hints.urgency).toBe("critical");
|
|
364
|
+
expect(hints.requiresAction).toBe(true);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test("missing --source-channel defaults to 'assistant_tool'", async () => {
|
|
368
|
+
const { exitCode } = await runCommand(["send", "--message", "hello"]);
|
|
369
|
+
|
|
370
|
+
expect(exitCode).toBe(0);
|
|
371
|
+
const body = lastSendBody();
|
|
372
|
+
expect(body.sourceChannel).toBe("assistant_tool");
|
|
373
|
+
// The context payload echoes the source channel via requestedBySource.
|
|
374
|
+
const payload = body.contextPayload as Record<string, unknown>;
|
|
375
|
+
expect(payload.requestedBySource).toBe("assistant_tool");
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test("missing --source-event-name defaults to 'assistant.share'", async () => {
|
|
379
|
+
const { exitCode } = await runCommand(["send", "--message", "hello"]);
|
|
380
|
+
|
|
381
|
+
expect(exitCode).toBe(0);
|
|
382
|
+
expect(lastSendBody().sourceEventName).toBe("assistant.share");
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test("explicit --urgency high still overrides defaults when --urgent is absent", async () => {
|
|
386
|
+
const { exitCode } = await runCommand([
|
|
387
|
+
"send",
|
|
388
|
+
"--message",
|
|
389
|
+
"stand-up reminder",
|
|
390
|
+
"--urgency",
|
|
391
|
+
"high",
|
|
392
|
+
]);
|
|
393
|
+
|
|
394
|
+
expect(exitCode).toBe(0);
|
|
395
|
+
const hints = lastSendBody().attentionHints as Record<string, unknown>;
|
|
396
|
+
expect(hints.urgency).toBe("high");
|
|
397
|
+
// Without --urgent or --requires-action, requiresAction stays at the new
|
|
398
|
+
// default of false.
|
|
399
|
+
expect(hints.requiresAction).toBe(false);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test("explicit --urgency wins even when --urgent is also passed (back-compat)", async () => {
|
|
403
|
+
const { exitCode } = await runCommand([
|
|
404
|
+
"send",
|
|
405
|
+
"--message",
|
|
406
|
+
"deploy complete",
|
|
407
|
+
"--urgent",
|
|
408
|
+
"--urgency",
|
|
409
|
+
"medium",
|
|
410
|
+
]);
|
|
411
|
+
|
|
412
|
+
expect(exitCode).toBe(0);
|
|
413
|
+
const hints = lastSendBody().attentionHints as Record<string, unknown>;
|
|
414
|
+
expect(hints.urgency).toBe("medium");
|
|
415
|
+
// --urgent still flips requiresAction since no explicit flag was passed.
|
|
416
|
+
expect(hints.requiresAction).toBe(true);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
test("explicit --no-requires-action wins even when --urgent is passed", async () => {
|
|
420
|
+
const { exitCode } = await runCommand([
|
|
421
|
+
"send",
|
|
422
|
+
"--message",
|
|
423
|
+
"fyi only",
|
|
424
|
+
"--urgent",
|
|
425
|
+
"--no-requires-action",
|
|
426
|
+
]);
|
|
427
|
+
|
|
428
|
+
expect(exitCode).toBe(0);
|
|
429
|
+
const hints = lastSendBody().attentionHints as Record<string, unknown>;
|
|
430
|
+
expect(hints.urgency).toBe("critical");
|
|
431
|
+
expect(hints.requiresAction).toBe(false);
|
|
290
432
|
});
|
|
291
433
|
});
|
|
292
434
|
|
|
@@ -342,7 +484,9 @@ describe("notifications list", () => {
|
|
|
342
484
|
expect(parsed.ok).toBe(true);
|
|
343
485
|
|
|
344
486
|
expect(ipcCalls).toHaveLength(1);
|
|
345
|
-
expect((ipcCalls[0].params?.body as Record<string, unknown>)?.limit).toBe(
|
|
487
|
+
expect((ipcCalls[0].params?.body as Record<string, unknown>)?.limit).toBe(
|
|
488
|
+
5,
|
|
489
|
+
);
|
|
346
490
|
});
|
|
347
491
|
|
|
348
492
|
test("list passes --source-event-name to IPC", async () => {
|
|
@@ -371,8 +515,37 @@ describe("notifications list", () => {
|
|
|
371
515
|
|
|
372
516
|
const { parsed, exitCode } = await runCommand(["list"]);
|
|
373
517
|
|
|
374
|
-
|
|
518
|
+
// Transport failure (no statusCode) maps to exit 10 per exitCodeFromIpcResult.
|
|
519
|
+
expect(exitCode).toBe(10);
|
|
375
520
|
expect(parsed.ok).toBe(false);
|
|
376
521
|
expect(parsed.error).toContain("Could not connect");
|
|
377
522
|
});
|
|
523
|
+
|
|
524
|
+
test("list maps daemon 4xx to exit 2", async () => {
|
|
525
|
+
ipcResponse = {
|
|
526
|
+
ok: false,
|
|
527
|
+
error: "Invalid limit",
|
|
528
|
+
statusCode: 400,
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const { parsed, exitCode } = await runCommand(["list"]);
|
|
532
|
+
|
|
533
|
+
expect(exitCode).toBe(2);
|
|
534
|
+
expect(parsed.ok).toBe(false);
|
|
535
|
+
expect(parsed.error).toBe("Invalid limit");
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
test("list maps daemon 5xx to exit 3", async () => {
|
|
539
|
+
ipcResponse = {
|
|
540
|
+
ok: false,
|
|
541
|
+
error: "Internal daemon error",
|
|
542
|
+
statusCode: 500,
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
const { parsed, exitCode } = await runCommand(["list"]);
|
|
546
|
+
|
|
547
|
+
expect(exitCode).toBe(3);
|
|
548
|
+
expect(parsed.ok).toBe(false);
|
|
549
|
+
expect(parsed.error).toBe("Internal daemon error");
|
|
550
|
+
});
|
|
378
551
|
});
|