ofiere-openclaw-plugin 4.11.0 → 4.12.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/prompt.ts +8 -2
- package/src/tools.ts +210 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofiere-openclaw-plugin",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenClaw plugin for Ofiere PM - 10 meta-tools with 13-action workflow mastery covering tasks, agents, projects, scheduling, knowledge, workflows, notifications, memory, prompts, and constellation agent architecture",
|
|
6
6
|
"keywords": ["openclaw", "ofiere", "project-management", "agents", "plugin"],
|
package/src/prompt.ts
CHANGED
|
@@ -9,14 +9,17 @@
|
|
|
9
9
|
// registered meta-tool and will be included in the system prompt.
|
|
10
10
|
|
|
11
11
|
const TOOL_DOCS: Record<string, string> = {
|
|
12
|
-
OFIERE_TASK_OPS: `- **OFIERE_TASK_OPS** — Manage tasks (action: "list", "create", "update", "delete")
|
|
12
|
+
OFIERE_TASK_OPS: `- **OFIERE_TASK_OPS** — Manage tasks and approvals (action: "list", "create", "update", "delete", "add_approval", "list_approvals", "resolve_approval")
|
|
13
13
|
- list: Filter by status, agent_id, space_id, folder_id, limit. Returns execution_plan, goals, constraints if present
|
|
14
14
|
- create: Requires title. IMPORTANT: Always pass agent_id with your own name to self-assign (e.g. agent_id: "celia")
|
|
15
15
|
- Optional: description, instructions, execution_plan, goals, constraints, system_prompt, priority, tags, dates
|
|
16
16
|
- For COMPLEX tasks: include execution_plan (step-by-step), goals, constraints, and system_prompt
|
|
17
17
|
- For SIMPLE tasks: just title and optionally description
|
|
18
18
|
- update: Requires task_id. All create fields + progress
|
|
19
|
-
- delete: Requires task_id. Removes task and subtasks
|
|
19
|
+
- delete: Requires task_id. Removes task and subtasks
|
|
20
|
+
- add_approval: Request sign-off on a task. Requires: task_id, approver_name. Optional: approver_type (human|agent, auto-detected), due_date, comment
|
|
21
|
+
- list_approvals: List approvals. Optional: task_id to filter, approval_status (pending|approved|rejected)
|
|
22
|
+
- resolve_approval: Approve or reject. Requires: approval_id, approval_status (approved|rejected). Optional: comment`,
|
|
20
23
|
|
|
21
24
|
OFIERE_AGENT_OPS: `- **OFIERE_AGENT_OPS** — Query agents (action: "list")
|
|
22
25
|
- list: See all agents with IDs, names, roles for task assignment`,
|
|
@@ -142,6 +145,9 @@ ${toolDocs}
|
|
|
142
145
|
- To change a node's configuration (e.g. update agent instructions, change template text), use "update_node" with just the fields that changed.
|
|
143
146
|
- When creating workflows with condition or loop nodes, specify sourceHandle on edges: "condition-true"/"condition-false" for conditions, "loop_body"/"done" for loops.
|
|
144
147
|
- Fill node data fields with actual content — include real task instructions, templates, and variable references. Do NOT leave fields empty unless intentionally blank.
|
|
148
|
+
- Use add_approval when a task needs sign-off from a human or another agent before proceeding. Approvers can be agents (by name) or humans.
|
|
149
|
+
- Task approvals (OFIERE_TASK_OPS) are SEPARATE from workflow gate approvals (human_approval nodes). Do not confuse them.
|
|
150
|
+
- When an agent completes critical work, consider adding an approval request for human review before marking the task DONE.
|
|
145
151
|
</ofiere-pm>`;
|
|
146
152
|
}
|
|
147
153
|
|
package/src/tools.ts
CHANGED
|
@@ -188,17 +188,21 @@ function registerTaskOps(
|
|
|
188
188
|
name: "OFIERE_TASK_OPS",
|
|
189
189
|
label: "Ofiere Task Operations",
|
|
190
190
|
description:
|
|
191
|
-
`Manage tasks in the Ofiere PM dashboard. All task operations go through this tool.\n\n` +
|
|
191
|
+
`Manage tasks and task approvals in the Ofiere PM dashboard. All task operations go through this tool.\n\n` +
|
|
192
192
|
`Actions:\n` +
|
|
193
193
|
`- "list": List/filter tasks. Optional: status, agent_id, space_id, folder_id, task_id, limit\n` +
|
|
194
194
|
`- "get": Get a single task by ID. Required: task_id\n` +
|
|
195
195
|
`- "create": Create a task. Required: title. Optional: agent_id, description, status, priority, space_id, folder_id, start_date, due_date, tags, instructions, execution_plan, goals, constraints, system_prompt, recurrence_type, recurrence_interval, scheduled_time\n` +
|
|
196
196
|
`- "update": Update a task. Required: task_id. Optional: all create fields + progress\n` +
|
|
197
|
-
`- "delete": Delete task + subtasks. Required: task_id\n
|
|
197
|
+
`- "delete": Delete task + subtasks. Required: task_id\n` +
|
|
198
|
+
`- "add_approval": Request approval on a task. Required: task_id, approver_name. Optional: approver_type (human|agent, auto-detected), due_date, comment\n` +
|
|
199
|
+
`- "list_approvals": List approvals. Optional: task_id, approval_status filter (pending|approved|rejected)\n` +
|
|
200
|
+
`- "resolve_approval": Approve or reject. Required: approval_id, approval_status (approved|rejected). Optional: comment\n\n` +
|
|
198
201
|
`For complex tasks, fill in execution_plan (step-by-step plan), goals, constraints, and system_prompt to help the executing agent.\n` +
|
|
199
202
|
`For simple tasks, just provide title and optionally description.\n` +
|
|
200
203
|
`agent_id: Pass your name to self-assign, another agent's name, or 'none'.\n` +
|
|
201
204
|
`For recurring tasks: set start_date + recurrence_type + recurrence_interval. Example: every 2 minutes = recurrence_type: "minutely", recurrence_interval: 2.\n` +
|
|
205
|
+
`Approvals: Use add_approval to request sign-off from humans or agents. Approvals are separate from workflow gate nodes.\n` +
|
|
202
206
|
`Status: PENDING, IN_PROGRESS, DONE, FAILED | Priority: 0=LOW, 1=MEDIUM, 2=HIGH, 3=CRITICAL`,
|
|
203
207
|
parameters: {
|
|
204
208
|
type: "object",
|
|
@@ -207,7 +211,7 @@ function registerTaskOps(
|
|
|
207
211
|
action: {
|
|
208
212
|
type: "string",
|
|
209
213
|
description: "The operation to perform",
|
|
210
|
-
enum: ["list", "get", "create", "update", "delete"],
|
|
214
|
+
enum: ["list", "get", "create", "update", "delete", "add_approval", "list_approvals", "resolve_approval"],
|
|
211
215
|
},
|
|
212
216
|
title: { type: "string", description: "Task title (required for create)" },
|
|
213
217
|
description: { type: "string", description: "Task description" },
|
|
@@ -280,6 +284,19 @@ function registerTaskOps(
|
|
|
280
284
|
},
|
|
281
285
|
system_prompt: { type: "string", description: "Custom system prompt injection for the executing agent" },
|
|
282
286
|
limit: { type: "number", description: "Max results for list (default 50)" },
|
|
287
|
+
approver_name: { type: "string", description: "Name of the approver (human name or agent name). Required for add_approval." },
|
|
288
|
+
approver_type: {
|
|
289
|
+
type: "string",
|
|
290
|
+
description: "Whether the approver is a human or an agent. Auto-detected from agents list if omitted.",
|
|
291
|
+
enum: ["human", "agent"],
|
|
292
|
+
},
|
|
293
|
+
approval_id: { type: "string", description: "Approval ID for resolve_approval action" },
|
|
294
|
+
approval_status: {
|
|
295
|
+
type: "string",
|
|
296
|
+
description: "Approval decision: approved or rejected. Used with resolve_approval.",
|
|
297
|
+
enum: ["approved", "rejected"],
|
|
298
|
+
},
|
|
299
|
+
comment: { type: "string", description: "Comment for approval (add_approval or resolve_approval)" },
|
|
283
300
|
},
|
|
284
301
|
},
|
|
285
302
|
async execute(_id: string, params: Record<string, unknown>) {
|
|
@@ -297,9 +314,15 @@ function registerTaskOps(
|
|
|
297
314
|
return handleUpdateTask(supabase, userId, params);
|
|
298
315
|
case "delete":
|
|
299
316
|
return handleDeleteTask(supabase, userId, params);
|
|
317
|
+
case "add_approval":
|
|
318
|
+
return handleAddApproval(supabase, userId, params);
|
|
319
|
+
case "list_approvals":
|
|
320
|
+
return handleListApprovals(supabase, userId, params);
|
|
321
|
+
case "resolve_approval":
|
|
322
|
+
return handleResolveApproval(supabase, userId, params);
|
|
300
323
|
default:
|
|
301
324
|
return err(
|
|
302
|
-
`Unknown action "${action}". Valid actions: list, get, create, update, delete`,
|
|
325
|
+
`Unknown action "${action}". Valid actions: list, get, create, update, delete, add_approval, list_approvals, resolve_approval`,
|
|
303
326
|
);
|
|
304
327
|
}
|
|
305
328
|
},
|
|
@@ -730,6 +753,189 @@ async function handleDeleteTask(
|
|
|
730
753
|
}
|
|
731
754
|
}
|
|
732
755
|
|
|
756
|
+
// ── Approval action handlers ─────────────────────────────────────────────────
|
|
757
|
+
|
|
758
|
+
async function handleAddApproval(
|
|
759
|
+
supabase: SupabaseClient,
|
|
760
|
+
userId: string,
|
|
761
|
+
params: Record<string, unknown>,
|
|
762
|
+
): Promise<ToolResult> {
|
|
763
|
+
try {
|
|
764
|
+
if (!params.task_id) return err("Missing required field: task_id");
|
|
765
|
+
if (!params.approver_name) return err("Missing required field: approver_name");
|
|
766
|
+
|
|
767
|
+
const taskId = params.task_id as string;
|
|
768
|
+
const approverName = params.approver_name as string;
|
|
769
|
+
|
|
770
|
+
// Verify task ownership
|
|
771
|
+
const { data: taskData, error: taskErr } = await supabase
|
|
772
|
+
.from("tasks")
|
|
773
|
+
.select("id, title")
|
|
774
|
+
.eq("id", taskId)
|
|
775
|
+
.eq("user_id", userId)
|
|
776
|
+
.single();
|
|
777
|
+
if (taskErr || !taskData) return err(`Task not found or not owned by you: ${taskId}`);
|
|
778
|
+
|
|
779
|
+
// Auto-detect approver_type if not provided
|
|
780
|
+
let approverType = params.approver_type as string | undefined;
|
|
781
|
+
if (!approverType) {
|
|
782
|
+
const { data: agentMatch } = await supabase
|
|
783
|
+
.from("agents")
|
|
784
|
+
.select("id")
|
|
785
|
+
.eq("user_id", userId)
|
|
786
|
+
.ilike("name", approverName)
|
|
787
|
+
.limit(1);
|
|
788
|
+
approverType = (agentMatch && agentMatch.length > 0) ? "agent" : "human";
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
const insertData: Record<string, unknown> = {
|
|
792
|
+
task_id: taskId,
|
|
793
|
+
approver_type: approverType,
|
|
794
|
+
approver_name: approverName,
|
|
795
|
+
status: "pending",
|
|
796
|
+
};
|
|
797
|
+
if (params.due_date) insertData.due_date = params.due_date;
|
|
798
|
+
if (params.comment) insertData.comment = params.comment;
|
|
799
|
+
|
|
800
|
+
const { data, error } = await supabase
|
|
801
|
+
.from("pm_approvals")
|
|
802
|
+
.insert(insertData)
|
|
803
|
+
.select()
|
|
804
|
+
.single();
|
|
805
|
+
|
|
806
|
+
if (error) return err(error.message);
|
|
807
|
+
|
|
808
|
+
// Log activity
|
|
809
|
+
try {
|
|
810
|
+
await supabase.from("pm_activities").insert({
|
|
811
|
+
user_id: userId,
|
|
812
|
+
entity_type: "task",
|
|
813
|
+
entity_id: taskId,
|
|
814
|
+
action_type: "approval_requested",
|
|
815
|
+
source: "agent",
|
|
816
|
+
source_name: _registrationAgentName || "agent",
|
|
817
|
+
content: `Requested ${approverType} approval from ${approverName}`,
|
|
818
|
+
metadata: { approval_id: data.id },
|
|
819
|
+
});
|
|
820
|
+
} catch { /* non-fatal */ }
|
|
821
|
+
|
|
822
|
+
return ok({
|
|
823
|
+
message: `Approval requested from ${approverName} (${approverType}) for task "${taskData.title}"`,
|
|
824
|
+
approval: data,
|
|
825
|
+
});
|
|
826
|
+
} catch (e) {
|
|
827
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
async function handleListApprovals(
|
|
832
|
+
supabase: SupabaseClient,
|
|
833
|
+
userId: string,
|
|
834
|
+
params: Record<string, unknown>,
|
|
835
|
+
): Promise<ToolResult> {
|
|
836
|
+
try {
|
|
837
|
+
// First get all task IDs owned by this user (for ownership guard)
|
|
838
|
+
const { data: userTasks, error: taskErr } = await supabase
|
|
839
|
+
.from("tasks")
|
|
840
|
+
.select("id")
|
|
841
|
+
.eq("user_id", userId);
|
|
842
|
+
if (taskErr) return err(taskErr.message);
|
|
843
|
+
|
|
844
|
+
const taskIds = (userTasks || []).map((t: { id: string }) => t.id);
|
|
845
|
+
if (taskIds.length === 0) return ok({ approvals: [], count: 0 });
|
|
846
|
+
|
|
847
|
+
let query = supabase
|
|
848
|
+
.from("pm_approvals")
|
|
849
|
+
.select("*")
|
|
850
|
+
.in("task_id", taskIds)
|
|
851
|
+
.order("created_at", { ascending: false });
|
|
852
|
+
|
|
853
|
+
if (params.task_id) query = query.eq("task_id", params.task_id as string);
|
|
854
|
+
if (params.approval_status) query = query.eq("status", params.approval_status as string);
|
|
855
|
+
|
|
856
|
+
const { data, error } = await query;
|
|
857
|
+
if (error) return err(error.message);
|
|
858
|
+
|
|
859
|
+
return ok({ approvals: data || [], count: (data || []).length });
|
|
860
|
+
} catch (e) {
|
|
861
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
async function handleResolveApproval(
|
|
866
|
+
supabase: SupabaseClient,
|
|
867
|
+
userId: string,
|
|
868
|
+
params: Record<string, unknown>,
|
|
869
|
+
): Promise<ToolResult> {
|
|
870
|
+
try {
|
|
871
|
+
if (!params.approval_id) return err("Missing required field: approval_id");
|
|
872
|
+
if (!params.approval_status) return err("Missing required field: approval_status");
|
|
873
|
+
|
|
874
|
+
const approvalId = params.approval_id as string;
|
|
875
|
+
const newStatus = params.approval_status as string;
|
|
876
|
+
|
|
877
|
+
if (!['approved', 'rejected'].includes(newStatus)) {
|
|
878
|
+
return err(`Invalid approval_status "${newStatus}". Must be "approved" or "rejected".`);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// Verify the approval belongs to a task owned by this user
|
|
882
|
+
const { data: approval, error: fetchErr } = await supabase
|
|
883
|
+
.from("pm_approvals")
|
|
884
|
+
.select("id, task_id, approver_name, status")
|
|
885
|
+
.eq("id", approvalId)
|
|
886
|
+
.single();
|
|
887
|
+
if (fetchErr || !approval) return err(`Approval not found: ${approvalId}`);
|
|
888
|
+
|
|
889
|
+
// Verify task ownership
|
|
890
|
+
const { data: taskData, error: taskErr } = await supabase
|
|
891
|
+
.from("tasks")
|
|
892
|
+
.select("id, title")
|
|
893
|
+
.eq("id", approval.task_id)
|
|
894
|
+
.eq("user_id", userId)
|
|
895
|
+
.single();
|
|
896
|
+
if (taskErr || !taskData) return err(`Approval's task not owned by you`);
|
|
897
|
+
|
|
898
|
+
if (approval.status !== 'pending') {
|
|
899
|
+
return err(`Approval already resolved as "${approval.status}". Cannot change.`);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
const updates: Record<string, unknown> = {
|
|
903
|
+
status: newStatus,
|
|
904
|
+
resolved_at: new Date().toISOString(),
|
|
905
|
+
};
|
|
906
|
+
if (params.comment !== undefined) updates.comment = params.comment;
|
|
907
|
+
|
|
908
|
+
const { error } = await supabase
|
|
909
|
+
.from("pm_approvals")
|
|
910
|
+
.update(updates)
|
|
911
|
+
.eq("id", approvalId);
|
|
912
|
+
|
|
913
|
+
if (error) return err(error.message);
|
|
914
|
+
|
|
915
|
+
// Log activity
|
|
916
|
+
try {
|
|
917
|
+
await supabase.from("pm_activities").insert({
|
|
918
|
+
user_id: userId,
|
|
919
|
+
entity_type: "task",
|
|
920
|
+
entity_id: approval.task_id,
|
|
921
|
+
action_type: `approval_${newStatus}`,
|
|
922
|
+
source: "agent",
|
|
923
|
+
source_name: _registrationAgentName || "agent",
|
|
924
|
+
content: `${newStatus === 'approved' ? 'Approved' : 'Rejected'} approval from ${approval.approver_name}${params.comment ? `: ${params.comment}` : ''}`,
|
|
925
|
+
metadata: { approval_id: approvalId },
|
|
926
|
+
});
|
|
927
|
+
} catch { /* non-fatal */ }
|
|
928
|
+
|
|
929
|
+
return ok({
|
|
930
|
+
message: `Approval ${approvalId} ${newStatus} for task "${taskData.title}"`,
|
|
931
|
+
approval_id: approvalId,
|
|
932
|
+
status: newStatus,
|
|
933
|
+
});
|
|
934
|
+
} catch (e) {
|
|
935
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
733
939
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
734
940
|
// META-TOOL 2: OFIERE_AGENT_OPS — Agent Management
|
|
735
941
|
// ═══════════════════════════════════════════════════════════════════════════════
|