palmier 0.6.7 → 0.6.9

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 (94) hide show
  1. package/README.md +14 -0
  2. package/dist/agents/agent-instructions.md +7 -37
  3. package/dist/agents/agent.d.ts +2 -2
  4. package/dist/agents/aider.d.ts +1 -1
  5. package/dist/agents/aider.js +3 -6
  6. package/dist/agents/claude.d.ts +1 -1
  7. package/dist/agents/claude.js +3 -6
  8. package/dist/agents/cline.d.ts +1 -1
  9. package/dist/agents/cline.js +3 -6
  10. package/dist/agents/codex.d.ts +1 -1
  11. package/dist/agents/codex.js +3 -6
  12. package/dist/agents/copilot.d.ts +1 -1
  13. package/dist/agents/copilot.js +3 -6
  14. package/dist/agents/cursor.d.ts +1 -1
  15. package/dist/agents/cursor.js +3 -6
  16. package/dist/agents/deepagents.d.ts +1 -1
  17. package/dist/agents/deepagents.js +3 -6
  18. package/dist/agents/droid.d.ts +1 -1
  19. package/dist/agents/droid.js +3 -6
  20. package/dist/agents/gemini.d.ts +1 -1
  21. package/dist/agents/gemini.js +3 -6
  22. package/dist/agents/goose.d.ts +1 -1
  23. package/dist/agents/goose.js +3 -6
  24. package/dist/agents/hermes.d.ts +1 -1
  25. package/dist/agents/hermes.js +3 -6
  26. package/dist/agents/kimi.d.ts +1 -1
  27. package/dist/agents/kimi.js +3 -6
  28. package/dist/agents/kiro.d.ts +1 -1
  29. package/dist/agents/kiro.js +3 -6
  30. package/dist/agents/openclaw.d.ts +1 -1
  31. package/dist/agents/openclaw.js +3 -6
  32. package/dist/agents/opencode.d.ts +1 -1
  33. package/dist/agents/opencode.js +3 -6
  34. package/dist/agents/qoder.d.ts +1 -1
  35. package/dist/agents/qoder.js +3 -6
  36. package/dist/agents/qwen.d.ts +1 -1
  37. package/dist/agents/qwen.js +3 -6
  38. package/dist/agents/shared-prompt.d.ts +3 -2
  39. package/dist/agents/shared-prompt.js +6 -4
  40. package/dist/commands/run.js +3 -7
  41. package/dist/mcp-handler.js +1 -1
  42. package/dist/mcp-tools.d.ts +6 -1
  43. package/dist/mcp-tools.js +72 -6
  44. package/dist/pwa/assets/{index-DAI3J-jU.css → index-C6Lz09EY.css} +1 -1
  45. package/dist/pwa/assets/{index-RrJvjqz9.js → index-CZejk2al.js} +42 -42
  46. package/dist/pwa/assets/{web-EzNEHXEh.js → web-C48txJFl.js} +1 -1
  47. package/dist/pwa/assets/{web-DQteXlI7.js → web-zj8Blync.js} +1 -1
  48. package/dist/pwa/index.html +2 -2
  49. package/dist/pwa/service-worker.js +1 -1
  50. package/dist/rpc-handler.js +27 -68
  51. package/dist/spawn-command.js +3 -1
  52. package/dist/task.js +2 -3
  53. package/dist/transports/http-transport.js +4 -5
  54. package/dist/types.d.ts +0 -1
  55. package/package.json +2 -2
  56. package/palmier-server/README.md +1 -1
  57. package/palmier-server/pwa/src/App.css +9 -0
  58. package/palmier-server/pwa/src/components/TaskCard.tsx +36 -8
  59. package/palmier-server/pwa/src/components/TaskForm.tsx +63 -53
  60. package/palmier-server/pwa/src/constants.ts +1 -1
  61. package/palmier-server/spec.md +1 -1
  62. package/src/agents/agent-instructions.md +7 -37
  63. package/src/agents/agent.ts +2 -2
  64. package/src/agents/aider.ts +3 -6
  65. package/src/agents/claude.ts +3 -6
  66. package/src/agents/cline.ts +3 -6
  67. package/src/agents/codex.ts +3 -6
  68. package/src/agents/copilot.ts +3 -6
  69. package/src/agents/cursor.ts +3 -6
  70. package/src/agents/deepagents.ts +3 -6
  71. package/src/agents/droid.ts +3 -6
  72. package/src/agents/gemini.ts +3 -6
  73. package/src/agents/goose.ts +3 -6
  74. package/src/agents/hermes.ts +3 -6
  75. package/src/agents/kimi.ts +3 -6
  76. package/src/agents/kiro.ts +3 -6
  77. package/src/agents/openclaw.ts +3 -6
  78. package/src/agents/opencode.ts +3 -6
  79. package/src/agents/qoder.ts +3 -6
  80. package/src/agents/qwen.ts +3 -6
  81. package/src/agents/shared-prompt.ts +7 -4
  82. package/src/commands/run.ts +3 -7
  83. package/src/mcp-handler.ts +1 -1
  84. package/src/mcp-tools.ts +78 -7
  85. package/src/rpc-handler.ts +29 -72
  86. package/src/spawn-command.ts +3 -1
  87. package/src/task.ts +2 -3
  88. package/src/transports/http-transport.ts +4 -5
  89. package/src/types.ts +0 -1
  90. package/test/agent-instructions.test.ts +137 -9
  91. package/test/agent-output-parsing.test.ts +1 -0
  92. package/test/task-parsing.test.ts +3 -3
  93. package/dist/commands/plan-generation.md +0 -22
  94. package/src/commands/plan-generation.md +0 -22
