palmier 0.6.7 → 0.6.8

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 (68) hide show
  1. package/README.md +14 -0
  2. package/dist/agents/agent-instructions.md +7 -37
  3. package/dist/agents/aider.js +1 -1
  4. package/dist/agents/claude.js +1 -1
  5. package/dist/agents/cline.js +1 -1
  6. package/dist/agents/codex.js +1 -1
  7. package/dist/agents/copilot.js +1 -1
  8. package/dist/agents/cursor.js +1 -1
  9. package/dist/agents/deepagents.js +1 -1
  10. package/dist/agents/droid.js +1 -1
  11. package/dist/agents/gemini.js +1 -1
  12. package/dist/agents/goose.js +1 -1
  13. package/dist/agents/hermes.js +1 -1
  14. package/dist/agents/kimi.js +1 -1
  15. package/dist/agents/kiro.js +1 -1
  16. package/dist/agents/openclaw.js +1 -1
  17. package/dist/agents/opencode.js +1 -1
  18. package/dist/agents/qoder.js +1 -1
  19. package/dist/agents/qwen.js +1 -1
  20. package/dist/agents/shared-prompt.d.ts +3 -2
  21. package/dist/agents/shared-prompt.js +6 -4
  22. package/dist/commands/run.js +2 -5
  23. package/dist/mcp-handler.js +1 -1
  24. package/dist/mcp-tools.d.ts +6 -1
  25. package/dist/mcp-tools.js +72 -6
  26. package/dist/pwa/assets/{index-DAI3J-jU.css → index-C6Lz09EY.css} +1 -1
  27. package/dist/pwa/assets/{index-RrJvjqz9.js → index-C8vJwUNi.js} +42 -42
  28. package/dist/pwa/assets/{web-DQteXlI7.js → web-6UChJFov.js} +1 -1
  29. package/dist/pwa/assets/{web-EzNEHXEh.js → web-NxTETXZK.js} +1 -1
  30. package/dist/pwa/index.html +2 -2
  31. package/dist/pwa/service-worker.js +1 -1
  32. package/dist/rpc-handler.js +0 -1
  33. package/dist/spawn-command.js +3 -1
  34. package/dist/transports/http-transport.js +4 -5
  35. package/package.json +1 -1
  36. package/palmier-server/README.md +1 -1
  37. package/palmier-server/pwa/src/App.css +9 -0
  38. package/palmier-server/pwa/src/components/TaskCard.tsx +36 -8
  39. package/palmier-server/pwa/src/components/TaskForm.tsx +63 -53
  40. package/palmier-server/pwa/src/constants.ts +1 -1
  41. package/palmier-server/spec.md +1 -1
  42. package/src/agents/agent-instructions.md +7 -37
  43. package/src/agents/aider.ts +1 -1
  44. package/src/agents/claude.ts +1 -1
  45. package/src/agents/cline.ts +1 -1
  46. package/src/agents/codex.ts +1 -1
  47. package/src/agents/copilot.ts +1 -1
  48. package/src/agents/cursor.ts +1 -1
  49. package/src/agents/deepagents.ts +1 -1
  50. package/src/agents/droid.ts +1 -1
  51. package/src/agents/gemini.ts +1 -1
  52. package/src/agents/goose.ts +1 -1
  53. package/src/agents/hermes.ts +1 -1
  54. package/src/agents/kimi.ts +1 -1
  55. package/src/agents/kiro.ts +1 -1
  56. package/src/agents/openclaw.ts +1 -1
  57. package/src/agents/opencode.ts +1 -1
  58. package/src/agents/qoder.ts +1 -1
  59. package/src/agents/qwen.ts +1 -1
  60. package/src/agents/shared-prompt.ts +7 -4
  61. package/src/commands/run.ts +2 -5
  62. package/src/mcp-handler.ts +1 -1
  63. package/src/mcp-tools.ts +78 -7
  64. package/src/rpc-handler.ts +0 -1
  65. package/src/spawn-command.ts +3 -1
  66. package/src/transports/http-transport.ts +4 -5
  67. package/test/agent-instructions.test.ts +68 -5
  68. package/test/fixtures/agent-instructions-snapshot.md +58 -0
