ofiere-openclaw-plugin 4.35.1 → 4.37.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 +2 -2
- package/src/tools.ts +281 -59
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofiere-openclaw-plugin",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.37.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "OpenClaw plugin for Ofiere PM -
|
|
5
|
+
"description": "OpenClaw plugin for Ofiere PM - 16 meta-tools covering tasks, agents, projects, scheduling, knowledge, workflows, notifications, memory, prompts, constellation, space file management, execution plan builder, SOP management, agent brain, talent management, and corporate frameworks",
|
|
6
6
|
"keywords": ["openclaw", "ofiere", "project-management", "agents", "plugin"],
|
|
7
7
|
"homepage": "https://github.com/gilanggemar/Ofiere",
|
|
8
8
|
"repository": {
|
package/src/tools.ts
CHANGED
|
@@ -4847,16 +4847,18 @@ function registerSOPOps(
|
|
|
4847
4847
|
`sop_data structure (JSON object):\n` +
|
|
4848
4848
|
`{\n` +
|
|
4849
4849
|
` title: string,\n` +
|
|
4850
|
-
`
|
|
4850
|
+
` purpose: string (why this procedure exists),\n` +
|
|
4851
4851
|
` scope: string (department or domain),\n` +
|
|
4852
|
-
`
|
|
4853
|
-
`
|
|
4854
|
-
`
|
|
4855
|
-
`
|
|
4856
|
-
`
|
|
4852
|
+
` applicability: string (who/what this applies to),\n` +
|
|
4853
|
+
` prerequisites: { conditions: [{text,checked}], required_tools: [string], required_permissions: [string], safety_warnings: [string] },\n` +
|
|
4854
|
+
` procedure_steps: [{ id, name, action, owner, output, decision_logic, failure_state, fallback_action, estimated_duration }],\n` +
|
|
4855
|
+
` expected_outputs: [string],\n` +
|
|
4856
|
+
` escalation_rules: [{ trigger, escalateTo, priority: "P1"|"P2"|"P3" }],\n` +
|
|
4857
|
+
` acceptance_criteria: [{ text, checked }],\n` +
|
|
4858
|
+
` rollback_procedure: string,\n` +
|
|
4857
4859
|
` notes: string\n` +
|
|
4858
4860
|
`}\n\n` +
|
|
4859
|
-
`Status values: "draft", "active", "archived"`,
|
|
4861
|
+
`Status values: "draft", "active", "archived", "under_review"`,
|
|
4860
4862
|
parameters: {
|
|
4861
4863
|
type: "object",
|
|
4862
4864
|
required: ["action"],
|
|
@@ -4868,19 +4870,21 @@ function registerSOPOps(
|
|
|
4868
4870
|
template_id: { type: "string", description: "Template ID (required for apply_template)" },
|
|
4869
4871
|
title: { type: "string", description: "SOP title" },
|
|
4870
4872
|
department: { type: "string", description: "Department name (e.g. Marketing & Revenue)" },
|
|
4871
|
-
status: { type: "string", enum: ["draft", "active", "archived"], description: "SOP status" },
|
|
4873
|
+
status: { type: "string", enum: ["draft", "active", "archived", "under_review"], description: "SOP status" },
|
|
4872
4874
|
sop_data: {
|
|
4873
4875
|
type: "object",
|
|
4874
|
-
description: "Structured SOP content — pass a complete SOPData JSON object",
|
|
4876
|
+
description: "Structured SOP content — pass a complete SOPData JSON object (accepts both old and new field names)",
|
|
4875
4877
|
properties: {
|
|
4876
4878
|
title: { type: "string" },
|
|
4877
|
-
|
|
4879
|
+
purpose: { type: "string", description: "Why this procedure exists (also accepts 'objective')" },
|
|
4878
4880
|
scope: { type: "string" },
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4881
|
+
applicability: { type: "string" },
|
|
4882
|
+
prerequisites: { type: "object", description: "{ conditions: [{text,checked}], required_tools: [string], required_permissions: [string], safety_warnings: [string] }" },
|
|
4883
|
+
procedure_steps: { type: "array", description: "Also accepts 'steps'", items: { type: "object", properties: { id: { type: "string" }, name: { type: "string" }, action: { type: "string" }, owner: { type: "string" }, output: { type: "string" }, decision_logic: { type: "string" }, failure_state: { type: "string" }, fallback_action: { type: "string" }, estimated_duration: { type: "string" } }, required: ["name", "action"] } },
|
|
4884
|
+
expected_outputs: { type: "array", description: "Also accepts 'deliverables'", items: { type: "string" } },
|
|
4885
|
+
escalation_rules: { type: "array", description: "Also accepts 'escalationRules'", items: { type: "object", properties: { trigger: { type: "string" }, escalateTo: { type: "string" }, priority: { type: "string", enum: ["P1", "P2", "P3"] } }, required: ["trigger", "escalateTo"] } },
|
|
4886
|
+
acceptance_criteria: { type: "array", description: "Also accepts 'successCriteria'", items: { type: "object", properties: { text: { type: "string" }, checked: { type: "boolean" } }, required: ["text"] } },
|
|
4887
|
+
rollback_procedure: { type: "string" },
|
|
4884
4888
|
notes: { type: "string" },
|
|
4885
4889
|
},
|
|
4886
4890
|
},
|
|
@@ -4928,56 +4932,74 @@ async function handleSOPCreate(
|
|
|
4928
4932
|
const title = params.title as string;
|
|
4929
4933
|
if (!title) return err("Missing required field: title");
|
|
4930
4934
|
|
|
4931
|
-
// Build the SOPData JSON
|
|
4935
|
+
// Build the SOPData JSON (new schema, backward-compat with old field names)
|
|
4932
4936
|
const sopDataRaw = params.sop_data as Record<string, unknown> | undefined;
|
|
4933
4937
|
let sopContent: string;
|
|
4934
4938
|
|
|
4935
4939
|
if (sopDataRaw) {
|
|
4936
|
-
//
|
|
4937
|
-
const
|
|
4938
|
-
|
|
4940
|
+
// Accept both old (steps) and new (procedure_steps) field names
|
|
4941
|
+
const rawSteps = sopDataRaw.procedure_steps || sopDataRaw.steps;
|
|
4942
|
+
const procedure_steps = Array.isArray(rawSteps)
|
|
4943
|
+
? (rawSteps as any[]).map((s: any, i: number) => ({
|
|
4939
4944
|
id: s.id || `step-${Date.now()}-${i}`,
|
|
4940
|
-
name: s.name || "",
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
output: s.output || "",
|
|
4945
|
+
name: s.name || "", action: s.action || "", owner: s.owner || "", output: s.output || "",
|
|
4946
|
+
decision_logic: s.decision_logic || "", failure_state: s.failure_state || "",
|
|
4947
|
+
fallback_action: s.fallback_action || "", estimated_duration: s.estimated_duration || "",
|
|
4944
4948
|
}))
|
|
4945
4949
|
: [];
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4950
|
+
// Prerequisites: accept old array or new object format
|
|
4951
|
+
const rawPrereqs = sopDataRaw.prerequisites;
|
|
4952
|
+
let prerequisites: any;
|
|
4953
|
+
if (rawPrereqs && typeof rawPrereqs === 'object' && !Array.isArray(rawPrereqs)) {
|
|
4954
|
+
const rp = rawPrereqs as any;
|
|
4955
|
+
prerequisites = {
|
|
4956
|
+
conditions: Array.isArray(rp.conditions) ? rp.conditions.map((p: any) => ({ text: p.text || "", checked: !!p.checked })) : [],
|
|
4957
|
+
required_tools: Array.isArray(rp.required_tools) ? rp.required_tools : [],
|
|
4958
|
+
required_permissions: Array.isArray(rp.required_permissions) ? rp.required_permissions : [],
|
|
4959
|
+
safety_warnings: Array.isArray(rp.safety_warnings) ? rp.safety_warnings : [],
|
|
4960
|
+
};
|
|
4961
|
+
} else {
|
|
4962
|
+
prerequisites = {
|
|
4963
|
+
conditions: Array.isArray(rawPrereqs) ? (rawPrereqs as any[]).map((p: any) => ({ text: p.text || "", checked: !!p.checked })) : [],
|
|
4964
|
+
required_tools: [], required_permissions: [], safety_warnings: [],
|
|
4965
|
+
};
|
|
4966
|
+
}
|
|
4967
|
+
const rawCriteria = sopDataRaw.acceptance_criteria || sopDataRaw.successCriteria;
|
|
4968
|
+
const acceptance_criteria = Array.isArray(rawCriteria)
|
|
4969
|
+
? (rawCriteria as any[]).map((c: any) => ({ text: c.text || "", checked: !!c.checked }))
|
|
4951
4970
|
: [];
|
|
4952
|
-
const
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
escalateTo: r.escalateTo || r.escalate_to || "",
|
|
4971
|
+
const rawEsc = sopDataRaw.escalation_rules || sopDataRaw.escalationRules;
|
|
4972
|
+
const escalation_rules = Array.isArray(rawEsc)
|
|
4973
|
+
? (rawEsc as any[]).map((r: any) => ({
|
|
4974
|
+
trigger: r.trigger || "", escalateTo: r.escalateTo || r.escalate_to || "",
|
|
4956
4975
|
priority: (r.priority || "P2") as "P1" | "P2" | "P3",
|
|
4957
4976
|
}))
|
|
4958
4977
|
: [];
|
|
4959
|
-
const
|
|
4960
|
-
|
|
4961
|
-
: [];
|
|
4978
|
+
const rawOutputs = sopDataRaw.expected_outputs || sopDataRaw.deliverables;
|
|
4979
|
+
const expected_outputs = Array.isArray(rawOutputs) ? (rawOutputs as string[]) : [];
|
|
4962
4980
|
|
|
4963
4981
|
const sopData = {
|
|
4964
4982
|
title: (sopDataRaw.title as string) || title,
|
|
4965
|
-
|
|
4983
|
+
sop_id: (sopDataRaw.sop_id as string) || "", version: (sopDataRaw.version as string) || "1.0.0",
|
|
4984
|
+
effective_date: "", review_date: "", author: "", approver: "", category: "", tags: [],
|
|
4985
|
+
purpose: (sopDataRaw.purpose as string) || (sopDataRaw.objective as string) || "",
|
|
4966
4986
|
scope: (sopDataRaw.scope as string) || "",
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4987
|
+
applicability: (sopDataRaw.applicability as string) || "",
|
|
4988
|
+
prerequisites, procedure_steps, expected_outputs,
|
|
4989
|
+
acceptance_criteria, escalation_rules,
|
|
4990
|
+
rollback_procedure: (sopDataRaw.rollback_procedure as string) || "",
|
|
4991
|
+
related_sops: [], references: [], change_log: [],
|
|
4972
4992
|
notes: (sopDataRaw.notes as string) || "",
|
|
4973
4993
|
};
|
|
4974
4994
|
sopContent = JSON.stringify(sopData);
|
|
4975
4995
|
} else {
|
|
4976
|
-
// Create empty SOP data
|
|
4977
4996
|
sopContent = JSON.stringify({
|
|
4978
|
-
title,
|
|
4979
|
-
|
|
4980
|
-
|
|
4997
|
+
title, sop_id: "", version: "1.0.0", effective_date: "", review_date: "", author: "", approver: "", category: "", tags: [],
|
|
4998
|
+
purpose: "", scope: "", applicability: "",
|
|
4999
|
+
prerequisites: { conditions: [], required_tools: [], required_permissions: [], safety_warnings: [] },
|
|
5000
|
+
procedure_steps: [{ id: `step-${Date.now()}-0`, name: "", action: "", owner: "", output: "", decision_logic: "", failure_state: "", fallback_action: "", estimated_duration: "" }],
|
|
5001
|
+
expected_outputs: [], acceptance_criteria: [], escalation_rules: [],
|
|
5002
|
+
rollback_procedure: "", related_sops: [], references: [], change_log: [], notes: "",
|
|
4981
5003
|
});
|
|
4982
5004
|
}
|
|
4983
5005
|
|
|
@@ -5044,20 +5066,38 @@ async function handleSOPUpdate(supabase: SupabaseClient, userId: string, params:
|
|
|
5044
5066
|
|
|
5045
5067
|
if (params.sop_data) {
|
|
5046
5068
|
const sopDataRaw = params.sop_data as Record<string, unknown>;
|
|
5047
|
-
const
|
|
5048
|
-
|
|
5069
|
+
const rawSteps = sopDataRaw.procedure_steps || sopDataRaw.steps;
|
|
5070
|
+
const procedure_steps = Array.isArray(rawSteps)
|
|
5071
|
+
? (rawSteps as any[]).map((s: any, i: number) => ({
|
|
5049
5072
|
id: s.id || `step-${Date.now()}-${i}`,
|
|
5050
5073
|
name: s.name || "", action: s.action || "", owner: s.owner || "", output: s.output || "",
|
|
5074
|
+
decision_logic: s.decision_logic || "", failure_state: s.failure_state || "",
|
|
5075
|
+
fallback_action: s.fallback_action || "", estimated_duration: s.estimated_duration || "",
|
|
5051
5076
|
}))
|
|
5052
5077
|
: [];
|
|
5053
|
-
const
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5078
|
+
const rawPrereqs = sopDataRaw.prerequisites;
|
|
5079
|
+
let prerequisites: any;
|
|
5080
|
+
if (rawPrereqs && typeof rawPrereqs === 'object' && !Array.isArray(rawPrereqs)) {
|
|
5081
|
+
const rp = rawPrereqs as any;
|
|
5082
|
+
prerequisites = {
|
|
5083
|
+
conditions: Array.isArray(rp.conditions) ? rp.conditions.map((p: any) => ({ text: p.text || "", checked: !!p.checked })) : [],
|
|
5084
|
+
required_tools: Array.isArray(rp.required_tools) ? rp.required_tools : [],
|
|
5085
|
+
required_permissions: Array.isArray(rp.required_permissions) ? rp.required_permissions : [],
|
|
5086
|
+
safety_warnings: Array.isArray(rp.safety_warnings) ? rp.safety_warnings : [],
|
|
5087
|
+
};
|
|
5088
|
+
} else {
|
|
5089
|
+
prerequisites = {
|
|
5090
|
+
conditions: Array.isArray(rawPrereqs) ? (rawPrereqs as any[]).map((p: any) => ({ text: p.text || "", checked: !!p.checked })) : [],
|
|
5091
|
+
required_tools: [], required_permissions: [], safety_warnings: [],
|
|
5092
|
+
};
|
|
5093
|
+
}
|
|
5094
|
+
const rawCriteria = sopDataRaw.acceptance_criteria || sopDataRaw.successCriteria;
|
|
5095
|
+
const acceptance_criteria = Array.isArray(rawCriteria)
|
|
5096
|
+
? (rawCriteria as any[]).map((c: any) => ({ text: c.text || "", checked: !!c.checked }))
|
|
5058
5097
|
: [];
|
|
5059
|
-
const
|
|
5060
|
-
|
|
5098
|
+
const rawEsc = sopDataRaw.escalation_rules || sopDataRaw.escalationRules;
|
|
5099
|
+
const escalation_rules = Array.isArray(rawEsc)
|
|
5100
|
+
? (rawEsc as any[]).map((r: any) => ({
|
|
5061
5101
|
trigger: r.trigger || "", escalateTo: r.escalateTo || r.escalate_to || "",
|
|
5062
5102
|
priority: (r.priority || "P2") as "P1" | "P2" | "P3",
|
|
5063
5103
|
}))
|
|
@@ -5065,11 +5105,16 @@ async function handleSOPUpdate(supabase: SupabaseClient, userId: string, params:
|
|
|
5065
5105
|
|
|
5066
5106
|
updates.content = JSON.stringify({
|
|
5067
5107
|
title: (sopDataRaw.title as string) || (params.title as string) || "",
|
|
5068
|
-
|
|
5108
|
+
sop_id: (sopDataRaw.sop_id as string) || "", version: (sopDataRaw.version as string) || "1.0.0",
|
|
5109
|
+
effective_date: "", review_date: "", author: "", approver: "", category: "", tags: [],
|
|
5110
|
+
purpose: (sopDataRaw.purpose as string) || (sopDataRaw.objective as string) || "",
|
|
5069
5111
|
scope: (sopDataRaw.scope as string) || "",
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5112
|
+
applicability: (sopDataRaw.applicability as string) || "",
|
|
5113
|
+
prerequisites, procedure_steps,
|
|
5114
|
+
expected_outputs: Array.isArray(sopDataRaw.expected_outputs || sopDataRaw.deliverables) ? (sopDataRaw.expected_outputs || sopDataRaw.deliverables) : [],
|
|
5115
|
+
acceptance_criteria, escalation_rules,
|
|
5116
|
+
rollback_procedure: (sopDataRaw.rollback_procedure as string) || "",
|
|
5117
|
+
related_sops: [], references: [], change_log: [],
|
|
5073
5118
|
notes: (sopDataRaw.notes as string) || "",
|
|
5074
5119
|
});
|
|
5075
5120
|
}
|
|
@@ -5152,6 +5197,182 @@ async function handleSOPApplyTemplate(
|
|
|
5152
5197
|
}
|
|
5153
5198
|
|
|
5154
5199
|
|
|
5200
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5201
|
+
// META-TOOL 16: OFIERE_FRAMEWORK_OPS — Corporate Frameworks
|
|
5202
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5203
|
+
|
|
5204
|
+
function registerFrameworkOps(
|
|
5205
|
+
api: any,
|
|
5206
|
+
supabase: SupabaseClient,
|
|
5207
|
+
userId: string,
|
|
5208
|
+
resolveAgent: (id?: string) => Promise<string | null>,
|
|
5209
|
+
): void {
|
|
5210
|
+
api.registerTool({
|
|
5211
|
+
name: "OFIERE_FRAMEWORK_OPS",
|
|
5212
|
+
label: "Ofiere Framework Operations",
|
|
5213
|
+
description:
|
|
5214
|
+
`Create and manage Corporate Frameworks — strategic mandates, KPIs, risk governance, and operational boundaries.\n\n` +
|
|
5215
|
+
`Actions:\n` +
|
|
5216
|
+
`- "create": Create a new framework. Required: agent_id, title. Provide framework body via 'content' (JSON string). Optional: department, category, status\n` +
|
|
5217
|
+
`- "list": List frameworks. Optional: agent_id to filter\n` +
|
|
5218
|
+
`- "get": Get full framework details. Required: framework_id\n` +
|
|
5219
|
+
`- "update": Modify framework. Required: framework_id. Optional: title, content, status, department, category\n` +
|
|
5220
|
+
`- "delete": Remove framework. Required: framework_id\n\n` +
|
|
5221
|
+
`content: A JSON string containing the full framework body. Example:\n` +
|
|
5222
|
+
`"{\\"mission\\":\\"Drive brand growth\\",\\"vision\\":\\"Market leader by 2027\\",\\"core_principles\\":[\\"Data-driven\\",\\"Customer-first\\"],` +
|
|
5223
|
+
`\\"decision_authority\\":\\"CMO approves >$10k spend\\",\\"budget_constraints\\":\\"$50k monthly cap\\",` +
|
|
5224
|
+
`\\"strategic_objectives\\":[{\\"objective\\":\\"Grow MRR\\",\\"target\\":\\"$100k\\",\\"measurement\\":\\"Revenue dashboard\\"}],` +
|
|
5225
|
+
`\\"risk_appetite\\":\\"moderate\\",\\"escalation_matrix\\":[{\\"trigger\\":\\"Budget >80%\\",\\"escalate_to\\":\\"CEO\\",\\"priority\\":\\"P1\\"}],` +
|
|
5226
|
+
`\\"notes\\":\\"Quarterly review\\"}"\n\n` +
|
|
5227
|
+
`Supported content fields: mission, vision, core_principles, decision_authority, budget_constraints, ` +
|
|
5228
|
+
`resource_limits, tool_stack, strategic_objectives, risk_appetite, compliance_requirements, ` +
|
|
5229
|
+
`escalation_matrix, team_composition, integration_points, review_frequency, notes — or any custom fields.\n\n` +
|
|
5230
|
+
`Status values: "draft", "active", "under_review", "archived"`,
|
|
5231
|
+
parameters: {
|
|
5232
|
+
type: "object",
|
|
5233
|
+
required: ["action"],
|
|
5234
|
+
properties: {
|
|
5235
|
+
action: { type: "string", enum: ["create", "list", "get", "update", "delete"], description: "Framework action" },
|
|
5236
|
+
framework_id: { type: "string", description: "Framework ID (required for get, update, delete)" },
|
|
5237
|
+
agent_id: { type: "string", description: "Agent ID (required for create, optional filter for list)" },
|
|
5238
|
+
title: { type: "string", description: "Framework title" },
|
|
5239
|
+
content: { type: "string", description: "Framework body as a JSON string containing mission, vision, core_principles, decision_authority, budget_constraints, strategic_objectives, risk_appetite, escalation_matrix, and any other fields. Pass as stringified JSON." },
|
|
5240
|
+
department: { type: "string" },
|
|
5241
|
+
category: { type: "string" },
|
|
5242
|
+
status: { type: "string", enum: ["draft", "active", "under_review", "archived"] },
|
|
5243
|
+
},
|
|
5244
|
+
},
|
|
5245
|
+
async execute(_id: string, params: Record<string, unknown>) {
|
|
5246
|
+
const action = params.action as string;
|
|
5247
|
+
switch (action) {
|
|
5248
|
+
case "create": return handleFWCreate(supabase, userId, resolveAgent, params);
|
|
5249
|
+
case "list": return handleFWList(supabase, userId, params);
|
|
5250
|
+
case "get": return handleFWGet(supabase, userId, params);
|
|
5251
|
+
case "update": return handleFWUpdate(supabase, userId, params);
|
|
5252
|
+
case "delete": return handleFWDelete(supabase, userId, params);
|
|
5253
|
+
default: return err(`Unknown action "${action}". Valid: create, list, get, update, delete`);
|
|
5254
|
+
}
|
|
5255
|
+
},
|
|
5256
|
+
});
|
|
5257
|
+
}
|
|
5258
|
+
|
|
5259
|
+
// ── Framework action handlers ────────────────────────────────────────────────
|
|
5260
|
+
|
|
5261
|
+
async function handleFWCreate(
|
|
5262
|
+
supabase: SupabaseClient, userId: string,
|
|
5263
|
+
resolveAgent: (id?: string) => Promise<string | null>,
|
|
5264
|
+
params: Record<string, unknown>,
|
|
5265
|
+
): Promise<ToolResult> {
|
|
5266
|
+
try {
|
|
5267
|
+
const agentIdRaw = params.agent_id as string;
|
|
5268
|
+
if (!agentIdRaw) return err("Missing required field: agent_id");
|
|
5269
|
+
const title = params.title as string;
|
|
5270
|
+
if (!title) return err("Missing required field: title");
|
|
5271
|
+
|
|
5272
|
+
// Accept content via: (1) 'content' string param (preferred, matches SOP pattern)
|
|
5273
|
+
// (2) 'framework_data' object param (legacy, kept for compat)
|
|
5274
|
+
let content: string;
|
|
5275
|
+
if (typeof params.content === "string" && params.content.trim()) {
|
|
5276
|
+
// Already a JSON string — merge with title
|
|
5277
|
+
try {
|
|
5278
|
+
const parsed = JSON.parse(params.content);
|
|
5279
|
+
content = JSON.stringify({ title, ...parsed });
|
|
5280
|
+
} catch {
|
|
5281
|
+
// Not valid JSON — wrap as notes
|
|
5282
|
+
content = JSON.stringify({ title, notes: params.content });
|
|
5283
|
+
}
|
|
5284
|
+
} else if (params.framework_data && typeof params.framework_data === "object") {
|
|
5285
|
+
content = JSON.stringify({ title, ...(params.framework_data as Record<string, unknown>) });
|
|
5286
|
+
} else {
|
|
5287
|
+
content = JSON.stringify({ title });
|
|
5288
|
+
}
|
|
5289
|
+
|
|
5290
|
+
const row = {
|
|
5291
|
+
user_id: userId, agent_id: agentIdRaw, title, content,
|
|
5292
|
+
status: (params.status as string) || "draft",
|
|
5293
|
+
department: (params.department as string) || null,
|
|
5294
|
+
category: (params.category as string) || null,
|
|
5295
|
+
};
|
|
5296
|
+
|
|
5297
|
+
const { data, error } = await supabase.from("frameworks").insert(row).select().single();
|
|
5298
|
+
if (error) return err(error.message);
|
|
5299
|
+
return ok({
|
|
5300
|
+
message: `Framework "${title}" created for agent ${agentIdRaw}`,
|
|
5301
|
+
framework: { id: data.id, title: data.title, status: data.status, agent_id: data.agent_id },
|
|
5302
|
+
});
|
|
5303
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
5304
|
+
}
|
|
5305
|
+
|
|
5306
|
+
async function handleFWList(supabase: SupabaseClient, userId: string, params: Record<string, unknown>): Promise<ToolResult> {
|
|
5307
|
+
try {
|
|
5308
|
+
let query = supabase.from("frameworks")
|
|
5309
|
+
.select("id, agent_id, title, status, department, category, created_at, updated_at")
|
|
5310
|
+
.eq("user_id", userId).order("updated_at", { ascending: false });
|
|
5311
|
+
if (params.agent_id) query = query.eq("agent_id", params.agent_id as string);
|
|
5312
|
+
const { data, error } = await query;
|
|
5313
|
+
if (error) return err(error.message);
|
|
5314
|
+
return ok({ frameworks: data || [], count: (data || []).length });
|
|
5315
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
5316
|
+
}
|
|
5317
|
+
|
|
5318
|
+
async function handleFWGet(supabase: SupabaseClient, userId: string, params: Record<string, unknown>): Promise<ToolResult> {
|
|
5319
|
+
try {
|
|
5320
|
+
if (!params.framework_id) return err("Missing required field: framework_id");
|
|
5321
|
+
const { data, error } = await supabase.from("frameworks").select("*")
|
|
5322
|
+
.eq("id", params.framework_id as string).eq("user_id", userId);
|
|
5323
|
+
if (error) return err(error.message);
|
|
5324
|
+
if (!data || data.length === 0) return err("Framework not found");
|
|
5325
|
+
const fw = data[0];
|
|
5326
|
+
let parsed: any = {};
|
|
5327
|
+
try { parsed = JSON.parse(fw.content || "{}"); } catch { /* leave empty */ }
|
|
5328
|
+
return ok({ framework: { ...fw, content: parsed } });
|
|
5329
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
5330
|
+
}
|
|
5331
|
+
|
|
5332
|
+
async function handleFWUpdate(supabase: SupabaseClient, userId: string, params: Record<string, unknown>): Promise<ToolResult> {
|
|
5333
|
+
try {
|
|
5334
|
+
if (!params.framework_id) return err("Missing required field: framework_id");
|
|
5335
|
+
const updates: Record<string, any> = { updated_at: new Date().toISOString() };
|
|
5336
|
+
if (params.title !== undefined) updates.title = params.title;
|
|
5337
|
+
if (params.status !== undefined) updates.status = params.status;
|
|
5338
|
+
if (params.department !== undefined) updates.department = params.department;
|
|
5339
|
+
if (params.category !== undefined) updates.category = params.category;
|
|
5340
|
+
|
|
5341
|
+
// Accept content via: (1) 'content' string param (preferred)
|
|
5342
|
+
// (2) 'framework_data' object param (legacy compat)
|
|
5343
|
+
if (typeof params.content === "string" && params.content.trim()) {
|
|
5344
|
+
try {
|
|
5345
|
+
const parsed = JSON.parse(params.content);
|
|
5346
|
+
updates.content = JSON.stringify({ title: parsed.title || (params.title as string) || "", ...parsed });
|
|
5347
|
+
} catch {
|
|
5348
|
+
updates.content = JSON.stringify({ title: (params.title as string) || "", notes: params.content });
|
|
5349
|
+
}
|
|
5350
|
+
} else if (params.framework_data && typeof params.framework_data === "object") {
|
|
5351
|
+
const raw = params.framework_data as Record<string, unknown>;
|
|
5352
|
+
updates.content = JSON.stringify({ title: (raw.title as string) || (params.title as string) || "", ...raw });
|
|
5353
|
+
}
|
|
5354
|
+
|
|
5355
|
+
const { data, error } = await supabase.from("frameworks").update(updates)
|
|
5356
|
+
.eq("id", params.framework_id as string).eq("user_id", userId)
|
|
5357
|
+
.select("id, title, status").maybeSingle();
|
|
5358
|
+
if (error) return err(error.message);
|
|
5359
|
+
if (!data) return err("Framework not found");
|
|
5360
|
+
return ok({ message: `Framework "${data.title}" updated`, framework: data });
|
|
5361
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
5362
|
+
}
|
|
5363
|
+
|
|
5364
|
+
async function handleFWDelete(supabase: SupabaseClient, userId: string, params: Record<string, unknown>): Promise<ToolResult> {
|
|
5365
|
+
try {
|
|
5366
|
+
if (!params.framework_id) return err("Missing required field: framework_id");
|
|
5367
|
+
const { data, error } = await supabase.from("frameworks").delete()
|
|
5368
|
+
.eq("id", params.framework_id as string).eq("user_id", userId)
|
|
5369
|
+
.select("id, title").maybeSingle();
|
|
5370
|
+
if (error) return err(error.message);
|
|
5371
|
+
if (!data) return err("Framework not found — nothing deleted");
|
|
5372
|
+
return ok({ message: `Framework "${data.title}" deleted`, deleted: true });
|
|
5373
|
+
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
5374
|
+
}
|
|
5375
|
+
|
|
5155
5376
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5156
5377
|
// META-TOOL 14: OFIERE_BRAIN_OPS — Agent Memory + Self-Improvement
|
|
5157
5378
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -5849,6 +6070,7 @@ export function registerTools(
|
|
|
5849
6070
|
registerSOPOps(api, supabase, userId, resolveAgent); // 13
|
|
5850
6071
|
registerBrainOps(api, supabase, userId, resolveAgent); // 14
|
|
5851
6072
|
registerTalentOps(api, supabase, userId); // 15
|
|
6073
|
+
registerFrameworkOps(api, supabase, userId, resolveAgent); // 16
|
|
5852
6074
|
|
|
5853
6075
|
// ── Register dynamic brain context hook ──
|
|
5854
6076
|
registerBrainContextHook(api, supabase, userId, fallbackAgentId);
|
|
@@ -5860,7 +6082,7 @@ export function registerTools(
|
|
|
5860
6082
|
registerBrainExtractionHook(api, supabase, userId, fallbackAgentId);
|
|
5861
6083
|
|
|
5862
6084
|
// ── Count and log ──
|
|
5863
|
-
const toolCount =
|
|
6085
|
+
const toolCount = 16;
|
|
5864
6086
|
const callerName = getCallingAgentName(api);
|
|
5865
6087
|
const agentLabel = fallbackAgentId || callerName || "auto-detect";
|
|
5866
6088
|
api.logger.info(`[ofiere] ${toolCount} meta-tools registered (agent: ${agentLabel})`);
|