ofiere-openclaw-plugin 4.40.1 → 4.41.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofiere-openclaw-plugin",
3
- "version": "4.40.1",
3
+ "version": "4.41.0",
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
@@ -134,6 +134,36 @@ export async function getAgentTier(
134
134
  return (await resolveAgentTier(supabase, agentId, userId)).tier;
135
135
  }
136
136
 
137
+ // Target-aware resolution: when a work target carries a subagent_id, the
138
+ // work is staff-tier regardless of which chief routes it. Uncached lookup —
139
+ // subagent rows can be deleted out from under us.
140
+ export interface TargetTierInput {
141
+ agentId: string | null;
142
+ subagentId?: string | null;
143
+ }
144
+
145
+ export async function resolveTargetTier(
146
+ supabase: SupabaseClient,
147
+ target: TargetTierInput,
148
+ userId: string,
149
+ ): Promise<TierResolution> {
150
+ if (target.subagentId) {
151
+ try {
152
+ const { data } = await supabase
153
+ .from("agent_subagents")
154
+ .select("id")
155
+ .eq("user_id", userId)
156
+ .eq("id", target.subagentId)
157
+ .maybeSingle();
158
+ if (data?.id) return { tier: "staff", source: "subagent" };
159
+ } catch {
160
+ // Fall through to chief lookup
161
+ }
162
+ }
163
+ if (target.agentId) return resolveAgentTier(supabase, target.agentId, userId);
164
+ return { tier: null, source: "none" };
165
+ }
166
+
137
167
  export function isDocKindValidForTier(
138
168
  docKind: "sop" | "framework",
139
169
  tier: AgentTier,
@@ -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
- resolveAgentTier,
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 (mainly to read its agent_id for tier check) ──
103
- async function loadTargetAgentId(args: {
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<string | null> {
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
- return (data?.agent_id as string) || null;
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 targetAgentId = await loadTargetAgentId({
171
+ const identity = await loadTargetIdentity({
163
172
  supabase, userId, targetKind, targetId,
164
173
  });
165
- if (!targetAgentId) return err("target_not_found_or_no_agent");
174
+ if (!identity || (!identity.agentId && !identity.subagentId)) {
175
+ return err("target_not_found_or_no_agent");
176
+ }
166
177
 
167
- const tierResolution = await resolveAgentTier(supabase, targetAgentId, userId);
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,