ofiere-openclaw-plugin 4.18.5 → 4.19.1
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 +2 -2
- package/src/prompt.ts +19 -0
- package/src/tools.ts +334 -1
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofiere-openclaw-plugin",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.19.1",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "OpenClaw plugin for Ofiere PM -
|
|
5
|
+
"description": "OpenClaw plugin for Ofiere PM - 13 meta-tools covering tasks, agents, projects, scheduling, knowledge, workflows, notifications, memory, prompts, constellation, space file management, execution plan builder, and SOP management",
|
|
6
6
|
"keywords": ["openclaw", "ofiere", "project-management", "agents", "plugin"],
|
|
7
7
|
"homepage": "https://github.com/gilanggemar/Ofiere",
|
|
8
8
|
"repository": {
|
package/src/prompt.ts
CHANGED
|
@@ -130,6 +130,19 @@ const TOOL_DOCS: Record<string, string> = {
|
|
|
130
130
|
- Plans are visual DAG drafts — they don't become real tasks until you call "execute"
|
|
131
131
|
- Execution maps ALL node fields into real tasks: execution_steps → custom_fields.execution_plan, goals → custom_fields.goals, constraints → custom_fields.constraints, system_prompt → custom_fields.system_prompt
|
|
132
132
|
- The user can see and edit your plans in the Planning Tab of the dashboard in real-time`,
|
|
133
|
+
|
|
134
|
+
OFIERE_SOP_OPS: `- **OFIERE_SOP_OPS** — Standard Operating Procedures for department chiefs (action: "list_templates", "create", "list", "get", "update", "delete", "list_subagents", "apply_template")
|
|
135
|
+
- list_templates: See available SOP templates (built-in + user-created)
|
|
136
|
+
- create: Create a new SOP. Required: agent_id, title, sop_data. Optional: department, status
|
|
137
|
+
- list: List SOPs. Optional: agent_id to filter by department chief
|
|
138
|
+
- get: Full SOP details with structured content. Required: sop_id
|
|
139
|
+
- update: Modify SOP content/status. Required: sop_id. Optional: title, sop_data, status, department
|
|
140
|
+
- delete: Remove an SOP. Required: sop_id
|
|
141
|
+
- list_subagents: View staff under a chief. Required: chief_agent_id
|
|
142
|
+
- apply_template: Create SOP from a saved template. Required: agent_id, template_id
|
|
143
|
+
- sop_data is a JSON object: { title, objective, scope, prerequisites[{text,checked}], steps[{name,action,owner,output}], deliverables[], escalationRules[{trigger,escalateTo,priority}], successCriteria[{text,checked}], notes }
|
|
144
|
+
- Status values: draft, active, archived
|
|
145
|
+
- SOPs appear in the SOP Manager page immediately via real-time sync`,
|
|
133
146
|
};
|
|
134
147
|
|
|
135
148
|
export function getSystemPrompt(state: {
|
|
@@ -195,6 +208,12 @@ ${toolDocs}
|
|
|
195
208
|
- When creating a plan with nodes, nest children inside each node's children[] array. Sequential children execute in order; set parallel: true on a parent node to fork its children into parallel branches.
|
|
196
209
|
- Always call OFIERE_PLAN_OPS action:"get" before action:"add_nodes" or action:"update" to see the current tree structure and node IDs.
|
|
197
210
|
- Execution ("execute") maps plan nodes 1:1 into real PM tasks with ALL enrichment fields preserved: execution_steps, goals, constraints, system_prompt. No data is lost in the handoff.
|
|
211
|
+
- SOP CREATION: When asked to create an SOP, use OFIERE_SOP_OPS action:"create" with a COMPLETE sop_data object. Fill ALL fields with actionable, department-specific content — do NOT leave fields empty.
|
|
212
|
+
- Each SOP step MUST have: a clear name, a specific action description, an assigned owner, and a concrete expected output.
|
|
213
|
+
- Include escalation rules with appropriate priority levels (P1=critical blockers, P2=scope changes, P3=advisory).
|
|
214
|
+
- When creating SOPs for department chiefs (Thalia=CMO, Ivy=COO, Daisy=CTO-Intel, Celia=CTO-Eng), tailor content to their domain expertise.
|
|
215
|
+
- Prerequisites should be actionable checklist items. Success criteria should be measurable outcomes.
|
|
216
|
+
- After creating an SOP, suggest the agent set it to "active" status when ready for execution.
|
|
198
217
|
</ofiere-pm>`;
|
|
199
218
|
}
|
|
200
219
|
|
package/src/tools.ts
CHANGED
|
@@ -4514,6 +4514,338 @@ async function handleExecutePlan(
|
|
|
4514
4514
|
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
4515
4515
|
}
|
|
4516
4516
|
|
|
4517
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
4518
|
+
// META-TOOL 13: OFIERE_SOP_OPS — Standard Operating Procedures
|
|
4519
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
4520
|
+
|
|
4521
|
+
function registerSOPOps(
|
|
4522
|
+
api: any,
|
|
4523
|
+
supabase: SupabaseClient,
|
|
4524
|
+
userId: string,
|
|
4525
|
+
resolveAgent: (id?: string) => Promise<string | null>,
|
|
4526
|
+
): void {
|
|
4527
|
+
api.registerTool({
|
|
4528
|
+
name: "OFIERE_SOP_OPS",
|
|
4529
|
+
label: "Ofiere SOP Operations",
|
|
4530
|
+
description:
|
|
4531
|
+
`Create, manage, and deploy Standard Operating Procedures for department chiefs.\n\n` +
|
|
4532
|
+
`Actions:\n` +
|
|
4533
|
+
`- "list_templates": List available SOP templates\n` +
|
|
4534
|
+
`- "create": Create a new SOP for an agent. Required: agent_id, title, sop_data. Optional: department, status\n` +
|
|
4535
|
+
`- "list": List SOPs. Optional: agent_id to filter\n` +
|
|
4536
|
+
`- "get": Get full SOP details. Required: sop_id\n` +
|
|
4537
|
+
`- "update": Modify SOP. Required: sop_id. Optional: title, sop_data, status, department\n` +
|
|
4538
|
+
`- "delete": Remove SOP. Required: sop_id\n` +
|
|
4539
|
+
`- "list_subagents": List subagents for a chief. Required: chief_agent_id\n` +
|
|
4540
|
+
`- "apply_template": Create SOP from template. Required: agent_id, template_id. Optional: title, department\n\n` +
|
|
4541
|
+
`sop_data structure (JSON object):\n` +
|
|
4542
|
+
`{\n` +
|
|
4543
|
+
` title: string,\n` +
|
|
4544
|
+
` objective: string (purpose and expected outcome),\n` +
|
|
4545
|
+
` scope: string (department or domain),\n` +
|
|
4546
|
+
` prerequisites: [{ text: string, checked: boolean }],\n` +
|
|
4547
|
+
` steps: [{ id: string, name: string, action: string, owner: string, output: string }],\n` +
|
|
4548
|
+
` deliverables: [string],\n` +
|
|
4549
|
+
` escalationRules: [{ trigger: string, escalateTo: string, priority: "P1"|"P2"|"P3" }],\n` +
|
|
4550
|
+
` successCriteria: [{ text: string, checked: boolean }],\n` +
|
|
4551
|
+
` notes: string\n` +
|
|
4552
|
+
`}\n\n` +
|
|
4553
|
+
`Status values: "draft", "active", "archived"`,
|
|
4554
|
+
parameters: {
|
|
4555
|
+
type: "object",
|
|
4556
|
+
required: ["action"],
|
|
4557
|
+
properties: {
|
|
4558
|
+
action: { type: "string", enum: ["list_templates", "create", "list", "get", "update", "delete", "list_subagents", "apply_template"], description: "Valid actions: list_templates, create, list, get, update, delete, list_subagents, apply_template" },
|
|
4559
|
+
sop_id: { type: "string", description: "SOP ID (required for get, update, delete)" },
|
|
4560
|
+
agent_id: { type: "string", description: "Agent ID (required for create, list filter, apply_template)" },
|
|
4561
|
+
chief_agent_id: { type: "string", description: "Chief agent ID (required for list_subagents)" },
|
|
4562
|
+
template_id: { type: "string", description: "Template ID (required for apply_template)" },
|
|
4563
|
+
title: { type: "string", description: "SOP title" },
|
|
4564
|
+
department: { type: "string", description: "Department name (e.g. Marketing & Revenue)" },
|
|
4565
|
+
status: { type: "string", enum: ["draft", "active", "archived"], description: "SOP status" },
|
|
4566
|
+
sop_data: {
|
|
4567
|
+
type: "object",
|
|
4568
|
+
description: "Structured SOP content — pass a complete SOPData JSON object",
|
|
4569
|
+
properties: {
|
|
4570
|
+
title: { type: "string" },
|
|
4571
|
+
objective: { type: "string" },
|
|
4572
|
+
scope: { type: "string" },
|
|
4573
|
+
prerequisites: { type: "array", items: { type: "object", properties: { text: { type: "string" }, checked: { type: "boolean" } }, required: ["text"] } },
|
|
4574
|
+
steps: { type: "array", items: { type: "object", properties: { id: { type: "string" }, name: { type: "string" }, action: { type: "string" }, owner: { type: "string" }, output: { type: "string" } }, required: ["name", "action"] } },
|
|
4575
|
+
deliverables: { type: "array", items: { type: "string" } },
|
|
4576
|
+
escalationRules: { type: "array", items: { type: "object", properties: { trigger: { type: "string" }, escalateTo: { type: "string" }, priority: { type: "string", enum: ["P1", "P2", "P3"] } }, required: ["trigger", "escalateTo"] } },
|
|
4577
|
+
successCriteria: { type: "array", items: { type: "object", properties: { text: { type: "string" }, checked: { type: "boolean" } }, required: ["text"] } },
|
|
4578
|
+
notes: { type: "string" },
|
|
4579
|
+
},
|
|
4580
|
+
},
|
|
4581
|
+
},
|
|
4582
|
+
},
|
|
4583
|
+
async execute(_id: string, params: Record<string, unknown>) {
|
|
4584
|
+
const action = params.action as string;
|
|
4585
|
+
switch (action) {
|
|
4586
|
+
case "list_templates": return handleSOPListTemplates(supabase, userId);
|
|
4587
|
+
case "create": return handleSOPCreate(supabase, userId, resolveAgent, params);
|
|
4588
|
+
case "list": return handleSOPList(supabase, userId, params);
|
|
4589
|
+
case "get": return handleSOPGet(supabase, userId, params);
|
|
4590
|
+
case "update": return handleSOPUpdate(supabase, userId, params);
|
|
4591
|
+
case "delete": return handleSOPDelete(supabase, userId, params);
|
|
4592
|
+
case "list_subagents": return handleSOPListSubagents(supabase, userId, params);
|
|
4593
|
+
case "apply_template": return handleSOPApplyTemplate(supabase, userId, resolveAgent, params);
|
|
4594
|
+
default: return err(`Unknown action "${action}". Valid: list_templates, create, list, get, update, delete, list_subagents, apply_template`);
|
|
4595
|
+
}
|
|
4596
|
+
},
|
|
4597
|
+
});
|
|
4598
|
+
}
|
|
4599
|
+
|
|
4600
|
+
// ── SOP action handlers ──────────────────────────────────────────────────────
|
|
4601
|
+
|
|
4602
|
+
async function handleSOPListTemplates(supabase: SupabaseClient, userId: string): Promise<ToolResult> {
|
|
4603
|
+
try {
|
|
4604
|
+
const { data, error } = await supabase
|
|
4605
|
+
.from("sop_templates")
|
|
4606
|
+
.select("id, name, description, is_default, created_at")
|
|
4607
|
+
.eq("user_id", userId)
|
|
4608
|
+
.order("created_at", { ascending: false });
|
|
4609
|
+
if (error) return err(error.message);
|
|
4610
|
+
return ok({ templates: data || [], count: (data || []).length });
|
|
4611
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
4612
|
+
}
|
|
4613
|
+
|
|
4614
|
+
async function handleSOPCreate(
|
|
4615
|
+
supabase: SupabaseClient, userId: string,
|
|
4616
|
+
resolveAgent: (id?: string) => Promise<string | null>,
|
|
4617
|
+
params: Record<string, unknown>,
|
|
4618
|
+
): Promise<ToolResult> {
|
|
4619
|
+
try {
|
|
4620
|
+
const agentIdRaw = params.agent_id as string;
|
|
4621
|
+
if (!agentIdRaw) return err("Missing required field: agent_id");
|
|
4622
|
+
const title = params.title as string;
|
|
4623
|
+
if (!title) return err("Missing required field: title");
|
|
4624
|
+
|
|
4625
|
+
// Build the SOPData JSON
|
|
4626
|
+
const sopDataRaw = params.sop_data as Record<string, unknown> | undefined;
|
|
4627
|
+
let sopContent: string;
|
|
4628
|
+
|
|
4629
|
+
if (sopDataRaw) {
|
|
4630
|
+
// Ensure each step has an id
|
|
4631
|
+
const steps = Array.isArray(sopDataRaw.steps)
|
|
4632
|
+
? (sopDataRaw.steps as any[]).map((s: any, i: number) => ({
|
|
4633
|
+
id: s.id || `step-${Date.now()}-${i}`,
|
|
4634
|
+
name: s.name || "",
|
|
4635
|
+
action: s.action || "",
|
|
4636
|
+
owner: s.owner || "",
|
|
4637
|
+
output: s.output || "",
|
|
4638
|
+
}))
|
|
4639
|
+
: [];
|
|
4640
|
+
const prerequisites = Array.isArray(sopDataRaw.prerequisites)
|
|
4641
|
+
? (sopDataRaw.prerequisites as any[]).map((p: any) => ({ text: p.text || "", checked: !!p.checked }))
|
|
4642
|
+
: [];
|
|
4643
|
+
const successCriteria = Array.isArray(sopDataRaw.successCriteria)
|
|
4644
|
+
? (sopDataRaw.successCriteria as any[]).map((c: any) => ({ text: c.text || "", checked: !!c.checked }))
|
|
4645
|
+
: [];
|
|
4646
|
+
const escalationRules = Array.isArray(sopDataRaw.escalationRules)
|
|
4647
|
+
? (sopDataRaw.escalationRules as any[]).map((r: any) => ({
|
|
4648
|
+
trigger: r.trigger || "",
|
|
4649
|
+
escalateTo: r.escalateTo || r.escalate_to || "",
|
|
4650
|
+
priority: (r.priority || "P2") as "P1" | "P2" | "P3",
|
|
4651
|
+
}))
|
|
4652
|
+
: [];
|
|
4653
|
+
const deliverables = Array.isArray(sopDataRaw.deliverables)
|
|
4654
|
+
? (sopDataRaw.deliverables as string[])
|
|
4655
|
+
: [];
|
|
4656
|
+
|
|
4657
|
+
const sopData = {
|
|
4658
|
+
title: (sopDataRaw.title as string) || title,
|
|
4659
|
+
objective: (sopDataRaw.objective as string) || "",
|
|
4660
|
+
scope: (sopDataRaw.scope as string) || "",
|
|
4661
|
+
prerequisites,
|
|
4662
|
+
steps,
|
|
4663
|
+
deliverables,
|
|
4664
|
+
escalationRules,
|
|
4665
|
+
successCriteria,
|
|
4666
|
+
notes: (sopDataRaw.notes as string) || "",
|
|
4667
|
+
};
|
|
4668
|
+
sopContent = JSON.stringify(sopData);
|
|
4669
|
+
} else {
|
|
4670
|
+
// Create empty SOP data
|
|
4671
|
+
sopContent = JSON.stringify({
|
|
4672
|
+
title, objective: "", scope: "", prerequisites: [],
|
|
4673
|
+
steps: [{ id: `step-${Date.now()}-0`, name: "", action: "", owner: "", output: "" }],
|
|
4674
|
+
deliverables: [], escalationRules: [], successCriteria: [], notes: "",
|
|
4675
|
+
});
|
|
4676
|
+
}
|
|
4677
|
+
|
|
4678
|
+
const row = {
|
|
4679
|
+
user_id: userId,
|
|
4680
|
+
agent_id: agentIdRaw,
|
|
4681
|
+
title,
|
|
4682
|
+
content: sopContent,
|
|
4683
|
+
status: (params.status as string) || "draft",
|
|
4684
|
+
department: (params.department as string) || null,
|
|
4685
|
+
};
|
|
4686
|
+
|
|
4687
|
+
const { data, error } = await supabase.from("agent_sops").insert(row).select().single();
|
|
4688
|
+
if (error) return err(error.message);
|
|
4689
|
+
|
|
4690
|
+
return ok({
|
|
4691
|
+
message: `SOP "${title}" created for agent ${agentIdRaw}`,
|
|
4692
|
+
sop: { id: data.id, title: data.title, status: data.status, agent_id: data.agent_id, department: data.department },
|
|
4693
|
+
});
|
|
4694
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
4695
|
+
}
|
|
4696
|
+
|
|
4697
|
+
async function handleSOPList(supabase: SupabaseClient, userId: string, params: Record<string, unknown>): Promise<ToolResult> {
|
|
4698
|
+
try {
|
|
4699
|
+
let query = supabase
|
|
4700
|
+
.from("agent_sops")
|
|
4701
|
+
.select("id, agent_id, title, status, department, created_at, updated_at")
|
|
4702
|
+
.eq("user_id", userId)
|
|
4703
|
+
.order("updated_at", { ascending: false });
|
|
4704
|
+
if (params.agent_id) query = query.eq("agent_id", params.agent_id as string);
|
|
4705
|
+
const { data, error } = await query;
|
|
4706
|
+
if (error) return err(error.message);
|
|
4707
|
+
return ok({ sops: data || [], count: (data || []).length });
|
|
4708
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
4709
|
+
}
|
|
4710
|
+
|
|
4711
|
+
async function handleSOPGet(supabase: SupabaseClient, userId: string, params: Record<string, unknown>): Promise<ToolResult> {
|
|
4712
|
+
try {
|
|
4713
|
+
if (!params.sop_id) return err("Missing required field: sop_id");
|
|
4714
|
+
const { data, error } = await supabase
|
|
4715
|
+
.from("agent_sops")
|
|
4716
|
+
.select("*")
|
|
4717
|
+
.eq("id", params.sop_id as string)
|
|
4718
|
+
.eq("user_id", userId)
|
|
4719
|
+
.maybeSingle();
|
|
4720
|
+
if (error) return err(error.message);
|
|
4721
|
+
if (!data) return err("SOP not found");
|
|
4722
|
+
|
|
4723
|
+
// Parse the content to return structured data
|
|
4724
|
+
let parsedContent: any = {};
|
|
4725
|
+
try { parsedContent = JSON.parse(data.content || "{}"); } catch { /* leave empty */ }
|
|
4726
|
+
|
|
4727
|
+
return ok({ sop: { ...data, content: parsedContent } });
|
|
4728
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
4729
|
+
}
|
|
4730
|
+
|
|
4731
|
+
async function handleSOPUpdate(supabase: SupabaseClient, userId: string, params: Record<string, unknown>): Promise<ToolResult> {
|
|
4732
|
+
try {
|
|
4733
|
+
if (!params.sop_id) return err("Missing required field: sop_id");
|
|
4734
|
+
const updates: Record<string, any> = { updated_at: new Date().toISOString() };
|
|
4735
|
+
if (params.title !== undefined) updates.title = params.title;
|
|
4736
|
+
if (params.status !== undefined) updates.status = params.status;
|
|
4737
|
+
if (params.department !== undefined) updates.department = params.department;
|
|
4738
|
+
|
|
4739
|
+
if (params.sop_data) {
|
|
4740
|
+
const sopDataRaw = params.sop_data as Record<string, unknown>;
|
|
4741
|
+
const steps = Array.isArray(sopDataRaw.steps)
|
|
4742
|
+
? (sopDataRaw.steps as any[]).map((s: any, i: number) => ({
|
|
4743
|
+
id: s.id || `step-${Date.now()}-${i}`,
|
|
4744
|
+
name: s.name || "", action: s.action || "", owner: s.owner || "", output: s.output || "",
|
|
4745
|
+
}))
|
|
4746
|
+
: [];
|
|
4747
|
+
const prerequisites = Array.isArray(sopDataRaw.prerequisites)
|
|
4748
|
+
? (sopDataRaw.prerequisites as any[]).map((p: any) => ({ text: p.text || "", checked: !!p.checked }))
|
|
4749
|
+
: [];
|
|
4750
|
+
const successCriteria = Array.isArray(sopDataRaw.successCriteria)
|
|
4751
|
+
? (sopDataRaw.successCriteria as any[]).map((c: any) => ({ text: c.text || "", checked: !!c.checked }))
|
|
4752
|
+
: [];
|
|
4753
|
+
const escalationRules = Array.isArray(sopDataRaw.escalationRules)
|
|
4754
|
+
? (sopDataRaw.escalationRules as any[]).map((r: any) => ({
|
|
4755
|
+
trigger: r.trigger || "", escalateTo: r.escalateTo || r.escalate_to || "",
|
|
4756
|
+
priority: (r.priority || "P2") as "P1" | "P2" | "P3",
|
|
4757
|
+
}))
|
|
4758
|
+
: [];
|
|
4759
|
+
|
|
4760
|
+
updates.content = JSON.stringify({
|
|
4761
|
+
title: (sopDataRaw.title as string) || (params.title as string) || "",
|
|
4762
|
+
objective: (sopDataRaw.objective as string) || "",
|
|
4763
|
+
scope: (sopDataRaw.scope as string) || "",
|
|
4764
|
+
prerequisites, steps,
|
|
4765
|
+
deliverables: Array.isArray(sopDataRaw.deliverables) ? sopDataRaw.deliverables : [],
|
|
4766
|
+
escalationRules, successCriteria,
|
|
4767
|
+
notes: (sopDataRaw.notes as string) || "",
|
|
4768
|
+
});
|
|
4769
|
+
}
|
|
4770
|
+
|
|
4771
|
+
const { data, error } = await supabase
|
|
4772
|
+
.from("agent_sops").update(updates)
|
|
4773
|
+
.eq("id", params.sop_id as string).eq("user_id", userId)
|
|
4774
|
+
.select("id, title, status").maybeSingle();
|
|
4775
|
+
if (error) return err(error.message);
|
|
4776
|
+
if (!data) return err("SOP not found");
|
|
4777
|
+
return ok({ message: `SOP "${data.title}" updated`, sop: data });
|
|
4778
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
4779
|
+
}
|
|
4780
|
+
|
|
4781
|
+
async function handleSOPDelete(supabase: SupabaseClient, userId: string, params: Record<string, unknown>): Promise<ToolResult> {
|
|
4782
|
+
try {
|
|
4783
|
+
if (!params.sop_id) return err("Missing required field: sop_id");
|
|
4784
|
+
const { data, error } = await supabase
|
|
4785
|
+
.from("agent_sops").delete()
|
|
4786
|
+
.eq("id", params.sop_id as string).eq("user_id", userId)
|
|
4787
|
+
.select("id, title").maybeSingle();
|
|
4788
|
+
if (error) return err(error.message);
|
|
4789
|
+
if (!data) return err("SOP not found — nothing deleted");
|
|
4790
|
+
return ok({ message: `SOP "${data.title}" deleted`, deleted: true });
|
|
4791
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
4792
|
+
}
|
|
4793
|
+
|
|
4794
|
+
async function handleSOPListSubagents(supabase: SupabaseClient, userId: string, params: Record<string, unknown>): Promise<ToolResult> {
|
|
4795
|
+
try {
|
|
4796
|
+
if (!params.chief_agent_id) return err("Missing required field: chief_agent_id");
|
|
4797
|
+
const { data, error } = await supabase
|
|
4798
|
+
.from("agent_subagents")
|
|
4799
|
+
.select("id, name, role, codename, color_hex, created_at")
|
|
4800
|
+
.eq("user_id", userId)
|
|
4801
|
+
.eq("chief_agent_id", params.chief_agent_id as string)
|
|
4802
|
+
.order("created_at", { ascending: true });
|
|
4803
|
+
if (error) return err(error.message);
|
|
4804
|
+
return ok({ subagents: data || [], count: (data || []).length, max_allowed: 5 });
|
|
4805
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
4806
|
+
}
|
|
4807
|
+
|
|
4808
|
+
async function handleSOPApplyTemplate(
|
|
4809
|
+
supabase: SupabaseClient, userId: string,
|
|
4810
|
+
resolveAgent: (id?: string) => Promise<string | null>,
|
|
4811
|
+
params: Record<string, unknown>,
|
|
4812
|
+
): Promise<ToolResult> {
|
|
4813
|
+
try {
|
|
4814
|
+
if (!params.agent_id) return err("Missing required field: agent_id");
|
|
4815
|
+
if (!params.template_id) return err("Missing required field: template_id");
|
|
4816
|
+
|
|
4817
|
+
// Fetch template
|
|
4818
|
+
const { data: template, error: tErr } = await supabase
|
|
4819
|
+
.from("sop_templates")
|
|
4820
|
+
.select("name, content")
|
|
4821
|
+
.eq("id", params.template_id as string)
|
|
4822
|
+
.eq("user_id", userId)
|
|
4823
|
+
.single();
|
|
4824
|
+
if (tErr || !template) return err("Template not found");
|
|
4825
|
+
|
|
4826
|
+
const title = (params.title as string) || template.name || "SOP from Template";
|
|
4827
|
+
|
|
4828
|
+
const row = {
|
|
4829
|
+
user_id: userId,
|
|
4830
|
+
agent_id: params.agent_id as string,
|
|
4831
|
+
template_id: params.template_id as string,
|
|
4832
|
+
title,
|
|
4833
|
+
content: template.content, // Template content is already JSON
|
|
4834
|
+
status: "draft",
|
|
4835
|
+
department: (params.department as string) || null,
|
|
4836
|
+
};
|
|
4837
|
+
|
|
4838
|
+
const { data, error } = await supabase.from("agent_sops").insert(row).select().single();
|
|
4839
|
+
if (error) return err(error.message);
|
|
4840
|
+
|
|
4841
|
+
return ok({
|
|
4842
|
+
message: `SOP "${title}" created from template "${template.name}" for agent ${params.agent_id}`,
|
|
4843
|
+
sop: { id: data.id, title: data.title, status: data.status, agent_id: data.agent_id },
|
|
4844
|
+
});
|
|
4845
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
4846
|
+
}
|
|
4847
|
+
|
|
4848
|
+
|
|
4517
4849
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
4518
4850
|
// Public: Register All Meta-Tools
|
|
4519
4851
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -4543,9 +4875,10 @@ export function registerTools(
|
|
|
4543
4875
|
registerConstellationOps(api, supabase, userId); // 10
|
|
4544
4876
|
registerFileOps(api, supabase, userId); // 11
|
|
4545
4877
|
registerPlanOps(api, supabase, userId, resolveAgent); // 12
|
|
4878
|
+
registerSOPOps(api, supabase, userId, resolveAgent); // 13
|
|
4546
4879
|
|
|
4547
4880
|
// ── Count and log ──
|
|
4548
|
-
const toolCount =
|
|
4881
|
+
const toolCount = 13;
|
|
4549
4882
|
const callerName = getCallingAgentName(api);
|
|
4550
4883
|
const agentLabel = fallbackAgentId || callerName || "auto-detect";
|
|
4551
4884
|
api.logger.info(`[ofiere] ${toolCount} meta-tools registered (agent: ${agentLabel})`);
|