palmier 1.1.21 → 1.1.23

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.
Files changed (51) hide show
  1. package/README.md +2 -19
  2. package/dist/agent-tools.d.ts +1 -1
  3. package/dist/agent-tools.js +36 -48
  4. package/dist/agents/agent-instructions.md +3 -1
  5. package/dist/agents/agent.d.ts +3 -8
  6. package/dist/agents/agent.js +0 -1
  7. package/dist/agents/aider.js +2 -7
  8. package/dist/agents/claude.js +2 -11
  9. package/dist/agents/cline.js +2 -7
  10. package/dist/agents/codex.js +2 -4
  11. package/dist/agents/copilot.js +2 -10
  12. package/dist/agents/cursor.js +2 -7
  13. package/dist/agents/deepagents.js +2 -7
  14. package/dist/agents/droid.js +2 -7
  15. package/dist/agents/goose.js +2 -4
  16. package/dist/agents/grok.js +2 -7
  17. package/dist/agents/hermes.js +2 -7
  18. package/dist/agents/kimi.js +2 -6
  19. package/dist/agents/kiro.js +2 -7
  20. package/dist/agents/openclaw.js +1 -1
  21. package/dist/agents/opencode.js +2 -7
  22. package/dist/agents/qoder.js +2 -7
  23. package/dist/agents/qwen.js +2 -4
  24. package/dist/agents/shared-prompt.js +3 -8
  25. package/dist/agents/wizard.js +0 -1
  26. package/dist/commands/pair.js +2 -4
  27. package/dist/commands/run.js +3 -15
  28. package/dist/commands/serve.js +5 -6
  29. package/dist/index.js +0 -22
  30. package/dist/nats-client.d.ts +1 -1
  31. package/dist/nats-client.js +1 -1
  32. package/dist/password-store.d.ts +1 -3
  33. package/dist/password-store.js +1 -7
  34. package/dist/pwa/assets/index-BPB3G_2J.css +1 -0
  35. package/dist/pwa/assets/{index-rqAuAC0q.js → index-De8hDN5I.js} +42 -46
  36. package/dist/pwa/assets/{web-ew6AFUex.js → web-BQ0rMZ_X.js} +1 -1
  37. package/dist/pwa/assets/{web-DQDmufih.js → web-miCfAJBc.js} +1 -1
  38. package/dist/pwa/assets/{web-Dp5Zj6HJ.js → web-o_D8jqAS.js} +1 -1
  39. package/dist/pwa/index.html +2 -2
  40. package/dist/rpc-handler.js +14 -15
  41. package/dist/task.d.ts +10 -0
  42. package/dist/task.js +42 -0
  43. package/dist/transports/http-transport.d.ts +1 -1
  44. package/dist/transports/http-transport.js +1 -3
  45. package/dist/transports/nats-transport.d.ts +1 -1
  46. package/dist/transports/nats-transport.js +3 -5
  47. package/dist/types.d.ts +0 -2
  48. package/package.json +2 -2
  49. package/dist/commands/passwords.d.ts +0 -3
  50. package/dist/commands/passwords.js +0 -25
  51. package/dist/pwa/assets/index-ByB7B0d2.css +0 -1
package/README.md CHANGED
@@ -13,7 +13,7 @@ The control surface is bidirectional:
13
13
  * **Phone → agents:** start ad-hoc sessions, register schedule- or event-triggered tasks, inspect session output, and respond to agent input/confirmation requests.
14
14
  * **Agents → phone:** agents can read device state (location, calendar, contacts, notifications, SMS, battery) and trigger actions (push notifications, full-screen alarms, SMS, email, contact/calendar writes, ringer mode).
15
15
 
16
- Capability access is opt-in per device: each capability is gated behind an Android permission and a per-host toggle. An optional yolo mode auto-approves agent input/confirmation requests.
16
+ Capability access is opt-in per device: each capability is gated behind an Android permission and a per-host toggle. Agents always run in yolo mode (all tool calls auto-approved) when their CLI supports it.
17
17
 
18
18
  ## Quick Start
19
19
 
@@ -158,21 +158,7 @@ Revoking the linked device also clears the host's linked-device record; device c
158
158
 
