palmier 0.9.25 → 0.9.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,14 +11,9 @@ Palmier installs, manages, and runs AI agent CLIs (Claude Code, Gemini CLI, Code
11
11
  The control surface is bidirectional:
12
12
 
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
- * **Agents → phone:** agents can call MCP tools to read device state (location, calendar, contacts, notifications, SMS, battery) and trigger actions (push notifications, full-screen alarms, SMS, email, contact/calendar writes, ringer mode).
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 MCP tool is gated behind an Android permission and a per-host toggle. An optional yolo mode auto-approves agent input/confirmation requests.
17
-
18
- It is not:
19
-
20
- * an agent runtime itself — Palmier shells out to the agent CLI and streams its stdio
21
- * a system for driving phone UI like a human tapping through apps — phone access is via OS-level APIs (FCM data messages, content providers, calendar/contacts APIs), not UI automation
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.
22
17
 
23
18
  ## Quick Start
24
19
 
@@ -34,7 +29,7 @@ It is not:
34
29
  powershell -c "irm https://palmier.me/install.ps1 | iex"
35
30
  ```
36
31
 
37
- The one-liner installs Node.js 24+ if needed (via [fnm](https://github.com/Schniz/fnm) on Linux/macOS, winget on Windows), installs `palmier` globally, and runs the setup wizard. If you already have Node.js 24+ and npm:
32
+ The one-liner installs Node.js 24+ if needed, installs `palmier` globally, and runs the setup wizard. If you already have Node.js 24+ and npm:
38
33
  ```bash
39
34
  npm install -g palmier && palmier init
40
35
  ```
@@ -187,7 +187,7 @@ function runAgentAuthFlow(label, command, args) {
187
187
  console.log(`Re-run ${cyan(cmd)} manually after this.\n`);
188
188
  return;
189
189
  }
190
- console.log(green(`Successfully authenticated ${label}.\n`));
190
+ console.log(green(`Finished authenticating ${label}.\n`));
191
191
  }
192
192
  async function waitForEnter(message) {
193
193
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
package/dist/mcp-tools.js CHANGED
@@ -1,8 +1,10 @@
1
+ import * as fs from "fs";
1
2
  import { StringCodec } from "nats";
2
3
  import { registerPending } from "./pending-requests.js";
3
4
  import { getLinkedDevice } from "./linked-device.js";
4
5
  import { getNotifications, onNotificationsChanged } from "./notification-store.js";
5
6
  import { getSmsMessages, onSmsChanged } from "./sms-store.js";
7
+ import { getTaskDir, readHistory, spliceUserMessage } from "./task.js";
6
8
  export class ToolError extends Error {
7
9
  statusCode;
8
10
  constructor(message, statusCode = 500) {
@@ -84,7 +86,9 @@ const requestInputTool = {
84
86
  input_questions: questions,
85
87
  });
86
88
  const response = await pendingPromise;
87
- if (response.length === 1 && response[0] === "aborted") {
89
+ const aborted = response.length === 1 && response[0] === "aborted";
90
+ recordUserInputToRun(ctx, questions, response, aborted);
91
+ if (aborted) {
88
92
  await ctx.publishEvent("_input", {
89
93
  event_type: "input-resolved", host_id: ctx.config.hostId,
90
94
  session_id: ctx.sessionId, status: "aborted",
@@ -98,6 +102,40 @@ const requestInputTool = {
98
102
  return { values: response };
99
103
  },
100
104
  };
105
+ /**
106
+ * Splice the user's answer into the task run file so the conversation history
107
+ * shows what the user typed, independent of whether the agent echoes it.
108
+ * Only writes when sessionId resolves to a task with an active run (i.e. the
109
+ * agent called via the REST endpoint with taskId); silently skips for pure
110
+ * MCP-protocol sessions where sessionId is a random UUID.
111
+ */
112
+ function recordUserInputToRun(ctx, questions, response, aborted) {
113
+ const taskId = ctx.sessionId;
114
+ if (!taskId)
115
+ return;
116
+ const taskDir = getTaskDir(ctx.config.projectRoot, taskId);
117
+ if (!fs.existsSync(taskDir))
118
+ return;
119
+ const { entries } = readHistory(ctx.config.projectRoot, { task_id: taskId, limit: 1 });
120
+ const runId = entries[0]?.run_id;
121
+ if (!runId)
122
+ return;
123
+ const content = aborted
124
+ ? "Cancel"
125
+ : questions.map((q, i) => `**${q}**\n\n${response[i] ?? ""}`).join("\n\n");
126
+ try {
127
+ spliceUserMessage(taskDir, runId, {
128
+ role: "user",
129
+ time: Date.now(),
130
+ content,
131
+ type: "input",
132
+ });
133
+ void ctx.publishEvent(taskId, { event_type: "result-updated", run_id: runId });
134
+ }
135
+ catch {
136
+ // Run file missing or unwritable — best-effort, do not fail the tool call.
137
+ }
138
+ }
101
139
  const requestConfirmationTool = {
102
140
  name: "request-confirmation",
103
141
  description: [