package/README.md CHANGED
@@ -36,6 +36,20 @@ It runs on your machine as a background daemon and connects to a mobile-friendly
36
36
 
37
37
  Palmier runs as a background daemon (systemd on Linux, Task Scheduler on Windows). It invokes your agent CLIs directly, schedules tasks via native OS timers, and exposes an API that the PWA connects to — either directly over HTTP or remotely through a relay server. Agents can interact with the user's mobile device during execution — requesting input, sending push notifications, and fetching GPS location.
38
38
 
39
+ ### MCP Server
40
+
41
+ Palmier exposes an [MCP](https://modelcontextprotocol.io) server at `http://localhost:<port>/mcp` (streamable HTTP transport). MCP-capable agents can register it to get tool definitions automatically. The same tools are also available as REST endpoints for curl-based agents.
42
+
43
+ **MCP server URL:** `http://localhost:<port>/mcp`
44
+
45
+ **Available tools:**
46
+ | Tool | Description |
47
+ |------|-------------|
48
+ | `notify` | Send a push notification to the user's device |
49
+ | `request-input` | Request input from the user (blocks until response) |
50
+ | `request-confirmation` | Request confirmation from the user (blocks until response) |
51
+ | `device-geolocation` | Get GPS location of the user's mobile device |
52
+
39
53
  ```
40
54
  ┌──────────────┐ HTTP ┌──────────────────┐
41
55
  │ │◄──────────────────────│ │
@@ -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
+
@@ -11,7 +11,7 @@ export class Aider {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = [];
16
16
  if (yolo) {
17
17
  args.push("--yes-always");
@@ -11,7 +11,7 @@ export class ClaudeAgent {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = ["--permission-mode", yolo ? "bypassPermissions" : "acceptEdits", "-p"];
16
16
  if (!yolo) {
17
17
  args.push("--allowedTools", "WebFetch");
@@ -11,7 +11,7 @@ export class Cline {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = [];
16
16
  if (yolo) {
17
17
  args.push("--yolo");
@@ -11,7 +11,7 @@ export class CodexAgent {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = ["exec", "--skip-git-repo-check", "--sandbox", yolo ? "danger-full-access" : "workspace-write"];
16
16
  if (!yolo) {
17
17
  const allPerms = [...(task.frontmatter.permissions ?? []), ...(extraPermissions ?? [])];
@@ -11,7 +11,7 @@ export class CopilotAgent {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = ["-p", prompt];
16
16
  if (yolo) {
17
17
  args.push("--yolo");
@@ -11,7 +11,7 @@ export class Cursor {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = [];
16
16
  if (yolo) {
17
17
  args.push("--force");
@@ -11,7 +11,7 @@ export class DeepAgents {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = [];
16
16
  if (yolo) {
17
17
  args.push("--auto-approve");
@@ -11,7 +11,7 @@ export class DroidAgent {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = ["exec", "--session-id", task.frontmatter.id];
16
16
  if (yolo) {
17
17
  args.push("--skip-permissions-unsafe");
@@ -11,7 +11,7 @@ export class GeminiAgent {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = ["--approval-mode", yolo ? "yolo" : "auto_edit"];
16
16
  if (!yolo) {
17
17
  const tools = ["run_shell_command", "web_fetch"];
@@ -11,7 +11,7 @@ export class GooseAgent {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = ["run"];
16
16
  if (followupPrompt) {
17
17
  args.push("--resume");
@@ -11,7 +11,7 @@ export class Hermes {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = ["chat"];
16
16
  if (yolo) {
17
17
  args.push("--trust-all-tools");
@@ -11,7 +11,7 @@ export class KimiAgent {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = [];
16
16
  if (yolo) {
17
17
  args.push("--yolo");
@@ -11,7 +11,7 @@ export class Kiro {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = [];
16
16
  if (yolo) {
17
17
  args.push("--trust-all-tools");
@@ -10,7 +10,7 @@ export class OpenClawAgent {
10
10
  }
11
11
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
12
12
  const yolo = extraPermissions === "yolo";
13
- const prompt = followupPrompt ?? (getAgentInstructions(task.frontmatter.id, yolo || !this.supportsPermissions) + "\n\n" + (task.body || task.frontmatter.user_prompt));
13
+ const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
14
14
  // OpenClaw does not support stdin as prompt.
15
15
  const args = ["agent", "--local", "--session-id", task.frontmatter.id, "--message", prompt];
16
16
  return { command: "openclaw", args };
@@ -11,7 +11,7 @@ export class OpenCodeAgent {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = ["run"];
16
16
  if (yolo) {
17
17
  args.push("--dangerously-skip-permissions");
@@ -11,7 +11,7 @@ export class Qoder {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = [];
16
16
  if (yolo) {
17
17
  args.push("--yolo");
@@ -11,7 +11,7 @@ export class QwenAgent {
11
11
  }
12
12
  getTaskRunCommandLine(task, followupPrompt, extraPermissions) {
13
13
  const yolo = extraPermissions === "yolo";
14
- 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);
15
15
  const args = ["--approval-mode", yolo ? "yolo" : "auto-edit"];
16
16
  if (followupPrompt) {
17
17
  args.push("-c");
@@ -1,7 +1,8 @@
1
+ import type { ParsedTask } from "../types.js";
1
2
  /**
2
- * Agent instructions with the serve daemon's HTTP port and task ID baked in.
3
+ * Build the full agent prompt: instructions + endpoint docs + task description.
3
4
  */
4
- export declare function getAgentInstructions(taskId: string, skipPermissions?: boolean): string;
5
+ export declare function getAgentInstructions(task: ParsedTask, skipPermissions?: boolean): string;
5
6
  export declare const TASK_SUCCESS_MARKER = "[PALMIER_TASK_SUCCESS]";
6
7
  export declare const TASK_FAILURE_MARKER = "[PALMIER_TASK_FAILURE]";
7
8
  export declare const TASK_REPORT_PREFIX = "[PALMIER_REPORT]";
@@ -2,16 +2,18 @@ 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";
5
6
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
7
  const AGENT_INSTRUCTIONS_TEMPLATE = fs.readFileSync(path.join(__dirname, "agent-instructions.md"), "utf-8");
7
8
  /**
8
- * Agent instructions with the serve daemon's HTTP port and task ID baked in.
9
+ * Build the full agent prompt: instructions + endpoint docs + task description.
9
10
  */
10
- export function getAgentInstructions(taskId, skipPermissions) {
11
+ export function getAgentInstructions(task, skipPermissions) {
11
12
  const port = loadConfig().httpPort ?? 9966;
13
+ const taskDescription = task.body || task.frontmatter.user_prompt;
12
14
  let instructions = AGENT_INSTRUCTIONS_TEMPLATE
13
- .replace(/\{\{PORT\}\}/g, String(port))
14
- .replace(/\{\{TASK_ID\}\}/g, taskId);
15
+ .replace(/\{\{ENDPOINT_DOCS\}\}/g, generateEndpointDocs(port, task.frontmatter.id))
16
+ .replace(/\{\{TASK_DESCRIPTION\}\}/g, taskDescription);
15
17
  if (skipPermissions) {
16
18
  instructions = instructions.replace(/## Permissions\r?\n[\s\S]*?(?=## |\r?\n---)/m, "");
17
19
  }
@@ -34,9 +34,6 @@ async function invokeAgentWithRetries(ctx, invokeTask) {
34
34
  }, 500);
35
35
  }
36
36
  const { command, args, stdin, env: agentEnv } = ctx.agent.getTaskRunCommandLine(invokeTask, undefined, ctx.task.frontmatter.yolo_mode ? "yolo" : ctx.transientPermissions);
37
- const truncate = (s, max = 100) => s.length > max ? s.slice(0, max) + "…" : s;
38
- const displayArgs = args.map((a) => truncate(a));
39
- console.log(`[invoke] ${command} ${displayArgs.join(" ")}${stdin ? ` (stdin: ${truncate(stdin, 100)})` : ""}`);
40
37
  const result = await spawnCommand(command, args, {
41
38
  cwd: getRunDir(ctx.taskDir, ctx.runId),
42
39
  env: { ...ctx.guiEnv, ...agentEnv, PALMIER_RUN_DIR: getRunDir(ctx.taskDir, ctx.runId), PALMIER_HTTP_PORT: String(ctx.config.httpPort ?? 9966) },
@@ -421,10 +418,10 @@ async function requestPermission(config, task, taskDir, requiredPermissions) {
421
418
  }
422
419
  async function requestConfirmation(config, task, taskDir) {
423
420
  const port = config.httpPort ?? 9966;
424
- const res = await fetch(`http://localhost:${port}/request-confirmation`, {
421
+ const res = await fetch(`http://localhost:${port}/request-confirmation?taskId=${encodeURIComponent(task.frontmatter.id)}`, {
425
422
  method: "POST",
426
423
  headers: { "Content-Type": "application/json" },
427
- body: JSON.stringify({ taskId: task.frontmatter.id, description: `Run task "${task.frontmatter.name || task.frontmatter.id}"?` }),
424
+ body: JSON.stringify({ description: `Run task "${task.frontmatter.name || task.frontmatter.id}"?` }),
428
425
  });
429
426
  const body = await res.json();
430
427
  if (typeof body.confirmed !== "boolean") {
@@ -68,7 +68,7 @@ export async function handleMcpRequest(body, sessionId, ctx) {
68
68
  body: rpcResult(id, {
69
69
  tools: agentTools.map((t) => ({
70
70
  name: t.name,
71
- description: t.description,
71
+ description: t.description.join(" "),
72
72
  inputSchema: t.inputSchema,
73
73
  })),
74
74
  }),
@@ -13,10 +13,15 @@ export interface ToolContext {
13
13
  }
14
14
  export interface ToolDefinition {
15
15
  name: string;
16
- description: string;
16
+ /** First line is the summary (used as endpoint header). Remaining lines become bullet points in docs. */
17
+ description: string[];
17
18
  inputSchema: object;
18
19
  handler: (args: Record<string, unknown>, ctx: ToolContext) => Promise<unknown>;
19
20
  }
20
21
  export declare const agentTools: ToolDefinition[];
21
22
  export declare const agentToolMap: Map<string, ToolDefinition>;
23
+ /**
24
+ * Generate the HTTP Endpoints markdown section for agent-instructions.md from the tool registry.
25
+ */
26
+ export declare function generateEndpointDocs(port: number, taskId: string): string;
22
27
  //# sourceMappingURL=mcp-tools.d.ts.map
package/dist/mcp-tools.js CHANGED
@@ -10,7 +10,10 @@ export class ToolError extends Error {
10
10
  }
11
11
  const notifyTool = {
12
12
  name: "notify",
13
- description: "Send a push notification to the user's device.",
13
+ description: [
14
+ "Send a push notification to the user's device.",
15
+ 'Response: `{"ok": true}` on success.',
16
+ ],
14
17
  inputSchema: {
15
18
  type: "object",
16
19
  properties: {
@@ -41,7 +44,12 @@ const notifyTool = {
41
44
  };
42
45
  const requestInputTool = {
43
46
  name: "request-input",
44
- description: "Request input from the user. The request blocks until the user responds.",
47
+ description: [
48
+ "Request input from the user.",
49
+ "The request blocks until the user responds.",
50
+ 'Response: `{"values": ["answer1", "answer2"]}` on success, or `{"aborted": true}` if the user declines.',
51
+ "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.",
52
+ ],
45
53
  inputSchema: {
46
54
  type: "object",
47
55
  properties: {
@@ -70,16 +78,26 @@ const requestInputTool = {
70
78
  });
71
79
  const response = await pendingPromise;
72
80
  if (response.length === 1 && response[0] === "aborted") {
73
- await ctx.publishEvent("_input", { event_type: "input-resolved", host_id: ctx.config.hostId, session_id: ctx.sessionId, status: "aborted" });
81
+ await ctx.publishEvent("_input", {
82
+ event_type: "input-resolved", host_id: ctx.config.hostId,
83
+ session_id: ctx.sessionId, status: "aborted",
84
+ });
74
85
  return { aborted: true };
75
86
  }
76
- await ctx.publishEvent("_input", { event_type: "input-resolved", host_id: ctx.config.hostId, session_id: ctx.sessionId, status: "provided" });
87
+ await ctx.publishEvent("_input", {
88
+ event_type: "input-resolved", host_id: ctx.config.hostId,
89
+ session_id: ctx.sessionId, status: "provided",
90
+ });
77
91
  return { values: response };
78
92
  },
79
93
  };
80
94
  const requestConfirmationTool = {
81
95
  name: "request-confirmation",
82
- description: "Request confirmation from the user. The request blocks until the user confirms or aborts.",
96
+ description: [
97
+ "Request confirmation from the user.",
98
+ "The request blocks until the user confirms or aborts.",
99
+ 'Response: `{"confirmed": true}` or `{"confirmed": false}`.',
100
+ ],
83
101
  inputSchema: {
84
102
  type: "object",
85
103
  properties: {
@@ -112,7 +130,12 @@ const requestConfirmationTool = {
112
130
  };
113
131
  const deviceGeolocationTool = {
114
132
  name: "device-geolocation",
115
- description: "Get the GPS location of the user's mobile device. Blocks until the device responds (up to 30 seconds).",
133
+ description: [
134
+ "Get the GPS location of the user's mobile device.",
135
+ "When you need the user's real-time location, use this endpoint.",
136
+ "Blocks until the device responds (up to 30 seconds).",
137
+ 'Response: `{"latitude": ..., "longitude": ..., "accuracy": ..., "timestamp": ...}` on success, or `{"error": "..."}` on failure.',
138
+ ],
116
139
  inputSchema: {
117
140
  type: "object",
118
141
  properties: {},
@@ -149,4 +172,47 @@ const deviceGeolocationTool = {
149
172
  };
150
173
  export const agentTools = [notifyTool, requestInputTool, requestConfirmationTool, deviceGeolocationTool];
151
174
  export const agentToolMap = new Map(agentTools.map((t) => [t.name, t]));
175
+ /**
176
+ * Generate the HTTP Endpoints markdown section for agent-instructions.md from the tool registry.
177
+ */
178
+ export function generateEndpointDocs(port, taskId) {
179
+ const baseUrl = `http://localhost:${port}`;
180
+ const lines = [
181
+ `The following HTTP endpoints are available during task execution. Use curl to call them.`,
182
+ "",
183
+ ];
184
+ for (const tool of agentTools) {
185
+ const schema = tool.inputSchema;
186
+ const props = schema.properties ?? {};
187
+ const required = new Set(schema.required ?? []);
188
+ // Build example JSON (body only, no taskId)
189
+ const example = {};
190
+ for (const [key, prop] of Object.entries(props)) {
191
+ if (prop.type === "array")
192
+ example[key] = ["..."];
193
+ else
194
+ example[key] = "...";
195
+ }
196
+ const queryUrl = `${baseUrl}/${tool.name}?taskId=${taskId}`;
197
+ const [header, ...details] = tool.description;
198
+ lines.push(`**\`POST ${queryUrl}\`** — ${header}`);
199
+ if (Object.keys(example).length > 0) {
200
+ lines.push("```json");
201
+ lines.push(JSON.stringify(example));
202
+ lines.push("```");
203
+ }
204
+ for (const [key, prop] of Object.entries(props)) {
205
+ const req = required.has(key) ? "required" : "optional";
206
+ let typeStr = prop.type ?? "unknown";
207
+ if (prop.type === "array" && prop.items?.type)
208
+ typeStr = `${prop.items.type} array`;
209
+ lines.push(`- \`${key}\` (${req}, ${typeStr}): ${prop.description ?? ""}`);
210
+ }
211
+ for (const detail of details) {
212
+ lines.push(`- ${detail}`);
213
+ }
214
+ lines.push("");
215
+ }
216
+ return lines.join("\n").trimEnd();
217
+ }
152
218
  //# sourceMappingURL=mcp-tools.js.map