159
159
  Palmier can store website passwords and PINs like a browser's password manager so agents can sign in to sites without ever seeing the credentials. When an agent driving a browser (via the `playwright-cli` skill) reaches a login or PIN field, it calls the `fill-password` tool with the page URL, the username, and the field's element ref. Palmier matches the saved secret by **origin**, fills it directly into the live browser session, and presses Enter to submit the form in the same step — so the plaintext secret sits on the page for as little time as possible and never enters the agent's context. If nothing is saved for that `(origin, username)`, Palmier prompts you in the app with a masked dialog. The dialog has a "Save for next time" checkbox (on by default): leave it checked to store the secret for reuse, or uncheck it to fill it just this once without saving. Either way it is filled into the page. The plaintext secret is never returned to the agent and is never written to task history.
160
160
 
161
- Saved passwords and PINs are encrypted at rest with AES-256-GCM under a host-local key, both stored in `~/.config/palmier/` (`passwords.enc` and `password-key`). Manage them from the app via **Manage saved passwords** in the side panel (lists origin and username only, with per-entry delete), or from the host CLI:
162
-
163
- ```bash
164
- # List saved passwords (origin and username only — never the password)
165
- palmier passwords list
166
-
167
- # Delete a specific saved password
168
- palmier passwords delete https://example.com alice
169
-
170
- # Delete every saved password for an origin
171
- palmier passwords delete https://example.com
172
-
173
- # Delete all saved passwords
174
- palmier passwords clear
175
- ```
161
+ Saved passwords and PINs are encrypted at rest with AES-256-GCM under a host-local key, both stored in `~/.config/palmier/` (`passwords.enc` and `password-key`). Manage them from the app via **Manage saved passwords** in the side panel (lists origin and username only, with per-entry delete).
176
162
 
177
163
  ### The `init` Command
178
164
 
@@ -229,9 +215,6 @@ The default network interface is detected once during `palmier init` and saved t
229
215
  | `palmier clients list` | List active client tokens |
230
216
  | `palmier clients revoke <token>` | Revoke a specific client token |
231
217
  | `palmier clients revoke-all` | Revoke all client tokens |
232
- | `palmier passwords list` | List saved passwords (origin and username only) |
233
- | `palmier passwords delete <origin> [username]` | Delete a saved password (omit username to delete all for the origin) |
234
- | `palmier passwords clear` | Delete all saved passwords |
235
218
  | `palmier info` | Show host connection info (address, mode) |
236
219
  | `palmier serve` | Run the persistent RPC handler (default command) |
237
220
  | `palmier restart` | Restart the palmier serve daemon |
@@ -1,4 +1,4 @@
1
- import { type NatsConnection } from "nats";
1
+ import { type NatsConnection } from "@nats-io/transport-node";
2
2
  import type { HostConfig } from "./types.js";
3
3
  export declare class ToolError extends Error {
4
4
  statusCode: number;
@@ -1,4 +1,3 @@
1
- import { StringCodec } from "nats";
2
1
  import { registerPending } from "./pending-requests.js";
3
2
  import { getLinkedDevice } from "./linked-device.js";
4
3
  import { getNotifications } from "./notification-store.js";
@@ -33,11 +32,10 @@ const notifyTool = {
33
32
  throw new ToolError("title and body are required", 400);
34
33
  if (!ctx.nc)
35
34
  throw new ToolError("NATS not connected — push notifications require server mode", 503);
36
- const sc = StringCodec();
37
35
  const payload = { hostId: ctx.config.hostId, title, body, session_id: ctx.taskId };
38
36
  const subject = `host.${ctx.config.hostId}.push.send`;
39
- const reply = await ctx.nc.request(subject, sc.encode(JSON.stringify(payload)), { timeout: 15_000 });
40
- const result = JSON.parse(sc.decode(reply.data));
37
+ const reply = await ctx.nc.request(subject, JSON.stringify(payload), { timeout: 15_000 });
38
+ const result = JSON.parse(reply.string());
41
39
  if (result.ok)
42
40
  return { ok: true };
43
41
  throw new ToolError(result.error ?? "Push notification failed", 502);
@@ -309,9 +307,8 @@ const deviceGeolocationTool = {
309
307
  const device = getLinkedDevice();
310
308
  if (!device)
311
309
  throw new ToolError("No linked device configured", 400);
312
- const sc = StringCodec();
313
- const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.geolocation`, sc.encode(JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken })), { timeout: 5_000 });
314
- const ack = JSON.parse(sc.decode(ackReply.data));
310
+ const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.geolocation`, JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken }), { timeout: 5_000 });
311
+ const ack = JSON.parse(ackReply.string());
315
312
  if (ack.error)
