palmier 0.6.8 → 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 (79) hide show
  1. package/dist/agents/agent.d.ts +2 -2
  2. package/dist/agents/aider.d.ts +1 -1
  3. package/dist/agents/aider.js +2 -5
  4. package/dist/agents/claude.d.ts +1 -1
  5. package/dist/agents/claude.js +2 -5
  6. package/dist/agents/cline.d.ts +1 -1
  7. package/dist/agents/cline.js +2 -5
  8. package/dist/agents/codex.d.ts +1 -1
  9. package/dist/agents/codex.js +2 -5
  10. package/dist/agents/copilot.d.ts +1 -1
  11. package/dist/agents/copilot.js +2 -5
  12. package/dist/agents/cursor.d.ts +1 -1
  13. package/dist/agents/cursor.js +2 -5
  14. package/dist/agents/deepagents.d.ts +1 -1
  15. package/dist/agents/deepagents.js +2 -5
  16. package/dist/agents/droid.d.ts +1 -1
  17. package/dist/agents/droid.js +2 -5
  18. package/dist/agents/gemini.d.ts +1 -1
  19. package/dist/agents/gemini.js +2 -5
  20. package/dist/agents/goose.d.ts +1 -1
  21. package/dist/agents/goose.js +2 -5
  22. package/dist/agents/hermes.d.ts +1 -1
  23. package/dist/agents/hermes.js +2 -5
  24. package/dist/agents/kimi.d.ts +1 -1
  25. package/dist/agents/kimi.js +2 -5
  26. package/dist/agents/kiro.d.ts +1 -1
  27. package/dist/agents/kiro.js +2 -5
  28. package/dist/agents/openclaw.d.ts +1 -1
  29. package/dist/agents/openclaw.js +2 -5
  30. package/dist/agents/opencode.d.ts +1 -1
  31. package/dist/agents/opencode.js +2 -5
  32. package/dist/agents/qoder.d.ts +1 -1
  33. package/dist/agents/qoder.js +2 -5
  34. package/dist/agents/qwen.d.ts +1 -1
  35. package/dist/agents/qwen.js +2 -5
  36. package/dist/agents/shared-prompt.js +1 -1
  37. package/dist/commands/run.js +1 -2
  38. package/dist/mcp-tools.d.ts +1 -1
  39. package/dist/mcp-tools.js +2 -2
  40. package/dist/pwa/assets/{index-C8vJwUNi.js → index-CZejk2al.js} +1 -1
  41. package/dist/pwa/assets/{web-NxTETXZK.js → web-C48txJFl.js} +1 -1
  42. package/dist/pwa/assets/{web-6UChJFov.js → web-zj8Blync.js} +1 -1
  43. package/dist/pwa/index.html +1 -1
  44. package/dist/pwa/service-worker.js +1 -1
  45. package/dist/rpc-handler.js +27 -67
  46. package/dist/task.js +2 -3
  47. package/dist/types.d.ts +0 -1
  48. package/package.json +2 -2
  49. package/palmier-server/pwa/src/constants.ts +1 -1
  50. package/src/agents/agent.ts +2 -2
  51. package/src/agents/aider.ts +2 -5
  52. package/src/agents/claude.ts +2 -5
  53. package/src/agents/cline.ts +2 -5
  54. package/src/agents/codex.ts +2 -5
  55. package/src/agents/copilot.ts +2 -5
  56. package/src/agents/cursor.ts +2 -5
  57. package/src/agents/deepagents.ts +2 -5
  58. package/src/agents/droid.ts +2 -5
  59. package/src/agents/gemini.ts +2 -5
  60. package/src/agents/goose.ts +2 -5
  61. package/src/agents/hermes.ts +2 -5
  62. package/src/agents/kimi.ts +2 -5
  63. package/src/agents/kiro.ts +2 -5
  64. package/src/agents/openclaw.ts +2 -5
  65. package/src/agents/opencode.ts +2 -5
  66. package/src/agents/qoder.ts +2 -5
  67. package/src/agents/qwen.ts +2 -5
  68. package/src/agents/shared-prompt.ts +1 -1
  69. package/src/commands/run.ts +1 -2
  70. package/src/mcp-tools.ts +2 -2
  71. package/src/rpc-handler.ts +29 -71
  72. package/src/task.ts +2 -3
  73. package/src/types.ts +0 -1
  74. package/test/agent-instructions.test.ts +84 -19
  75. package/test/agent-output-parsing.test.ts +1 -0
  76. package/test/task-parsing.test.ts +3 -3
  77. package/dist/commands/plan-generation.md +0 -22
  78. package/src/commands/plan-generation.md +0 -22
  79. package/test/fixtures/agent-instructions-snapshot.md +0 -58