@@ -12,7 +12,7 @@ The host supports **Linux** (systemd) and **Windows** (Task Scheduler for both d
12
12
 
13
13
  ### 1.2 Components
14
14
 
15
- * **Host Binary (Node.js):** Runs persistently on the user's host machine as a NATS + HTTP RPC handler. Manages file system operations (task CRUD), OS-level scheduling (systemd), and task generation. Provides a CLI with commands: `palmier init` (provisioning), `palmier pair` (generate pairing code for device pairing), `palmier clients` (manage client tokens), `palmier run <task-id>` (executes a task via the configured agent tool), `palmier uninstall` (stop daemon and remove all scheduled tasks), and `palmier serve` (persistent RPC handler, default command). The `serve` process always starts a local HTTP server (bound to `127.0.0.1` by default, or `0.0.0.0` if LAN mode is enabled) alongside the NATS transport. Localhost-only HTTP endpoints (`/notify`, `/request-input`, `/request-confirmation`, `/request-permission`, `/device-geolocation`) are used by agents and the `palmier run` process for interactive flows via held HTTP connections. `/request-input` and `/notify` are task-independent (no `taskId` required) input requests use an internal `requestId` for routing. `palmier run` is a short-lived process invoked by systemd. Task execution is abstracted through an `AgentTool` interface (`src/agents/agent.ts`) so different AI CLI tools can be supported — each agent implements `getPlanGenerationCommandLine()`, `getTaskRunCommandLine()`, and `init()`. The task's `agent` field (e.g., `"claude"`) selects which agent is used.
15
+ * **Host Binary (Node.js):** Runs persistently on the user's host machine as a NATS + HTTP RPC handler. Manages file system operations (task CRUD), OS-level scheduling (systemd), and task generation. Provides a CLI with commands: `palmier init` (provisioning), `palmier pair` (generate pairing code for device pairing), `palmier clients` (manage client tokens), `palmier run <task-id>` (executes a task via the configured agent tool), `palmier uninstall` (stop daemon and remove all scheduled tasks), and `palmier serve` (persistent RPC handler, default command). The `serve` process always starts a local HTTP server (bound to `127.0.0.1` by default, or `0.0.0.0` if LAN mode is enabled) alongside the NATS transport. Exposes a localhost-only MCP server at `/mcp` (streamable HTTP transport) with tools: `notify`, `request-input`, `request-confirmation`, `device-geolocation`. The same tools are auto-generated as REST endpoints (`/notify`, `/request-input`, etc.) from a shared tool registry zero duplication. REST endpoints require `taskId` in the body for session identification. `/request-permission` remains a separate endpoint (not part of the MCP tool registry). MCP sessions track agent names from `initialize` clientInfo for logging and UI display. `palmier run` is a short-lived process invoked by systemd. Task execution is abstracted through an `AgentTool` interface (`src/agents/agent.ts`) so different AI CLI tools can be supported — each agent implements `getPlanGenerationCommandLine()`, `getTaskRunCommandLine()`, and `init()`. The task's `agent` field (e.g., `"claude"`) selects which agent is used.
16
16
 
17
17
  * **Web Server (Node.js):** Serves the PWA assets (React) via `app.palmier.me` (Cloudflare proxied), manages Web Push VAPID keys, and provides host registration. Uses **PostgreSQL** for persistent storage (host registrations, push subscriptions, FCM tokens). Connects to NATS via TCP to subscribe to `host-event.>` for sending push notifications (confirmations, dismissals, completion/failure). For `POST /api/push/respond` (confirmation responses via push notification action buttons), the Web Server forwards the response to the host via the `task.user_input` NATS RPC. Subscribes to `host.*.push.send` NATS subjects to relay push notification requests from the host CLI. Subscribes to `host.*.fcm.geolocation` to relay device geolocation requests via FCM. Co-located with the NATS server on the same machine.
18
18
 
@@ -1,4 +1,4 @@
1
- You are an AI agent executing a task on behalf of the user via the Palmier platform. Follow these instructions carefully.
1
+ You are an AI agent executing a task on behalf of the user. Follow these instructions carefully.
2
2
 
3
3
  ## Reporting Output
4
4
 
@@ -13,46 +13,16 @@ When you are done, output exactly one of these markers as the very last line (no
13
13
 
14
14
  ## Permissions
15
15
 
16
- If the task fails because a tool was denied or you lack the required permissions, print each required permission on its own line using this exact format:
16
+ Whenever a tool you are trying to use is denied or you lack the required permissions, print each required permission on its own line using this exact format:
17
17
  [PALMIER_PERMISSION] <tool_name> | <description>
18
18
 
19
19
  ## HTTP Endpoints
20
20
 
21
- The following HTTP endpoints are available at http://localhost:{{PORT}} during task execution. Use curl to call them. All endpoints require `taskId` in the request body.
22
-
23
- **`POST /request-input`** — Request input from the user. The request blocks until the user responds.
24
- ```json
25
- {"taskId": "{{TASK_ID}}", "description": "optional context", "questions": ["question 1", "question 2"]}
26
- ```
27
- - `taskId` (required, string): The current task ID.
28
- - `questions` (required, string array): Questions to present to the user.
29
- - `description` (optional, string): Context or heading for the input request.
30
- - Response: `{"values": ["answer1", "answer2"]}` on success, or `{"aborted": true}` if the user declines.
31
- - When you need information from the user (credentials, answers to questions, preferences, clarifications, etc.), do not guess, fail, or prompt via stdout, even in a non-interactive environment. Use this endpoint instead.
32
-
33
- **`POST /request-confirmation`** — Request confirmation from the user. The request blocks until the user confirms or aborts.
34
- ```json
35
- {"taskId": "{{TASK_ID}}", "description": "What the user is confirming"}
36
- ```
37
- - `taskId` (required, string): The current task ID.
38
- - `description` (required, string): What the user is confirming.
39
- - Response: `{"confirmed": true}` or `{"confirmed": false}`.
40
-
41
- **`POST /device-geolocation`** — Get the GPS location of the user's mobile device. Blocks until the device responds (up to 30 seconds).
42
- ```json
43
- {"taskId": "{{TASK_ID}}"}
44
- ```
45
- - `taskId` (required, string): The current task ID.
46
- - Response: `{"latitude": ..., "longitude": ..., "accuracy": ..., "timestamp": ...}` on success, or `{"error": "..."}` on failure.
47
-
48
- **`POST /notify`** — Send a push notification to the user's device.
49
- ```json
50
- {"taskId": "{{TASK_ID}}", "title": "...", "body": "..."}
51
- ```
52
- - `taskId` (required, string): The current task ID.
53
- - `title` (required, string): Notification title.
54
- - `body` (required, string): Notification body.
21
+ {{ENDPOINT_DOCS}}
22
+
23
+ The task to execute follows below:
55
24
 
56
25
  ---
57
26
 
58
- The task to execute follows below.
27
+ {{TASK_DESCRIPTION}}
28
+
@@ -31,8 +31,8 @@ export interface CommandLine {
31
31
  * Abstracts how plans are generated and tasks are executed across different AI agents.
32
32
  */
33
33
  export interface AgentTool {
34
- /** Return the command and args used to generate a plan from a prompt. */
35
- getPlanGenerationCommandLine(prompt: string): CommandLine;
34
+ /** Return the command and args for a short, non-interactive prompt (e.g. generating a task name). */
35
+ getPromptCommandLine(prompt: string): CommandLine;
36
36
 
37
37
  /** Return the command and args used to run a task. If followupPrompt is provided, use it instead of the task's prompt,
38
38
  * and treat it as a continuation of the original run (reuse the same session, etc).
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class Aider implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "aider",
12
- args: ["--message", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "aider", args: ["--message", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = [];
20
17
 
21
18
  if (yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class ClaudeAgent implements AgentTool {
8
8
  supportsPermissions = true;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "claude",
12
- args: ["-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "claude", args: ["-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = ["--permission-mode", yolo ? "bypassPermissions" : "acceptEdits", "-p"];
20
17
 
21
18
  if (!yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class Cline implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "cline ",
12
- args: ["--yolo", "-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "cline ", args: ["--yolo", "-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = [];
20
17
 
21
18
  if (yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class CodexAgent implements AgentTool {
8
8
  supportsPermissions = true;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "codex",
12
- args: ["exec", "--skip-git-repo-check", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "codex", args: ["exec", "--skip-git-repo-check", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = ["exec", "--skip-git-repo-check", "--sandbox", yolo ? "danger-full-access" : "workspace-write"];
20
17
 
21
18
  if (!yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class CopilotAgent implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "copilot",
12
- args: ["-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "copilot", args: ["-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = ["-p", prompt];
20
17
 
21
18
  if (yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class Cursor implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "cursor",
12
- args: ["-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "cursor", args: ["-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = [];
20
17
 
21
18
  if (yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class DeepAgents implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "deepagents",
12
- args: ["--non-interactive", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "deepagents", args: ["--non-interactive", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = [];
20
17
 
21
18
  if (yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class DroidAgent implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "droid",
12
- args: ["exec", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "droid", args: ["exec", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = ["exec", "--session-id", task.frontmatter.id];
20
17
 
21
18
  if (yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class GeminiAgent implements AgentTool {
8
8
  supportsPermissions = true;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "gemini",
12
- args: ["--prompt", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "gemini", args: ["--prompt", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = ["--approval-mode", yolo ? "yolo" : "auto_edit"];
20
17
 
21
18
  if (!yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class GooseAgent implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "goose",
12
- args: ["run", "--text", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "goose", args: ["run", "--text", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = ["run"];
20
17
 
21
18
  if (followupPrompt) {args.push("--resume");} // continue mode for followups
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class Hermes implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "hermes",
12
- args: ["chat", "-q", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "hermes", args: ["chat", "-q", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = ["chat"];
20
17
 
21
18
  if (yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class KimiAgent implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "kimi",
12
- args: ["-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "kimi", args: ["-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = [];
20
17
 
21
18
  if (yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class Kiro implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "kiro-cli",
12
- args: ["--no-interactive", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "kiro-cli", args: ["--no-interactive", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = [];
20
17
 
21
18
  if (yolo) {
@@ -5,16 +5,13 @@ import { getAgentInstructions } from "./shared-prompt.js";
5
5
 
6
6
  export class OpenClawAgent implements AgentTool {
7
7
  supportsPermissions = false;
8
- getPlanGenerationCommandLine(prompt: string): CommandLine {
9
- return {
10
- command: "openclaw",
11
- args: ["agent", "--local", "--agent", "main", "--message", prompt],
12
- };
8
+ getPromptCommandLine(prompt: string): CommandLine {
9
+ return { command: "openclaw", args: ["agent", "--local", "--agent", "main", "--message", prompt] };
13
10
  }
14
11
 
15
12
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
16
13
  const yolo = extraPermissions === "yolo";
17
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
14
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
18
15
  // OpenClaw does not support stdin as prompt.
19
16
  const args = ["agent", "--local", "--session-id", task.frontmatter.id, "--message", prompt];
20
17
 
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class OpenCodeAgent implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "opencode",
12
- args: ["run", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "opencode", args: ["run", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = ["run"];
20
17
 
21
18
  if (yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class Qoder implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "qodercli",
12
- args: ["-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "qodercli", args: ["-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = [];
20
17
 
21
18
  if (yolo) {
@@ -6,16 +6,13 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class QwenAgent implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "qwen",
12
- args: ["-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "qwen", args: ["-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
17
14
  const yolo = extraPermissions === "yolo";
18
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
15
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
19
16
  const args = ["--approval-mode", yolo ? "yolo" : "auto-edit"];
20
17
 
21
18
  if (followupPrompt) { args.push("-c"); }
@@ -2,6 +2,8 @@ import * as fs from "fs";
2
2
  import * as path from "path";
3
3
  import { fileURLToPath } from "url";
4
4
  import { loadConfig } from "../config.js";
5
+ import { generateEndpointDocs } from "../mcp-tools.js";
6
+ import type { ParsedTask } from "../types.js";
5
7
 
6
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
9
 
@@ -11,13 +13,14 @@ const AGENT_INSTRUCTIONS_TEMPLATE = fs.readFileSync(
11
13
  );
12
14
 
13
15
  /**
14
- * Agent instructions with the serve daemon's HTTP port and task ID baked in.
16
+ * Build the full agent prompt: instructions + endpoint docs + task description.
15
17
  */
16
- export function getAgentInstructions(taskId: string, skipPermissions?: boolean): string {
18
+ export function getAgentInstructions(task: ParsedTask, skipPermissions?: boolean): string {
17
19
  const port = loadConfig().httpPort ?? 9966;
20
+ const taskDescription = task.frontmatter.user_prompt;
18
21
  let instructions = AGENT_INSTRUCTIONS_TEMPLATE
19
- .replace(/\{\{PORT\}\}/g, String(port))
20
- .replace(/\{\{TASK_ID\}\}/g, taskId);
22
+ .replace(/\{\{ENDPOINT_DOCS\}\}/g, generateEndpointDocs(port, task.frontmatter.id))
23
+ .replace(/\{\{TASK_DESCRIPTION\}\}/g, taskDescription);
21
24
  if (skipPermissions) {
22
25
  instructions = instructions.replace(/## Permissions\r?\n[\s\S]*?(?=## |\r?\n---)/m, "");
23
26
  }
@@ -65,9 +65,6 @@ async function invokeAgentWithRetries(
65
65
  const { command, args, stdin, env: agentEnv } = ctx.agent.getTaskRunCommandLine(
66
66
  invokeTask, undefined, ctx.task.frontmatter.yolo_mode ? "yolo" : ctx.transientPermissions,
67
67
  );
68
- const truncate = (s: string, max = 100) => s.length > max ? s.slice(0, max) + "…" : s;
69
- const displayArgs = args.map((a) => truncate(a));
70
- console.log(`[invoke] ${command} ${displayArgs.join(" ")}${stdin ? ` (stdin: ${truncate(stdin, 100)})` : ""}`);
71
68
  const result = await spawnCommand(command, args, {
72
69
  cwd: getRunDir(ctx.taskDir, ctx.runId),
73
70
  env: { ...ctx.guiEnv, ...agentEnv, PALMIER_RUN_DIR: getRunDir(ctx.taskDir, ctx.runId), PALMIER_HTTP_PORT: String(ctx.config.httpPort ?? 9966) },
@@ -275,7 +272,7 @@ export async function runCommand(taskId: string): Promise<void> {
275
272
  await appendAndNotify(ctx, {
276
273
  role: "user",
277
274
  time: Date.now(),
278
- content: task.body || task.frontmatter.user_prompt,
275
+ content: task.frontmatter.user_prompt,
279
276
  });
280
277
 
281
278
  const result = await invokeAgentWithRetries(ctx, task);
@@ -365,7 +362,6 @@ async function runCommandTriggeredMode(
365
362
  const perLinePrompt = `${ctx.task.frontmatter.user_prompt}\n\nProcess this input:\n${line}`;
366
363
  const perLineTask: ParsedTask = {
367
364
  frontmatter: { ...ctx.task.frontmatter, user_prompt: perLinePrompt },
368
- body: "",
369
365
  };
370
366
 
371
367
  const result = await invokeAgentWithRetries(ctx, perLineTask);
@@ -516,10 +512,10 @@ async function requestConfirmation(
516
512
  taskDir: string,
517
513
  ): Promise<boolean> {
518
514
  const port = config.httpPort ?? 9966;
519
- const res = await fetch(`http://localhost:${port}/request-confirmation`, {
515
+ const res = await fetch(`http://localhost:${port}/request-confirmation?taskId=${encodeURIComponent(task.frontmatter.id)}`, {
520
516
  method: "POST",
521
517
  headers: { "Content-Type": "application/json" },
522
- body: JSON.stringify({ taskId: task.frontmatter.id, description: `Run task "${task.frontmatter.name || task.frontmatter.id}"?` }),
518
+ body: JSON.stringify({ description: `Run task "${task.frontmatter.name || task.frontmatter.id}"?` }),
523
519
  });
524
520
  const body = await res.json() as { confirmed?: boolean; error?: string };
525
521
  if (typeof body.confirmed !== "boolean") {
@@ -90,7 +90,7 @@ export async function handleMcpRequest(body: string, sessionId: string | undefin
90
90
  body: rpcResult(id, {
91
91
  tools: agentTools.map((t) => ({
92
92
  name: t.name,
93
- description: t.description,
93
+ description: t.description.join(" "),
94
94
  inputSchema: t.inputSchema,
95
95
  })),
96
96
  }),