316
313
  throw new ToolError(ack.error, 502);
317
314
  const locationPromise = new Promise((resolve, reject) => {
@@ -323,7 +320,7 @@ const deviceGeolocationTool = {
323
320
  (async () => {
324
321
  for await (const msg of sub) {
325
322
  clearTimeout(timer);
326
- resolve(sc.decode(msg.data));
323
+ resolve(msg.string());
327
324
  }
328
325
  })();
329
326
  });
@@ -350,9 +347,8 @@ const readContactsTool = {
350
347
  const device = getLinkedDevice();
351
348
  if (!device)
352
349
  throw new ToolError("No linked device configured", 400);
353
- const sc = StringCodec();
354
- const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.contacts`, sc.encode(JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken, action: "read" })), { timeout: 5_000 });
355
- const ack = JSON.parse(sc.decode(ackReply.data));
350
+ const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.contacts`, JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken, action: "read" }), { timeout: 5_000 });
351
+ const ack = JSON.parse(ackReply.string());
356
352
  if (ack.error)
357
353
  throw new ToolError(ack.error, 502);
358
354
  const responsePromise = new Promise((resolve, reject) => {
@@ -364,7 +360,7 @@ const readContactsTool = {
364
360
  (async () => {
365
361
  for await (const msg of sub) {
366
362
  clearTimeout(timer);
367
- resolve(sc.decode(msg.data));
363
+ resolve(msg.string());
368
364
  }
369
365
  })();
370
366
  });
@@ -399,12 +395,11 @@ const createContactTool = {
399
395
  const { name, phone, email } = args;
400
396
  if (!name)
401
397
  throw new ToolError("name is required", 400);
402
- const sc = StringCodec();
403
- const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.contacts`, sc.encode(JSON.stringify({
398
+ const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.contacts`, JSON.stringify({
404
399
  hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken,
405
400
  action: "create", name, phone, email,
406
- })), { timeout: 5_000 });
407
- const ack = JSON.parse(sc.decode(ackReply.data));
401
+ }), { timeout: 5_000 });
402
+ const ack = JSON.parse(ackReply.string());
408
403
  if (ack.error)
409
404
  throw new ToolError(ack.error, 502);
410
405
  const responsePromise = new Promise((resolve, reject) => {
@@ -416,7 +411,7 @@ const createContactTool = {
416
411
  (async () => {
417
412
  for await (const msg of sub) {
418
413
  clearTimeout(timer);
419
- resolve(sc.decode(msg.data));
414
+ resolve(msg.string());
420
415
  }
421
416
  })();
422
417
  });
@@ -448,14 +443,13 @@ const readCalendarTool = {
448
443
  if (!device)
449
444
  throw new ToolError("No linked device configured", 400);
450
445
  const { startDate, endDate } = args;
451
- const sc = StringCodec();
452
- const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.calendar`, sc.encode(JSON.stringify({
446
+ const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.calendar`, JSON.stringify({
453
447
  hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken,
454
448
  action: "read",
455
449
  ...(startDate ? { startDate: String(startDate) } : {}),
456
450
  ...(endDate ? { endDate: String(endDate) } : {}),
457
- })), { timeout: 5_000 });
458
- const ack = JSON.parse(sc.decode(ackReply.data));
451
+ }), { timeout: 5_000 });
452
+ const ack = JSON.parse(ackReply.string());
459
453
  if (ack.error)
460
454
  throw new ToolError(ack.error, 502);
461
455
  const responsePromise = new Promise((resolve, reject) => {
@@ -467,7 +461,7 @@ const readCalendarTool = {
467
461
  (async () => {
468
462
  for await (const msg of sub) {
469
463
  clearTimeout(timer);
470
- resolve(sc.decode(msg.data));
464
+ resolve(msg.string());
471
465
  }
472
466
  })();
473
467
  });
@@ -504,15 +498,14 @@ const createCalendarEventTool = {
504
498
  const { title, startTime, endTime, location, description } = args;
505
499
  if (!title || !startTime || !endTime)
506
500
  throw new ToolError("title, startTime, and endTime are required", 400);
507
- const sc = StringCodec();
508
- const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.calendar`, sc.encode(JSON.stringify({
501
+ const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.calendar`, JSON.stringify({
509
502
  hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken,
510
503
  action: "create",
511
504
  title, startTime: String(startTime), endTime: String(endTime),
512
505
  ...(location ? { location } : {}),
513
506
  ...(description ? { description } : {}),
514
- })), { timeout: 5_000 });
515
- const ack = JSON.parse(sc.decode(ackReply.data));
507
+ }), { timeout: 5_000 });
508
+ const ack = JSON.parse(ackReply.string());
516
509
  if (ack.error)
