ofiere-openclaw-plugin 4.40.1 → 4.41.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 +1 -1
- package/src/agent-tier.ts +62 -14
- package/src/attachments.ts +20 -9
- package/src/prompt.ts +3 -2
- package/src/tools.ts +22 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofiere-openclaw-plugin",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.41.1",
|
|
4
4
|
"type": "module",
|
|
5
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"],
|
package/src/agent-tier.ts
CHANGED
|
@@ -53,12 +53,12 @@ export async function resolveAgentTier(
|
|
|
53
53
|
if (!id || !userId) return { tier: null, source: "none" };
|
|
54
54
|
|
|
55
55
|
const key = k(userId, id);
|
|
56
|
-
const hit = cache.get(key);
|
|
57
|
-
if (hit && hit.expires > Date.now()) return hit.value;
|
|
58
|
-
|
|
59
|
-
let result: TierResolution = { tier: null, source: "none" };
|
|
60
56
|
|
|
61
|
-
// 1. Manual override
|
|
57
|
+
// 1. Manual override — ALWAYS fresh (uncached). Direct-SQL writes to
|
|
58
|
+
// agent_tier_overrides must win immediately, even when a non-override
|
|
59
|
+
// branch is already cached from a prior call.
|
|
60
|
+
let overrideRow: { tier?: string | null } | null = null;
|
|
61
|
+
let overrideQueryFailed = false;
|
|
62
62
|
try {
|
|
63
63
|
const { data } = await supabase
|
|
64
64
|
.from("agent_tier_overrides")
|
|
@@ -66,23 +66,41 @@ export async function resolveAgentTier(
|
|
|
66
66
|
.eq("user_id", userId)
|
|
67
67
|
.eq("agent_id", id)
|
|
68
68
|
.maybeSingle();
|
|
69
|
-
|
|
70
|
-
result = { tier: data.tier, source: "manual" };
|
|
71
|
-
cache.set(key, { value: result, expires: Date.now() + TTL_MS });
|
|
72
|
-
return result;
|
|
73
|
-
}
|
|
69
|
+
overrideRow = data ?? null;
|
|
74
70
|
} catch {
|
|
75
|
-
// Table missing in older installs —
|
|
71
|
+
// Table missing in older installs — skip override path
|
|
72
|
+
overrideQueryFailed = true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (overrideRow?.tier === "c-suite" || overrideRow?.tier === "staff") {
|
|
76
|
+
const result: TierResolution = { tier: overrideRow.tier, source: "manual" };
|
|
77
|
+
cache.set(key, { value: result, expires: Date.now() + TTL_MS });
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Override absent: if cache holds a stale `manual` entry from a deleted
|
|
82
|
+
// override row, drop it so the auto branches re-resolve.
|
|
83
|
+
if (!overrideQueryFailed) {
|
|
84
|
+
const cachedManual = cache.get(key);
|
|
85
|
+
if (cachedManual && cachedManual.value.source === "manual") {
|
|
86
|
+
cache.delete(key);
|
|
87
|
+
}
|
|
76
88
|
}
|
|
77
89
|
|
|
78
|
-
// 2.
|
|
90
|
+
// 2. Cache lookup for non-override branches.
|
|
91
|
+
const hit = cache.get(key);
|
|
92
|
+
if (hit && hit.expires > Date.now()) return hit.value;
|
|
93
|
+
|
|
94
|
+
let result: TierResolution = { tier: null, source: "none" };
|
|
95
|
+
|
|
96
|
+
// 3. C-Suite: hardcoded roster
|
|
79
97
|
if (ROSTER_IDS.has(id)) {
|
|
80
98
|
result = { tier: "c-suite", source: "roster" };
|
|
81
99
|
cache.set(key, { value: result, expires: Date.now() + TTL_MS });
|
|
82
100
|
return result;
|
|
83
101
|
}
|
|
84
102
|
|
|
85
|
-
//
|
|
103
|
+
// 4. agent_architectures.executive_role / department_role
|
|
86
104
|
try {
|
|
87
105
|
const { data } = await supabase
|
|
88
106
|
.from("agent_architectures")
|
|
@@ -105,7 +123,7 @@ export async function resolveAgentTier(
|
|
|
105
123
|
// Table missing — fall through
|
|
106
124
|
}
|
|
107
125
|
|
|
108
|
-
//
|
|
126
|
+
// 5. Staff: registered as subagent
|
|
109
127
|
try {
|
|
110
128
|
const { data } = await supabase
|
|
111
129
|
.from("agent_subagents")
|
|
@@ -134,6 +152,36 @@ export async function getAgentTier(
|
|
|
134
152
|
return (await resolveAgentTier(supabase, agentId, userId)).tier;
|
|
135
153
|
}
|
|
136
154
|
|
|
155
|
+
// Target-aware resolution: when a work target carries a subagent_id, the
|
|
156
|
+
// work is staff-tier regardless of which chief routes it. Uncached lookup —
|
|
157
|
+
// subagent rows can be deleted out from under us.
|
|
158
|
+
export interface TargetTierInput {
|
|
159
|
+
agentId: string | null;
|
|
160
|
+
subagentId?: string | null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function resolveTargetTier(
|
|
164
|
+
supabase: SupabaseClient,
|
|
165
|
+
target: TargetTierInput,
|
|
166
|
+
userId: string,
|
|
167
|
+
): Promise<TierResolution> {
|
|
168
|
+
if (target.subagentId) {
|
|
169
|
+
try {
|
|
170
|
+
const { data } = await supabase
|
|
171
|
+
.from("agent_subagents")
|
|
172
|
+
.select("id")
|
|
173
|
+
.eq("user_id", userId)
|
|
174
|
+
.eq("id", target.subagentId)
|
|
175
|
+
.maybeSingle();
|
|
176
|
+
if (data?.id) return { tier: "staff", source: "subagent" };
|
|
177
|
+
} catch {
|
|
178
|
+
// Fall through to chief lookup
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (target.agentId) return resolveAgentTier(supabase, target.agentId, userId);
|
|
182
|
+
return { tier: null, source: "none" };
|
|
183
|
+
}
|
|
184
|
+
|
|
137
185
|
export function isDocKindValidForTier(
|
|
138
186
|
docKind: "sop" | "framework",
|
|
139
187
|
tier: AgentTier,
|
package/src/attachments.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
11
11
|
import { renderAttachmentBlock } from "./sop-render.js";
|
|
12
12
|
import {
|
|
13
|
-
|
|
13
|
+
resolveTargetTier,
|
|
14
14
|
isDocKindValidForTier,
|
|
15
15
|
invalidateAgentTier,
|
|
16
16
|
} from "./agent-tier.js";
|
|
@@ -99,24 +99,33 @@ async function applyAttachmentWrite(args: {
|
|
|
99
99
|
return { ok: false, error: "unsupported target_kind" };
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
// ── Load target row (
|
|
103
|
-
|
|
102
|
+
// ── Load target row identity (chief agent + optional subagent) for tier check ──
|
|
103
|
+
interface TargetIdentity {
|
|
104
|
+
agentId: string | null;
|
|
105
|
+
subagentId: string | null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function loadTargetIdentity(args: {
|
|
104
109
|
supabase: SupabaseClient;
|
|
105
110
|
userId: string;
|
|
106
111
|
targetKind: TargetKind;
|
|
107
112
|
targetId: string;
|
|
108
|
-
}): Promise<
|
|
113
|
+
}): Promise<TargetIdentity | null> {
|
|
109
114
|
const { supabase, userId, targetKind, targetId } = args;
|
|
110
115
|
const tbl =
|
|
111
116
|
targetKind === "conversation" ? "conversations" :
|
|
112
117
|
targetKind === "task" ? "tasks" : "scheduler_events";
|
|
113
118
|
const { data } = await supabase
|
|
114
119
|
.from(tbl)
|
|
115
|
-
.select("agent_id")
|
|
120
|
+
.select("agent_id, subagent_id")
|
|
116
121
|
.eq("user_id", userId)
|
|
117
122
|
.eq("id", targetId)
|
|
118
123
|
.maybeSingle();
|
|
119
|
-
|
|
124
|
+
if (!data) return null;
|
|
125
|
+
return {
|
|
126
|
+
agentId: (data.agent_id as string | null) ?? null,
|
|
127
|
+
subagentId: (data.subagent_id as string | null) ?? null,
|
|
128
|
+
};
|
|
120
129
|
}
|
|
121
130
|
|
|
122
131
|
// ── Validate that every doc id belongs to userId in the right table ──
|
|
@@ -159,12 +168,14 @@ export async function handleProposeAttach(args: {
|
|
|
159
168
|
(x): x is string => typeof x === "string",
|
|
160
169
|
);
|
|
161
170
|
|
|
162
|
-
const
|
|
171
|
+
const identity = await loadTargetIdentity({
|
|
163
172
|
supabase, userId, targetKind, targetId,
|
|
164
173
|
});
|
|
165
|
-
if (!
|
|
174
|
+
if (!identity || (!identity.agentId && !identity.subagentId)) {
|
|
175
|
+
return err("target_not_found_or_no_agent");
|
|
176
|
+
}
|
|
166
177
|
|
|
167
|
-
const tierResolution = await
|
|
178
|
+
const tierResolution = await resolveTargetTier(supabase, identity, userId);
|
|
168
179
|
if (!tierResolution.tier) {
|
|
169
180
|
return err("agent_unclassified — set tier in dashboard agent settings or via override");
|
|
170
181
|
}
|
package/src/prompt.ts
CHANGED
|
@@ -49,11 +49,12 @@ Actions: "list", "create", "update", "delete", "add_approval", "list_approvals",
|
|
|
49
49
|
- resolve_approval: Approve/reject. Requires: approval_id, approval_status. Optional: comment`,
|
|
50
50
|
|
|
51
51
|
OFIERE_AGENT_OPS: `Query + manage agents and their staff subagents.
|
|
52
|
-
Actions: "list", "list_subagents", "create_subagent", "delete_subagent"
|
|
52
|
+
Actions: "list", "list_subagents", "create_subagent", "delete_subagent", "invalidate_tier_cache"
|
|
53
53
|
- list: All top-level agents (chiefs / native + OpenClaw) with IDs, names, roles. Use for task assignment lookup.
|
|
54
54
|
- list_subagents: Staff under a chief. Required: chief_agent_id.
|
|
55
55
|
- create_subagent: Add a staff subagent under a chief (max 5 per chief). Required: chief_agent_id, name. Optional: role (default "Staff"), codename, color_hex.
|
|
56
|
-
- delete_subagent: Remove a staff subagent. Required: subagent_id
|
|
56
|
+
- delete_subagent: Remove a staff subagent. Required: subagent_id.
|
|
57
|
+
- invalidate_tier_cache: Flush plugin's in-process tier cache (5 min TTL). Optional: agent_id. Use after direct-DB mutation of agent_tier_overrides.`,
|
|
57
58
|
|
|
58
59
|
OFIERE_PROJECT_OPS: `Manage PM hierarchy.
|
|
59
60
|
Actions: "list_spaces", "create_space", "update_space", "delete_space", "list_folders", "create_folder", "update_folder", "delete_folder", "list_dependencies", "add_dependency", "remove_dependency"
|
package/src/tools.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
handleCommitAttach,
|
|
19
19
|
registerAttachmentContextHook,
|
|
20
20
|
} from "./attachments.js";
|
|
21
|
+
import { invalidateAgentTier } from "./agent-tier.js";
|
|
21
22
|
|
|
22
23
|
// ─── Tool result shape (matches OpenClaw SDK) ────────────────────────────────
|
|
23
24
|
|
|
@@ -1093,7 +1094,8 @@ function registerAgentOps(
|
|
|
1093
1094
|
`- "list": List all top-level agents (chiefs / native + OpenClaw) with their IDs, names, roles, and status. Use this to find the correct agent_id for task assignment.\n` +
|
|
1094
1095
|
`- "list_subagents": List staff subagents under a chief. Required: chief_agent_id.\n` +
|
|
1095
1096
|
`- "create_subagent": Create a staff subagent under a chief (max 5 per chief). Required: chief_agent_id, name. Optional: role (default "Staff"), codename, color_hex (default "#64748b").\n` +
|
|
1096
|
-
`- "delete_subagent": Remove a staff subagent. Required: subagent_id
|
|
1097
|
+
`- "delete_subagent": Remove a staff subagent. Required: subagent_id.\n` +
|
|
1098
|
+
`- "invalidate_tier_cache": Flush the plugin's in-process tier resolver cache (5 min TTL). Optional: agent_id to flush a single entry; omit to flush all entries for the calling user. Use after any direct-DB mutation of agent_tier_overrides.`,
|
|
1097
1099
|
parameters: {
|
|
1098
1100
|
type: "object",
|
|
1099
1101
|
required: ["action"],
|
|
@@ -1101,10 +1103,11 @@ function registerAgentOps(
|
|
|
1101
1103
|
action: {
|
|
1102
1104
|
type: "string",
|
|
1103
1105
|
description: "The operation to perform",
|
|
1104
|
-
enum: ["list", "list_subagents", "create_subagent", "delete_subagent"],
|
|
1106
|
+
enum: ["list", "list_subagents", "create_subagent", "delete_subagent", "invalidate_tier_cache"],
|
|
1105
1107
|
},
|
|
1106
1108
|
chief_agent_id: { type: "string", description: "Chief agent ID (required for list_subagents, create_subagent)" },
|
|
1107
1109
|
subagent_id: { type: "string", description: "Subagent ID (required for delete_subagent)" },
|
|
1110
|
+
agent_id: { type: "string", description: "Agent ID (optional for invalidate_tier_cache — omit to flush all entries for the user)" },
|
|
1108
1111
|
name: { type: "string", description: "Subagent display name (required for create_subagent)" },
|
|
1109
1112
|
role: { type: "string", description: "Subagent role label, e.g. 'Staff', 'Analyst'. Defaults to 'Staff'." },
|
|
1110
1113
|
codename: { type: "string", description: "Optional subagent codename" },
|
|
@@ -1123,13 +1126,29 @@ function registerAgentOps(
|
|
|
1123
1126
|
return handleCreateSubagent(supabase, userId, params);
|
|
1124
1127
|
case "delete_subagent":
|
|
1125
1128
|
return handleDeleteSubagent(supabase, userId, params);
|
|
1129
|
+
case "invalidate_tier_cache":
|
|
1130
|
+
return handleInvalidateTierCache(userId, params);
|
|
1126
1131
|
default:
|
|
1127
|
-
return err(`Unknown action "${action}". Valid actions: list, list_subagents, create_subagent, delete_subagent`);
|
|
1132
|
+
return err(`Unknown action "${action}". Valid actions: list, list_subagents, create_subagent, delete_subagent, invalidate_tier_cache`);
|
|
1128
1133
|
}
|
|
1129
1134
|
},
|
|
1130
1135
|
});
|
|
1131
1136
|
}
|
|
1132
1137
|
|
|
1138
|
+
function handleInvalidateTierCache(userId: string, params: Record<string, unknown>): ToolResult {
|
|
1139
|
+
const agentId = params.agent_id as string | undefined;
|
|
1140
|
+
invalidateAgentTier(userId, agentId);
|
|
1141
|
+
return ok({
|
|
1142
|
+
ok: true,
|
|
1143
|
+
scope: agentId ? "single" : "user",
|
|
1144
|
+
user_id: userId,
|
|
1145
|
+
agent_id: agentId ?? null,
|
|
1146
|
+
message: agentId
|
|
1147
|
+
? `Plugin tier cache flushed for agent ${agentId}.`
|
|
1148
|
+
: `Plugin tier cache flushed for all entries belonging to user ${userId}.`,
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1133
1152
|
async function handleListAgents(
|
|
1134
1153
|
api: any,
|
|
1135
1154
|
supabase: SupabaseClient,
|