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
@@ -1 +1 @@
1
- import{W as t}from"./index-rqAuAC0q.js";class s extends t{constructor(){super(),this.handleVisibilityChange=()=>{const e={isActive:document.hidden!==!0};this.notifyListeners("appStateChange",e),document.hidden?this.notifyListeners("pause",null):this.notifyListeners("resume",null)},document.addEventListener("visibilitychange",this.handleVisibilityChange,!1)}exitApp(){throw this.unimplemented("Not implemented on web.")}async getInfo(){throw this.unimplemented("Not implemented on web.")}async getLaunchUrl(){return{url:""}}async getState(){return{isActive:document.hidden!==!0}}async minimizeApp(){throw this.unimplemented("Not implemented on web.")}async toggleBackButtonHandler(){throw this.unimplemented("Not implemented on web.")}async getAppLanguage(){return{value:navigator.language.split("-")[0].toLowerCase()}}}export{s as AppWeb};
1
+ import{W as t}from"./index-De8hDN5I.js";class s extends t{constructor(){super(),this.handleVisibilityChange=()=>{const e={isActive:document.hidden!==!0};this.notifyListeners("appStateChange",e),document.hidden?this.notifyListeners("pause",null):this.notifyListeners("resume",null)},document.addEventListener("visibilitychange",this.handleVisibilityChange,!1)}exitApp(){throw this.unimplemented("Not implemented on web.")}async getInfo(){throw this.unimplemented("Not implemented on web.")}async getLaunchUrl(){return{url:""}}async getState(){return{isActive:document.hidden!==!0}}async minimizeApp(){throw this.unimplemented("Not implemented on web.")}async toggleBackButtonHandler(){throw this.unimplemented("Not implemented on web.")}async getAppLanguage(){return{value:navigator.language.split("-")[0].toLowerCase()}}}export{s as AppWeb};
@@ -1 +1 @@
1
- import{W as p}from"./index-rqAuAC0q.js";class f extends p{constructor(){super(...arguments),this.group="CapacitorStorage"}async configure({group:e}){typeof e=="string"&&(this.group=e)}async get(e){return{value:this.impl.getItem(this.applyPrefix(e.key))}}async set(e){this.impl.setItem(this.applyPrefix(e.key),e.value)}async remove(e){this.impl.removeItem(this.applyPrefix(e.key))}async keys(){return{keys:this.rawKeys().map(t=>t.substring(this.prefix.length))}}async clear(){for(const e of this.rawKeys())this.impl.removeItem(e)}async migrate(){var e;const t=[],s=[],n="_cap_",o=Object.keys(this.impl).filter(i=>i.indexOf(n)===0);for(const i of o){const r=i.substring(n.length),a=(e=this.impl.getItem(i))!==null&&e!==void 0?e:"",{value:l}=await this.get({key:r});typeof l=="string"?s.push(r):(await this.set({key:r,value:a}),t.push(r))}return{migrated:t,existing:s}}async removeOld(){const e="_cap_",t=Object.keys(this.impl).filter(s=>s.indexOf(e)===0);for(const s of t)this.impl.removeItem(s)}get impl(){return window.localStorage}get prefix(){return this.group==="NativeStorage"?"":`${this.group}.`}rawKeys(){return Object.keys(this.impl).filter(e=>e.indexOf(this.prefix)===0)}applyPrefix(e){return this.prefix+e}}export{f as PreferencesWeb};
1
+ import{W as p}from"./index-De8hDN5I.js";class f extends p{constructor(){super(...arguments),this.group="CapacitorStorage"}async configure({group:e}){typeof e=="string"&&(this.group=e)}async get(e){return{value:this.impl.getItem(this.applyPrefix(e.key))}}async set(e){this.impl.setItem(this.applyPrefix(e.key),e.value)}async remove(e){this.impl.removeItem(this.applyPrefix(e.key))}async keys(){return{keys:this.rawKeys().map(t=>t.substring(this.prefix.length))}}async clear(){for(const e of this.rawKeys())this.impl.removeItem(e)}async migrate(){var e;const t=[],s=[],n="_cap_",o=Object.keys(this.impl).filter(i=>i.indexOf(n)===0);for(const i of o){const r=i.substring(n.length),a=(e=this.impl.getItem(i))!==null&&e!==void 0?e:"",{value:l}=await this.get({key:r});typeof l=="string"?s.push(r):(await this.set({key:r,value:a}),t.push(r))}return{migrated:t,existing:s}}async removeOld(){const e="_cap_",t=Object.keys(this.impl).filter(s=>s.indexOf(e)===0);for(const s of t)this.impl.removeItem(s)}get impl(){return window.localStorage}get prefix(){return this.group==="NativeStorage"?"":`${this.group}.`}rawKeys(){return Object.keys(this.impl).filter(e=>e.indexOf(this.prefix)===0)}applyPrefix(e){return this.prefix+e}}export{f as PreferencesWeb};
@@ -1 +1 @@
1
- import{W as i}from"./index-rqAuAC0q.js";function o(){const t=window.navigator.connection||window.navigator.mozConnection||window.navigator.webkitConnection;let n="unknown";const e=t?t.type||t.effectiveType:null;if(e&&typeof e=="string")switch(e){case"bluetooth":case"cellular":n="cellular";break;case"none":n="none";break;case"ethernet":case"wifi":case"wimax":n="wifi";break;case"other":case"unknown":n="unknown";break;case"slow-2g":case"2g":case"3g":n="cellular";break;case"4g":n="wifi";break}return n}class s extends i{constructor(){super(),this.handleOnline=()=>{const e={connected:!0,connectionType:o()};this.notifyListeners("networkStatusChange",e)},this.handleOffline=()=>{const n={connected:!1,connectionType:"none"};this.notifyListeners("networkStatusChange",n)},typeof window<"u"&&(window.addEventListener("online",this.handleOnline),window.addEventListener("offline",this.handleOffline))}async getStatus(){if(!window.navigator)throw this.unavailable("Browser does not support the Network Information API");const n=window.navigator.onLine,e=o();return{connected:n,connectionType:n?e:"none"}}}const r=new s;export{r as Network,s as NetworkWeb};
1
+ import{W as i}from"./index-De8hDN5I.js";function o(){const t=window.navigator.connection||window.navigator.mozConnection||window.navigator.webkitConnection;let n="unknown";const e=t?t.type||t.effectiveType:null;if(e&&typeof e=="string")switch(e){case"bluetooth":case"cellular":n="cellular";break;case"none":n="none";break;case"ethernet":case"wifi":case"wimax":n="wifi";break;case"other":case"unknown":n="unknown";break;case"slow-2g":case"2g":case"3g":n="cellular";break;case"4g":n="wifi";break}return n}class s extends i{constructor(){super(),this.handleOnline=()=>{const e={connected:!0,connectionType:o()};this.notifyListeners("networkStatusChange",e)},this.handleOffline=()=>{const n={connected:!1,connectionType:"none"};this.notifyListeners("networkStatusChange",n)},typeof window<"u"&&(window.addEventListener("online",this.handleOnline),window.addEventListener("offline",this.handleOffline))}async getStatus(){if(!window.navigator)throw this.unavailable("Browser does not support the Network Information API");const n=window.navigator.onLine,e=o();return{connected:n,connectionType:n?e:"none"}}}const r=new s;export{r as Network,s as NetworkWeb};
@@ -8,8 +8,8 @@
8
8
  <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
9
9
  <title>Palmier</title>
10
10
  <meta name="description" content="Your personal AI agent runs on your own computer, with local data and credential storage. Use your existing AI subscription and connect your calendar, contacts, notifications, and more." />
11
- <script type="module" crossorigin src="/assets/index-rqAuAC0q.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-ByB7B0d2.css">
11
+ <script type="module" crossorigin src="/assets/index-De8hDN5I.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-BPB3G_2J.css">
13
13
  <link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
14
14
  <body>
15
15
  <div id="root"></div>
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "crypto";
2
2
  import * as fs from "fs";
3
3
  import * as path from "path";
4
- import { listTasks, parseTaskFile, taskDisplayName, writeTaskFile, getTaskDir, readTaskStatus, writeTaskStatus, readHistory, deleteHistoryEntry, appendTaskList, removeFromTaskList, isTaskInList, appendHistory, createRunDir, appendRunMessage, getRunDir, writeFollowupStatus, readFollowupStatus, deleteFollowupStatus, isRunStarred, setRunStarred, formatAgentModelComment, extractAgentModelComment } from "./task.js";
4
+ import { listTasks, parseTaskFile, taskDisplayName, writeTaskFile, getTaskDir, readTaskStatus, writeTaskStatus, readHistory, deleteHistoryEntry, appendTaskList, removeFromTaskList, isTaskInList, createPendingRun, readPendingRun, appendRunMessage, getRunDir, writeFollowupStatus, readFollowupStatus, deleteFollowupStatus, isRunStarred, setRunStarred, formatAgentModelComment, extractAgentModelComment } from "./task.js";
5
5
  import { resolvePending, getPending, listPending } from "./pending-requests.js";
6
6
  import { getPlatform } from "./platform/index.js";
7
7
  import { onChildDone, resolveCommand, spawnCommand } from "./spawn-command.js";
@@ -136,6 +136,8 @@ async function generateName(projectRoot, userPrompt, agentName) {
136
136
  return userPrompt;
137
137
  }
138
138
  }
139
+ // Longer than the slowest observed launch (~5s on Windows) before the run process claims its run.
140
+ const DUPLICATE_RUN_WINDOW_MS = 15_000;
139
141
  /** Active follow-up child processes, keyed by "taskId:runId". */
140
142
  const activeFollowups = new Map();
