macroclaw 0.44.0 → 0.46.0
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/package.json +1 -1
- package/src/claude.test.ts +1 -1
- package/src/claude.ts +1 -1
- package/src/orchestrator.test.ts +26 -0
- package/src/orchestrator.ts +6 -2
- package/src/prompt-builder.test.ts +15 -16
- package/src/prompt-builder.ts +20 -13
- package/workspace-template/.claude/skills/{schedule → schedule-event}/SKILL.md +1 -1
- package/workspace-template/CLAUDE.md +3 -1
package/package.json
CHANGED
package/src/claude.test.ts
CHANGED
|
@@ -479,7 +479,7 @@ describe("Claude factory", () => {
|
|
|
479
479
|
claude.newSession(textResult);
|
|
480
480
|
const args = spawnArgs();
|
|
481
481
|
expect(args).toContain("--disallowedTools");
|
|
482
|
-
expect(args).toContain("CronList,CronDelete,CronCreate,AskUserQuestion");
|
|
482
|
+
expect(args).toContain("CronList,CronDelete,CronCreate,AskUserQuestion,RemoteTrigger");
|
|
483
483
|
});
|
|
484
484
|
|
|
485
485
|
it("spawns with stdin: pipe, stdout: pipe, stderr: pipe", () => {
|
package/src/claude.ts
CHANGED
|
@@ -257,7 +257,7 @@ export class Claude {
|
|
|
257
257
|
"--input-format", "stream-json",
|
|
258
258
|
"--output-format", "stream-json",
|
|
259
259
|
"--verbose",
|
|
260
|
-
"--disallowedTools", "CronList,CronDelete,CronCreate,AskUserQuestion",
|
|
260
|
+
"--disallowedTools", "CronList,CronDelete,CronCreate,AskUserQuestion,RemoteTrigger",
|
|
261
261
|
];
|
|
262
262
|
|
|
263
263
|
if (mode.kind === "resume") {
|
package/src/orchestrator.test.ts
CHANGED
|
@@ -930,6 +930,32 @@ describe("Orchestrator", () => {
|
|
|
930
930
|
expect(callCount).toBeGreaterThanOrEqual(2);
|
|
931
931
|
});
|
|
932
932
|
|
|
933
|
+
it("passes action and actionReason through result XML for silent background agent", async () => {
|
|
934
|
+
saveSessions({ mainSessionId: "test-session" }, tmpSettingsDir);
|
|
935
|
+
const { process: bgProc, resolve: resolveBg } = pendingProcess("bg-sid");
|
|
936
|
+
|
|
937
|
+
let callCount = 0;
|
|
938
|
+
const claude = mockClaude((): ClaudeProcess<unknown> => {
|
|
939
|
+
callCount++;
|
|
940
|
+
if (callCount === 1) return bgProc;
|
|
941
|
+
return autoProcess({ action: "silent", actionReason: "nothing to report" }, `sid-${callCount}`);
|
|
942
|
+
});
|
|
943
|
+
const { orch } = makeOrchestrator(claude);
|
|
944
|
+
|
|
945
|
+
orch.handleBackgroundCommand("check something");
|
|
946
|
+
await waitForProcessing();
|
|
947
|
+
|
|
948
|
+
resolveBg({ action: "silent", actionReason: "no new results" });
|
|
949
|
+
await waitForProcessing(100);
|
|
950
|
+
|
|
951
|
+
const prompts = sentPrompts(claude);
|
|
952
|
+
const bgResultPrompt = prompts.find((p: string) => p?.includes("background-agent-result"));
|
|
953
|
+
expect(bgResultPrompt).toBeDefined();
|
|
954
|
+
expect(bgResultPrompt).toContain('action="silent"');
|
|
955
|
+
expect(bgResultPrompt).toContain('action-reason="no new results"');
|
|
956
|
+
expect(bgResultPrompt).not.toContain("<text>");
|
|
957
|
+
});
|
|
958
|
+
|
|
933
959
|
it("feeds error back to queue on spawn failure", async () => {
|
|
934
960
|
saveSessions({ mainSessionId: "test-session" }, tmpSettingsDir);
|
|
935
961
|
const { process: bgProc, reject: rejectBg } = pendingProcess("bg-sid");
|
package/src/orchestrator.ts
CHANGED
|
@@ -437,8 +437,12 @@ export class Orchestrator {
|
|
|
437
437
|
return this.#prompts.backgroundAgentResult(
|
|
438
438
|
name,
|
|
439
439
|
request.name,
|
|
440
|
-
{
|
|
441
|
-
|
|
440
|
+
{
|
|
441
|
+
action: request.response.action,
|
|
442
|
+
actionReason: request.response.actionReason,
|
|
443
|
+
text: request.response.message,
|
|
444
|
+
files: request.response.files,
|
|
445
|
+
},
|
|
442
446
|
{ backgroundedEvent },
|
|
443
447
|
);
|
|
444
448
|
case "background-agent-progress":
|
|
@@ -170,12 +170,12 @@ describe("backgroundAgentResult", () => {
|
|
|
170
170
|
const result = p.backgroundAgentResult(
|
|
171
171
|
"bg-research",
|
|
172
172
|
"research",
|
|
173
|
-
{ text: "found 3 papers" },
|
|
174
|
-
"Forward to user.",
|
|
173
|
+
{ action: "send", actionReason: "completed", text: "found 3 papers" },
|
|
175
174
|
);
|
|
176
175
|
expect(result).toContain('type="background-agent-result"');
|
|
177
176
|
expect(result).toContain('<original-event name="research" />');
|
|
178
|
-
expect(result).toContain("
|
|
177
|
+
expect(result).toContain('action="send"');
|
|
178
|
+
expect(result).toContain('action-reason="completed"');
|
|
179
179
|
expect(result).toContain("<text>found 3 papers</text>");
|
|
180
180
|
expect(result).toContain("</result>");
|
|
181
181
|
expect(result).not.toContain("<files>");
|
|
@@ -185,27 +185,26 @@ describe("backgroundAgentResult", () => {
|
|
|
185
185
|
const result = p.backgroundAgentResult(
|
|
186
186
|
"bg-research",
|
|
187
187
|
"research",
|
|
188
|
-
{ text: "here are the screenshots", files: ["/tmp/screenshot.png"] },
|
|
189
|
-
"Forward to user.",
|
|
188
|
+
{ action: "send", actionReason: "done", text: "here are the screenshots", files: ["/tmp/screenshot.png"] },
|
|
190
189
|
);
|
|
191
|
-
expect(result).toContain("
|
|
190
|
+
expect(result).toContain('action="send"');
|
|
192
191
|
expect(result).toContain("<text>here are the screenshots</text>");
|
|
193
192
|
expect(result).toContain('<file path="/tmp/screenshot.png" />');
|
|
194
193
|
expect(result).toContain("</result>");
|
|
195
194
|
});
|
|
196
195
|
|
|
197
|
-
it("
|
|
196
|
+
it("builds self-closing result for silent action", () => {
|
|
198
197
|
const result = p.backgroundAgentResult(
|
|
199
|
-
"bg-
|
|
200
|
-
"
|
|
201
|
-
{
|
|
202
|
-
"Forward to user.",
|
|
198
|
+
"bg-heartbeat",
|
|
199
|
+
"cron-heartbeat",
|
|
200
|
+
{ action: "silent", actionReason: "no new results" },
|
|
203
201
|
);
|
|
204
|
-
expect(result).toContain("
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
expect(
|
|
208
|
-
expect(
|
|
202
|
+
expect(result).toContain('action="silent"');
|
|
203
|
+
expect(result).toContain('action-reason="no new results"');
|
|
204
|
+
expect(result).toContain("<result ");
|
|
205
|
+
expect(result).toContain("/>");
|
|
206
|
+
expect(result).not.toContain("<text>");
|
|
207
|
+
expect(result).not.toContain("</result>");
|
|
209
208
|
});
|
|
210
209
|
});
|
|
211
210
|
|
package/src/prompt-builder.ts
CHANGED
|
@@ -25,7 +25,7 @@ Event format: every incoming message is wrapped in an <event> XML block. Attribu
|
|
|
25
25
|
- button-click — user tapped an inline button. Label in <button>.
|
|
26
26
|
- schedule-trigger — automated scheduled task. Contains <schedule> with name and optional missed-by/scheduled-at attributes. Prefer action="silent" when nothing noteworthy.
|
|
27
27
|
- background-agent-start — you are a background agent. Complete the task in <text> and return a result.
|
|
28
|
-
- background-agent-result — a background agent has finished. Contains <original-event name="..." /> linking to the agent that produced it, and a <result> block with <text> and
|
|
28
|
+
- background-agent-result — a background agent has finished. Contains <original-event name="..." /> linking to the agent that produced it, and a <result> block with action and action-reason attributes, plus optional <text> and <files>. If action="send", forward to the user (use action="send") — summarize or add context as appropriate. If action="silent", the agent had nothing to report — use action="silent" and do not send a message, but keep the context in mind.
|
|
29
29
|
- background-agent-progress — interim progress update from a still-running background agent. Contains <original-event name="..." /> and a <progress> element. This is NOT a final result. Do not report to the user unless it contains exceptionally important information (errors, blockers, urgent findings). Keep this context in mind — if the user later asks about progress of a background task, use the latest progress update to answer.
|
|
30
30
|
- peek — status check on a running session. Contains <target-event name="..." /> identifying the event being peeked at. Only consider progress since that event started. Respond with a brief status update (2-3 sentences): what has been done, what's happening now, what's remaining. Return plain text, not structured output.
|
|
31
31
|
- health-check — automated status check on a background agent. Contains <target-event name="..." />. Report whether the task is complete or still in progress.
|
|
@@ -46,7 +46,7 @@ Inner elements:
|
|
|
46
46
|
- <original-event name="..." /> — in background-agent-result, links to the agent that produced the result.
|
|
47
47
|
- <target-event name="..." /> — in peek, identifies the event being checked on.
|
|
48
48
|
- <progress> — interim status from a still-running background agent.
|
|
49
|
-
- <result> — wraps the output from a completed background agent. Contains <text> and
|
|
49
|
+
- <result> — wraps the output from a completed background agent. Attributes: action ("send"|"silent"), action-reason. Contains optional <text> and <files>.
|
|
50
50
|
- <instructions> — inline guidance for how to handle this specific event. Always follow these instructions.
|
|
51
51
|
|
|
52
52
|
Background agents: spawn alongside any response via backgroundAgents array:
|
|
@@ -78,7 +78,7 @@ interface BuildXmlFields {
|
|
|
78
78
|
targetEvent?: string;
|
|
79
79
|
instructions?: string;
|
|
80
80
|
progress?: string;
|
|
81
|
-
result?: {
|
|
81
|
+
result?: { action: string; actionReason: string; text?: string; files?: string[] };
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
export class PromptBuilder {
|
|
@@ -130,16 +130,23 @@ export class PromptBuilder {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
if (fields.result) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
lines.push(` <file path="${esc(f)}" />`);
|
|
133
|
+
const resultAttrs = [`action="${esc(fields.result.action)}" action-reason="${esc(fields.result.actionReason)}"`];
|
|
134
|
+
if (fields.result.text || fields.result.files?.length) {
|
|
135
|
+
lines.push(`<result ${resultAttrs.join(" ")}>`);
|
|
136
|
+
if (fields.result.text) {
|
|
137
|
+
lines.push(`<text>${esc(fields.result.text)}</text>`);
|
|
139
138
|
}
|
|
140
|
-
|
|
139
|
+
if (fields.result.files?.length) {
|
|
140
|
+
lines.push("<files>");
|
|
141
|
+
for (const f of fields.result.files) {
|
|
142
|
+
lines.push(` <file path="${esc(f)}" />`);
|
|
143
|
+
}
|
|
144
|
+
lines.push("</files>");
|
|
145
|
+
}
|
|
146
|
+
lines.push("</result>");
|
|
147
|
+
} else {
|
|
148
|
+
lines.push(`<result ${resultAttrs.join(" ")} />`);
|
|
141
149
|
}
|
|
142
|
-
lines.push("</result>");
|
|
143
150
|
}
|
|
144
151
|
|
|
145
152
|
if (fields.button) {
|
|
@@ -182,8 +189,8 @@ export class PromptBuilder {
|
|
|
182
189
|
return PromptBuilder.#buildXml(name, "background-agent-start", "background", this.#localTime(), { text });
|
|
183
190
|
}
|
|
184
191
|
|
|
185
|
-
backgroundAgentResult(name: string, originalEvent: string, result: {
|
|
186
|
-
return PromptBuilder.#buildXml(name, "background-agent-result", "main", this.#localTime(), { originalEvent, result,
|
|
192
|
+
backgroundAgentResult(name: string, originalEvent: string, result: { action: string; actionReason: string; text?: string; files?: string[] }, opts?: { backgroundedEvent?: string }): string {
|
|
193
|
+
return PromptBuilder.#buildXml(name, "background-agent-result", "main", this.#localTime(), { originalEvent, result, backgroundedEvent: opts?.backgroundedEvent });
|
|
187
194
|
}
|
|
188
195
|
|
|
189
196
|
backgroundAgentProgress(name: string, originalEvent: string, progress: string, instructions: string, opts?: { backgroundedEvent?: string }): string {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: schedule
|
|
2
|
+
name: schedule-event
|
|
3
3
|
description: "Schedule events, reminders, and recurring tasks. Use when the user wants to: set a reminder, schedule something for later, create a recurring task, set up a periodic check, automate a prompt on a schedule, or plan a one-time or repeating event at a specific time."
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -63,6 +63,8 @@ Skills live in `.claude/skills/`.
|
|
|
63
63
|
|
|
64
64
|
When creating new skills, always put them in `.claude/skills/` within this workspace.
|
|
65
65
|
|
|
66
|
+
**Never use the built-in `/schedule` skill** — it manages cloud remote triggers which we don't use. Always use `/schedule-event` for scheduling.
|
|
67
|
+
|
|
66
68
|
**Before using a skill**, check `TOOLS.md` for operational notes — custom instructions, overrides, workarounds, and tips that supplement the skill's own SKILL.md. Update `TOOLS.md` when you discover new issues or tricks.
|
|
67
69
|
|
|
68
70
|
## Workspace Structure — Keep It Clean!
|
|
@@ -79,7 +81,7 @@ When creating new skills, always put them in `.claude/skills/` within this works
|
|
|
79
81
|
Structure:
|
|
80
82
|
- `.claude/skills/` — local agent skills
|
|
81
83
|
- `memory/` — daily logs (YYYY-MM-DD.md)
|
|
82
|
-
- `data/schedule.json` — scheduled events and reminders (hot-reloaded, no restart needed) (use schedule skill to modify)
|
|
84
|
+
- `data/schedule.json` — scheduled events and reminders (hot-reloaded, no restart needed) (use schedule-event skill to modify)
|
|
83
85
|
|
|
84
86
|
## Safety
|
|
85
87
|
|