ofiere-openclaw-plugin 1.0.2 → 1.1.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.
Files changed (3) hide show
  1. package/index.ts +5 -1
  2. package/package.json +1 -1
  3. package/src/tools.ts +113 -20
package/index.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
5
5
  import { parseOfiereConfig } from "./src/config.js";
6
6
  import { getSupabase } from "./src/supabase.js";
7
- import { registerTools } from "./src/tools.js";
7
+ import { registerTools, probeApiForAgentName } from "./src/tools.js";
8
8
  import { getSystemPrompt } from "./src/prompt.js";
9
9
  import { registerCli } from "./src/cli.js";
10
10
  import { seedAgentCache } from "./src/agent-resolver.js";
@@ -70,6 +70,10 @@ const ofierePlugin = {
70
70
  // ── Connect to Supabase and register tools ────────────────────────────
71
71
  try {
72
72
  const supabase = getSupabase(config.supabaseUrl, config.serviceRoleKey);
73
+
74
+ // Probe the api object for any agent identity info (for debugging + fallback)
75
+ probeApiForAgentName(api, api.logger);
76
+
73
77
  registerTools(api, supabase, config);
74
78
  promptState.toolCount = 5;
75
79
  promptState.ready = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofiere-openclaw-plugin",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "OpenClaw plugin for Ofiere PM — manage tasks, agents, and projects from your agent",
6
6
  "keywords": ["openclaw", "ofiere", "project-management", "agents", "plugin"],
package/src/tools.ts CHANGED
@@ -30,19 +30,74 @@ function err(message: string): ToolResult {
30
30
 
31
31
  // ─── Helper: extract calling agent's accountId from OpenClaw context ─────────
32
32
 
33
+ // Module-level: set once at registration time from index.ts
34
+ let _registrationAgentName = "";
35
+ export function setRegistrationAgentName(name: string) {
36
+ if (name && !_registrationAgentName) _registrationAgentName = name;
37
+ }
38
+
33
39
  function getCallingAgentName(api: any): string {
34
- // OpenClaw passes agent context in various ways — try all known paths
40
+ // OpenClaw passes agent context in various ways — try ALL known paths
41
+ try {
42
+ const candidates = [
43
+ api?.agentContext?.accountId,
44
+ api?.agentContext?.name,
45
+ api?.agentContext?.id,
46
+ api?.currentAgent?.accountId,
47
+ api?.currentAgent?.name,
48
+ api?.currentAgent?.id,
49
+ api?.agent?.accountId,
50
+ api?.agent?.name,
51
+ api?.agent?.id,
52
+ api?.agentId,
53
+ api?.agentName,
54
+ api?.accountId,
55
+ api?.name,
56
+ api?.id,
57
+ api?.metadata?.agentId,
58
+ api?.metadata?.accountId,
59
+ api?.metadata?.agentName,
60
+ api?.context?.agentId,
61
+ api?.context?.accountId,
62
+ api?.context?.agent?.name,
63
+ ];
64
+ for (const c of candidates) {
65
+ if (typeof c === "string" && c.trim()) return c.trim();
66
+ }
67
+ } catch {
68
+ // ignore
69
+ }
70
+ return "";
71
+ }
72
+
73
+ /**
74
+ * Probe the API object for any agent identity info. Called once at registration.
75
+ * Logs ALL available keys for debugging.
76
+ */
77
+ export function probeApiForAgentName(api: any, logger?: any): string {
78
+ // Direct detection
79
+ const name = getCallingAgentName(api);
80
+ if (name) {
81
+ logger?.info?.(`[ofiere] Detected agent from API: "${name}"`);
82
+ setRegistrationAgentName(name);
83
+ return name;
84
+ }
85
+
86
+ // Log all top-level keys for future debugging
35
87
  try {
36
- return (
37
- api?.agentContext?.accountId ||
38
- api?.agentContext?.name ||
39
- api?.currentAgent?.accountId ||
40
- api?.currentAgent?.name ||
41
- ""
42
- );
88
+ const keys = Object.keys(api || {});
89
+ logger?.debug?.(`[ofiere] API object keys: ${JSON.stringify(keys)}`);
90
+ // Check if any key looks like it contains agent info
91
+ for (const key of keys) {
92
+ const val = api[key];
93
+ if (typeof val === "string" && val.length > 0 && val.length < 50) {
94
+ logger?.debug?.(`[ofiere] API.${key} = "${val}"`);
95
+ }
96
+ }
43
97
  } catch {
44
- return "";
98
+ // ignore
45
99
  }
100
+ return "";
46
101
  }
47
102
 
48
103
  // ─── Tool Registration ───────────────────────────────────────────────────────
@@ -57,11 +112,23 @@ export function registerTools(
57
112
 
58
113
  /**
59
114
  * Resolve the agent ID for the calling agent.
60
- * Priority: explicit param > runtime context > env var fallback
115
+ * Priority: explicit param > runtime context > registration-time detection > env var > DB fallback
61
116
  */
62
117
  async function resolveAgent(explicitId?: string): Promise<string | null> {
63
- // 1. Explicit agent_id passed by the LLM (e.g. "create task for Daisy")
64
- if (explicitId && explicitId.trim()) return explicitId.trim();
118
+ // 1. Explicit agent_id passed by the LLM (e.g. "ivy", "daisy", or a UUID)
119
+ if (explicitId && explicitId.trim()) {
120
+ const trimmed = explicitId.trim();
121
+ // If it looks like a UUID or our ID format, use directly
122
+ if (trimmed.match(/^[0-9a-f]{8}-/) || trimmed.match(/^agent-/)) {
123
+ return trimmed;
124
+ }
125
+ // Otherwise treat as a name and resolve to the actual agent ID
126
+ try {
127
+ return await resolveAgentId(trimmed, userId, supabase);
128
+ } catch {
129
+ return trimmed; // fallback: use as-is
130
+ }
131
+ }
65
132
 
66
133
  // 2. Runtime: read calling agent's name from OpenClaw context
67
134
  const callerName = getCallingAgentName(api);
@@ -69,12 +136,37 @@ export function registerTools(
69
136
  try {
70
137
  return await resolveAgentId(callerName, userId, supabase);
71
138
  } catch {
72
- // Fall through to env var
139
+ // Fall through
140
+ }
141
+ }
142
+
143
+ // 3. Registration-time detection (set when plugin was loaded)
144
+ if (_registrationAgentName) {
145
+ try {
146
+ return await resolveAgentId(_registrationAgentName, userId, supabase);
147
+ } catch {
148
+ // Fall through
73
149
  }
74
150
  }
75
151
 
76
- // 3. Env var fallback (OFIERE_AGENT_ID — legacy single-agent mode)
77
- return fallbackAgentId || null;
152
+ // 4. Env var fallback (OFIERE_AGENT_ID — legacy single-agent mode)
153
+ if (fallbackAgentId) return fallbackAgentId;
154
+
155
+ // 5. Nuclear fallback: query the FIRST agent for this user
156
+ try {
157
+ const { data } = await supabase
158
+ .from("agents")
159
+ .select("id")
160
+ .eq("user_id", userId)
161
+ .order("name", { ascending: true })
162
+ .limit(1)
163
+ .single();
164
+ if (data?.id) return data.id;
165
+ } catch {
166
+ // ignore
167
+ }
168
+
169
+ return null;
78
170
  }
79
171
 
80
172
  // ── OFIERE_LIST_TASKS — Required (read-only, no side effects) ────────
@@ -134,21 +226,22 @@ export function registerTools(
134
226
  label: "Create Ofiere Task",
135
227
  description:
136
228
  "Create a new task in the Ofiere PM dashboard. " +
137
- "If agent_id is not provided, the task is automatically assigned to you (the calling agent). " +
229
+ "IMPORTANT: You MUST always pass your own name as agent_id (e.g. 'ivy', 'daisy') to assign the task to yourself. " +
230
+ "If you want to assign to a different agent, pass their name instead. " +
138
231
  "Pass agent_id as 'none' or 'unassigned' to create an unassigned task. " +
139
232
  "The task will appear in the dashboard immediately via real-time sync.",
140
233
  parameters: {
141
234
  type: "object",
142
- required: ["title"],
235
+ required: ["title", "agent_id"],
143
236
  properties: {
144
237
  title: { type: "string", description: "Task title (required)" },
145
238
  description: { type: "string", description: "Task description" },
146
239
  agent_id: {
147
240
  type: "string",
148
241
  description:
149
- "Agent ID to assign the task to. If omitted, assigns to yourself. " +
150
- "Pass 'none' or 'unassigned' to create a task with no assignee. " +
151
- "Use OFIERE_LIST_AGENTS to see available agents.",
242
+ "REQUIRED. Your own agent name (e.g. 'ivy', 'daisy', 'celia') to self-assign, " +
243
+ "or another agent's name to assign to them. " +
244
+ "Pass 'none' or 'unassigned' to create a task with no assignee.",
152
245
  },
153
246
  status: {
154
247
  type: "string",