@@ -3,18 +3,57 @@ import assert from "node:assert/strict";
3
3
  import * as fs from "fs";
4
4
  import * as path from "path";
5
5
  import { fileURLToPath } from "url";
6
- import { generateEndpointDocs, agentTools } from "../src/mcp-tools.js";
6
+ import { generateEndpointDocs, type ToolDefinition } from "../src/mcp-tools.js";
7
7
 
8
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
9
  const templatePath = path.join(__dirname, "..", "src", "agents", "agent-instructions.md");
10
10
  const template = fs.readFileSync(templatePath, "utf-8");
11
11
 
12
+ /** Mock tools with a known, stable shape for testing */
13
+ const mockTools: ToolDefinition[] = [
14
+ {
15
+ name: "mock-action",
16
+ description: [
17
+ "Perform a mock action.",
18
+ 'Response: `{"ok": true}` on success.',
19
+ ],
20
+ inputSchema: {
21
+ type: "object",
22
+ properties: {
23
+ title: { type: "string", description: "Action title" },
24
+ detail: { type: "string", description: "Optional detail" },
25
+ },
26
+ required: ["title"],
27
+ },
28
+ handler: async () => ({ ok: true }),
29
+ },
30
+ {
31
+ name: "mock-query",
32
+ description: [
33
+ "Query mock data from the device.",
34
+ "Blocks until the device responds.",
35
+ 'Response: `{"data": ...}` on success.',
36
+ ],
37
+ inputSchema: {
38
+ type: "object",
39
+ properties: {
40
+ tags: {
41
+ type: "array",
42
+ items: { type: "string" },
43
+ description: "Filter tags",
44
+ },
45
+ },
46
+ },
47
+ handler: async () => ({ data: [] }),
48
+ },
49
+ ];
50
+
12
51
  /** Minimal replica of getAgentInstructions that doesn't need host.json */
