@jiggai/recipes 0.4.70 → 0.4.72

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.
@@ -2,7 +2,7 @@
2
2
  "id": "recipes",
3
3
  "name": "Recipes",
4
4
  "description": "Markdown recipes that scaffold agents and teams (workspace-local).",
5
- "version": "0.4.70",
5
+ "version": "0.4.72",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jiggai/recipes",
3
- "version": "0.4.70",
3
+ "version": "0.4.72",
4
4
  "description": "ClawRecipes plugin for OpenClaw (markdown recipes -> scaffold agents/teams)",
5
5
  "main": "index.ts",
6
6
  "type": "commonjs",
@@ -305,16 +305,28 @@ export async function executeWorkflowNodes(opts: {
305
305
  `- openclaw recipes workflows resume --team-id ${teamId} --run-id ${runId}`,
306
306
  ].join('\n');
307
307
 
308
- await toolsInvoke<ToolTextResult>(api, {
309
- tool: 'message',
310
- args: {
311
- action: 'send',
312
- channel,
313
- target,
314
- ...(accountId ? { accountId } : {}),
315
- message: msg,
316
- },
317
- });
308
+ // Deliver the approval prompt via the OpenClaw `message` tool. The tool
309
+ // requires the calling agent to have `group:messaging` (or `message`)
310
+ // in its tools.allow policy — see openclaw/openclaw#74780. If delivery
311
+ // fails (misconfigured policy, channel adapter down, etc.) we log and
312
+ // continue; approval.json is durable, so operators can still approve
313
+ // via the kitchen UI or `openclaw recipes workflows approve` CLI.
314
+ try {
315
+ await toolsInvoke<ToolTextResult>(api, {
316
+ tool: 'message',
317
+ args: {
318
+ action: 'send',
319
+ channel,
320
+ target,
321
+ ...(accountId ? { accountId } : {}),
322
+ message: msg,
323
+ },
324
+ });
325
+ } catch (err) {
326
+ const errMsg = err instanceof Error ? err.message : String(err);
327
+ console.warn(`[workflow] tools.invoke('message') failed for run ${runId} on node ${node.id}: ${errMsg}`);
328
+ console.warn(`[workflow] approval message not delivered for run ${runId}; approve via kitchen UI or CLI`);
329
+ }
318
330
 
319
331
  const waitingTs = new Date().toISOString();
320
332
  await appendRunLog(runLogPath, (cur) => ({
@@ -12,7 +12,7 @@ import type { WorkflowLane, WorkflowNode, RunLog } from './workflow-types';
12
12
  import { dequeueNextTask, enqueueTask, hasPendingTaskFor, releaseTaskClaim, compactQueue } from './workflow-queue';
13
13
  import { currentLockOwner, isLockHolderDead } from './lock-liveness';
14
14
  import { loadPriorLlmInput, loadProposedPostTextFromPriorNode } from './workflow-node-output-readers';
15
- import { readTextFile } from './workflow-runner-io';
15
+ import { readTextFile, readJsonFile } from './workflow-runner-io';
16
16
  import { resolveApprovalBindingTarget } from './workflow-node-executor';
17
17
  import { buildKitchenWorkflowReviewUrl } from './kitchen-review-url';
18
18
  import {
@@ -45,7 +45,7 @@ async function buildMemoryContext(teamDir: string): Promise<string> {
45
45
  // Read pinned items first (highest priority)
46
46
  const pinnedPath = path.join(memoryDir, 'pinned.jsonl');
47
47
  if (await fileExists(pinnedPath)) {
48
- const pinnedContent = await fs.readFile(pinnedPath, 'utf8');
48
+ const pinnedContent = await readTextFile(pinnedPath);
49
49
  const pinnedItems = pinnedContent.trim().split('\n').filter(Boolean);
50
50
 
51
51
  if (pinnedItems.length > 0) {
@@ -75,7 +75,7 @@ async function buildMemoryContext(teamDir: string): Promise<string> {
75
75
  if (currentTokens > maxTokens * 0.8) break; // Leave room for recent items
76
76
 
77
77
  const filePath = path.join(memoryDir, filename);
78
- const content = await fs.readFile(filePath, 'utf8');
78
+ const content = await readTextFile(filePath);
79
79
  const items = content.trim().split('\n').filter(Boolean);
80
80
 
81
81
  if (items.length > 0) {
@@ -151,7 +151,7 @@ async function buildTemplateVars(
151
151
  if (nid && nrOutPath) {
152
152
  try {
153
153
  const outAbs = path.resolve(teamDir, nrOutPath);
154
- const outputContent = await fs.readFile(outAbs, 'utf8');
154
+ const outputContent = await readTextFile(outAbs);
155
155
  vars[`${nid}.output`] = outputContent;
156
156
 
157
157
  try {
@@ -314,7 +314,7 @@ async function checkWaitingHandoffs(api: OpenClawPluginApi, teamId: string, team
314
314
  const runPath = path.join(runDir, 'run.json');
315
315
  let run: RunLog;
316
316
  try {
317
- const raw = await fs.readFile(runPath, 'utf8');
317
+ const raw = await readTextFile(runPath);
318
318
  run = JSON.parse(raw) as RunLog;
319
319
  } catch { continue; }
320
320
 
@@ -335,7 +335,7 @@ async function checkWaitingHandoffs(api: OpenClawPluginApi, teamId: string, team
335
335
  nodeOutputRel: string;
336
336
  };
337
337
  try {
338
- marker = JSON.parse(await fs.readFile(waitPath, 'utf8'));
338
+ marker = await readJsonFile<typeof marker>(waitPath);
339
339
  } catch { continue; }
340
340
 
341
341
  // Check timeout
@@ -416,7 +416,7 @@ async function checkWaitingHandoffs(api: OpenClawPluginApi, teamId: string, team
416
416
  const workflowsDir = path.join(teamDir, 'shared-context', 'workflows');
417
417
  let workflow;
418
418
  try {
419
- const wfRaw = await fs.readFile(path.join(workflowsDir, run.workflow.file), 'utf8');
419
+ const wfRaw = await readTextFile(path.join(workflowsDir, run.workflow.file));
420
420
  workflow = normalizeWorkflow(JSON.parse(wfRaw));
421
421
  } catch { workflow = null; }
422
422
 
@@ -1505,7 +1505,7 @@ export async function runWorkflowWorkerTick(api: OpenClawPluginApi, opts: {
1505
1505
  if (!wf.endsWith('.json')) continue;
1506
1506
  try {
1507
1507
  const wfPath = path.join(targetWorkflowsDir, wf);
1508
- const wfRaw = await fs.readFile(wfPath, 'utf8');
1508
+ const wfRaw = await readTextFile(wfPath);
1509
1509
  const wfParsed = JSON.parse(wfRaw);
1510
1510
  if (String(wfParsed.id ?? '') === targetWorkflowId || String(wfParsed.name ?? '') === targetWorkflowId) {
1511
1511
  targetWorkflowFile = wf;