141
143
  /**
@@ -253,7 +255,6 @@ export function createRpcHandler(config) {
253
255
  requires_confirmation: params.requires_confirmation ?? true,
254
256
  ...(params.schedule_type ? { schedule_type: params.schedule_type } : {}),
255
257
  ...(params.schedule_values?.length ? { schedule_values: params.schedule_values } : {}),
256
- ...(params.yolo_mode ? { yolo_mode: true } : {}),
257
258
  ...(params.foreground_mode ? { foreground_mode: true } : {}),
258
259
  ...(params.command ? { command: params.command } : {}),
259
260
  },
@@ -297,11 +298,6 @@ export function createRpcHandler(config) {
297
298
  existing.frontmatter.schedule_enabled = params.schedule_enabled;
298
299
  if (params.requires_confirmation !== undefined)
299
300
  existing.frontmatter.requires_confirmation = params.requires_confirmation;
300
- if (params.yolo_mode !== undefined) {
301
- existing.frontmatter.yolo_mode = params.yolo_mode || undefined;
302
- if (params.yolo_mode)
303
- delete existing.frontmatter.permissions;
304
- }
305
301
  if (params.foreground_mode !== undefined)
306
302
  existing.frontmatter.foreground_mode = params.foreground_mode || undefined;
307
303
  if (params.command !== undefined) {
@@ -356,7 +352,6 @@ export function createRpcHandler(config) {
356
352
  schedule_enabled: false,
357
353
  requires_confirmation: params.requires_confirmation ?? false,
358
354
  one_off: true,
359
- ...(params.yolo_mode ? { yolo_mode: true } : {}),
360
355
  ...(params.foreground_mode ? { foreground_mode: true } : {}),
361
356
  ...(params.command ? { command: params.command } : {}),
362
357
  },
@@ -364,8 +359,7 @@ export function createRpcHandler(config) {
364
359
  writeTaskFile(taskDir, task);
365
360
  // One-off run: do NOT append to tasks.jsonl.
366
361
  const oneoffAgentVersion = config.agents?.find((a) => a.key === params.agent)?.version;
367
- const runId = createRunDir(taskDir, name, Date.now(), params.agent, oneoffAgentVersion);
368
- appendHistory(config.projectRoot, { task_id: id, run_id: runId });
362
+ const runId = createPendingRun(config.projectRoot, taskDir, id, name, params.agent, oneoffAgentVersion);
369
363
  const platform = getPlatform();
370
364
  platform.installTaskTimer(config, task);
371
365
  try {
@@ -382,17 +376,22 @@ export function createRpcHandler(config) {
382
376
  case "task.run": {
383
377
  const params = request.params;
384
378
  const runTaskDir = getTaskDir(config.projectRoot, params.id);
379
+ const launchingRunId = readPendingRun(runTaskDir);
380
+ if (launchingRunId && Date.now() - Number(launchingRunId) < DUPLICATE_RUN_WINDOW_MS) {
381
+ console.log(`[task.run] Task ${params.id} run ${launchingRunId} is still launching, ignoring duplicate request`);
382
+ return { ok: true, task_id: params.id, run_id: launchingRunId };
383
+ }
385
384
  let taskRunId;
386
385
  try {
387
386
  const platform = getPlatform();
387
+ // Must stay synchronous up to here so a concurrent duplicate sees the pending run.
388
+ const runTask = parseTaskFile(runTaskDir);
389
+ const taskRunAgentVersion = config.agents?.find((a) => a.key === runTask.frontmatter.agent)?.version;
390
+ taskRunId = createPendingRun(config.projectRoot, runTaskDir, params.id, taskDisplayName(runTask.frontmatter), runTask.frontmatter.agent, taskRunAgentVersion);
388
391
  if (platform.isTaskRunning(params.id)) {
389
392
  console.log(`[task.run] Task ${params.id} is already running, killing stale process`);
390
393
  await platform.stopTask(params.id);
391
394
  }
392
- const runTask = parseTaskFile(runTaskDir);
393
- const taskRunAgentVersion = config.agents?.find((a) => a.key === runTask.frontmatter.agent)?.version;
394
- taskRunId = createRunDir(runTaskDir, taskDisplayName(runTask.frontmatter), Date.now(), runTask.frontmatter.agent, taskRunAgentVersion);
395
- appendHistory(config.projectRoot, { task_id: params.id, run_id: taskRunId });
396
395
  await platform.startTask(params.id);
397
396
  return { ok: true, task_id: params.id, run_id: taskRunId };
398
397
  }
@@ -435,7 +434,7 @@ export function createRpcHandler(config) {
435
434
  name: taskDisplayName(followupTask.frontmatter), run_id: params.run_id,
436
435
  });
437
436
  const followupAgent = getAgent(followupTask.frontmatter.agent);
438
- const { args: cmdArgs, stdin, env: followupAgentEnv, files: followupFiles } = followupAgent.getTaskRunCommandLine(followupTask, params.message, followupTask.frontmatter.yolo_mode ? "yolo" : followupTask.frontmatter.permissions);
437
+ const { args: cmdArgs, stdin, env: followupAgentEnv, files: followupFiles } = followupAgent.getTaskRunCommandLine(followupTask, params.message);
439
438
  const cmd = followupAgent.command;
440
439
  if (followupFiles) {
441
440
  for (const f of followupFiles)
package/dist/task.d.ts CHANGED
@@ -21,6 +21,16 @@ export declare function isRunStarred(runDir: string): boolean;
21
21
  export declare function setRunStarred(runDir: string, starred: boolean): void;
22
22
  /** Returns the run ID (timestamp string used as directory name). */
23
23
  export declare function createRunDir(taskDir: string, taskName: string, startTime: number, agent?: string, agentVersion?: string): string;
24
+ /** The run dir created for the next run process that it has not claimed yet, or null. */
25
+ export declare function readPendingRun(taskDir: string): string | null;
26
+ /**
27
+ * Create a run dir (plus its history entry) for the run process about to be
28
+ * launched to claim. A previous pending run that was never claimed is discarded
29
+ * so it doesn't linger as an empty session.
30
+ */
31
+ export declare function createPendingRun(projectRoot: string, taskDir: string, taskId: string, taskName: string, agent?: string, agentVersion?: string): string;
32
+ /** Take ownership of the pending run; null if there is none or another process took it first. */
33
+ export declare function claimPendingRun(taskDir: string): string | null;
24
34
  export declare function getRunDir(taskDir: string, runId: string): string;
25
35
  export declare function appendRunMessage(taskDir: string, runId: string, msg: ConversationMessage): void;
26
36
  export declare function beginStreamingMessage(taskDir: string, runId: string, time: number, stream?: "stdout" | "stderr"): StreamingMessageWriter;
package/dist/task.js CHANGED
@@ -155,6 +155,48 @@ export function createRunDir(taskDir, taskName, startTime, agent, agentVersion)
155
155
  fs.writeFileSync(path.join(runDir, "TASKRUN.md"), content, "utf-8");
156
156
  return runId;
157
157
  }
158
+ const PENDING_RUN_FILE = "pending-run";
159
+ function isUnclaimedRun(taskDir, runId) {
160
+ if (!/^\d+$/.test(runId) || !fs.existsSync(path.join(taskDir, runId, "TASKRUN.md")))
161
+ return false;
162
+ return !readRunMessages(taskDir, runId).some((m) => m.role === "status");
163
+ }
164
+ /** The run dir created for the next run process that it has not claimed yet, or null. */
165
+ export function readPendingRun(taskDir) {
166
+ let runId;
167
+ try {
168
+ runId = fs.readFileSync(path.join(taskDir, PENDING_RUN_FILE), "utf-8").trim();
169
+ }
170
+ catch {
171
+ return null;
172
+ }
173
+ return isUnclaimedRun(taskDir, runId) ? runId : null;
174
+ }
175
+ /**
176
+ * Create a run dir (plus its history entry) for the run process about to be
177
+ * launched to claim. A previous pending run that was never claimed is discarded
178
+ * so it doesn't linger as an empty session.
179
+ */
180
+ export function createPendingRun(projectRoot, taskDir, taskId, taskName, agent, agentVersion) {
181
+ const stale = readPendingRun(taskDir);
182
+ if (stale)
183
+ deleteHistoryEntry(projectRoot, taskId, stale);
184
+ const runId = createRunDir(taskDir, taskName, Date.now(), agent, agentVersion);
185
+ appendHistory(projectRoot, { task_id: taskId, run_id: runId });
186
+ fs.writeFileSync(path.join(taskDir, PENDING_RUN_FILE), runId, "utf-8");
187
+ return runId;
188
+ }
189
+ /** Take ownership of the pending run; null if there is none or another process took it first. */
190
+ export function claimPendingRun(taskDir) {
191
+ const runId = readPendingRun(taskDir);
192
+ try {
193
+ fs.unlinkSync(path.join(taskDir, PENDING_RUN_FILE));
194
+ }
195
+ catch {
196
+ return null;
197
+ }
198
+ return runId;
199
+ }
158
200
  export function getRunDir(taskDir, runId) {
159
201
  return path.join(taskDir, runId);
160
202
  }
@@ -1,3 +1,3 @@
1
- import { type NatsConnection } from "nats";
1
+ import { type NatsConnection } from "@nats-io/transport-node";
2
2
  import type { HostConfig, RpcMessage } from "../types.js";
3
3
  export declare function startHttpTransport(config: HostConfig, handleRpc: (req: RpcMessage) => Promise<unknown>, port: number, getNc: () => NatsConnection | undefined, pairingCode?: string, onReady?: () => void): Promise<void>;
@@ -1,7 +1,6 @@
1
1
  import * as http from "node:http";
2
2
  import * as os from "os";
3
3
  import * as path from "node:path";
4
- import { StringCodec } from "nats";
5
4
  import { validateClient, addClient } from "../client-store.js";
6
5
  import { registerPending } from "../pending-requests.js";
7
6
  import * as fs from "node:fs";
@@ -106,9 +105,8 @@ export async function startHttpTransport(config, handleRpc, port, getNc, pairing
106
105
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
107
106
  }
108
107
  async function publishEvent(taskId, payload) {
109
- const sc = StringCodec();
110
108
  const subject = `host-event.${config.hostId}.${taskId}`;
111
- getNc()?.publish(subject, sc.encode(JSON.stringify(payload)));
109
+ getNc()?.publish(subject, JSON.stringify(payload));
112
110
  broadcastSseEvent({ task_id: taskId, ...payload });
113
111
  }