13
- function buildInstructions(taskId: string, skipPermissions?: boolean): string {
52
+ function buildInstructions(taskId: string, opts?: { skipPermissions?: boolean }): string {
14
53
  let instructions = template
15
- .replace(/\{\{ENDPOINT_DOCS\}\}/g, generateEndpointDocs(9966, taskId))
54
+ .replace(/\{\{ENDPOINT_DOCS\}\}/g, generateEndpointDocs(9966, taskId, mockTools))
16
55
  .replace(/\{\{TASK_DESCRIPTION\}\}/g, "Test task prompt");
17
- if (skipPermissions) {
56
+ if (opts?.skipPermissions) {
18
57
  instructions = instructions.replace(/## Permissions\r?\n[\s\S]*?(?=## |\r?\n---)/m, "");
19
58
  }
20
59
  return instructions;
@@ -28,13 +67,13 @@ describe("getAgentInstructions", () => {
28
67
  });
29
68
 
30
69
  it("strips Permissions section when skipPermissions is true", () => {
31
- const result = buildInstructions("test-task-id", true);
70
+ const result = buildInstructions("test-task-id", { skipPermissions: true });
32
71
  assert.doesNotMatch(result, /## Permissions/);
33
72
  assert.doesNotMatch(result, /PALMIER_PERMISSION/);
34
73
  });
35
74
 
36
75
  it("preserves other sections when Permissions is stripped", () => {
37
- const result = buildInstructions("test-task-id", true);
76
+ const result = buildInstructions("test-task-id", { skipPermissions: true });
38
77
  assert.match(result, /## Reporting Output/);
39
78
  assert.match(result, /## Completion/);
40
79
  assert.match(result, /## HTTP Endpoints/);
@@ -60,22 +99,38 @@ describe("getAgentInstructions", () => {
60
99
  const result = buildInstructions("test");
61
100
  assert.match(result, /Test task prompt/);
62
101
  });
63
- });
64
102
 
65
- describe("full agent instruction snapshot", () => {
66
- it("matches the expected full text exactly", () => {
67
- const result = buildInstructions("test-task-id").replace(/\r\n/g, "\n").trimEnd();
68
- const snapshotPath = path.join(__dirname, "fixtures", "agent-instructions-snapshot.md");
69
- const expected = fs.readFileSync(snapshotPath, "utf-8").replace(/\r\n/g, "\n").trimEnd();
70
- assert.equal(result, expected);
71
- });
72
103
  });
73
104
 
105
+
74
106
  describe("generateEndpointDocs", () => {
75
- const docs = generateEndpointDocs(9966, "test-id");
107
+ const docs = generateEndpointDocs(9966, "test-id", mockTools);
108
+
109
+ it("matches expected full output", () => {
110
+ const expected = [
111
+ "The following HTTP endpoints are available during task execution. Use curl to call them.",
112
+ "",
113
+ "**`POST http://localhost:9966/mock-action?taskId=test-id`** — Perform a mock action.",
114
+ "```json",
115
+ '{"title":"...","detail":"..."}',
116
+ "```",
117
+ "- `title` (required, string): Action title",
118
+ "- `detail` (optional, string): Optional detail",
119
+ '- Response: `{"ok": true}` on success.',
120
+ "",
121
+ "**`POST http://localhost:9966/mock-query?taskId=test-id`** — Query mock data from the device.",
122
+ "```json",
123
+ '{"tags":["..."]}',
124
+ "```",
125
+ "- `tags` (optional, string array): Filter tags",
126
+ "- Blocks until the device responds.",
127
+ '- Response: `{"data": ...}` on success.',
128
+ ].join("\n");
129
+ assert.equal(docs, expected);
130
+ });
76
131
 
77
- it("generates docs for all MCP tools", () => {
78
- for (const tool of agentTools) {
132
+ it("generates docs for all provided tools", () => {
133
+ for (const tool of mockTools) {
79
134
  assert.match(docs, new RegExp(`POST http://localhost:9966/${tool.name}\\?taskId=`), `Missing endpoint for ${tool.name}`);
80
135
  }
81
136
  });
@@ -97,8 +152,8 @@ describe("generateEndpointDocs", () => {
97
152
  });
98
153
 
99
154
  it("includes response descriptions", () => {
100
- for (const tool of agentTools) {
101
- if (tool.responseDescription) {
155
+ for (const tool of mockTools) {
156
+ if (tool.description.length > 1) {
102
157
  assert.match(docs, /Response:/, `Missing response description for ${tool.name}`);
103
158
  }
104
159
  }
@@ -106,6 +161,16 @@ describe("generateEndpointDocs", () => {
106
161
 
107
162
  it("marks required and optional parameters correctly", () => {
108
163
  assert.match(docs, /\(required, string\)/);
164
+ // "detail" has no required entry, so it should be optional
109
165
  assert.match(docs, /\(optional, string\)/);
110
166
  });
167
+
168
+ it("handles array-type parameters", () => {
169
+ assert.match(docs, /\(optional, string array\)/);
170
+ assert.match(docs, /Filter tags/);
171
+ });
172
+
173
+ it("renders multi-line descriptions as bullet points", () => {
174
+ assert.match(docs, /- Blocks until the device responds\./);
175
+ });
111
176
  });
@@ -71,3 +71,4 @@ describe("parsePermissions", () => {
71
71
  });
72
72
  });
73
73
 
74
+
@@ -19,7 +19,7 @@ This is the task body.`;
19
19
  assert.equal(result.frontmatter.id, "abc123");
20
20
  assert.equal(result.frontmatter.name, "Test Task");
21
21
  assert.equal(result.frontmatter.agent, "claude");
22
- assert.equal(result.body, "This is the task body.");
22
+ assert.equal(result.frontmatter.user_prompt, "Do something");
23
23
  });
24
24
 
25
25
  it("defaults agent to claude when not specified", () => {
@@ -68,7 +68,7 @@ requires_confirmation: false
68
68
  assert.throws(() => parseTaskContent("---\nname: test\n---\n"), /must include at least: id/);
69
69
  });
70
70
 
71
- it("handles empty body", () => {
71
+ it("handles empty body gracefully", () => {
72
72
  const content = `---
73
73
  id: abc123
74
74
  user_prompt: test
@@ -78,6 +78,6 @@ requires_confirmation: false
78
78
  ---`;
79
79
 
80
80
  const result = parseTaskContent(content);
81
- assert.equal(result.body, "");
81
+ assert.equal(result.frontmatter.id, "abc123");
82
82
  });
83
83
  });
@@ -1,22 +0,0 @@
1
- You are a task planning assistant. Given a task description, produce a Markdown execution plan for an AI agent to follow. Do not execute any part of the plan yourself.
2
-
3
- ## Output Format
4
-
5
- Start with a YAML frontmatter block (no code fences), then the plan body:
6
-
7
- ---
8
- task_name: <concise label, 3-6 words>
9
- ---
10
-
11
- <plan body>
12
-
13
- ## Plan Body Guidelines
14
-
15
- - Write a numbered sequence of concrete, actionable steps.
16
- - If the task produces formatted output (report, email, summary, etc.), specify the structure, sections, and tone.
17
- - When a step requires user input, simply state what information is needed from the user. Do **not** specify how to obtain it — the agent has its own tool for requesting user input.
18
- - Preserve relative time expressions (e.g., "today", "yesterday", "last week") exactly as written — do **not** resolve them to specific dates. The plan may be executed on a different day than it was generated.
19
- - If the task involves opening a web browser or application, include a final step to close it before finishing.
20
-
21
- ## Task Description
22
-
@@ -1,22 +0,0 @@
1
- You are a task planning assistant. Given a task description, produce a Markdown execution plan for an AI agent to follow. Do not execute any part of the plan yourself.
2
-
3
- ## Output Format
4
-
5
- Start with a YAML frontmatter block (no code fences), then the plan body:
6
-
7
- ---
8
- task_name: <concise label, 3-6 words>
9
- ---
10
-
11
- <plan body>
12
-
13
- ## Plan Body Guidelines
14
-
15
- - Write a numbered sequence of concrete, actionable steps.
16
- - If the task produces formatted output (report, email, summary, etc.), specify the structure, sections, and tone.
17
- - When a step requires user input, simply state what information is needed from the user. Do **not** specify how to obtain it — the agent has its own tool for requesting user input.
18
- - Preserve relative time expressions (e.g., "today", "yesterday", "last week") exactly as written — do **not** resolve them to specific dates. The plan may be executed on a different day than it was generated.
19
- - If the task involves opening a web browser or application, include a final step to close it before finishing.
20
-
21
- ## Task Description
22
-
@@ -1,58 +0,0 @@
1
- You are an AI agent executing a task on behalf of the user. Follow these instructions carefully.
2
-
3
- ## Reporting Output
4
-
5
- If you generate report or output files, print each file path on its own line using this exact format:
6
- [PALMIER_REPORT] <filename>
7
-
8
- ## Completion
9
-
10
- When you are done, output exactly one of these markers as the very last line (no other text on the same line):
11
- [PALMIER_TASK_SUCCESS]
12
- [PALMIER_TASK_FAILURE]
13
-
14
- ## Permissions
15
-
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
- [PALMIER_PERMISSION] <tool_name> | <description>
18
-
19
- ## HTTP Endpoints
20
-
21
- The following HTTP endpoints are available during task execution. Use curl to call them.
22
-
23
- **`POST http://localhost:9966/notify?taskId=test-task-id`** — Send a push notification to the user's device.
24
- ```json
25
- {"title":"...","body":"..."}
26
- ```
27
- - `title` (required, string): Notification title
28
- - `body` (required, string): Notification body
29
- - Response: `{"ok": true}` on success.
30
-
31
- **`POST http://localhost:9966/request-input?taskId=test-task-id`** — Request input from the user.
32
- ```json
33
- {"description":"...","questions":["..."]}
34
- ```
35
- - `description` (optional, string): Context or heading for the input request
36
- - `questions` (required, string array): Questions to present to the user
37
- - The request blocks until the user responds.
38
- - Response: `{"values": ["answer1", "answer2"]}` on success, or `{"aborted": true}` if the user declines.
39
- - 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.
40
-
41
- **`POST http://localhost:9966/request-confirmation?taskId=test-task-id`** — Request confirmation from the user.
42
- ```json
43
- {"description":"..."}
44
- ```
45
- - `description` (required, string): What the user is confirming
46
- - The request blocks until the user confirms or aborts.
47
- - Response: `{"confirmed": true}` or `{"confirmed": false}`.
48
-
49
- **`POST http://localhost:9966/device-geolocation?taskId=test-task-id`** — Get the GPS location of the user's mobile device.
50
- - When you need the user's real-time location, use this endpoint.
51
- - Blocks until the device responds (up to 30 seconds).
52
- - Response: `{"latitude": ..., "longitude": ..., "accuracy": ..., "timestamp": ...}` on success, or `{"error": "..."}` on failure.
53
-
54
- The task to execute follows below:
55
-
56
- ---
57
-
58
- Test task prompt