privateer-agent 0.15.0 → 0.15.2
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/README.md +447 -570
- package/extensions/privateer-brand.ts +9 -9
- package/package.json +1 -1
- package/src/cli/chat.ts +22 -10
- package/src/config/whatsNew.json +3 -3
- package/src/mcp/catalog.ts +479 -0
- package/src/remote/liveTaskSession.ts +12 -1
- package/src/remote/remoteBridge.ts +83 -22
- package/src/ui/palette.ts +17 -0
|
@@ -64,9 +64,9 @@ const VERSION: string = (() => {
|
|
|
64
64
|
// The Privateer mark: our symbol — a padlock (with a keyhole) fused into an anchor,
|
|
65
65
|
// "bring your own model" meets lock-and-key privacy — drawn from the app's logo. It's
|
|
66
66
|
// rendered with terminal HALF-BLOCKS, so each text row packs TWO pixel rows. The mark is
|
|
67
|
-
// a SINGLE colour (every set pixel is
|
|
67
|
+
// a SINGLE colour (every set pixel is WHITE), so a cell never needs two different
|
|
68
68
|
// colours: both pixels set → a full block "█", top only → "▀", bottom only → "▄", none →
|
|
69
|
-
// a space — all painted with
|
|
69
|
+
// a space — all painted with WHITE as FOREGROUND, no background cells at all. That
|
|
70
70
|
// makes the mark inherit the theme's ink (dark on light, light on dark) with one colour,
|
|
71
71
|
// and sidesteps the old bg-bleed hazard entirely. Every built line is MARK_W visible
|
|
72
72
|
// cells wide (SGR escapes don't count), so the text column beside it stays aligned. To
|
|
@@ -91,9 +91,9 @@ const PIXELS = [
|
|
|
91
91
|
".OO..OO..OO.", "..OO.OO.OO..",
|
|
92
92
|
"..OOOOOOOO..", "...OOOOOO...", "....OOOO....", ".....OO.....",
|
|
93
93
|
];
|
|
94
|
-
// Build the mark for a given palette (
|
|
94
|
+
// Build the mark for a given palette (WHITE is the ink). Cheap — called once per
|
|
95
95
|
// header factory invocation, i.e. once per theme, not per frame. Each cell resets SGR so
|
|
96
|
-
// the
|
|
96
|
+
// the colour can never bleed into the row padding the framer adds after it.
|
|
97
97
|
function buildMark(p: Palette): string[] {
|
|
98
98
|
const rows: string[] = [];
|
|
99
99
|
for (let r = 0; r < PIXELS.length; r += 2) {
|
|
@@ -103,9 +103,9 @@ function buildMark(p: Palette): string[] {
|
|
|
103
103
|
for (let x = 0; x < MARK_W; x++) {
|
|
104
104
|
const t = top[x] === "O";
|
|
105
105
|
const b = bot[x] === "O";
|
|
106
|
-
if (t && b) line += `${p.
|
|
107
|
-
else if (t) line += `${p.
|
|
108
|
-
else if (b) line += `${p.
|
|
106
|
+
if (t && b) line += `${p.WHITE}█${p.RESET}`;
|
|
107
|
+
else if (t) line += `${p.WHITE}▀${p.RESET}`;
|
|
108
|
+
else if (b) line += `${p.WHITE}▄${p.RESET}`;
|
|
109
109
|
else line += " ";
|
|
110
110
|
}
|
|
111
111
|
rows.push(line);
|
|
@@ -277,7 +277,7 @@ function contextLine(p: Palette): string {
|
|
|
277
277
|
const cost = truncated
|
|
278
278
|
? `${p.YELLOW} · too big, ${fmtBytes(loadedBytes)} loaded · ${p.INK}/context${p.RESET}`
|
|
279
279
|
: `${p.DIM} · ~${tok} tok/turn${p.RESET}`;
|
|
280
|
-
return `${p.
|
|
280
|
+
return `${p.WHITE}⚓${p.DIM} ${p.RESET}${p.INK}${nearest}${p.RESET}${more}${cost}`;
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
// ── "What's New" — a tiny in-banner changelog ────────────────────────────────
|
|
@@ -314,7 +314,7 @@ function renderBanner(width: number, p: Palette, mark: string[], modelProvider?:
|
|
|
314
314
|
// sits beside the lock body (not the shackle); the rest follows in reading order.
|
|
315
315
|
const text: string[] = [
|
|
316
316
|
"",
|
|
317
|
-
`${p.BOLD}${p.
|
|
317
|
+
`${p.BOLD}${p.SKY}✻ ${p.SKY}P${p.INK}RIVATEER${p.RESET}${p.DIM} privateer-agent ${p.INK}v${VERSION}${p.RESET}`,
|
|
318
318
|
`${p.DIM}Chart your own course privately.${p.RESET}`,
|
|
319
319
|
"",
|
|
320
320
|
accountLine(p, modelProvider),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "privateer-agent",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"description": "Privacy-first terminal coding agent — bring your own model across 20 providers (Anthropic, OpenAI, OpenRouter, Google, local Ollama…). Safe-by-default permissions, MCP, sub-agents, workflows, and verifiable TEE inference. Built on the Pi toolkit.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/cli/chat.ts
CHANGED
|
@@ -336,16 +336,22 @@ async function main() {
|
|
|
336
336
|
|
|
337
337
|
async function runTurn(text: string, remote: boolean, echo = true): Promise<void> {
|
|
338
338
|
if (turnActive) {
|
|
339
|
-
|
|
340
|
-
//
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
339
|
+
// A message sent while a turn runs STEERS it, the way typing into a running
|
|
340
|
+
// session does in any agent: Pi delivers it after the tool calls in flight and
|
|
341
|
+
// before the next model call. Refusing it ("busy") left the driver no way to
|
|
342
|
+
// correct course short of Stop, which throws the turn's work away.
|
|
343
|
+
if (remote) console.log(`\n${DIM}⟿ [app] ${text.slice(0, 80)}${RESET}`);
|
|
344
|
+
try {
|
|
345
|
+
const mentions = await resolveMentions(text, cwd);
|
|
346
|
+
await session.steer(mentions.text, mentions.images.length ? mentions.images : undefined);
|
|
347
|
+
console.log(`${DIM}(steering the running turn)${RESET}`);
|
|
348
|
+
} catch (e) {
|
|
349
|
+
// An extension command can't be queued (Pi refuses it mid-stream). Say so to
|
|
350
|
+
// whoever sent it — the app has already echoed the message into its feed.
|
|
351
|
+
const m = `Not sent — ${(e as Error).message || "this can't be added to a running turn"}. Wait for the turn to finish, or press Stop.`;
|
|
352
|
+
console.log(`\n${DIM}${m}${RESET}`);
|
|
353
|
+
if (remote) relay?.sendNotice(m);
|
|
354
|
+
}
|
|
349
355
|
return;
|
|
350
356
|
}
|
|
351
357
|
turnActive = true;
|
|
@@ -375,6 +381,12 @@ async function main() {
|
|
|
375
381
|
if (remote) relay?.sendNotice(m);
|
|
376
382
|
}
|
|
377
383
|
await session.prompt(mentions.text, mentions.images.length ? { images: mentions.images } : undefined);
|
|
384
|
+
// A steer that landed after the agent's last check (while its final reply was
|
|
385
|
+
// streaming) sits in Pi's queue with nothing left to pick it up. Run it now
|
|
386
|
+
// rather than leave the driver's message unanswered until some later prompt.
|
|
387
|
+
for (let q = session.clearQueue?.(); q && q.steering.length + q.followUp.length > 0; q = session.clearQueue?.()) {
|
|
388
|
+
await session.prompt([...q.steering, ...q.followUp].join("\n\n"));
|
|
389
|
+
}
|
|
378
390
|
} catch (e) {
|
|
379
391
|
failure = (e as Error).message || "The turn failed.";
|
|
380
392
|
console.log(`\n${RED}${failure}${RESET}`);
|
package/src/config/whatsNew.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "The banner's What's New list, newest first: the two to four things a returning user should notice, not a changelog. `reviewedFor` is the release this list was last looked at for. A release can't ship until it equals package.json's version (scripts/check-whats-new.mjs, run by `npm test` and by release.yml before anything builds), so every release, patches included, is a deliberate look at this list. If nothing new is worth a line, bumping `reviewedFor` alone is the answer. `since` is the release an entry arrived in. `cmd` is shown in the accent colour.",
|
|
3
|
-
"reviewedFor": "0.15.
|
|
3
|
+
"reviewedFor": "0.15.2",
|
|
4
4
|
"items": [
|
|
5
|
+
{ "text": "Driving from the app: a message sent mid-turn steers it, and questions survive a dropped link", "since": "0.15.1" },
|
|
5
6
|
{ "text": "MCP on Pi's built-in support — sign OAuth connectors back in with", "cmd": "/mcp", "since": "0.15.0" },
|
|
6
7
|
{ "text": "Sub-agents are back: hand work to bounded parallel agents", "since": "0.15.0" },
|
|
7
|
-
{ "text": "API keys can expire and carry their own credit limit — set them in the app", "since": "0.14.6" }
|
|
8
|
-
{ "text": "Save to Cargo with no app attached — it lands on your next app open", "since": "0.14.4" }
|
|
8
|
+
{ "text": "API keys can expire and carry their own credit limit — set them in the app", "since": "0.14.6" }
|
|
9
9
|
]
|
|
10
10
|
}
|
package/src/mcp/catalog.ts
CHANGED
|
@@ -227,6 +227,485 @@ export const MCP_CATALOG: CatalogEntry[] = [
|
|
|
227
227
|
needs: "oauth",
|
|
228
228
|
},
|
|
229
229
|
|
|
230
|
+
// ── More remote OAuth servers (added 2026-10-20) ───────────────────────────
|
|
231
|
+
// Mirrors the block of the same name in treeview client/components/mcpCatalog.ts,
|
|
232
|
+
// minus the rows whose registration refuses a loopback redirect (Calendly). Each
|
|
233
|
+
// was probed on 2026-10-20: initialize → 401, OAuth discovery, and a dynamic
|
|
234
|
+
// client registration with http://127.0.0.1:<port>/callback. A full consent →
|
|
235
|
+
// tools/list round trip has not been run.
|
|
236
|
+
{
|
|
237
|
+
// Distinct id from the stdio `notion` entry: the two coexist, and this one
|
|
238
|
+
// needs no integration token.
|
|
239
|
+
id: "notion-remote",
|
|
240
|
+
name: "notion-remote",
|
|
241
|
+
label: "Notion",
|
|
242
|
+
blurb: "Pages, databases, and search. Sign in via browser.",
|
|
243
|
+
transport: "http",
|
|
244
|
+
url: "https://mcp.notion.com/mcp",
|
|
245
|
+
oauth: true,
|
|
246
|
+
needs: "oauth",
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
id: "supabase-remote",
|
|
250
|
+
name: "supabase-remote",
|
|
251
|
+
label: "Supabase",
|
|
252
|
+
blurb: "Projects, tables, and SQL. Sign in via browser.",
|
|
253
|
+
transport: "http",
|
|
254
|
+
url: "https://mcp.supabase.com/mcp",
|
|
255
|
+
oauth: true,
|
|
256
|
+
needs: "oauth",
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
id: "gitlab",
|
|
260
|
+
name: "gitlab",
|
|
261
|
+
label: "GitLab",
|
|
262
|
+
blurb: "Projects, issues, and merge requests. Sign in via browser.",
|
|
263
|
+
transport: "http",
|
|
264
|
+
url: "https://gitlab.com/api/v4/mcp",
|
|
265
|
+
oauth: true,
|
|
266
|
+
needs: "oauth",
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
id: "vercel",
|
|
270
|
+
name: "vercel",
|
|
271
|
+
label: "Vercel",
|
|
272
|
+
blurb: "Deployments, logs, and projects. Sign in via browser.",
|
|
273
|
+
transport: "http",
|
|
274
|
+
url: "https://mcp.vercel.com/",
|
|
275
|
+
oauth: true,
|
|
276
|
+
needs: "oauth",
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
id: "netlify",
|
|
280
|
+
name: "netlify",
|
|
281
|
+
label: "Netlify",
|
|
282
|
+
blurb: "Sites, deploys, and forms. Sign in via browser.",
|
|
283
|
+
transport: "http",
|
|
284
|
+
url: "https://netlify-mcp.netlify.app/mcp",
|
|
285
|
+
oauth: true,
|
|
286
|
+
needs: "oauth",
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
id: "cloudflare",
|
|
290
|
+
name: "cloudflare",
|
|
291
|
+
label: "Cloudflare",
|
|
292
|
+
blurb: "Your whole Cloudflare account through its API. Sign in via browser.",
|
|
293
|
+
transport: "http",
|
|
294
|
+
url: "https://mcp.cloudflare.com/mcp",
|
|
295
|
+
oauth: true,
|
|
296
|
+
needs: "oauth",
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
id: "cloudflare-workers",
|
|
300
|
+
name: "cloudflare-workers",
|
|
301
|
+
label: "Cloudflare Workers",
|
|
302
|
+
blurb: "Workers, KV, R2, and D1. Sign in via browser.",
|
|
303
|
+
transport: "http",
|
|
304
|
+
url: "https://bindings.mcp.cloudflare.com/mcp",
|
|
305
|
+
oauth: true,
|
|
306
|
+
needs: "oauth",
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
id: "cloudflare-observability",
|
|
310
|
+
name: "cloudflare-observability",
|
|
311
|
+
label: "Cloudflare Observability",
|
|
312
|
+
blurb: "Worker logs and analytics. Sign in via browser.",
|
|
313
|
+
transport: "http",
|
|
314
|
+
url: "https://observability.mcp.cloudflare.com/mcp",
|
|
315
|
+
oauth: true,
|
|
316
|
+
needs: "oauth",
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
id: "neon",
|
|
320
|
+
name: "neon",
|
|
321
|
+
label: "Neon",
|
|
322
|
+
blurb: "Serverless Postgres projects, branches, and SQL. Sign in via browser.",
|
|
323
|
+
transport: "http",
|
|
324
|
+
url: "https://mcp.neon.tech/mcp",
|
|
325
|
+
oauth: true,
|
|
326
|
+
needs: "oauth",
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
id: "prisma",
|
|
330
|
+
name: "prisma",
|
|
331
|
+
label: "Prisma Postgres",
|
|
332
|
+
blurb: "Databases, migrations, and queries. Sign in via browser.",
|
|
333
|
+
transport: "http",
|
|
334
|
+
url: "https://mcp.prisma.io/mcp",
|
|
335
|
+
oauth: true,
|
|
336
|
+
needs: "oauth",
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
id: "instantdb",
|
|
340
|
+
name: "instantdb",
|
|
341
|
+
label: "InstantDB",
|
|
342
|
+
blurb: "Apps, schemas, and data. Sign in via browser.",
|
|
343
|
+
transport: "http",
|
|
344
|
+
url: "https://mcp.instantdb.com/mcp",
|
|
345
|
+
oauth: true,
|
|
346
|
+
needs: "oauth",
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
id: "buildkite",
|
|
350
|
+
name: "buildkite",
|
|
351
|
+
label: "Buildkite",
|
|
352
|
+
blurb: "Pipelines, builds, and jobs. Sign in via browser.",
|
|
353
|
+
transport: "http",
|
|
354
|
+
url: "https://mcp.buildkite.com/mcp",
|
|
355
|
+
oauth: true,
|
|
356
|
+
needs: "oauth",
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
id: "grafana",
|
|
360
|
+
name: "grafana",
|
|
361
|
+
label: "Grafana",
|
|
362
|
+
blurb: "Dashboards, metrics, and alerts. Sign in via browser.",
|
|
363
|
+
transport: "http",
|
|
364
|
+
url: "https://mcp.grafana.com/mcp",
|
|
365
|
+
oauth: true,
|
|
366
|
+
needs: "oauth",
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
id: "honeycomb",
|
|
370
|
+
name: "honeycomb",
|
|
371
|
+
label: "Honeycomb",
|
|
372
|
+
blurb: "Traces, queries, and SLOs. Sign in via browser.",
|
|
373
|
+
transport: "http",
|
|
374
|
+
url: "https://mcp.honeycomb.io/mcp",
|
|
375
|
+
oauth: true,
|
|
376
|
+
needs: "oauth",
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
id: "posthog",
|
|
380
|
+
name: "posthog",
|
|
381
|
+
label: "PostHog",
|
|
382
|
+
blurb: "Product analytics, flags, and errors. Sign in via browser.",
|
|
383
|
+
transport: "http",
|
|
384
|
+
url: "https://mcp.posthog.com/mcp",
|
|
385
|
+
oauth: true,
|
|
386
|
+
needs: "oauth",
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
id: "amplitude",
|
|
390
|
+
name: "amplitude",
|
|
391
|
+
label: "Amplitude",
|
|
392
|
+
blurb: "Charts, cohorts, and experiments. Sign in via browser.",
|
|
393
|
+
transport: "http",
|
|
394
|
+
url: "https://mcp.amplitude.com/mcp",
|
|
395
|
+
oauth: true,
|
|
396
|
+
needs: "oauth",
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
id: "mixpanel",
|
|
400
|
+
name: "mixpanel",
|
|
401
|
+
label: "Mixpanel",
|
|
402
|
+
blurb: "Events, funnels, and reports. Sign in via browser.",
|
|
403
|
+
transport: "http",
|
|
404
|
+
url: "https://mcp.mixpanel.com/mcp",
|
|
405
|
+
oauth: true,
|
|
406
|
+
needs: "oauth",
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
id: "semgrep",
|
|
410
|
+
name: "semgrep",
|
|
411
|
+
label: "Semgrep",
|
|
412
|
+
blurb: "Scan code for security issues. Sign in via browser.",
|
|
413
|
+
transport: "http",
|
|
414
|
+
url: "https://mcp.semgrep.ai/mcp",
|
|
415
|
+
oauth: true,
|
|
416
|
+
needs: "oauth",
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
id: "port",
|
|
420
|
+
name: "port",
|
|
421
|
+
label: "Port",
|
|
422
|
+
blurb: "Your software catalog and developer portal. Sign in via browser.",
|
|
423
|
+
transport: "http",
|
|
424
|
+
url: "https://mcp.port.io/v1",
|
|
425
|
+
oauth: true,
|
|
426
|
+
needs: "oauth",
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
id: "stytch",
|
|
430
|
+
name: "stytch",
|
|
431
|
+
label: "Stytch",
|
|
432
|
+
blurb: "Auth projects, users, and settings. Sign in via browser.",
|
|
433
|
+
transport: "http",
|
|
434
|
+
url: "https://mcp.stytch.dev/mcp",
|
|
435
|
+
oauth: true,
|
|
436
|
+
needs: "oauth",
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
id: "globalping",
|
|
440
|
+
name: "globalping",
|
|
441
|
+
label: "Globalping",
|
|
442
|
+
blurb: "Ping, traceroute, and DNS from probes worldwide. Sign in via browser.",
|
|
443
|
+
transport: "http",
|
|
444
|
+
url: "https://mcp.globalping.dev/mcp",
|
|
445
|
+
oauth: true,
|
|
446
|
+
needs: "oauth",
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
id: "apify",
|
|
450
|
+
name: "apify",
|
|
451
|
+
label: "Apify",
|
|
452
|
+
blurb: "Run scrapers and actors. Sign in via browser.",
|
|
453
|
+
transport: "http",
|
|
454
|
+
url: "https://mcp.apify.com",
|
|
455
|
+
oauth: true,
|
|
456
|
+
needs: "oauth",
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
id: "parallel-search",
|
|
460
|
+
name: "parallel-search",
|
|
461
|
+
label: "Parallel Search",
|
|
462
|
+
blurb: "Web search built for agents. Sign in via browser.",
|
|
463
|
+
transport: "http",
|
|
464
|
+
url: "https://search-mcp.parallel.ai/mcp",
|
|
465
|
+
oauth: true,
|
|
466
|
+
needs: "oauth",
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
id: "clickup",
|
|
470
|
+
name: "clickup",
|
|
471
|
+
label: "ClickUp",
|
|
472
|
+
blurb: "Tasks, docs, and spaces. Sign in via browser.",
|
|
473
|
+
transport: "http",
|
|
474
|
+
url: "https://mcp.clickup.com/mcp",
|
|
475
|
+
oauth: true,
|
|
476
|
+
needs: "oauth",
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
id: "monday",
|
|
480
|
+
name: "monday",
|
|
481
|
+
label: "monday.com",
|
|
482
|
+
blurb: "Boards, items, and updates. Sign in via browser.",
|
|
483
|
+
transport: "http",
|
|
484
|
+
url: "https://mcp.monday.com/mcp",
|
|
485
|
+
oauth: true,
|
|
486
|
+
needs: "oauth",
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
id: "todoist",
|
|
490
|
+
name: "todoist",
|
|
491
|
+
label: "Todoist",
|
|
492
|
+
blurb: "Tasks, projects, and due dates. Sign in via browser.",
|
|
493
|
+
transport: "http",
|
|
494
|
+
url: "https://ai.todoist.net/mcp",
|
|
495
|
+
oauth: true,
|
|
496
|
+
needs: "oauth",
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
id: "airtable",
|
|
500
|
+
name: "airtable",
|
|
501
|
+
label: "Airtable",
|
|
502
|
+
blurb: "Bases, tables, and records. Sign in via browser.",
|
|
503
|
+
transport: "http",
|
|
504
|
+
url: "https://mcp.airtable.com/mcp",
|
|
505
|
+
oauth: true,
|
|
506
|
+
needs: "oauth",
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
id: "miro",
|
|
510
|
+
name: "miro",
|
|
511
|
+
label: "Miro",
|
|
512
|
+
blurb: "Boards, sticky notes, and diagrams. Sign in via browser.",
|
|
513
|
+
transport: "http",
|
|
514
|
+
url: "https://mcp.miro.com/",
|
|
515
|
+
oauth: true,
|
|
516
|
+
needs: "oauth",
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
id: "canva",
|
|
520
|
+
name: "canva",
|
|
521
|
+
label: "Canva",
|
|
522
|
+
blurb: "Find, create, and export designs. Sign in via browser.",
|
|
523
|
+
transport: "http",
|
|
524
|
+
url: "https://mcp.canva.com/mcp",
|
|
525
|
+
oauth: true,
|
|
526
|
+
needs: "oauth",
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
id: "webflow",
|
|
530
|
+
name: "webflow",
|
|
531
|
+
label: "Webflow",
|
|
532
|
+
blurb: "Sites, pages, and CMS collections. Sign in via browser.",
|
|
533
|
+
transport: "http",
|
|
534
|
+
url: "https://mcp.webflow.com/mcp",
|
|
535
|
+
oauth: true,
|
|
536
|
+
needs: "oauth",
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
id: "wix",
|
|
540
|
+
name: "wix",
|
|
541
|
+
label: "Wix",
|
|
542
|
+
blurb: "Sites, stores, and bookings. Sign in via browser.",
|
|
543
|
+
transport: "http",
|
|
544
|
+
url: "https://mcp.wix.com/mcp",
|
|
545
|
+
oauth: true,
|
|
546
|
+
needs: "oauth",
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
id: "cloudinary",
|
|
550
|
+
name: "cloudinary",
|
|
551
|
+
label: "Cloudinary",
|
|
552
|
+
blurb: "Upload, search, and transform media assets. Sign in via browser.",
|
|
553
|
+
transport: "http",
|
|
554
|
+
url: "https://asset-management.mcp.cloudinary.com/mcp",
|
|
555
|
+
oauth: true,
|
|
556
|
+
needs: "oauth",
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
id: "attio",
|
|
560
|
+
name: "attio",
|
|
561
|
+
label: "Attio",
|
|
562
|
+
blurb: "CRM records, lists, and notes. Sign in via browser.",
|
|
563
|
+
transport: "http",
|
|
564
|
+
url: "https://mcp.attio.com/mcp",
|
|
565
|
+
oauth: true,
|
|
566
|
+
needs: "oauth",
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
id: "intercom",
|
|
570
|
+
name: "intercom",
|
|
571
|
+
label: "Intercom",
|
|
572
|
+
blurb: "Conversations, contacts, and tickets. Sign in via browser.",
|
|
573
|
+
transport: "http",
|
|
574
|
+
url: "https://mcp.intercom.com/mcp",
|
|
575
|
+
oauth: true,
|
|
576
|
+
needs: "oauth",
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
id: "zapier",
|
|
580
|
+
name: "zapier",
|
|
581
|
+
label: "Zapier",
|
|
582
|
+
blurb: "Run actions across thousands of apps. Sign in via browser.",
|
|
583
|
+
transport: "http",
|
|
584
|
+
url: "https://mcp.zapier.com/api/mcp/mcp",
|
|
585
|
+
oauth: true,
|
|
586
|
+
needs: "oauth",
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
id: "paypal",
|
|
590
|
+
name: "paypal",
|
|
591
|
+
label: "PayPal",
|
|
592
|
+
blurb: "Invoices, orders, and transactions. Sign in via browser.",
|
|
593
|
+
transport: "http",
|
|
594
|
+
url: "https://mcp.paypal.com/mcp",
|
|
595
|
+
oauth: true,
|
|
596
|
+
needs: "oauth",
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
id: "square",
|
|
600
|
+
name: "square",
|
|
601
|
+
label: "Square",
|
|
602
|
+
blurb: "Payments, catalog, and orders. Sign in via browser.",
|
|
603
|
+
transport: "http",
|
|
604
|
+
url: "https://mcp.squareup.com/mcp",
|
|
605
|
+
oauth: true,
|
|
606
|
+
needs: "oauth",
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
id: "ramp",
|
|
610
|
+
name: "ramp",
|
|
611
|
+
label: "Ramp",
|
|
612
|
+
blurb: "Spend, cards, and reimbursements. Sign in via browser.",
|
|
613
|
+
transport: "http",
|
|
614
|
+
url: "https://ramp-mcp-remote.ramp.com/mcp",
|
|
615
|
+
oauth: true,
|
|
616
|
+
needs: "oauth",
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
id: "morningstar",
|
|
620
|
+
name: "morningstar",
|
|
621
|
+
label: "Morningstar",
|
|
622
|
+
blurb: "Fund and stock research data. Sign in via browser.",
|
|
623
|
+
transport: "http",
|
|
624
|
+
url: "https://mcp.morningstar.com/mcp",
|
|
625
|
+
oauth: true,
|
|
626
|
+
needs: "oauth",
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
id: "thoughtspot",
|
|
630
|
+
name: "thoughtspot",
|
|
631
|
+
label: "ThoughtSpot",
|
|
632
|
+
blurb: "Ask questions of your analytics data. Sign in via browser.",
|
|
633
|
+
transport: "http",
|
|
634
|
+
url: "https://agent.thoughtspot.app/mcp",
|
|
635
|
+
oauth: true,
|
|
636
|
+
needs: "oauth",
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
id: "jam",
|
|
640
|
+
name: "jam",
|
|
641
|
+
label: "Jam",
|
|
642
|
+
blurb: "Bug reports with recordings and logs. Sign in via browser.",
|
|
643
|
+
transport: "http",
|
|
644
|
+
url: "https://mcp.jam.dev/mcp",
|
|
645
|
+
oauth: true,
|
|
646
|
+
needs: "oauth",
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
id: "granola",
|
|
650
|
+
name: "granola",
|
|
651
|
+
label: "Granola",
|
|
652
|
+
blurb: "Your meeting notes. Sign in via browser.",
|
|
653
|
+
transport: "http",
|
|
654
|
+
url: "https://mcp.granola.ai/mcp",
|
|
655
|
+
oauth: true,
|
|
656
|
+
needs: "oauth",
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
id: "fathom",
|
|
660
|
+
name: "fathom",
|
|
661
|
+
label: "Fathom",
|
|
662
|
+
blurb: "Meeting recordings, transcripts, and summaries. Sign in via browser.",
|
|
663
|
+
transport: "http",
|
|
664
|
+
url: "https://api.fathom.ai/mcp",
|
|
665
|
+
oauth: true,
|
|
666
|
+
needs: "oauth",
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
id: "fireflies",
|
|
670
|
+
name: "fireflies",
|
|
671
|
+
label: "Fireflies.ai",
|
|
672
|
+
blurb: "Meeting transcripts and summaries. Sign in via browser.",
|
|
673
|
+
transport: "http",
|
|
674
|
+
url: "https://api.fireflies.ai/mcp",
|
|
675
|
+
oauth: true,
|
|
676
|
+
needs: "oauth",
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
id: "otter",
|
|
680
|
+
name: "otter",
|
|
681
|
+
label: "Otter.ai",
|
|
682
|
+
blurb: "Meeting transcripts and notes. Sign in via browser.",
|
|
683
|
+
transport: "http",
|
|
684
|
+
url: "https://mcp.otter.ai/mcp",
|
|
685
|
+
oauth: true,
|
|
686
|
+
needs: "oauth",
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
id: "read-ai",
|
|
690
|
+
name: "read-ai",
|
|
691
|
+
label: "Read AI",
|
|
692
|
+
blurb: "Meeting reports and recaps. Sign in via browser.",
|
|
693
|
+
transport: "http",
|
|
694
|
+
url: "https://api.read.ai/mcp",
|
|
695
|
+
oauth: true,
|
|
696
|
+
needs: "oauth",
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
id: "tldv",
|
|
700
|
+
name: "tldv",
|
|
701
|
+
label: "tl;dv",
|
|
702
|
+
blurb: "Meeting recordings and highlights. Sign in via browser.",
|
|
703
|
+
transport: "http",
|
|
704
|
+
url: "https://mcp.tldv.io/mcp",
|
|
705
|
+
oauth: true,
|
|
706
|
+
needs: "oauth",
|
|
707
|
+
},
|
|
708
|
+
|
|
230
709
|
// ── Local, token-authorized ─────────────────────────────────────────────────
|
|
231
710
|
{
|
|
232
711
|
id: "brave-search",
|
|
@@ -138,7 +138,14 @@ export async function createLiveTaskSession(spec: TaskSpec, deps: LiveTaskDeps):
|
|
|
138
138
|
};
|
|
139
139
|
|
|
140
140
|
const runTurn = async (text: string): Promise<void> => {
|
|
141
|
-
if (
|
|
141
|
+
if (stopped) return;
|
|
142
|
+
// A message sent while a turn runs steers it (delivered before the next model call)
|
|
143
|
+
// rather than being dropped — the driver must be able to correct course mid-turn.
|
|
144
|
+
if (turnActive) {
|
|
145
|
+
try { await session.steer(text); }
|
|
146
|
+
catch (e) { bridge.sendNotice(`Not sent — ${(e as Error).message || "this can't be added to a running turn"}.`); }
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
142
149
|
turnActive = true;
|
|
143
150
|
let failure: string | undefined;
|
|
144
151
|
lastAnswer = ""; // keep the CLOSING answer, not the whole session
|
|
@@ -160,6 +167,10 @@ export async function createLiveTaskSession(spec: TaskSpec, deps: LiveTaskDeps):
|
|
|
160
167
|
`Use the save_attachment tool with the ref number to write one to disk.]`
|
|
161
168
|
: "";
|
|
162
169
|
await session.prompt(text + note);
|
|
170
|
+
// A steer that arrived after the agent's last check would otherwise sit unread.
|
|
171
|
+
for (let q = session.clearQueue?.(); q && q.steering.length + q.followUp.length > 0; q = session.clearQueue?.()) {
|
|
172
|
+
await session.prompt([...q.steering, ...q.followUp].join("\n\n"));
|
|
173
|
+
}
|
|
163
174
|
} catch (e) {
|
|
164
175
|
turnErrored = true;
|
|
165
176
|
failure = (e as Error).message || "The turn failed.";
|