114
112
  function makeToolContext(taskId) {
@@ -1,3 +1,3 @@
1
- import { type NatsConnection } from "nats";
1
+ import { type NatsConnection } from "@nats-io/transport-node";
2
2
  import type { HostConfig, RpcMessage } from "../types.js";
3
3
  export declare function startNatsTransport(config: HostConfig, handleRpc: (req: RpcMessage) => Promise<unknown>, nc: NatsConnection): Promise<void>;
@@ -1,6 +1,4 @@
1
- import { StringCodec } from "nats";
2
1
  export async function startNatsTransport(config, handleRpc, nc) {
3
- const sc = StringCodec();
4
2
  const subject = `host.${config.hostId}.rpc.>`;
5
3
  console.log(`[nats] Subscribing to: ${subject}`);
6
4
  const sub = nc.subscribe(subject);
@@ -19,7 +17,7 @@ export async function startNatsTransport(config, handleRpc, nc) {
19
17
  const method = rpcIdx >= 0 ? subjectTokens.slice(rpcIdx + 1).join(".") : "";
20
18
  let params = {};
21
19
  if (msg.data && msg.data.length > 0) {
22
- const raw = sc.decode(msg.data).trim();
20
+ const raw = msg.string().trim();
23
21
  if (raw.length > 0) {
24
22
  try {
25
23
  params = JSON.parse(raw);
@@ -27,7 +25,7 @@ export async function startNatsTransport(config, handleRpc, nc) {
27
25
  catch {
28
26
  console.error(`[nats] Failed to parse RPC params for ${method}`);
29
27
  if (msg.reply) {
30
- msg.respond(sc.encode(JSON.stringify({ error: "Invalid JSON" })));
28
+ msg.respond(JSON.stringify({ error: "Invalid JSON" }));
31
29
  }
32
30
  return;
33
31
  }
@@ -47,7 +45,7 @@ export async function startNatsTransport(config, handleRpc, nc) {
47
45
  }
48
46
  console.log(`[nats] RPC done: ${method}`, JSON.stringify(response).slice(0, 200));
49
47
  if (msg.reply) {
50
- msg.respond(sc.encode(JSON.stringify(response)));
48
+ msg.respond(JSON.stringify(response));
51
49
  }
52
50
  }
53
51
  async function consumeSubscription(subscription) {
package/dist/types.d.ts CHANGED
@@ -9,7 +9,6 @@ export interface HostConfig {
9
9
  key: string;
10
10
  label: string;
11
11
  supportsPermissions?: boolean;
12
- supportsYolo?: boolean;
13
12
  npmPackage?: string;
14
13
  /** Runtime marker for "managed by Palmier" — present iff Palmier installed/manages this agent. */
15
14
  version?: string;
@@ -42,7 +41,6 @@ export interface TaskFrontmatter {
42
41
  schedule_values?: string[];
43
42
  schedule_enabled: boolean;
44
43
  requires_confirmation: boolean;
45
- yolo_mode?: boolean;
46
44
  foreground_mode?: boolean;
47
45
  permissions?: RequiredPermission[];
48
46
  command?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "palmier",
3
- "version": "1.1.21",
3
+ "version": "1.1.23",
4
4
  "description": "Palmier host CLI - provisions, executes tasks, and serves NATS RPC",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Hongxu Cai",
@@ -36,10 +36,10 @@
36
36
  "start": "node dist/index.js"
37
37
  },
38
38
  "dependencies": {
39
+ "@nats-io/transport-node": "^3.4.0",
39
40
  "commander": "^13.1.0",
40
41
  "cross-spawn": "^7.0.6",
41
42
  "dotenv": "^16.4.7",
42
- "nats": "^2.29.1",
43
43
  "yaml": "^2.7.0"
44
44
  },
45
45
  "devDependencies": {
@@ -1,3 +0,0 @@
1
- export declare function passwordsListCommand(): Promise<void>;
2
- export declare function passwordsDeleteCommand(origin: string, username?: string): Promise<void>;
3
- export declare function passwordsClearCommand(): Promise<void>;
@@ -1,25 +0,0 @@
1
- import { listPasswords, deletePassword, clearPasswords, normalizeOrigin } from "../password-store.js";
2
- export async function passwordsListCommand() {
3
- const entries = listPasswords();
4
- if (entries.length === 0) {
5
- console.log("No saved passwords.");
6
- return;
7
- }
8
- console.log(`${entries.length} saved password(s):\n`);
9
- for (const e of entries) {
10
- console.log(` ${e.origin} ${e.username}`);
11
- }
12
- }
13
- export async function passwordsDeleteCommand(origin, username) {
14
- if (deletePassword(normalizeOrigin(origin), username)) {
15
- console.log("Password deleted.");
16
- }
17
- else {
18
- console.error("No matching saved password.");
19
- process.exit(1);
20
- }
21
- }
22
- export async function passwordsClearCommand() {
23
- const count = clearPasswords();
24
- console.log(`Cleared ${count} saved password(s).`);
25
- }
@@ -1 +0,0 @@
1
- @font-face{font-family:Plus Jakarta Sans Variable;font-style:normal;font-display:swap;font-weight:200 800;src:url(data:font/woff2;base64,d09GMgABAAAAAAa0ABQAAAAADOwAAAZHAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbbhwoP0hWQVJtP01WQVJGBmA/U1RBVIEcAGQvXBEICoMkgmcLFgAwhEwBNgIkAyYEIAWGXAdiDAcbOgtRlHLSRcD8TEzkdoghPRuspKefbVnJIM5yRfA8vZv9uTNJSCZY21Scrigr6qyJ/C3sE1OFKuVP3e+lxUTbYio1zb/kFdk2bufY2BlhWERRBrcDUBhB5GEpd2Cy+MDxqf9zLPX+XVsk5r+s50d6IpWLatqbdT6f2MAikiOKQocbjya2QT0DqPDX4R0jQBgAUAiCRhDA0EkvYeLk9X3I2bEWI8jBAihIANm+kUWQBgggWChcBBdFhXlAUgOCAgBA0AgKjUJ8HKvjddFCKSgAVklEBYAeEJgCQJH0qB3B+neDg4sAA4hAD0AVIEAA0IACJGeaUjMIIIhBUE/zNABjTQMBwEVRcwcYBUCObLERABAgjAA1M/ZSa9hSi4OlpsnZ2KBOVUsAZlRGl1W0NZ6gSwhQPIAFs7YFL0QS91vRQgdgx2VDAnIegKpQvwAJIoLgooBAGtCJcHizg0TDiz8vhPgIcVXaDJu37ZyDAIjFy4AAiC1JAA0olJwN6nFAACQk/zfQVUuA3t5ELKMT9hpBSRj+HBSggYCIAIEkUoC4PCIQIWHGA8+IStxf0PW15ntP22gc+Wo+BLyQAHECQiAVd+c3Ba8gZ4NOACBaLOhsbIMkAXEmuHsSAvkf4oJaAogH6nEQAzoBIEihUAwAA0AACAObAGcANYAAABaqgMmpWAhiiVyrvNyqFV8tMQ5Iyq6r9Lf7W82i8ILl2cv8zbfdJ5lnPzSnvt/XXPfRR/5pH3xA+u5uS0798EPJ3Fft33w36Wc+8E/7aIB/9fvvb1pE9erNXLB9mkw//cSg8Cnx03VGXDvZnRzKqV9Xkhn7eRHb3wVjHpg19nkZpX9bBU+vnHegrsuQcbW9Bi7oO33h+8SU0Tly/MbeYUuG1cftVz6oNxrW0qH1iezq446fkDFo/rgvTkqt7zmteFzncZ0uxXti09FFF2z+C7CODpjUudOE5NSeb3bv/mbPKcmOEzpWQ3VBgBAuj/AOY3qMm7UvgA8qo3+qvAUBBAX0wADQG4AG+jCKaSxkBe/zvcTG8jRpRZgqhCog4sd++cOo7J+/RdvaaMoc78Ri/PNPSclx6fDvG1Kt3qQFi1rq+5EaavTtt9TVvfvucYsA5wc4oESU+E1ikF9TkrOkIsIH5Fawx7SBNLnMcWlM3skBluCmQNs7GeyFvYVDamsqwhxx18n/+WdxzDgOLU1AqbUlb3m+KOT9+ONvY/7XlrdLS//5v7nN97XHj0VzJpJvz4spq0V7ioXDuv2YrSxtF/KZmvbWfKtorXSh8dfvgGu+ev/nFSaXM6jyuCE/f/EpmuP7Rqu8R98vP/+tXUdrOgKlZP9Q/s+fRF9+3CHZd4EugfPv158me9X7v/6aSMCxMJmIr//65bNXngl7+uiPP5oKx+y27D8trp+psUzMacpm80XlnYeMAct0LynxhNYgaP3dtn8/LBAi+ksr+7NvjjtJV+UXrz0uLXV1x08AZRN1S2A8EaVE5afk/dfGjz//+fdHn36Xdttz2d9/z+bawZ8/slNN1aYtq+ZPGzNuPGdwOsfYflwmHCkPgs5DcKizvdpS/3gEUMW9/lvQvWp1bGTW1fpngC/uSVYAfH3TeSusL8TtlHUD4KAAEHjahk4YpuffGwEBN/dUmpHqdHyJ3Ap8O3UlQ4gCNrMRwLBBKiskL6a+RoBi/9XGlgc8L4/CUejQxaiIyqmomdhktOhfgDbDfaNDb4+yKIPd6IgmzDa0CByijWFmL2dlSRKagTIWXeIU9HDphoZiJeBTjAefHMxDhVSRFUgVcOkW3EGMahYVMFjWHhMluB2wAcbHYqF1LpsDF9C6s+CI2fDgh4wSuFEyGadjXAmIk3CugIRibLIti9ZtC8S4VSqfikGqPaoI122XyRYLBmsOmdiiTpqK1OklUQzpMcZmQRQV4M4oJCMkfRQXK+qvjifUcQd1bRdetW/LWjacYxvcttnVjWg5h0q4xw6rZyejSpZVZ78LzC4uyDNRQ4bymHSTMyM+SZ7D75mg/7YTlmNz7W8T00h0VEiGKB+F7iWYZFvSTiA4LVxttm2ATt5EoUWLJbY4EnLGrfsvEROlHtzlKn3H9VUT5tU/2dt3/EBv7foYzV/W4upyj04woO/gh6Vwwt3WGQAA) format("woff2-variations");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Plus Jakarta Sans Variable;font-style:normal;font-display:swap;font-weight:200 800;src:url(/assets/plus-jakarta-sans-vietnamese-wght-normal-qRpaaN48.woff2) format("woff2-variations");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Plus Jakarta Sans Variable;font-style:normal;font-display:swap;font-weight:200 800;src:url(/assets/plus-jakarta-sans-latin-ext-wght-normal-DmpS2jIq.woff2) format("woff2-variations");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Plus Jakarta Sans Variable;font-style:normal;font-display:swap;font-weight:200 800;src:url(/assets/plus-jakarta-sans-latin-wght-normal-eXO_dkmS.woff2) format("woff2-variations");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--color-bg: #F7F8FC;--color-surface: #ffffff;--color-primary: #2E5CE5;--color-primary-hover: #1D4ED8;--color-primary-subtle: #EEF2FF;--color-secondary: #64748B;--color-secondary-hover: #475569;--color-text: #1A1F36;--color-text-secondary: #697386;--color-muted: #94A3B8;--color-success: #22C55E;--color-error: #EF4444;--color-error-bg: #FEF2F2;--color-warning: #F59E0B;--color-border: #E2E8F0;--color-border-subtle: #F1F5F9;--color-hover: rgba(15, 23, 42, .06);--color-overlay: rgba(15, 23, 42, .5);--color-input-focus: rgba(46, 92, 229, .15);--shadow-xs: 0 1px 2px rgba(15, 23, 42, .05);--shadow-sm: 0 1px 3px rgba(15, 23, 42, .06), 0 1px 2px rgba(15, 23, 42, .04);--shadow-md: 0 4px 6px -1px rgba(15, 23, 42, .07), 0 2px 4px -2px rgba(15, 23, 42, .05);--shadow-lg: 0 10px 15px -3px rgba(15, 23, 42, .08), 0 4px 6px -4px rgba(15, 23, 42, .04);--shadow-xl: 0 20px 25px -5px rgba(15, 23, 42, .1), 0 8px 10px -6px rgba(15, 23, 42, .06);--radius-sm: 8px;--radius-md: 12px;--radius-lg: 16px;--radius-full: 50%;--font-sans: "Plus Jakarta Sans Variable", "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--space-xs: 4px;--space-sm: 8px;--space-md: 16px;--space-lg: 24px;--space-xl: 32px;--space-2xl: 48px;--transition-fast: .12s ease;--transition-base: .2s ease}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html{font-size:16px;-webkit-text-size-adjust:100%}body{font-family:var(--font-sans);background:var(--color-bg);color:var(--color-text);line-height:1.6;min-height:100dvh;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;overscroll-behavior-y:contain}.pull-to-refresh{position:fixed;top:-48px;left:0;right:0;height:48px;display:flex;align-items:center;justify-content:center;pointer-events:none;z-index:40;transition:opacity .15s}.pull-to-refresh-badge{width:40px;height:40px;border-radius:50%;background:var(--color-surface);border:1px solid var(--color-border);box-shadow:var(--shadow-md);display:flex;align-items:center;justify-content:center;color:var(--color-text-secondary)}#root{min-height:100dvh}.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:1px solid transparent;border-radius:var(--radius-sm);font-family:var(--font-sans);font-size:.875rem;font-weight:600;padding:10px 18px;cursor:pointer;transition:all var(--transition-base);-webkit-tap-highlight-color:transparent;letter-spacing:-.01em;line-height:1.25}.btn:disabled{opacity:.45;cursor:not-allowed}.btn:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.btn-primary{background:var(--color-primary);color:#fff;box-shadow:0 1px 2px #2e5ce54d,inset 0 1px #ffffff1a}.btn-primary:hover:not(:disabled){background:var(--color-primary-hover);box-shadow:0 2px 4px #2e5ce559,inset 0 1px #ffffff1a;transform:translateY(-.5px)}.btn-primary:active:not(:disabled){transform:translateY(0);box-shadow:0 1px 2px #2e5ce54d}.btn-secondary{background:var(--color-surface);color:var(--color-text);border-color:var(--color-border);box-shadow:var(--shadow-xs)}.btn-secondary:hover:not(:disabled){background:var(--color-border-subtle);border-color:#cbd5e1}.btn-danger{background:var(--color-error);color:#fff;box-shadow:0 1px 2px #ef44444d}.btn-danger:hover:not(:disabled){background:#dc2626;box-shadow:0 2px 4px #ef444459}.btn-link{background:none;border:none;color:var(--color-primary);padding:0;font-weight:500;box-shadow:none}.btn-link:hover:not(:disabled){text-decoration:underline;background:none;box-shadow:none}.btn-spinner{display:inline-block;width:14px;height:14px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:spin .6s linear infinite;vertical-align:middle;margin-right:6px}.btn-spinner-current{border-color:#7f7f7f40;border-top-color:currentColor}.btn-sm{font-size:.8125rem;padding:6px 12px;border-radius:6px}.btn-full{width:100%}.form-label{display:flex;flex-direction:column;gap:6px;font-size:.8125rem;font-weight:600;color:var(--color-text);margin-bottom:var(--space-md);letter-spacing:-.01em}.form-input,.form-textarea,.form-select{font-family:var(--font-sans);font-size:.9375rem;padding:10px 12px;border:1.5px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-surface);color:var(--color-text);transition:border-color var(--transition-fast),box-shadow var(--transition-fast);width:100%}.form-input:focus,.form-textarea:focus,.form-select:focus{outline:none;border-color:var(--color-primary);box-shadow:0 0 0 3px var(--color-input-focus)}.form-input-mono{font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.8125rem}.form-input::placeholder,.form-textarea::placeholder{color:var(--color-muted)}.form-textarea{resize:vertical;min-height:80px;line-height:1.5}.form-error{background:var(--color-error-bg);color:var(--color-error);border:1px solid #FECACA;border-radius:var(--radius-sm);padding:var(--space-sm) var(--space-md);font-size:.8125rem;font-weight:500;margin-bottom:var(--space-md)}.form-warning{background:#fef3c7;color:#92400e;border:1px solid #FDE68A;border-radius:var(--radius-sm);padding:var(--space-sm) var(--space-md);font-size:.8125rem;font-weight:500;margin-bottom:var(--space-md)}.dashboard{min-height:100dvh;display:flex;flex-direction:column;padding-bottom:0}.dashboard-main{flex:1;padding:var(--space-md);width:100%}.status-dot{display:inline-block;width:8px;height:8px;border-radius:var(--radius-full);flex-shrink:0;box-shadow:0 0 0 2px #fffc}.status-spinner{display:inline-flex;align-items:center;gap:2px;flex-shrink:0;height:8px}.status-spinner:before,.status-spinner>span,.status-spinner:after{content:"";display:block;width:3px;height:3px;border-radius:var(--radius-full);background-color:var(--color-success);animation:marching-dot 1.2s ease-in-out infinite}.status-spinner:before{animation-delay:0s}.status-spinner>span{animation-delay:.2s}.status-spinner:after{animation-delay:.4s}@keyframes marching-dot{0%,60%,to{opacity:.25;transform:scale(.8)}30%{opacity:1;transform:scale(1)}}.pair-page{display:flex;align-items:flex-start;justify-content:center;min-height:100dvh;padding:var(--space-xl) var(--space-md);background:var(--color-bg)}.pair-card{width:100%;max-width:420px;display:flex;flex-direction:column;gap:var(--space-lg)}.pair-header{text-align:center}.pair-title{font-size:1.5rem;font-weight:700;color:var(--color-text);letter-spacing:-.02em}.pair-subtitle{margin-top:var(--space-xs);font-size:.875rem;color:var(--color-text-secondary)}.pair-instructions{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-md);display:flex;flex-direction:column;gap:var(--space-md)}.pair-instruction-block{display:flex;flex-direction:column;gap:var(--space-sm)}.pair-instruction-heading{font-size:.8125rem;font-weight:600;color:var(--color-text);letter-spacing:-.01em}.pair-steps{list-style:none;counter-reset:pair-step;display:flex;flex-direction:column;gap:6px;padding:0}.pair-steps li{counter-increment:pair-step;font-size:.8125rem;color:var(--color-text-secondary);line-height:1.5;display:flex;flex-wrap:wrap;gap:0 var(--space-sm)}.pair-steps li:before{content:counter(pair-step) ".";font-weight:600;color:var(--color-muted);min-width:16px;flex-shrink:0}.pair-steps li .pair-command{flex-basis:100%}.pair-steps code{font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.75rem;background:var(--color-bg);border:1px solid var(--color-border);border-radius:4px;padding:1px 5px;color:var(--color-text);white-space:nowrap}.pair-command{display:block;margin-top:4px;padding:6px 10px!important;border-radius:var(--radius-sm)!important;overflow-x:auto;-webkit-overflow-scrolling:touch}.pair-platform-label{display:block;flex-basis:100%;margin-top:8px;font-size:.75rem;font-weight:600;color:var(--color-muted)}.pair-instruction-divider{height:1px;background:var(--color-border)}.pair-form{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-lg);display:flex;flex-direction:column;gap:var(--space-md);box-shadow:var(--shadow-sm)}.pair-code-input{font-size:1.5rem!important;letter-spacing:.25em;text-align:center;padding:14px 12px!important;text-transform:uppercase}.pair-code-input::placeholder{letter-spacing:.25em;opacity:.35}.pair-label-hint{font-weight:400;color:var(--color-muted);font-size:.75rem}.pair-error{font-size:.8125rem;color:var(--color-error);background:var(--color-error-bg);padding:var(--space-sm) var(--space-md);border-radius:var(--radius-sm);line-height:1.4}.loading-state,.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--space-2xl) 0;gap:var(--space-sm)}.empty-state-text{font-size:1rem;font-weight:600;color:var(--color-text-secondary)}.empty-state-hint{font-size:.8125rem;color:var(--color-muted)}.revoked-state{display:flex;flex-direction:column;align-items:center;text-align:center;padding:var(--space-2xl) var(--space-lg);margin:var(--space-md) 0;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);animation:revokedFadeIn .35s ease}@keyframes revokedFadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.revoked-icon{width:56px;height:56px;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-full);background:var(--color-error-bg);color:var(--color-error);margin-bottom:var(--space-md)}.revoked-title{font-size:1.125rem;font-weight:700;color:var(--color-text);margin-bottom:var(--space-xs)}.revoked-description{font-size:.875rem;line-height:1.6;color:var(--color-text-secondary);max-width:320px;margin-bottom:var(--space-lg)}.revoked-command{padding:8px 16px;background:var(--color-bg);border:1px solid var(--color-border);border-radius:var(--radius-sm);margin-bottom:var(--space-lg)}.revoked-command code{font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.8125rem;color:var(--color-text);letter-spacing:-.01em}.revoked-actions{display:flex;gap:var(--space-sm);width:100%;max-width:280px}.revoked-actions .btn{flex:1}.spinner{width:24px;height:24px;border:2.5px solid var(--color-border);border-top-color:var(--color-primary);border-radius:var(--radius-full);animation:spin .7s linear infinite}.spinner-lg{width:40px;height:40px;border-width:3px}.capability-toggles-loading{display:flex;justify-content:center;padding:var(--space-lg)}@keyframes spin{to{transform:rotate(360deg)}}.session-composer{background:var(--color-surface);border-radius:var(--radius-md);border:1px solid var(--color-border);box-shadow:var(--shadow-sm);padding:var(--space-sm);margin-bottom:var(--space-md);display:flex;flex-direction:column;gap:var(--space-sm)}.session-composer:focus-within{border-color:var(--color-primary);box-shadow:0 0 0 2px var(--color-input-focus)}.session-composer-textarea{width:100%;border:none;resize:vertical;outline:none;background:transparent;color:var(--color-text);font-family:var(--font-sans);font-size:.9375rem;line-height:1.5;padding:var(--space-xs) var(--space-sm);min-height:3.5em}.session-composer-textarea::placeholder{color:var(--color-muted)}.session-composer-controls{display:flex;align-items:center;gap:var(--space-sm);padding-left:var(--space-xs)}.session-composer-yolo{display:inline-flex;align-items:center;gap:4px;font-size:.8rem;color:var(--color-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none}.session-composer-yolo input{margin:0;cursor:pointer}.session-composer-controls .chat-send-btn{margin-left:auto}.fab{position:fixed;right:var(--space-lg);bottom:calc(var(--space-lg) + env(safe-area-inset-bottom,0px));width:56px;height:56px;border-radius:50%;border:none;background:var(--color-primary);color:#fff;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:var(--shadow-lg);transition:background var(--transition-base),transform var(--transition-fast),box-shadow var(--transition-base);z-index:50;-webkit-tap-highlight-color:transparent}.fab:hover{background:var(--color-primary-hover);box-shadow:var(--shadow-xl);transform:translateY(-1px)}.fab:active{transform:translateY(0)}.fab:focus-visible{outline:2px solid var(--color-primary);outline-offset:3px}.section-label{font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--color-muted);margin:var(--space-md) 0 var(--space-xs)}.agent-picker-section-inline{display:flex;align-items:center;gap:var(--space-xs)}.agent-picker-section-inline .form-select{width:auto;min-width:0;font-size:.8rem;padding:4px 8px}.agent-picker-section-inline .agent-picker-single{font-size:.8rem;color:var(--color-text)}.task-list{display:flex;flex-direction:column;gap:10px;padding-bottom:calc(56px + var(--space-lg) * 2 + env(safe-area-inset-bottom,0px))}.task-card{background:var(--color-surface);border-radius:var(--radius-md);border:1px solid var(--color-border);box-shadow:var(--shadow-sm);padding:var(--space-md);display:flex;flex-direction:column;gap:var(--space-sm);cursor:pointer;transition:box-shadow var(--transition-base),border-color var(--transition-base);-webkit-tap-highlight-color:transparent}.task-card:hover{box-shadow:var(--shadow-md);border-color:#cbd5e1}.task-card-header{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm)}.task-card-title-row{display:flex;align-items:center;gap:10px;min-width:0;flex:1}.task-card-name{font-size:.9375rem;font-weight:600;letter-spacing:-.01em;color:var(--color-text);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.task-card-meta{display:flex;flex-wrap:wrap;gap:var(--space-sm);font-size:.75rem;color:var(--color-text-secondary)}.task-card-agent{color:var(--color-text-tertiary)}.task-card-last-event-link{color:var(--color-primary);cursor:pointer;text-decoration:underline}.task-card-actions{display:flex;gap:var(--space-xs);flex-shrink:0}.task-card-menu{position:relative}.task-card-menu-btn{background:none;border:none;color:var(--color-text-secondary);font-size:1.5rem;min-width:44px;min-height:44px;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);cursor:pointer;line-height:1}.task-card-menu-btn:hover{background:var(--color-hover);color:var(--color-text)}.task-card-menu-dropdown{position:absolute;right:0;top:100%;margin-top:4px;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);box-shadow:var(--shadow-md);min-width:150px;z-index:10;overflow:hidden}.task-card-menu-dropdown button{display:flex;align-items:center;gap:var(--space-sm);width:100%;text-align:left;padding:var(--space-sm) var(--space-md);background:none;border:none;font-size:.9375rem;color:var(--color-text);cursor:pointer;white-space:nowrap}.task-card-menu-dropdown button:hover{background:var(--color-hover)}.task-card-menu-dropdown .menu-item-danger{color:var(--color-error)}.menu-icon{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;font-size:.875rem;flex-shrink:0;line-height:1}.bottom-sheet-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--color-overlay);z-index:1000;display:flex;align-items:flex-end;justify-content:center;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);animation:fadeIn var(--transition-fast)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.bottom-sheet{background:var(--color-surface);border-radius:var(--radius-lg) var(--radius-lg) 0 0;width:100%;max-width:480px;padding:var(--space-sm) var(--space-md) var(--space-xl);box-shadow:var(--shadow-xl);animation:sheetSlideUp .25s cubic-bezier(.16,1,.3,1)}@keyframes sheetSlideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}.bottom-sheet-handle{width:36px;height:4px;background:var(--color-border);border-radius:2px;margin:0 auto var(--space-md)}.bottom-sheet-title{font-size:.9375rem;font-weight:600;color:var(--color-text);padding:0 var(--space-xs) var(--space-md);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bottom-sheet-actions{display:flex;flex-direction:column}.bottom-sheet-actions button{display:flex;align-items:center;gap:var(--space-md);width:100%;text-align:left;padding:var(--space-md);background:none;border:none;border-radius:var(--radius-sm);font-size:.9375rem;font-family:var(--font-sans);color:var(--color-text);cursor:pointer;transition:background var(--transition-fast)}.bottom-sheet-actions button:active{background:var(--color-hover)}.bottom-sheet-actions .menu-item-danger{color:var(--color-error)}.task-form-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:transparent;display:flex;align-items:stretch;justify-content:center;z-index:100;padding:0;overflow:hidden;overscroll-behavior:contain}.task-form{background:var(--color-surface);border-radius:0;padding:var(--space-lg) var(--space-md);width:100%;max-width:none;max-height:none;overflow-y:auto;animation:slideUp .25s cubic-bezier(.16,1,.3,1);display:flex;flex-direction:column;gap:var(--space-md)}@media(min-width:600px){.task-form-overlay{background:var(--color-overlay);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);align-items:center;padding:var(--space-md)}.task-form{border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);max-width:540px;max-height:90dvh;padding:var(--space-lg)}}@keyframes slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.task-form-header{display:flex;align-items:center;justify-content:space-between}.task-form h2{font-size:1.0625rem;font-weight:700;letter-spacing:-.02em;margin-bottom:0}.plan-actions{display:flex;gap:var(--space-sm);flex-wrap:wrap}.permissions-dialog{display:flex;flex-direction:column;gap:var(--space-md);overflow:hidden;flex:1;min-height:0}.permissions-dialog h2{margin:0;font-size:1.125rem}.permissions-dialog-scroll{flex:1;min-height:0;overflow-y:auto}.permissions-empty{color:var(--color-text-secondary);font-size:.875rem;font-style:italic;margin:var(--space-md) 0}.permissions-section{border-top:1px solid var(--color-border);padding-top:var(--space-md);margin-top:var(--space-md)}.permissions-section h3{margin:0 0 var(--space-xs) 0;font-size:.95rem}.permissions-list{margin:0;padding-left:1.25em}.permission-item{margin-bottom:var(--space-sm);display:flex;flex-direction:column}.permission-tool{font-weight:600;font-family:var(--font-mono, monospace)}.permission-desc{color:var(--color-text-secondary);font-size:.9em}.permissions-dialog-actions{display:flex;gap:var(--space-sm);align-items:center;justify-content:flex-end}@media(min-width:600px){.permissions-dialog{max-width:none}}.result-times{display:flex;flex-direction:column;gap:var(--space-xs);font-size:.8125rem;color:var(--color-text-secondary);margin-bottom:var(--space-sm)}.skeleton-line{height:.875rem;border-radius:var(--radius-sm);background:var(--color-border);animation:skeleton-pulse 1.2s ease-in-out infinite}@keyframes skeleton-pulse{0%,to{opacity:.4}50%{opacity:1}}.granted-permissions-row{flex-basis:100%}.granted-permissions-row .btn-link{padding:0}.schedule-section{display:flex;flex-direction:column;gap:var(--space-sm)}.schedule-section-title{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--color-muted);margin:0}.schedule-section-hint{font-weight:400;text-transform:none;letter-spacing:0;color:var(--color-text-secondary);margin-left:6px}.schedule-reactive{display:flex;flex-direction:column;gap:var(--space-xs)}.yolo-inline{display:inline-flex;align-items:center;gap:4px;font-size:.8rem;color:var(--color-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap}.yolo-inline input{margin:0;cursor:pointer}.yolo-warning{flex-basis:100%;margin:0;font-size:.75rem;color:var(--color-text-secondary);line-height:1.4}.trigger-row-card{display:flex;align-items:center;gap:8px;padding:4px 0}.trigger-row-content{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0}.trigger-row-top{display:flex;align-items:center;gap:8px}.trigger-row-top .form-select{flex:0 0 auto;width:auto}.trigger-row-top .form-input{flex:1}.trigger-details{display:flex;gap:6px}.trigger-details .form-input[type=date]{flex:1;min-width:0}.trigger-details .form-input[type=time]{flex:0 0 auto;width:auto}.schedule-section>.form-select,.trigger-row-card .form-select,.trigger-row-card .form-input{margin-bottom:0;font-size:.8125rem;padding:6px 8px;height:32px;box-sizing:border-box;min-width:0}.schedule-section>.form-select{width:100%}.trigger-row-card .form-select,.trigger-row-card .form-input{flex:1}.trigger-remove-btn{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border:none;border-radius:var(--radius-full);background:transparent;color:var(--color-muted);font-size:1.1rem;line-height:1;cursor:pointer;transition:all var(--transition-fast);flex-shrink:0}.trigger-remove-btn:hover{background:var(--color-error-bg);color:var(--color-error)}.trigger-add-btn{display:inline-flex;align-items:center;gap:4px;border:1px dashed var(--color-border);border-radius:var(--radius-sm);background:transparent;color:var(--color-text-secondary);font-family:var(--font-sans);font-size:.8125rem;font-weight:500;padding:8px 14px;cursor:pointer;transition:all var(--transition-fast);width:100%;justify-content:center}.trigger-add-btn:hover{border-color:var(--color-primary);color:var(--color-primary);background:var(--color-primary-subtle)}.form-select{width:auto;min-width:80px}.toggles-section{display:flex;flex-direction:column;gap:10px;margin-bottom:var(--space-md)}.toggle-label{display:flex;align-items:center;gap:10px;font-size:.875rem;font-weight:500;cursor:pointer;-webkit-tap-highlight-color:transparent;color:var(--color-text)}.toggle-label input[type=checkbox]{width:16px;height:16px;accent-color:var(--color-primary);border-radius:4px}.toggles-group{display:flex;flex-direction:column;gap:var(--space-xs)}.command-section,.command-section-active .toggle-label,.task-title-field,.task-title-help{margin-bottom:0}.command-help-text{font-size:.75rem;color:var(--color-text-secondary);line-height:1.4;margin:6px 0 10px}.command-section-active .form-input{font-size:.8125rem;padding:6px 8px;height:32px;box-sizing:border-box}.form-actions{display:flex;gap:var(--space-sm);position:sticky;bottom:0;background:var(--color-surface);padding:var(--space-sm) 0}.confirm-modal-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--color-overlay);display:flex;align-items:center;justify-content:center;z-index:1000;padding:var(--space-md);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.confirm-modal{background:var(--color-surface);border-radius:var(--radius-lg);border:1px solid var(--color-border);padding:var(--space-xl);width:100%;max-width:380px;text-align:center;box-shadow:var(--shadow-xl);animation:slideUp .25s cubic-bezier(.16,1,.3,1)}.confirm-modal-title{font-size:1.125rem;font-weight:700;letter-spacing:-.02em;margin-bottom:var(--space-xs)}.confirm-modal-subtitle{font-size:.8rem;color:var(--color-muted);margin-bottom:var(--space-sm)}.confirm-modal-message{font-size:.9375rem;color:var(--color-text-secondary);margin-bottom:var(--space-lg);line-height:1.5}.confirm-modal-actions{display:flex;gap:var(--space-sm);justify-content:center}.confirm-modal-actions .btn{flex:1;padding:10px var(--space-lg);font-size:.875rem}.choice-options-stacked{flex-direction:column}.permission-modal{text-align:left;max-width:400px}.permission-modal .confirm-modal-title{text-align:center}.permission-modal .confirm-modal-message{text-align:center;word-break:break-word}.permission-list{display:flex;flex-direction:column;gap:var(--space-xs);margin-bottom:var(--space-lg)}.permission-item{display:flex;flex-direction:column;gap:2px;padding:var(--space-sm);background:var(--color-hover);border-radius:var(--radius-sm)}.permission-name{font-size:.8125rem;font-weight:600;color:var(--color-text)}.permission-desc{font-size:.75rem;color:var(--color-text-secondary);line-height:1.4}.permission-actions{display:flex;gap:var(--space-sm)}.permission-actions .btn{flex:1;padding:10px var(--space-md);font-size:.875rem}.permission-abort-link{display:block;width:100%;margin-top:var(--space-sm);background:none;border:none;color:var(--color-error);font-size:.8125rem;cursor:pointer;text-align:center;padding:var(--space-xs) 0;opacity:.8}.permission-abort-link:hover:not(:disabled){opacity:1;text-decoration:underline}.permission-abort-link:disabled{opacity:.45;cursor:not-allowed}.input-modal{text-align:left;max-width:400px}.input-modal .confirm-modal-title{text-align:center}.input-modal .confirm-modal-message{text-align:center;word-break:break-word}.input-list{display:flex;flex-direction:column;gap:var(--space-md);margin-bottom:var(--space-lg)}.input-item{display:flex;flex-direction:column;gap:var(--space-xs)}.input-label{font-size:.8125rem;font-weight:600;color:var(--color-text)}.input-field{width:100%;padding:var(--space-sm) var(--space-md);font-size:.875rem;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-surface);color:var(--color-text);box-sizing:border-box}.input-field:focus{outline:none;border-color:var(--color-primary);box-shadow:0 0 0 2px rgba(var(--color-primary-rgb, 99, 102, 241),.2)}.input-save-toggle{margin-bottom:var(--space-lg)}.input-actions{display:flex;gap:var(--space-sm)}.input-actions .btn{flex:1;padding:10px var(--space-md);font-size:.875rem}.passwords-dialog{text-align:left;max-width:420px}.passwords-dialog .confirm-modal-title{text-align:center}.passwords-dialog-scroll{max-height:min(50vh,400px);overflow-y:auto;margin-bottom:var(--space-lg);scrollbar-width:thin;scrollbar-color:var(--color-border) transparent}.passwords-empty{font-size:.875rem;color:var(--color-muted);text-align:center;padding:var(--space-md) 0;line-height:1.5}.passwords-list{list-style:none;margin:0;padding:0}.password-item{display:flex;align-items:center;gap:var(--space-sm);padding:var(--space-sm) 0;border-bottom:1px solid var(--color-border)}.password-item:last-child{border-bottom:none}.password-item-text{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.password-item-origin{font-size:.875rem;font-weight:500;color:var(--color-text);overflow-wrap:anywhere}.password-item-username{font-size:.8125rem;color:var(--color-muted);overflow-wrap:anywhere}.password-delete-btn{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;background:none;cursor:pointer;color:var(--color-muted);border-radius:6px;flex-shrink:0;opacity:.7;transition:all var(--transition-fast)}.password-delete-btn:hover{color:var(--color-error);background:var(--color-error-bg);opacity:1}.host-picker-inline{margin:0 calc(var(--space-sm) * -1)}.host-picker-list{max-height:240px;overflow-y:auto;overflow-x:hidden;padding:2px var(--space-sm);scrollbar-width:thin;scrollbar-color:var(--color-border) transparent}.host-picker-list::-webkit-scrollbar{width:6px}.host-picker-list::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:3px}.host-picker-item-wrapper{position:relative}.host-picker-item{position:relative;display:flex;align-items:center;gap:8px;padding:9px 12px 9px 14px;margin:1px 0;border:none;background:none;cursor:pointer;font-family:var(--font-sans);font-size:.8125rem;color:var(--color-text-secondary);text-align:left;border-radius:8px;transition:background var(--transition-fast),color var(--transition-fast);-webkit-tap-highlight-color:transparent}.host-picker-item:hover{background:var(--color-border-subtle);color:var(--color-text)}.host-picker-item-active{background:var(--color-primary-subtle);color:var(--color-text);font-weight:500}.host-picker-item-active:hover{background:var(--color-primary-subtle)}.host-picker-item-active:before{content:"";position:absolute;left:4px;top:9px;bottom:9px;width:2px;border-radius:2px;background:var(--color-primary)}.host-picker-item-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:flex;align-items:center;gap:6px}.host-picker-pending{font-size:.6875rem;font-weight:500;color:var(--color-muted);background:var(--color-border-subtle);padding:1px 6px;border-radius:4px;flex-shrink:0}.host-picker-item-actions{flex-shrink:0;width:24px;display:flex;align-items:center;justify-content:center}.host-picker-edit-btn{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:none;cursor:pointer;color:var(--color-muted);border-radius:6px;flex-shrink:0;opacity:.7;transition:all var(--transition-fast)}.host-picker-item:hover .host-picker-edit-btn,.host-picker-edit-btn:focus-visible{opacity:1}.host-picker-edit-btn:hover{color:var(--color-primary);background:#2e5ce51f;opacity:1}.host-picker-rename-input{font-size:.8125rem!important;padding:3px 6px!important;flex:1;min-width:0}.host-picker-delete-btn{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:none;cursor:pointer;color:var(--color-muted);border-radius:6px;flex-shrink:0;opacity:.7;transition:all var(--transition-fast)}@media(hover:hover)and (pointer:fine){.host-picker-delete-btn{opacity:0}.host-picker-item:hover .host-picker-delete-btn,.host-picker-delete-btn:focus-visible{opacity:.7}}.host-picker-delete-btn:hover{color:var(--color-error);background:var(--color-error-bg);opacity:1}.hamburger-btn{display:flex;align-items:center;justify-content:center;width:40px;height:40px;padding:0;border:none;background:none;color:var(--color-text-secondary);cursor:pointer;border-radius:var(--radius-sm);transition:background var(--transition-fast),color var(--transition-fast);-webkit-tap-highlight-color:transparent}.hamburger-btn:hover{background:var(--color-border-subtle);color:var(--color-text)}.hamburger-btn:active{background:var(--color-primary-subtle);color:var(--color-primary)}.drawer-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--color-overlay);z-index:100;animation:drawerFadeIn .2s ease}.drawer-panel{position:fixed;top:0;left:0;bottom:0;width:296px;max-width:84vw;background:var(--color-surface);box-shadow:var(--shadow-xl);z-index:101;display:flex;flex-direction:column;overflow-y:auto;overscroll-behavior:contain;animation:drawerSlideIn .25s ease;scrollbar-width:thin;scrollbar-color:var(--color-border) transparent}.drawer-panel::-webkit-scrollbar{width:6px}.drawer-panel::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:3px}.drawer-close-btn{position:absolute;top:8px;right:8px;z-index:1;display:flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:none;background:none;color:var(--color-text-secondary);cursor:pointer;border-radius:var(--radius-sm);transition:background var(--transition-fast)}.drawer-close-btn:hover{background:var(--color-border-subtle);color:var(--color-text)}.drawer-section{padding:var(--space-md);display:flex;flex-direction:column;gap:var(--space-sm)}.drawer-section+.drawer-section{padding-top:0}.drawer-section:has(>.host-picker-inline){padding-bottom:var(--space-xs)}.drawer-section-label{font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--color-muted);margin:0 0 var(--space-xs)}.drawer-section-hint{font-size:.8125rem;color:var(--color-muted);line-height:1.45;margin:0 0 var(--space-sm) 0}.pair-checkbox{display:flex;align-items:flex-start;gap:var(--space-sm);padding:var(--space-sm) 0;cursor:pointer}.pair-checkbox input[type=checkbox]{margin-top:.2rem;flex-shrink:0}.pair-checkbox-text{display:flex;flex-direction:column;gap:.15rem}.pair-checkbox-title{font-size:.9375rem;font-weight:500}.pair-checkbox-hint{font-size:.8125rem;color:var(--color-muted);line-height:1.4}.drawer-toggle-group{display:flex;flex-direction:column;gap:var(--space-sm)}.drawer-toggle-group-divided{border-top:1px solid var(--color-border);padding-top:var(--space-sm);margin-top:var(--space-xs)}.drawer-toggle{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm)}.drawer-toggle-label{font-size:.85rem;color:var(--color-text)}.toggle-switch{position:relative;width:40px;height:22px;border-radius:11px;border:none;background:var(--color-border);cursor:pointer;padding:0;transition:background .2s;flex-shrink:0}.toggle-switch-on{background:var(--color-primary)}.toggle-switch-thumb{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;transition:transform .2s}.toggle-switch-on .toggle-switch-thumb{transform:translate(18px)}.toggle-switch:disabled{opacity:.5;cursor:not-allowed}.drawer-footer{margin-top:auto;padding:var(--space-md);display:flex;flex-direction:column;gap:var(--space-md);border-top:1px solid var(--color-border-subtle)}.drawer-version{font-size:.75rem;color:var(--color-muted);font-variant-numeric:tabular-nums}.drawer-agents{display:flex;flex-direction:column;gap:var(--space-sm)}.drawer-agents-section{display:flex;flex-direction:column;gap:3px}.drawer-agents-heading{font-size:.6875rem;font-weight:600;color:var(--color-muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px}.drawer-agents-row{display:flex;justify-content:space-between;align-items:baseline;gap:var(--space-sm);font-size:.75rem;color:var(--color-text-secondary)}.drawer-agents-version{font-variant-numeric:tabular-nums;color:var(--color-muted)}.drawer-host-info{display:flex;flex-direction:column;gap:3px}.drawer-host-time{font-size:.75rem;color:var(--color-text-secondary);font-variant-numeric:tabular-nums}.drawer-legal{display:flex;align-items:center;flex-wrap:wrap;gap:0 var(--space-xs);font-size:.6875rem;color:var(--color-muted);letter-spacing:.02em}.drawer-legal a{color:var(--color-muted);text-decoration:none;transition:color var(--transition-fast)}.drawer-legal a:hover{color:var(--color-primary)}.drawer-legal-sep{opacity:.55}.drawer-divider{height:1px;background:var(--color-border-subtle);margin:0 var(--space-md)}@keyframes drawerFadeIn{0%{opacity:0}to{opacity:1}}@keyframes drawerFadeOut{0%{opacity:1}to{opacity:0}}@keyframes drawerSlideIn{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes drawerSlideOut{0%{transform:translate(0)}to{transform:translate(-100%)}}.drawer-overlay-closing{animation:drawerFadeOut .2s ease forwards}.drawer-panel-closing{animation:drawerSlideOut .2s ease forwards}.app-header{position:sticky;top:0;z-index:10;background:color-mix(in srgb,var(--color-surface) 92%,transparent);border-bottom:1px solid var(--color-border);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.app-title-bar{position:relative;display:flex;align-items:center;justify-content:center;padding:8px 0}.app-title-bar .hamburger-btn{position:absolute;left:4px;top:50%;transform:translateY(-50%)}.app-title{margin:0;font-size:1rem;font-weight:700;letter-spacing:-.01em;color:#1e3a8a}.conn-status{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:inline-flex;color:#60a5fa}.conn-status--lan{color:#2563eb}.conn-status--disconnected{color:#dc2626}.conn-status-btn{background:none;border:none;padding:4px 8px;cursor:pointer;line-height:1;display:inline-flex;align-items:center;justify-content:center;border-radius:6px;color:inherit}.conn-status-btn:hover,.conn-status-btn:focus-visible{background:var(--color-bg);outline:none}.conn-status-popover{position:absolute;top:calc(100% + 6px);right:0;background:var(--color-text);color:var(--color-surface);font-size:.75rem;font-weight:500;padding:6px 10px;border-radius:6px;white-space:nowrap;pointer-events:none;opacity:0;transform:translateY(-2px);transition:opacity .12s,transform .12s;z-index:20}.conn-status:hover .conn-status-popover,.conn-status-popover--open{opacity:1;transform:translateY(0)}.conn-status--connecting .conn-status-btn>svg{animation:conn-status-pulse 1.2s ease-in-out infinite}@keyframes conn-status-pulse{0%,to{opacity:.35}50%{opacity:1}}.tab-bar{display:flex;align-items:center}.tab-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:12px 0;border:none;background:none;font-family:var(--font-sans);font-size:.875rem;font-weight:600;color:var(--color-text-secondary);cursor:pointer;border-bottom:2px solid transparent;transition:color var(--transition-fast),border-color var(--transition-fast)}.tab-icon{flex-shrink:0}.tab-btn:hover{color:var(--color-text)}.tab-btn-active{color:var(--color-primary);border-bottom-color:var(--color-primary)}.sessions-view{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center}.sessions-card{background:var(--color-surface);border-radius:var(--radius-md);border:1px solid var(--color-border);box-shadow:var(--shadow-sm);padding:var(--space-md);display:flex;align-items:center;gap:var(--space-sm);cursor:pointer;transition:box-shadow var(--transition-base),border-color var(--transition-base);-webkit-tap-highlight-color:transparent}.sessions-card:hover{box-shadow:var(--shadow-md);border-color:#cbd5e1}.sessions-card-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--space-xs)}.sessions-card-name{font-size:.9375rem;font-weight:600;letter-spacing:-.01em;color:var(--color-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sessions-card-meta{display:flex;flex-wrap:wrap;gap:var(--space-sm);font-size:.75rem;color:var(--color-text-secondary)}.sessions-card-chevron{flex-shrink:0;align-self:center;color:var(--color-text-secondary);font-size:1.25rem;line-height:1;opacity:.4;transition:opacity var(--transition-base)}.sessions-card:hover .sessions-card-chevron{opacity:.8}.star-button{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;margin:-8px 0;border:none;border-radius:var(--radius-full);background:none;cursor:pointer;color:var(--color-muted);transition:color var(--transition-fast),background var(--transition-fast);-webkit-tap-highlight-color:transparent}.star-button:hover{background:var(--color-hover)}.star-button--on{color:var(--color-warning)}.star-button:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.sessions-filter-chip{display:inline-flex;align-items:center;gap:var(--space-xs);padding:4px 10px;font-size:.8125rem;color:var(--color-text-secondary);background:var(--color-bg);border:1px solid var(--color-border);border-radius:999px}.sessions-filter-chip button{display:inline-flex;align-items:center;justify-content:center;border:none;background:none;cursor:pointer;padding:0;color:var(--color-text-secondary);font-size:1rem;line-height:1}.sessions-filter-chip button:hover{color:var(--color-text)}.run-detail{display:flex;flex-direction:column;gap:var(--space-md);background:var(--color-surface);border-radius:var(--radius-md);border:1px solid var(--color-border);box-shadow:var(--shadow-sm);padding:var(--space-md)}.run-detail-header{display:flex;align-items:center;justify-content:space-between}.run-detail-back{display:inline-flex;align-items:center;gap:var(--space-xs);align-self:flex-start;border:none;background:none;cursor:pointer;padding:0;color:var(--color-text-secondary);font-size:.8125rem}.run-detail-back:hover{color:var(--color-text)}.run-detail-back--footer{align-self:center;margin-top:var(--space-sm);padding:8px 16px;border:1px solid var(--color-border);border-radius:var(--radius-md)}.run-detail-back--footer:hover{background:var(--color-surface-hover, var(--color-bg))}.chat-thread{display:flex;flex-direction:column;gap:var(--space-sm);overflow-y:auto;flex:1;min-height:0}.chat-message{display:flex;flex-direction:column;gap:var(--space-xs);max-width:85%;min-width:0;padding:var(--space-sm) var(--space-md);border-radius:var(--radius-md);font-size:.8125rem;line-height:1.6}.chat-message--assistant{align-self:flex-start;background:var(--color-surface);border:1px solid var(--color-border)}.chat-message--user{align-self:flex-end;background:var(--color-primary-subtle);border:1px solid var(--color-border-subtle)}.chat-message--assistant.chat-message--stderr .chat-message-content,.chat-message--assistant.chat-message--stderr .chat-message-content pre,.chat-message--assistant.chat-message--stderr .chat-message-content code{color:var(--color-error)}.chat-message-agent{font-size:.6875rem;font-weight:500;color:var(--color-text-tertiary);margin-bottom:var(--space-xs)}.chat-message-agent-version{font-weight:400;opacity:.7}.chat-message-content{color:var(--color-text);overflow-wrap:break-word}.chat-message-content h1,.chat-message-content h2,.chat-message-content h3,.chat-message-content h4{margin:.75em 0 .25em;font-weight:600}.chat-message-content h1{font-size:1.1rem}.chat-message-content h2{font-size:1rem}.chat-message-content h3{font-size:.9375rem}.chat-message-content p{margin:.5em 0}.chat-message-content ul,.chat-message-content ol{margin:.5em 0;padding-left:1.5em}.chat-message-content code{font-size:.8em;background:var(--color-hover);padding:.15em .35em;border-radius:4px}.chat-message-content pre{background:var(--color-bg);border-radius:var(--radius-sm);padding:var(--space-sm);overflow-x:auto;margin:.5em 0}.chat-message-content pre code{background:none;padding:0}.chat-message-content table{border-collapse:collapse;width:100%;margin:.5em 0}.chat-message-content th,.chat-message-content td{border:1px solid var(--color-border);padding:.35em .6em;text-align:left}.chat-message-content th{background:var(--color-bg);font-weight:600}.chat-message-meta{display:flex;align-items:center;gap:var(--space-xs);font-size:.6875rem;color:var(--color-muted)}.chat-message-type{font-weight:600;text-transform:uppercase;letter-spacing:.04em;font-size:.625rem}.chat-message-attachments{display:flex;flex-wrap:wrap;gap:var(--space-xs)}.chat-attachment-chip{display:inline-flex;align-items:center;padding:.2em .6em;font-size:.75rem;background:var(--color-bg);border:1px solid var(--color-border);border-radius:var(--radius-sm);cursor:pointer;color:var(--color-primary);font-weight:500}.chat-attachment-chip:hover{background:var(--color-primary-subtle)}.chat-message-report{margin-top:var(--space-xs);padding:var(--space-sm);background:var(--color-bg);border-radius:var(--radius-sm);border:1px solid var(--color-border);font-size:.8125rem;line-height:1.6;color:var(--color-text-secondary)}.chat-status{display:flex;flex-direction:column;align-items:center;gap:var(--space-xs);font-size:.6875rem;color:var(--color-muted);padding:var(--space-xs) 0}.chat-status>div{display:flex;align-items:center;gap:var(--space-xs)}.chat-status--error{color:var(--color-error)}.chat-status-detail{margin-top:var(--space-xs);padding:var(--space-sm);background:var(--color-bg);border:1px solid var(--color-border);border-radius:var(--radius-sm);font-size:.75rem;font-family:SF Mono,Fira Code,monospace;white-space:pre-wrap;word-break:break-word;max-width:100%;max-height:200px;overflow-y:auto;color:var(--color-text-secondary)}.chat-status-time{color:var(--color-muted)}.chat-stop-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border-radius:50%;flex-shrink:0;margin-left:auto;background:var(--color-error);color:#fff;border-color:var(--color-error)}.chat-stop-btn:hover:not(:disabled){background:#dc2626;border-color:#dc2626}.chat-input-bar{display:flex;gap:var(--space-xs);padding:var(--space-sm) 0;flex-shrink:0}.chat-input{flex:1;min-width:0;padding:var(--space-sm) var(--space-md);border:1px solid var(--color-border);border-radius:var(--radius-md);font-size:.8125rem;outline:none;background:var(--color-surface);color:var(--color-text)}.chat-input:focus{border-color:var(--color-primary);box-shadow:0 0 0 2px var(--color-primary-subtle)}.chat-input:disabled{opacity:.6}.chat-send-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border-radius:50%;flex-shrink:0}.report-dialog-overlay{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;background:var(--color-overlay);display:flex;align-items:stretch;justify-content:center}.report-dialog{display:flex;flex-direction:column;width:100%;max-width:800px;background:var(--color-surface);overflow:hidden}.report-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-sm) var(--space-md);border-bottom:1px solid var(--color-border);flex-shrink:0}.report-dialog-title{font-size:.875rem;font-weight:600;color:var(--color-text)}.report-dialog-close{display:flex;align-items:center;justify-content:center;border:none;background:none;cursor:pointer;color:var(--color-text-secondary);padding:4px;border-radius:var(--radius-sm)}.report-dialog-close:hover{background:var(--color-hover);color:var(--color-text)}.report-dialog-body{flex:1;overflow-y:auto;padding:var(--space-md);font-size:.8125rem;line-height:1.6;color:var(--color-text)}.report-dialog-body h1,.report-dialog-body h2,.report-dialog-body h3,.report-dialog-body h4{margin:.75em 0 .25em;font-weight:600}.report-dialog-body h1{font-size:1.1rem}.report-dialog-body h2{font-size:1rem}.report-dialog-body h3{font-size:.9375rem}.report-dialog-body p{margin:.5em 0}.report-dialog-body img{max-width:100%;height:auto}.report-dialog-body ul,.report-dialog-body ol{margin:.5em 0;padding-left:1.5em}.report-dialog-body code{font-size:.8em;background:var(--color-hover);padding:.15em .35em;border-radius:4px}.report-dialog-body pre{background:var(--color-bg);border-radius:var(--radius-sm);padding:var(--space-sm);overflow-x:auto;margin:.5em 0}.report-dialog-body pre code{background:none;padding:0}.report-dialog-body table{border-collapse:collapse;width:100%;margin:.5em 0}.report-dialog-body th,.report-dialog-body td{border:1px solid var(--color-border);padding:.35em .6em;text-align:left}.report-dialog-body th{background:var(--color-bg);font-weight:600}.report-dialog-body--frame{display:flex;padding:0}.file-viewer-frame{flex:1;width:100%;border:none;background:#fff}.file-viewer-text{margin:0;white-space:pre-wrap;word-break:break-word;font-size:.75rem}.file-viewer-loading{display:flex;flex-direction:column;gap:var(--space-sm)}.file-viewer-message{color:var(--color-text-secondary)}.file-viewer-download{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-sm)}.md-file-link{cursor:pointer}.md-file-image{max-width:100%;height:auto;cursor:zoom-in}.md-image-loading{display:block;width:100%;max-width:240px;height:6rem}.md-image-missing{font-style:italic;color:var(--color-text-secondary)}.chat-typing-indicator{display:flex;align-items:center;gap:4px;padding:4px 0}.chat-typing-indicator span{width:6px;height:6px;border-radius:50%;background:var(--color-muted);animation:chat-typing 1.4s infinite}.chat-typing-indicator span:nth-child(2){animation-delay:.2s}.chat-typing-indicator span:nth-child(3){animation-delay:.4s}@keyframes chat-typing{0%,60%,to{opacity:.3;transform:scale(.8)}30%{opacity:1;transform:scale(1)}}.pair-consent{font-size:.75rem;color:var(--color-muted);text-align:center;line-height:1.5}.pair-consent a{color:var(--color-muted);text-decoration:underline}.pair-consent a:hover{color:var(--color-text-secondary)}.dashboard-content{display:flex;flex-direction:column;flex:1;min-height:100dvh;max-width:800px;margin:0 auto;width:100%}.drawer-panel-desktop{position:sticky;top:0;height:100dvh;width:320px;min-width:320px;background:var(--color-surface);border-right:1px solid var(--color-border);display:flex;flex-direction:column;overflow-y:auto;overscroll-behavior:contain;animation:none}@media(min-width:768px){.dashboard{flex-direction:row}.dashboard-main{padding:var(--space-lg)}.fab{right:max(var(--space-lg),calc((100vw - 1080px)/2))}}.swipe-row{position:relative;overflow:hidden;touch-action:pan-y}.swipe-row-action{position:absolute;top:0;right:0;bottom:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;background:var(--color-error, #dc2626);color:#fff;border:none;font-size:.75rem;font-weight:600;cursor:pointer;padding:0}.swipe-row-action--hidden{visibility:hidden;transition:visibility 0s linear .2s}.swipe-row-action-label{font-size:.75rem;line-height:1}.swipe-row-action:focus-visible{outline:2px solid var(--color-accent, #2E5CE5);outline-offset:-4px}.swipe-row-content{position:relative;background:var(--color-surface);transition:transform .2s cubic-bezier(.22,1,.36,1);will-change:transform}.swipe-row-content-dragging{transition:none}.app-filter-help{margin-top:var(--space-xs)}.app-filter-trigger{display:inline-block;padding:0;font-size:.875rem;font-weight:500;text-align:left}.app-filter-selected{display:inline-flex;align-items:center;gap:var(--space-sm);padding:6px 10px;border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);max-width:100%}.app-filter-selected-name{font-size:.9375rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-filter-selected-pkg{font-size:.75rem;color:var(--color-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-filter-selected-clear{display:flex;align-items:center;justify-content:center;width:22px;height:22px;padding:0;margin-left:auto;border:none;background:none;color:var(--color-muted);font-size:.875rem;cursor:pointer;border-radius:var(--radius-sm)}.app-filter-selected-clear:hover{background:var(--color-border-subtle);color:var(--color-text)}.app-filter-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--color-overlay);display:flex;align-items:center;justify-content:center;z-index:1100;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);padding:var(--space-md)}.app-filter-dialog{background:var(--color-surface);width:100%;max-width:480px;max-height:80dvh;border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);display:flex;flex-direction:column;overflow:hidden}.app-filter-header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-md) var(--space-lg);border-bottom:1px solid var(--color-border)}.app-filter-header h2{margin:0;font-size:1rem;font-weight:600}.app-filter-close{display:flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:none;background:none;color:var(--color-muted);cursor:pointer;border-radius:var(--radius-sm)}.app-filter-close:hover{background:var(--color-border-subtle);color:var(--color-text)}.app-filter-search{margin:var(--space-md) var(--space-lg) 0;width:calc(100% - 2 * var(--space-lg))}.app-filter-list{flex:1;overflow-y:auto;list-style:none;margin:0;padding:var(--space-xs) 0 var(--space-md)}.app-filter-row{display:flex;align-items:center;gap:var(--space-md);padding:10px var(--space-lg);cursor:pointer;-webkit-user-select:none;user-select:none}.app-filter-row:hover{background:var(--color-hover, rgba(0, 0, 0, .04))}.app-filter-row-labels{display:flex;flex-direction:column;min-width:0;flex:1}.app-filter-row-name{font-size:.9375rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-filter-row-pkg{font-size:.75rem;color:var(--color-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-filter-empty{padding:var(--space-lg);text-align:center;color:var(--color-muted);font-size:.875rem}.app-filter-skeleton{cursor:default}.app-filter-skeleton-bar{flex:1;height:14px;border-radius:4px;background:linear-gradient(90deg,var(--color-border-subtle) 25%,var(--color-border) 50%,var(--color-border-subtle) 75%);background-size:200% 100%;animation:appFilterShimmer 1.2s ease-in-out infinite}@keyframes appFilterShimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.pair-setup{min-height:100dvh;display:flex;justify-content:center;padding:var(--space-lg);background:var(--color-bg)}.pair-setup-inner{width:100%;max-width:480px;display:flex;flex-direction:column;gap:var(--space-lg)}.pair-setup-title{margin:0;font-size:1.25rem;font-weight:600;line-height:1.35}.pair-setup-description{margin:0;color:var(--color-muted);font-size:.875rem}.pair-setup-loading{padding:var(--space-xl, 32px) var(--space-lg);min-height:200px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-md);color:var(--color-muted);font-size:.95rem}.pair-setup-actions{margin-top:var(--space-md)}