palmier 1.1.24 → 1.1.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.
@@ -1 +1 @@
1
- import{W as i}from"./index-CXiDfnsf.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-CPtDlrQZ.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 +1 @@
1
- import{W as t}from"./index-CXiDfnsf.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-CPtDlrQZ.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-CXiDfnsf.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-CPtDlrQZ.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};
@@ -8,7 +8,7 @@
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-CXiDfnsf.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-CPtDlrQZ.js"></script>
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-DWgTuvMe.css">
13
13
  <link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
14
14
  <body>
@@ -1,12 +1,13 @@
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, createPendingRun, readPendingRun, 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, writeUserReply, 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";
8
8
  import crossSpawn from "cross-spawn";
9
9
  import { getAgent, getPromptCommandLine, getNpmInstalledVersion } from "./agents/agent.js";
10
+ import { getResumePrompt } from "./agents/shared-prompt.js";
10
11
  import { validateClient, revokeClient } from "./client-store.js";
11
12
  import { publishHostEvent } from "./events.js";
12
13
  import { getLinkedDevice, setLinkedDevice, clearLinkedDevice, clearLinkedDeviceIfMatches } from "./linked-device.js";
@@ -413,6 +414,15 @@ export function createRpcHandler(config) {
413
414
  const followupTaskDir = getTaskDir(config.projectRoot, params.id);
414
415
  const followupTask = parseTaskFile(followupTaskDir);
415
416
  const followupRunDir = getRunDir(followupTaskDir, params.run_id);
417
+ const followupPrevStatus = readTaskStatus(followupTaskDir);
418
+ if (followupPrevStatus?.running_state === "needs_input"
419
+ && followupPrevStatus.run_id === params.run_id
420
+ && getPlatform().isTaskRunning(params.id)) {
421
+ if (!writeUserReply(followupRunDir, params.message)) {
422
+ return { error: "A reply is already being delivered for this run" };
423
+ }
424
+ return { ok: true, task_id: params.id, run_id: params.run_id };
425
+ }
416
426
  appendRunMessage(followupTaskDir, params.run_id, {
417
427
  role: "user",
418
428
  time: Date.now(),
@@ -431,7 +441,7 @@ export function createRpcHandler(config) {
431
441
  name: taskDisplayName(followupTask.frontmatter), run_id: params.run_id,
432
442
  });
433
443
  const followupAgent = getAgent(followupTask.frontmatter.agent);
434
- const { args: cmdArgs, stdin, env: followupAgentEnv, files: followupFiles } = followupAgent.getTaskRunCommandLine(followupTask, params.message);
444
+ const { args: cmdArgs, stdin, env: followupAgentEnv, files: followupFiles } = followupAgent.getTaskRunCommandLine(followupTask, getResumePrompt(params.message));
435
445
  const cmd = followupAgent.command;
436
446
  if (followupFiles) {
437
447
  for (const f of followupFiles)
package/dist/task.d.ts CHANGED
@@ -17,6 +17,9 @@ export interface FollowupStatus {
17
17
  export declare function writeFollowupStatus(runDir: string, status: FollowupStatus): void;
18
18
  export declare function readFollowupStatus(runDir: string): FollowupStatus | undefined;
19
19
  export declare function deleteFollowupStatus(runDir: string): void;
20
+ /** Returns false if an earlier reply hasn't been picked up yet. */
21
+ export declare function writeUserReply(runDir: string, message: string): boolean;
22
+ export declare function takeUserReply(runDir: string): string | undefined;
20
23
  export declare function isRunStarred(runDir: string): boolean;
21
24
  export declare function setRunStarred(runDir: string, starred: boolean): void;
22
25
  /** Returns the run ID (timestamp string used as directory name). */
@@ -50,15 +53,6 @@ export declare class StreamingMessageWriter {
50
53
  write(chunk: string): void;
51
54
  end(): void;
52
55
  }
53
- /**
54
- * Splice a user message into a running assistant stream: close the current
55
- * assistant block, write the user message, open a new assistant block. Direct
56
- * appends only, so an existing StreamingMessageWriter keeps working — its
57
- * subsequent chunks land in the new block.
58
- */
59
- export declare function spliceUserMessage(taskDir: string, runId: string, userMsg: ConversationMessage,
60
- /** Optional text to append to the current assistant block before ending it. */
61
- assistantAppend?: string): void;
62
56
  export declare function readRunMessages(taskDir: string, runId: string): ConversationMessage[];
63
57
  export declare function appendHistory(projectRoot: string, entry: HistoryEntry): void;
64
58
  export declare function deleteHistoryEntry(projectRoot: string, taskId: string, runId: string): boolean;
package/dist/task.js CHANGED
@@ -132,6 +132,35 @@ export function deleteFollowupStatus(runDir) {
132
132
  }
133
133
  catch { /* ignore */ }
134
134
  }
135
+ // A file rather than a daemon request, so a run waiting on the user survives a daemon restart.
136
+ const USER_REPLY_FILE = ".user-reply";
137
+ /** Returns false if an earlier reply hasn't been picked up yet. */
138
+ export function writeUserReply(runDir, message) {
139
+ try {
140
+ fs.writeFileSync(path.join(runDir, USER_REPLY_FILE), message, { encoding: "utf-8", flag: "wx" });
141
+ return true;
142
+ }
143
+ catch (err) {
144
+ if (err.code === "EEXIST")
145
+ return false;
146
+ throw err;
147
+ }
148
+ }
149
+ export function takeUserReply(runDir) {
150
+ const filePath = path.join(runDir, USER_REPLY_FILE);
151
+ let reply;
152
+ try {
153
+ reply = fs.readFileSync(filePath, "utf-8");
154
+ }
155
+ catch {
156
+ return undefined;
157
+ }
158
+ // Empty means the writer has created the file but not filled it yet.
159
+ if (!reply)
160
+ return undefined;
161
+ fs.unlinkSync(filePath);
162
+ return reply;
163
+ }
135
164
  // A marker file rather than TASKRUN.md frontmatter, which a running agent process appends to concurrently.
136
165
  const STARRED_MARKER = ".starred";
137
166
  export function isRunStarred(runDir) {
@@ -243,24 +272,6 @@ export class StreamingMessageWriter {
243
272
  fs.appendFileSync(this.filePath, "\n\n", "utf-8");
244
273
  }
245
274
  }
246
- /**
247
- * Splice a user message into a running assistant stream: close the current
248
- * assistant block, write the user message, open a new assistant block. Direct
249
- * appends only, so an existing StreamingMessageWriter keeps working — its
250
- * subsequent chunks land in the new block.
251
- */
252
- export function spliceUserMessage(taskDir, runId, userMsg,
253
- /** Optional text to append to the current assistant block before ending it. */
254
- assistantAppend) {
255
- const filePath = path.join(taskDir, runId, "TASKRUN.md");
256
- if (assistantAppend) {
257
- fs.appendFileSync(filePath, assistantAppend, "utf-8");
258
- }
259
- fs.appendFileSync(filePath, "\n\n", "utf-8");
260
- appendRunMessage(taskDir, runId, userMsg);
261
- const delimiter = `<!-- palmier:message role="assistant" time="${Date.now()}" stream="stdout" -->`;
262
- fs.appendFileSync(filePath, `${delimiter}\n\n`, "utf-8");
263
- }
264
275
  export function readRunMessages(taskDir, runId) {
265
276
  const raw = fs.readFileSync(path.join(taskDir, runId, "TASKRUN.md"), "utf-8");
266
277
  const fmMatch = raw.match(/^---\n[\s\S]*?\n---\n([\s\S]*)$/);
package/dist/types.d.ts CHANGED
@@ -55,13 +55,16 @@ export interface ParsedTask {
55
55
  * - `finished`: agent completed successfully
56
56
  * - `aborted`: user declined confirmation or input
57
57
  * - `failed`: agent exited with an error
58
+ * - `needs_input`: agent stopped to ask the user something and will resume on their reply
58
59
  */
59
- export type TaskRunningState = "started" | "finished" | "aborted" | "failed";
60
+ export type TaskRunningState = "started" | "finished" | "aborted" | "failed" | "needs_input";
60
61
  export interface TaskStatus {
61
62
  running_state: TaskRunningState;
62
63
  time_stamp: number;
63
64
  /** PID of the palmier run process (used on Windows to kill the process tree). */
64
65
  pid?: number;
66
+ /** The run a live `palmier run` process is holding open for the user's reply. */
67
+ run_id?: string;
65
68
  }
66
69
  export interface HistoryEntry {
67
70
  task_id: string;
@@ -71,7 +74,7 @@ export interface ConversationMessage {
71
74
  role: "assistant" | "user" | "status";
72
75
  time: number;
73
76
  content: string;
74
- type?: "input" | "confirmation" | "started" | "finished" | "failed" | "aborted" | "stopped" | "error";
77
+ type?: "input" | "confirmation" | "started" | "finished" | "failed" | "aborted" | "stopped" | "error" | "needs_input";
75
78
  /** Only present on runs recorded by older hosts; never written anymore. */
76
79
  attachments?: string[];
77
80
  /** For assistant messages, identifies which stream the agent CLI emitted this segment on.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "palmier",
3
- "version": "1.1.24",
3
+ "version": "1.1.26",
4
4
  "description": "Palmier host CLI - provisions, executes tasks, and serves NATS RPC",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Hongxu Cai",
@@ -1,2 +0,0 @@
1
- import type { AgentTool } from "./agent.js";
2
- export declare const aiderAgent: AgentTool;
@@ -1,13 +0,0 @@
1
- import { getAgentInstructions } from "./shared-prompt.js";
2
- export const aiderAgent = {
3
- label: "Aider",
4
- command: "aider",
5
- promptArgs: ["--message"],
6
- probeArg: "--version",
7
- getTaskRunCommandLine(task, followupPrompt) {
8
- const prompt = followupPrompt ?? getAgentInstructions(task);
9
- const args = ["--yes-always"];
10
- args.push("--message", prompt);
11
- return { args };
12
- },
13
- };
@@ -1,2 +0,0 @@
1
- import type { AgentTool } from "./agent.js";
2
- export declare const clineAgent: AgentTool;
@@ -1,14 +0,0 @@
1
- import { getAgentInstructions } from "./shared-prompt.js";
2
- export const clineAgent = {
3
- label: "Cline CLI",
4
- command: "cline",
5
- promptArgs: ["--yolo", "-p"],
6
- probeArg: "--version",
7
- npmPackage: "cline",
8
- getTaskRunCommandLine(task, followupPrompt) {
9
- const prompt = followupPrompt ?? getAgentInstructions(task);
10
- const args = ["--yolo"];
11
- args.push(prompt);
12
- return { args };
13
- },
14
- };