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.
- package/dist/agents/agent.d.ts +2 -2
- package/dist/agents/aider.d.ts +1 -1
- package/dist/agents/aider.js +2 -5
- package/dist/agents/claude.d.ts +1 -1
- package/dist/agents/claude.js +2 -5
- package/dist/agents/cline.d.ts +1 -1
- package/dist/agents/cline.js +2 -5
- package/dist/agents/codex.d.ts +1 -1
- package/dist/agents/codex.js +2 -5
- package/dist/agents/copilot.d.ts +1 -1
- package/dist/agents/copilot.js +2 -5
- package/dist/agents/cursor.d.ts +1 -1
- package/dist/agents/cursor.js +2 -5
- package/dist/agents/deepagents.d.ts +1 -1
- package/dist/agents/deepagents.js +2 -5
- package/dist/agents/droid.d.ts +1 -1
- package/dist/agents/droid.js +2 -5
- package/dist/agents/gemini.d.ts +1 -1
- package/dist/agents/gemini.js +2 -5
- package/dist/agents/goose.d.ts +1 -1
- package/dist/agents/goose.js +2 -5
- package/dist/agents/hermes.d.ts +1 -1
- package/dist/agents/hermes.js +2 -5
- package/dist/agents/kimi.d.ts +1 -1
- package/dist/agents/kimi.js +2 -5
- package/dist/agents/kiro.d.ts +1 -1
- package/dist/agents/kiro.js +2 -5
- package/dist/agents/openclaw.d.ts +1 -1
- package/dist/agents/openclaw.js +2 -5
- package/dist/agents/opencode.d.ts +1 -1
- package/dist/agents/opencode.js +2 -5
- package/dist/agents/qoder.d.ts +1 -1
- package/dist/agents/qoder.js +2 -5
- package/dist/agents/qwen.d.ts +1 -1
- package/dist/agents/qwen.js +2 -5
- package/dist/agents/shared-prompt.js +1 -1
- package/dist/commands/run.js +1 -2
- package/dist/mcp-tools.d.ts +1 -1
- package/dist/mcp-tools.js +2 -2
- package/dist/pwa/assets/{index-C8vJwUNi.js → index-CZejk2al.js} +1 -1
- package/dist/pwa/assets/{web-NxTETXZK.js → web-C48txJFl.js} +1 -1
- package/dist/pwa/assets/{web-6UChJFov.js → web-zj8Blync.js} +1 -1
- package/dist/pwa/index.html +1 -1
- package/dist/pwa/service-worker.js +1 -1
- package/dist/rpc-handler.js +27 -67
- package/dist/task.js +2 -3
- package/dist/types.d.ts +0 -1
- package/package.json +2 -2
- package/palmier-server/pwa/src/constants.ts +1 -1
- package/src/agents/agent.ts +2 -2
- package/src/agents/aider.ts +2 -5
- package/src/agents/claude.ts +2 -5
- package/src/agents/cline.ts +2 -5
- package/src/agents/codex.ts +2 -5
- package/src/agents/copilot.ts +2 -5
- package/src/agents/cursor.ts +2 -5
- package/src/agents/deepagents.ts +2 -5
- package/src/agents/droid.ts +2 -5
- package/src/agents/gemini.ts +2 -5
- package/src/agents/goose.ts +2 -5
- package/src/agents/hermes.ts +2 -5
- package/src/agents/kimi.ts +2 -5
- package/src/agents/kiro.ts +2 -5
- package/src/agents/openclaw.ts +2 -5
- package/src/agents/opencode.ts +2 -5
- package/src/agents/qoder.ts +2 -5
- package/src/agents/qwen.ts +2 -5
- package/src/agents/shared-prompt.ts +1 -1
- package/src/commands/run.ts +1 -2
- package/src/mcp-tools.ts +2 -2
- package/src/rpc-handler.ts +29 -71
- package/src/task.ts +2 -3
- package/src/types.ts +0 -1
- package/test/agent-instructions.test.ts +84 -19
- package/test/agent-output-parsing.test.ts +1 -0
- package/test/task-parsing.test.ts +3 -3
- package/dist/commands/plan-generation.md +0 -22
- package/src/commands/plan-generation.md +0 -22
- 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,
|
|
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
|
|
78
|
-
for (const tool of
|
|
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
|
|
101
|
-
if (tool.
|
|
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
|
});
|
|
@@ -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.
|
|
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.
|
|
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
|