517
510
  throw new ToolError(ack.error, 502);
518
511
  const responsePromise = new Promise((resolve, reject) => {
@@ -524,7 +517,7 @@ const createCalendarEventTool = {
524
517
  (async () => {
525
518
  for await (const msg of sub) {
526
519
  clearTimeout(timer);
527
- resolve(sc.decode(msg.data));
520
+ resolve(msg.string());
528
521
  }
529
522
  })();
530
523
  });
@@ -558,12 +551,11 @@ const sendSmsTool = {
558
551
  const { to, body } = args;
559
552
  if (!to || !body)
560
553
  throw new ToolError("to and body are required", 400);
561
- const sc = StringCodec();
562
- const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.sms`, sc.encode(JSON.stringify({
554
+ const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.sms`, JSON.stringify({
563
555
  hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken,
564
556
  action: "send", to, body,
565
- })), { timeout: 5_000 });
566
- const ack = JSON.parse(sc.decode(ackReply.data));
557
+ }), { timeout: 5_000 });
558
+ const ack = JSON.parse(ackReply.string());
567
559
  if (ack.error)
568
560
  throw new ToolError(ack.error, 502);
569
561
  const responsePromise = new Promise((resolve, reject) => {
@@ -575,7 +567,7 @@ const sendSmsTool = {
575
567
  (async () => {
576
568
  for await (const msg of sub) {
577
569
  clearTimeout(timer);
578
- resolve(sc.decode(msg.data));
570
+ resolve(msg.string());
579
571
  }
580
572
  })();
581
573
  });
@@ -610,15 +602,14 @@ const sendAlarmTool = {
610
602
  const { title, description } = args;
611
603
  if (!title)
612
604
  throw new ToolError("title is required", 400);
613
- const sc = StringCodec();
614
605
  const payload = {
615
606
  hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken,
616
607
  title,
617
608
  };
618
609
  if (description)
619
610
  payload.description = description;
620
- const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.alarm`, sc.encode(JSON.stringify(payload)), { timeout: 5_000 });
621
- const ack = JSON.parse(sc.decode(ackReply.data));
611
+ const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.alarm`, JSON.stringify(payload), { timeout: 5_000 });
612
+ const ack = JSON.parse(ackReply.string());
622
613
  if (ack.error)
623
614
  throw new ToolError(ack.error, 502);
624
615
  const responsePromise = new Promise((resolve, reject) => {
@@ -630,7 +621,7 @@ const sendAlarmTool = {
630
621
  (async () => {
631
622
  for await (const msg of sub) {
632
623
  clearTimeout(timer);
633
- resolve(sc.decode(msg.data));
624
+ resolve(msg.string());
634
625
  }
635
626
  })();
636
627
  });
@@ -657,9 +648,8 @@ const readBatteryTool = {
657
648
  const device = getLinkedDevice();
658
649
  if (!device)
659
650
  throw new ToolError("No linked device configured", 400);
660
- const sc = StringCodec();
661
- const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.battery`, sc.encode(JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken })), { timeout: 5_000 });
662
- const ack = JSON.parse(sc.decode(ackReply.data));
651
+ const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.battery`, JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken }), { timeout: 5_000 });
652
+ const ack = JSON.parse(ackReply.string());
663
653
  if (ack.error)
664
654
  throw new ToolError(ack.error, 502);
