ofiere-openclaw-plugin 4.47.0 → 4.48.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.47.0",
3
+ "version": "4.48.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"],
@@ -15,7 +15,7 @@ import {
15
15
  invalidateAgentTier,
16
16
  } from "./agent-tier.js";
17
17
  import { issueAttachmentToken, verifyAttachmentToken } from "./attach-token.js";
18
- import { loadSubagentRow, buildStaffPersonaBlock, readDispatchSubagentId, loadChiefStaffDefaults, loadDispatchContextBySession, resolveStaffModels, type SubagentRow, type DispatchContextRow } from "./staffPersona.js";
18
+ import { loadSubagentRow, buildStaffPersonaBlock, readDispatchSubagentId, loadChiefStaffDefaults, loadChiefAgentConfig, loadDispatchContextBySession, resolveStaffModels, type SubagentRow, type DispatchContextRow } from "./staffPersona.js";
19
19
 
20
20
  interface ToolResult {
21
21
  content: Array<{ type: "text"; text: string }>;
@@ -453,8 +453,11 @@ export function registerAttachmentContextHook(args: {
453
453
  if (subagentId) {
454
454
  staffRow = await loadSubagentRow(supabase, userId, subagentId);
455
455
  if (staffRow && staffRow.chief_agent_id === resolvedAgentId) {
456
- const chiefDefaults = await loadChiefStaffDefaults(supabase, userId, staffRow.chief_agent_id).catch(() => null);
457
- const resolved = resolveStaffModels(staffRow, chiefDefaults);
456
+ const [chiefDefaults, chiefConfig] = await Promise.all([
457
+ loadChiefStaffDefaults(supabase, userId, staffRow.chief_agent_id).catch(() => null),
458
+ loadChiefAgentConfig(supabase, userId, staffRow.chief_agent_id).catch(() => null),
459
+ ]);
460
+ const resolved = resolveStaffModels(staffRow, chiefDefaults, chiefConfig);
458
461
  staffPrefix = buildStaffPersonaBlock(staffRow, resolved) + "\n\n---\n\n";
459
462
  api.logger?.debug?.(
460
463
  `[ofiere-staff] subagent ${subagentId} (${staffRow.name}) reporting to ${resolvedAgentId} — persona injected; resolved models: ${JSON.stringify(resolved)}`,
@@ -127,7 +127,7 @@ export interface ResolvedStaffModels {
127
127
  tool_call_model: string | null;
128
128
  function_call_model: string | null;
129
129
  vision_model: string | null;
130
- source: Record<StaffModelSlot, "staff" | "chief_default" | "chief_fallback" | null>;
130
+ source: Record<StaffModelSlot, "staff" | "chief_default" | "chief_primary" | "chief_fallback" | null>;
131
131
  }
132
132
 
133
133
  const SLOTS: StaffModelSlot[] = [
@@ -138,6 +138,17 @@ const SLOTS: StaffModelSlot[] = [
138
138
  "vision_model",
139
139
  ];
140
140
 
141
+ // Cycle 7b BUGSHOOT-3 — chief's own ofie_agent_config row, used as the third
142
+ // fallback tier when neither the staff slot nor the chief STAFF MODEL slot is
143
+ // set. Mirrors the dispatcher's loadStaffPrimaryModel chain.
144
+ export interface ChiefAgentConfigRow {
145
+ agent_id: string;
146
+ primary_model: string | null;
147
+ coding_model: string | null;
148
+ tool_call_model: string | null;
149
+ vision_model: string | null;
150
+ }
151
+
141
152
  export async function loadChiefStaffDefaults(
142
153
  supabase: SupabaseClient,
143
154
  userId: string,
@@ -155,19 +166,42 @@ export async function loadChiefStaffDefaults(
155
166
  return data as ChiefStaffDefaultsRow;
156
167
  }
157
168
 
169
+ export async function loadChiefAgentConfig(
170
+ supabase: SupabaseClient,
171
+ userId: string,
172
+ chiefAgentId: string,
173
+ ): Promise<ChiefAgentConfigRow | null> {
174
+ const { data, error } = await supabase
175
+ .from("ofie_agent_config")
176
+ .select("agent_id, primary_model, coding_model, tool_call_model, vision_model")
177
+ .eq("user_id", userId)
178
+ .eq("agent_id", chiefAgentId)
179
+ .maybeSingle();
180
+ if (error || !data) return null;
181
+ return data as ChiefAgentConfigRow;
182
+ }
183
+
158
184
  /**
159
- * Resolution chain at delegate_to_staff time:
160
- * staff.<slot> (per-staff override on agent_subagents)
161
- * ?? chief_staff_defaults.<slot> (chief-level "STAFF MODEL")
162
- * ?? null (caller falls back to the chief's own model — that lookup
163
- * happens in the OpenClaw runtime, not here).
185
+ * Resolution chain at delegate_to_staff time (BUGSHOOT-3):
186
+ * 1. staff.<slot> (per-staff override)
187
+ * 2. chief_staff_defaults.<slot> (chief-level STAFF MODEL)
188
+ * 3. ofie_agent_config.<slot> (chief's OWN slot, NEW)
189
+ * 4. ofie_agent_config.primary_model (chief's primary, last-resort sub)
190
+ * 5. null (gateway picks gateway default)
191
+ *
192
+ * Tier 3 was added in BUGSHOOT-3 because the prior chain stopped at tier 2 and
193
+ * relied on an implicit "gateway default" — which is opaque and led to BUG 10
194
+ * when the staff override held a model id the gateway rejected. With tier 3,
195
+ * an empty staff slot deterministically inherits the chief's actual model.
164
196
  *
165
- * The chief's own model is intentionally not touched. Empty staff slot +
166
- * empty chief STAFF MODEL = inherit chief's primary_model (gateway default).
197
+ * Tier 4 (slot primary_model fallback) handles the common case where chief
198
+ * has only a primary_model set and no per-slot routing — a staff with
199
+ * coding_model blank still inherits chief's primary_model rather than null.
167
200
  */
168
201
  export function resolveStaffModels(
169
202
  staff: SubagentRow,
170
203
  chiefDefaults: ChiefStaffDefaultsRow | null,
204
+ chiefConfig?: ChiefAgentConfigRow | null,
171
205
  ): ResolvedStaffModels {
172
206
  const out: ResolvedStaffModels = {
173
207
  primary_model: null,
@@ -183,15 +217,26 @@ export function resolveStaffModels(
183
217
  vision_model: null,
184
218
  },
185
219
  };
220
+ const chiefPrimary = typeof chiefConfig?.primary_model === "string" && chiefConfig.primary_model.trim()
221
+ ? chiefConfig.primary_model.trim()
222
+ : null;
186
223
  for (const slot of SLOTS) {
187
224
  const staffVal = (staff as any)[slot] as string | null | undefined;
188
225
  const chiefVal = chiefDefaults ? (chiefDefaults as any)[slot] as string | null | undefined : null;
226
+ const chiefSlotVal = chiefConfig ? (chiefConfig as any)[slot] as string | null | undefined : null;
189
227
  if (staffVal && staffVal.trim()) {
190
228
  out[slot] = staffVal;
191
229
  out.source[slot] = "staff";
192
230
  } else if (chiefVal && chiefVal.trim()) {
193
231
  out[slot] = chiefVal;
194
232
  out.source[slot] = "chief_default";
233
+ } else if (chiefSlotVal && chiefSlotVal.trim()) {
234
+ out[slot] = chiefSlotVal;
235
+ out.source[slot] = "chief_primary";
236
+ } else if (chiefPrimary) {
237
+ // Sub-fallback: chief has a primary_model but no per-slot override.
238
+ out[slot] = chiefPrimary;
239
+ out.source[slot] = "chief_primary";
195
240
  } else {
196
241
  out[slot] = null;
197
242
  out.source[slot] = "chief_fallback";
@@ -225,8 +270,11 @@ export function buildStaffPersonaBlock(
225
270
  lines.push(``, `## Operating Instructions`, staff.instructions.trim());
226
271
  }
227
272
  if (resolved && (resolved.primary_model || resolved.coding_model || resolved.tool_call_model || resolved.function_call_model || resolved.vision_model)) {
228
- const sourceLabel = (s: "staff" | "chief_default" | "chief_fallback" | null) =>
229
- s === "staff" ? "per-staff override" : s === "chief_default" ? "chief STAFF MODEL" : "inherits chief";
273
+ const sourceLabel = (s: "staff" | "chief_default" | "chief_primary" | "chief_fallback" | null) =>
274
+ s === "staff" ? "per-staff override"
275
+ : s === "chief_default" ? "chief STAFF MODEL"
276
+ : s === "chief_primary" ? "chief's own model"
277
+ : "inherits chief (gateway default)";
230
278
  const rows: string[] = [];
231
279
  if (resolved.primary_model) rows.push(`- primary: \`${resolved.primary_model}\` (${sourceLabel(resolved.source.primary_model)})`);
232
280
  if (resolved.coding_model) rows.push(`- coding: \`${resolved.coding_model}\` (${sourceLabel(resolved.source.coding_model)})`);