@vellumai/cli 0.3.8 → 0.3.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/cli",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "CLI tools for vellum-assistant",
5
5
  "type": "module",
6
6
  "exports": {
@@ -61,24 +61,6 @@ interface PendingConfirmation {
61
61
  persistentDecisionsAllowed?: boolean;
62
62
  }
63
63
 
64
- interface CreateRunResponse {
65
- id: string;
66
- status: string;
67
- messageId: string | null;
68
- createdAt: string;
69
- }
70
-
71
- interface GetRunResponse {
72
- id: string;
73
- status: string;
74
- messageId: string | null;
75
- pendingConfirmation: PendingConfirmation | null;
76
- pendingSecret: PendingSecret | null;
77
- error: string | null;
78
- createdAt: string;
79
- updatedAt: string;
80
- }
81
-
82
64
  interface SubmitDecisionResponse {
83
65
  accepted: boolean;
84
66
  }
@@ -87,6 +69,11 @@ interface AddTrustRuleResponse {
87
69
  accepted: boolean;
88
70
  }
89
71
 
72
+ interface PendingInteractionsResponse {
73
+ pendingConfirmation: (PendingConfirmation & { requestId: string }) | null;
74
+ pendingSecret: (PendingSecret & { requestId?: string }) | null;
75
+ }
76
+
90
77
  type TrustDecision = "always_allow" | "always_allow_high_risk" | "always_deny";
91
78
 
92
79
  interface HealthResponse {
@@ -177,55 +164,20 @@ async function sendMessage(
177
164
  );
178
165
  }
179
166
 
180
- async function createRun(
181
- baseUrl: string,
182
- assistantId: string,
183
- content: string,
184
- signal?: AbortSignal,
185
- bearerToken?: string,
186
- ): Promise<CreateRunResponse> {
187
- return runtimeRequest<CreateRunResponse>(
188
- baseUrl,
189
- assistantId,
190
- "/runs",
191
- {
192
- method: "POST",
193
- body: JSON.stringify({ conversationKey: assistantId, content }),
194
- signal,
195
- },
196
- bearerToken,
197
- );
198
- }
199
-
200
- async function getRun(
201
- baseUrl: string,
202
- assistantId: string,
203
- runId: string,
204
- bearerToken?: string,
205
- ): Promise<GetRunResponse> {
206
- return runtimeRequest<GetRunResponse>(
207
- baseUrl,
208
- assistantId,
209
- `/runs/${runId}`,
210
- undefined,
211
- bearerToken,
212
- );
213
- }
214
-
215
167
  async function submitDecision(
216
168
  baseUrl: string,
217
169
  assistantId: string,
218
- runId: string,
170
+ requestId: string,
219
171
  decision: "allow" | "deny",
220
172
  bearerToken?: string,
221
173
  ): Promise<SubmitDecisionResponse> {
222
174
  return runtimeRequest<SubmitDecisionResponse>(
223
175
  baseUrl,
224
176
  assistantId,
225
- `/runs/${runId}/decision`,
177
+ "/confirm",
226
178
  {
227
179
  method: "POST",
228
- body: JSON.stringify({ decision }),
180
+ body: JSON.stringify({ requestId, decision }),
229
181
  },
230
182
  bearerToken,
231
183
  );
@@ -234,7 +186,7 @@ async function submitDecision(
234
186
  async function addTrustRule(
235
187
  baseUrl: string,
236
188
  assistantId: string,
237
- runId: string,
189
+ requestId: string,
238
190
  pattern: string,
239
191
  scope: string,
240
192
  decision: "allow" | "deny",
@@ -243,15 +195,30 @@ async function addTrustRule(
243
195
  return runtimeRequest<AddTrustRuleResponse>(
244
196
  baseUrl,
245
197
  assistantId,
246
- `/runs/${runId}/trust-rule`,
198
+ "/trust-rules",
247
199
  {
248
200
  method: "POST",
249
- body: JSON.stringify({ pattern, scope, decision }),
201
+ body: JSON.stringify({ requestId, pattern, scope, decision }),
250
202
  },
251
203
  bearerToken,
252
204
  );
253
205
  }
254
206
 
207
+ async function pollPendingInteractions(
208
+ baseUrl: string,
209
+ assistantId: string,
210
+ bearerToken?: string,
211
+ ): Promise<PendingInteractionsResponse> {
212
+ const params = new URLSearchParams({ conversationKey: assistantId });
213
+ return runtimeRequest<PendingInteractionsResponse>(
214
+ baseUrl,
215
+ assistantId,
216
+ `/pending-interactions?${params.toString()}`,
217
+ undefined,
218
+ bearerToken,
219
+ );
220
+ }
221
+
255
222
  function formatConfirmationPreview(toolName: string, input: Record<string, unknown>): string {
256
223
  switch (toolName) {
257
224
  case "bash":
@@ -282,7 +249,7 @@ function formatConfirmationPreview(toolName: string, input: Record<string, unkno
282
249
  async function handleConfirmationPrompt(
283
250
  baseUrl: string,
284
251
  assistantId: string,
285
- runId: string,
252
+ requestId: string,
286
253
  confirmation: PendingConfirmation,
287
254
  chatApp: ChatAppHandle,
288
255
  bearerToken?: string,
@@ -305,7 +272,7 @@ async function handleConfirmationPrompt(
305
272
  const index = await chatApp.showSelection("Tool Approval", options);
306
273
 
307
274
  if (index === 0) {
308
- await submitDecision(baseUrl, assistantId, runId, "allow", bearerToken);
275
+ await submitDecision(baseUrl, assistantId, requestId, "allow", bearerToken);
309
276
  chatApp.addStatus("\u2714 Allowed", "green");
310
277
  return;
311
278
  }
@@ -313,7 +280,7 @@ async function handleConfirmationPrompt(
313
280
  await handlePatternSelection(
314
281
  baseUrl,
315
282
  assistantId,
316
- runId,
283
+ requestId,
317
284
  confirmation,
318
285
  chatApp,
319
286
  "always_allow",
@@ -325,7 +292,7 @@ async function handleConfirmationPrompt(
325
292
  await handlePatternSelection(
326
293
  baseUrl,
327
294
  assistantId,
328
- runId,
295
+ requestId,
329
296
  confirmation,
330
297
  chatApp,
331
298
  "always_deny",
@@ -334,14 +301,14 @@ async function handleConfirmationPrompt(
334
301
  return;
335
302
  }
336
303
 
337
- await submitDecision(baseUrl, assistantId, runId, "deny", bearerToken);
304
+ await submitDecision(baseUrl, assistantId, requestId, "deny", bearerToken);
338
305
  chatApp.addStatus("\u2718 Denied", "yellow");
339
306
  }
340
307
 
341
308
  async function handlePatternSelection(
342
309
  baseUrl: string,
343
310
  assistantId: string,
344
- runId: string,
311
+ requestId: string,
345
312
  confirmation: PendingConfirmation,
346
313
  chatApp: ChatAppHandle,
347
314
  trustDecision: TrustDecision,
@@ -358,7 +325,7 @@ async function handlePatternSelection(
358
325
  await handleScopeSelection(
359
326
  baseUrl,
360
327
  assistantId,
361
- runId,
328
+ requestId,
362
329
  confirmation,
363
330
  chatApp,
364
331
  selectedPattern,
@@ -368,14 +335,14 @@ async function handlePatternSelection(
368
335
  return;
369
336
  }
370
337
 
371
- await submitDecision(baseUrl, assistantId, runId, "deny", bearerToken);
338
+ await submitDecision(baseUrl, assistantId, requestId, "deny", bearerToken);
372
339
  chatApp.addStatus("\u2718 Denied", "yellow");
373
340
  }
374
341
 
375
342
  async function handleScopeSelection(
376
343
  baseUrl: string,
377
344
  assistantId: string,
378
- runId: string,
345
+ requestId: string,
379
346
  confirmation: PendingConfirmation,
380
347
  chatApp: ChatAppHandle,
381
348
  selectedPattern: string,
@@ -393,7 +360,7 @@ async function handleScopeSelection(
393
360
  await addTrustRule(
394
361
  baseUrl,
395
362
  assistantId,
396
- runId,
363
+ requestId,
397
364
  selectedPattern,
398
365
  scopeOptions[index].scope,
399
366
  ruleDecision,
@@ -402,7 +369,7 @@ async function handleScopeSelection(
402
369
  await submitDecision(
403
370
  baseUrl,
404
371
  assistantId,
405
- runId,
372
+ requestId,
406
373
  ruleDecision === "deny" ? "deny" : "allow",
407
374
  bearerToken,
408
375
  );
@@ -415,7 +382,7 @@ async function handleScopeSelection(
415
382
  return;
416
383
  }
417
384
 
418
- await submitDecision(baseUrl, assistantId, runId, "deny", bearerToken);
385
+ await submitDecision(baseUrl, assistantId, requestId, "deny", bearerToken);
419
386
  chatApp.addStatus("\u2718 Denied", "yellow");
420
387
  }
421
388
 
@@ -1474,9 +1441,8 @@ function ChatApp({
1474
1441
  const controller = new AbortController();
1475
1442
  const timeoutId = setTimeout(() => controller.abort(), SEND_TIMEOUT_MS);
1476
1443
 
1477
- let runId: string | undefined;
1478
1444
  try {
1479
- const runResult = await createRun(
1445
+ const sendResult = await sendMessage(
1480
1446
  runtimeUrl,
1481
1447
  assistantId,
1482
1448
  trimmed,
@@ -1484,29 +1450,20 @@ function ChatApp({
1484
1450
  bearerToken,
1485
1451
  );
1486
1452
  clearTimeout(timeoutId);
1487
- runId = runResult.id;
1488
- } catch (createErr) {
1489
- clearTimeout(timeoutId);
1490
- const is409 = createErr instanceof Error && createErr.message.includes("HTTP 409");
1491
- if (is409) {
1492
- h.setBusy(false);
1493
- h.hideSpinner();
1494
- h.showError("Assistant is still working. Please wait and try again.");
1495
- return;
1496
- }
1497
- const sendResult = await sendMessage(
1498
- runtimeUrl,
1499
- assistantId,
1500
- trimmed,
1501
- undefined,
1502
- bearerToken,
1503
- );
1504
1453
  if (!sendResult.accepted) {
1505
1454
  h.setBusy(false);
1506
1455
  h.hideSpinner();
1507
1456
  h.showError("Message was not accepted by the assistant");
1508
1457
  return;
1509
1458
  }
1459
+ } catch (sendErr) {
1460
+ clearTimeout(timeoutId);
1461
+ h.setBusy(false);
1462
+ h.hideSpinner();
1463
+ const errorMsg = `Failed to send: ${sendErr instanceof Error ? sendErr.message : sendErr}`;
1464
+ h.showError(errorMsg);
1465
+ chatLogRef.current.push({ role: "error", content: errorMsg });
1466
+ return;
1510
1467
  }
1511
1468
 
1512
1469
  h.showSpinner("Working...");
@@ -1514,75 +1471,47 @@ function ChatApp({
1514
1471
  while (true) {
1515
1472
  await new Promise((resolve) => setTimeout(resolve, RESPONSE_POLL_INTERVAL_MS));
1516
1473
 
1517
- if (runId) {
1518
- try {
1519
- const runStatus = await getRun(runtimeUrl, assistantId, runId, bearerToken);
1474
+ // Check for pending confirmations/secrets
1475
+ try {
1476
+ const pending = await pollPendingInteractions(runtimeUrl, assistantId, bearerToken);
1477
+
1478
+ if (pending.pendingConfirmation) {
1479
+ h.hideSpinner();
1480
+ await handleConfirmationPrompt(
1481
+ runtimeUrl,
1482
+ assistantId,
1483
+ pending.pendingConfirmation.requestId,
1484
+ pending.pendingConfirmation,
1485
+ h,
1486
+ bearerToken,
1487
+ );
1488
+ h.showSpinner("Working...");
1489
+ continue;
1490
+ }
1520
1491
 
1521
- if (runStatus.status === "needs_confirmation" && runStatus.pendingConfirmation) {
1522
- h.hideSpinner();
1523
- await handleConfirmationPrompt(
1492
+ if (pending.pendingSecret) {
1493
+ const secretRequestId = pending.pendingSecret.requestId ?? "";
1494
+ h.hideSpinner();
1495
+ await h.handleSecretPrompt(pending.pendingSecret, async (value, delivery) => {
1496
+ await runtimeRequest(
1524
1497
  runtimeUrl,
1525
1498
  assistantId,
1526
- runId,
1527
- runStatus.pendingConfirmation,
1528
- h,
1499
+ "/secret",
1500
+ {
1501
+ method: "POST",
1502
+ body: JSON.stringify({ requestId: secretRequestId, value, delivery }),
1503
+ },
1529
1504
  bearerToken,
1530
1505
  );
1531
- h.showSpinner("Working...");
1532
- continue;
1533
- }
1534
-
1535
- if (runStatus.status === "needs_secret" && runStatus.pendingSecret) {
1536
- h.hideSpinner();
1537
- await h.handleSecretPrompt(runStatus.pendingSecret, async (value, delivery) => {
1538
- await runtimeRequest(
1539
- runtimeUrl,
1540
- assistantId,
1541
- `/runs/${runId}/secret`,
1542
- {
1543
- method: "POST",
1544
- body: JSON.stringify({ value, delivery }),
1545
- },
1546
- bearerToken,
1547
- );
1548
- });
1549
- h.showSpinner("Working...");
1550
- continue;
1551
- }
1552
-
1553
- if (runStatus.status === "completed") {
1554
- try {
1555
- const pollResult = await pollMessages(runtimeUrl, assistantId, bearerToken);
1556
- for (const msg of pollResult.messages) {
1557
- if (!seenMessageIdsRef.current.has(msg.id)) {
1558
- seenMessageIdsRef.current.add(msg.id);
1559
- if (msg.role === "assistant") {
1560
- h.addMessage(msg);
1561
- chatLogRef.current.push({ role: "assistant", content: msg.content });
1562
- }
1563
- }
1564
- }
1565
- } catch {
1566
- // Final poll failure; continue to cleanup
1567
- }
1568
- h.setBusy(false);
1569
- h.hideSpinner();
1570
- return;
1571
- }
1572
-
1573
- if (runStatus.status === "failed") {
1574
- h.setBusy(false);
1575
- h.hideSpinner();
1576
- const errorMsg = runStatus.error ?? "Run failed";
1577
- h.showError(errorMsg);
1578
- chatLogRef.current.push({ role: "error", content: errorMsg });
1579
- return;
1580
- }
1581
- } catch {
1582
- // Run status poll failure; fall through to message poll
1506
+ });
1507
+ h.showSpinner("Working...");
1508
+ continue;
1583
1509
  }
1510
+ } catch {
1511
+ // Pending interactions poll failure; fall through to message poll
1584
1512
  }
1585
1513
 
1514
+ // Poll for new messages to detect completion
1586
1515
  try {
1587
1516
  const pollResult = await pollMessages(runtimeUrl, assistantId, bearerToken);
1588
1517
  for (const msg of pollResult.messages) {
@@ -1591,11 +1520,9 @@ function ChatApp({
1591
1520
  if (msg.role === "assistant") {
1592
1521
  h.addMessage(msg);
1593
1522
  chatLogRef.current.push({ role: "assistant", content: msg.content });
1594
- if (!runId) {
1595
- h.setBusy(false);
1596
- h.hideSpinner();
1597
- return;
1598
- }
1523
+ h.setBusy(false);
1524
+ h.hideSpinner();
1525
+ return;
1599
1526
  }
1600
1527
  }
1601
1528
  }
@@ -1613,14 +1540,9 @@ function ChatApp({
1613
1540
  h.showError(errorMsg);
1614
1541
  chatLogRef.current.push({ role: "error", content: errorMsg });
1615
1542
  } else {
1616
- const is409 = error instanceof Error && error.message.includes("HTTP 409");
1617
- if (is409) {
1618
- h.showError("Assistant is still working. Please wait and try again.");
1619
- } else {
1620
- const errorMsg = `Failed to send: ${error instanceof Error ? error.message : error}`;
1621
- h.showError(errorMsg);
1622
- chatLogRef.current.push({ role: "error", content: errorMsg });
1623
- }
1543
+ const errorMsg = `Failed to send: ${error instanceof Error ? error.message : error}`;
1544
+ h.showError(errorMsg);
1545
+ chatLogRef.current.push({ role: "error", content: errorMsg });
1624
1546
  }
1625
1547
  }
1626
1548
  },