665
655
  const responsePromise = new Promise((resolve, reject) => {
@@ -671,7 +661,7 @@ const readBatteryTool = {
671
661
  (async () => {
672
662
  for await (const msg of sub) {
673
663
  clearTimeout(timer);
674
- resolve(sc.decode(msg.data));
664
+ resolve(msg.string());
675
665
  }
676
666
  })();
677
667
  });
@@ -704,9 +694,8 @@ const setRingerModeTool = {
704
694
  const { mode } = args;
705
695
  if (!["normal", "vibrate", "silent"].includes(mode))
706
696
  throw new ToolError("mode must be 'normal', 'vibrate', or 'silent'", 400);
707
- const sc = StringCodec();
708
- const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.ringer`, sc.encode(JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken, mode })), { timeout: 5_000 });
709
- const ack = JSON.parse(sc.decode(ackReply.data));
697
+ const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.ringer`, JSON.stringify({ hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken, mode }), { timeout: 5_000 });
698
+ const ack = JSON.parse(ackReply.string());
710
699
  if (ack.error)
711
700
  throw new ToolError(ack.error, 502);
712
701
  const responsePromise = new Promise((resolve, reject) => {
@@ -718,7 +707,7 @@ const setRingerModeTool = {
718
707
  (async () => {
719
708
  for await (const msg of sub) {
720
709
  clearTimeout(timer);
721
- resolve(sc.decode(msg.data));
710
+ resolve(msg.string());
722
711
  }
723
712
  })();
724
713
  });
@@ -755,7 +744,6 @@ const sendEmailTool = {
755
744
  const { to, subject, body, cc, bcc } = args;
756
745
  if (!to)
757
746
  throw new ToolError("to is required", 400);
758
- const sc = StringCodec();
759
747
  const payload = {
760
748
  hostId: ctx.config.hostId, requestId: ctx.taskId, fcmToken: device.fcmToken,
761
749
  to,
@@ -768,8 +756,8 @@ const sendEmailTool = {
768
756
  payload.cc = cc;
769
757
  if (bcc)
770
758
  payload.bcc = bcc;
771
- const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.email`, sc.encode(JSON.stringify(payload)), { timeout: 5_000 });
772
- const ack = JSON.parse(sc.decode(ackReply.data));
759
+ const ackReply = await ctx.nc.request(`host.${ctx.config.hostId}.fcm.email`, JSON.stringify(payload), { timeout: 5_000 });
760
+ const ack = JSON.parse(ackReply.string());
773
761
  if (ack.error)
774
762
  throw new ToolError(ack.error, 502);
775
763
  const responsePromise = new Promise((resolve, reject) => {
@@ -781,7 +769,7 @@ const sendEmailTool = {
781
769
  (async () => {
782
770
  for await (const msg of sub) {
783
771
  clearTimeout(timer);
784
- resolve(sc.decode(msg.data));
772
+ resolve(msg.string());
785
773
  }
786
774
  })();
787
775
  });
@@ -29,7 +29,9 @@ Whenever a tool you are trying to use is denied or you lack the required permiss
29
29
 
30
30
  ## Browsers
31
31
 
32
- When launching a browser with the Playwright CLI `open` command, always pass `--headed` (never run headless) and `--persistent` (reuse a persistent profile so logins and cookies survive across runs).
32
+ Whenever the task needs a browser, launch it with the Playwright CLI (`playwright-cli open`). Do not use any other browser tool, automation library, or launch method.
33
+
34
+ When launching, always pass `--headed` (never run headless) and `--persistent` (reuse a persistent profile so logins and cookies survive across runs).
33
35
 
34
36
  When the task is finished, close any browser you opened with the Playwright CLI `close` command (pass the same `-s=<session>` if you used one) before printing the completion marker.
35
37
 
@@ -1,4 +1,4 @@
1
- import type { ParsedTask, RequiredPermission } from "../types.js";
1
+ import type { ParsedTask } from "../types.js";
2
2
  export interface CommandLine {
3
3
  args: string[];
4
4
  /** If provided, the string is written to the process's stdin and then the pipe is closed. */
@@ -28,8 +28,6 @@ export interface AgentTool {
28
28
  /** Whether this agent supports permission overrides (e.g. --allowedTools).
29
29
  * When falsy, the permissions section is omitted from agent instructions. */
30
30
  supportsPermissions?: boolean;
31
- /** Whether this agent supports yolo mode (auto-approve all tools). */
32
- supportsYolo?: boolean;
33
31
  /** When true, the run loop persists the agent's stderr output only if the invocation fails. */
34
32
  suppressStdErr?: boolean;
35
33
  /** Matches the model name (capture group 1) in the banner the CLI prints on stderr.
@@ -40,10 +38,8 @@ export interface AgentTool {
40
38
  * Surfaced next to the agent in the installer. */
41
39
  freeUsage?: string;
42
40
  /** Return the command and args used to run a task. If followupPrompt is provided, use it instead of the task's prompt,
43
- * and treat it as a continuation of the original run (reuse the same session, etc).
44
- * extraPermissions: pass an array of RequiredPermission for transient permissions granted for this run only,
45
- * or pass `"yolo"` to enable yolo mode (auto-approve all tools, skip permission instructions). */
46
- getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine;
41
+ * and treat it as a continuation of the original run (reuse the same session, etc). */
42
+ getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string): CommandLine;
47
43
  }
48
44
  export declare function getPromptCommandLine(agent: AgentTool, prompt: string): CommandLine;
49
45
  export declare function probeAgent(agent: AgentTool): Promise<boolean>;
@@ -56,7 +52,6 @@ export interface DetectedAgent {
56
52
  key: string;
57
53
  label: string;
58
54
  supportsPermissions?: boolean;
59
- supportsYolo?: boolean;
60
55
  npmPackage?: string;
61
56
  /** Runtime marker for "managed by Palmier" — present iff Palmier installed/manages this agent. */
62
57
  version?: string;
@@ -121,7 +121,6 @@ export async function detectAgents(previous, newlyInstalled) {
121
121
  key,
122
122
  label: agent.label,
123
123
  supportsPermissions: agent.supportsPermissions,
124
- supportsYolo: agent.supportsYolo,
125
124
  ...(agent.npmPackage ? { npmPackage: agent.npmPackage } : {}),
126
125
  ...(version ? { version } : {}),
127
126
  });
@@ -4,14 +4,9 @@ export const aiderAgent = {
4
4
  command: "aider",
5
5
  promptArgs: ["--message"],
6
6
  probeArg: "--version",
7
- supportsYolo: true,
8
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
9
- const yolo = extraPermissions === "yolo";
7
+ getTaskRunCommandLine(task, followupPrompt) {
10
8
  const prompt = followupPrompt ?? getAgentInstructions(task);
11
- const args = [];
12
- if (yolo) {
13
- args.push("--yes-always");
14
- }
9
+ const args = ["--yes-always"];
15
10
  args.push("--message", prompt);
16
11
  return { args };
17
12
  },
@@ -6,20 +6,11 @@ export const claudeAgent = {
6
6
  probeArg: "--version",
7
7
  authArgs: ["auth", "login"],
8
8
  supportsPermissions: true,
9
- supportsYolo: true,
10
9
  npmPackage: "@anthropic-ai/claude-code",
11
10
  freeUsage: "Free with Claude Pro",
12
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
- const yolo = extraPermissions === "yolo";
11
+ getTaskRunCommandLine(task, followupPrompt) {
14
12
  const prompt = followupPrompt ?? getAgentInstructions(task);
15
- const args = ["--permission-mode", yolo ? "bypassPermissions" : "acceptEdits", "-p"];
16
- if (!yolo) {
17
- args.push("--allowedTools", "WebFetch", "Skill(playwright-cli:*)", "Bash(curl:*)", "Bash(wget:*)", "Bash(playwright:*)", "Bash(playwright-cli:*)");
18
- const allPerms = [...(task.frontmatter.permissions ?? []), ...(extraPermissions ?? [])];
19
- for (const p of allPerms) {
20
- args.push(p.name);
21
- }
22
- }
13
+ const args = ["--permission-mode", "bypassPermissions", "-p"];
23
14
  if (followupPrompt) {
24
15
  args.push("-c");
25
16
  }
@@ -4,15 +4,10 @@ export const clineAgent = {
4
4
  command: "cline",
5
5
  promptArgs: ["--yolo", "-p"],
6
6
  probeArg: "--version",
7
- supportsYolo: true,
8
7
  npmPackage: "cline",
9
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
10
- const yolo = extraPermissions === "yolo";
8
+ getTaskRunCommandLine(task, followupPrompt) {
11
9
  const prompt = followupPrompt ?? getAgentInstructions(task);
12
- const args = [];
13
- if (yolo) {
14
- args.push("--yolo");
15
- }
10
+ const args = ["--yolo"];
16
11
  args.push(prompt);
17
12
  return { args };
18
13
  },
@@ -5,15 +5,13 @@ export const codexAgent = {
5
5
  promptArgs: ["exec", "--skip-git-repo-check"],
6
6
  probeArg: "--version",
7
7
  authArgs: ["login"],
8
- supportsYolo: true,
9
8
  suppressStdErr: true,
10
9
  stderrModelPattern: /^model: (.+)$/m,
11
10
  npmPackage: "@openai/codex",
12
11
  freeUsage: "Free Tier",
13
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
14
- const yolo = extraPermissions === "yolo";
12
+ getTaskRunCommandLine(task, followupPrompt) {
15
13
  const prompt = followupPrompt ?? getAgentInstructions(task);
16
- const args = ["exec", "--skip-git-repo-check", "--sandbox", yolo ? "danger-full-access" : "workspace-write"];
14
+ const args = ["exec", "--skip-git-repo-check", "--sandbox", "danger-full-access"];
17
15
  if (followupPrompt) {
18
16
  args.push("resume", "--last");
19
17
  }
@@ -5,21 +5,13 @@ export const copilotAgent = {
5
5
  promptArgs: ["-p"],
6
6
  probeArg: "-v",
7
7
  authArgs: ["login"],
8
- supportsYolo: true,
9
8
  suppressStdErr: true,
10
9
  npmPackage: "@github/copilot",
11
10
  freeUsage: "Free Tier",
12
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
- const yolo = extraPermissions === "yolo";
11
+ getTaskRunCommandLine(task, followupPrompt) {
14
12
  const prompt = followupPrompt ?? getAgentInstructions(task);
15
13
  const args = ["-p", prompt];
16
- if (yolo) {
17
- args.push("--yolo");
18
- }
19
- else {
20
- const allPerms = [...(task.frontmatter.permissions ?? []), ...(extraPermissions ?? [])];
21
- args.push(`--allow-tool=${["web_fetch", ...allPerms.map((p) => p.name)].join(",")}`);
22
- }
14
+ args.push("--yolo");
23
15
  if (followupPrompt) {
24
16
  args.push("--continue");
25
17
  }
@@ -4,14 +4,9 @@ export const cursorAgent = {
4
4
  command: "cursor",
5
5
  promptArgs: ["-p"],
6
6
  probeArg: "--version",
7
- supportsYolo: true,
8
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
9
- const yolo = extraPermissions === "yolo";
7
+ getTaskRunCommandLine(task, followupPrompt) {
10
8
  const prompt = followupPrompt ?? getAgentInstructions(task);
11
- const args = [];
12
- if (yolo) {
13
- args.push("--force");
14
- }
9
+ const args = ["--force"];
15
10
  if (followupPrompt) {
16
11
  args.push("--continue");
17
12
  }
@@ -4,14 +4,9 @@ export const deepAgentsAgent = {
4
4
  command: "deepagents",
5
5
  promptArgs: ["--non-interactive"],
6
6
  probeArg: "--version",
7
- supportsYolo: true,
8
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
9
- const yolo = extraPermissions === "yolo";
7
+ getTaskRunCommandLine(task, followupPrompt) {
10
8
  const prompt = followupPrompt ?? getAgentInstructions(task);
11
- const args = [];
12
- if (yolo) {
13
- args.push("--auto-approve");
14
- }
9
+ const args = ["--auto-approve"];
15
10
  if (followupPrompt) {
16
11
  args.push("--resume");
17
12
  }
@@ -4,15 +4,10 @@ export const droidAgent = {
4
4
  command: "droid",
5
5
  promptArgs: ["exec"],
6
6
  probeArg: "--version",
7
- supportsYolo: true,
8
7
  npmPackage: "@factory/cli",
9
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
10
- const yolo = extraPermissions === "yolo";
8
+ getTaskRunCommandLine(task, followupPrompt) {
11
9
  const prompt = followupPrompt ?? getAgentInstructions(task);
12
- const args = ["exec", "--session-id", task.frontmatter.id];
13
- if (yolo) {
14
- args.push("--skip-permissions-unsafe");
15
- }
10
+ const args = ["exec", "--session-id", task.frontmatter.id, "--skip-permissions-unsafe"];
16
11
  args.push(prompt);
17
12
  return { args };
18
13
  },
@@ -4,15 +4,13 @@ export const gooseAgent = {
4
4
  command: "goose",
5
5
  promptArgs: ["run", "--text"],
6
6
  probeArg: "--version",
7
- supportsYolo: true,
8
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
9
- const yolo = extraPermissions === "yolo";
7
+ getTaskRunCommandLine(task, followupPrompt) {
10
8
  const prompt = followupPrompt ?? getAgentInstructions(task);
11
9
  const args = ["run"];
12
10
  if (followupPrompt) {
13
11
  args.push("--resume");
14
12
  }
15
13
  args.push("--text", prompt);
16
- return { args, ...(yolo ? { env: { GOOSE_MODE: "auto" } } : {}) };
14
+ return { args, env: { GOOSE_MODE: "auto" } };
17
15
  },
18
16
  };
@@ -5,16 +5,11 @@ export const grokAgent = {
5
5
  promptArgs: ["-p"],
6
6
  probeArg: "--version",
7
7
  authArgs: ["login"],
8
- supportsYolo: true,
9
8
  npmPackage: "@xai-official/grok",
10
9
  freeUsage: "Free Tier",
11
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
12
- const yolo = extraPermissions === "yolo";
10
+ getTaskRunCommandLine(task, followupPrompt) {
13
11
  const prompt = followupPrompt ?? getAgentInstructions(task);
14
- const args = [];
15
- if (yolo) {
16
- args.push("--always-approve");
17
- }
12
+ const args = ["--always-approve"];
18
13
  if (followupPrompt) {
19
14
  args.push("-c");
20
15
  }
@@ -4,14 +4,9 @@ export const hermesAgent = {
4
4
  command: "hermes",
5
5
  promptArgs: ["chat", "-q"],
6
6
  probeArg: "--version",
7
- supportsYolo: true,
8
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
9
- const yolo = extraPermissions === "yolo";
7
+ getTaskRunCommandLine(task, followupPrompt) {
10
8
  const prompt = followupPrompt ?? getAgentInstructions(task);
11
- const args = ["chat"];
12
- if (yolo) {
13
- args.push("--trust-all-tools");
14
- }
9
+ const args = ["chat", "--trust-all-tools"];
15
10
  if (followupPrompt) {
16
11
  args.push("--continue");
17
12
  }
@@ -4,19 +4,15 @@ export const kimiAgent = {
4
4
  command: "kimi",
5
5
  promptArgs: ["-p"],
6
6
  probeArg: "--version",
7
- supportsYolo: false,
8
7
  suppressStdErr: true,
9
8
  npmPackage: "@moonshot-ai/kimi-code",
10
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
11
- const yolo = extraPermissions === "yolo";
9
+ getTaskRunCommandLine(task, followupPrompt) {
12
10
  const prompt = followupPrompt ?? getAgentInstructions(task);
13
11
  const args = [];
14
- if (yolo) {
15
- args.push("--yolo");
16
- }
17
12
  if (followupPrompt) {
18
13
  args.push("--continue");
19
14
  }
15
+ // Print mode (-p) already implies yolo.
20
16
  args.push("-p", prompt);
21
17
  return { args };
22
18
  },
@@ -4,14 +4,9 @@ export const kiroAgent = {
4
4
  command: "kiro-cli",
5
5
  promptArgs: ["--no-interactive"],
6
6
  probeArg: "--version",
7
- supportsYolo: true,
8
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
9
- const yolo = extraPermissions === "yolo";
7
+ getTaskRunCommandLine(task, followupPrompt) {
10
8
  const prompt = followupPrompt ?? getAgentInstructions(task);
11
- const args = [];
12
- if (yolo) {
13
- args.push("--trust-all-tools");
14
- }
9
+ const args = ["--trust-all-tools"];
15
10
  if (followupPrompt) {
16
11
  args.push("--resume");
17
12
  }
@@ -5,7 +5,7 @@ export const openClawAgent = {
5
5
  promptArgs: ["agent", "--local", "--agent", "main", "--message"],
6
6
  probeArg: "--version",
7
7
  npmPackage: "openclaw",
8
- getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
8
+ getTaskRunCommandLine(task, followupPrompt) {
9
9
  const prompt = followupPrompt ?? getAgentInstructions(task);
10
10
  // OpenClaw does not support stdin as prompt.
11
11
  const args = ["agent", "--local", "--session-id", task.frontmatter.id, "--message", prompt];