ofiere-openclaw-plugin 4.50.0-probe.2 → 4.50.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/dist/src/tools.js CHANGED
@@ -21,6 +21,35 @@ function ok(data) {
21
21
  content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
22
22
  };
23
23
  }
24
+ // Extract plain text from a message that may be a plain string, a `{text}`
25
+ // envelope, or — under OpenClaw 2026.5+ — a structured content array such as
26
+ // `[{type:'text', text:'...'}, {type:'thinking', thinking:'...'}]`.
27
+ //
28
+ // BUG 12 fix (cycle 7b BUGSHOOT-5 → cycle 10 pivot): the prior inline
29
+ // expression returned the array value itself instead of joining the text
30
+ // parts because `||` short-circuited on the truthy array. Result: every
31
+ // staff dispatch persisted empty content. Centralising the logic in one
32
+ // helper avoids the same bug recurring at every call site.
33
+ function extractMessageText(msg) {
34
+ if (typeof msg === "string")
35
+ return msg;
36
+ if (typeof msg?.text === "string")
37
+ return msg.text;
38
+ if (typeof msg?.content === "string")
39
+ return msg.content;
40
+ if (typeof msg?.message?.content === "string")
41
+ return msg.message.content;
42
+ const arr = Array.isArray(msg?.content) ? msg.content
43
+ : Array.isArray(msg?.message?.content) ? msg.message.content
44
+ : null;
45
+ if (arr) {
46
+ return arr
47
+ .filter((c) => c?.type === "text" && typeof c?.text === "string")
48
+ .map((c) => c.text)
49
+ .join("\n");
50
+ }
51
+ return "";
52
+ }
24
53
  function err(message) {
25
54
  return {
26
55
  content: [{ type: "text", text: `Error: ${message}` }],
@@ -6385,12 +6414,6 @@ supabase, config) {
6385
6414
  registerAttachmentContextHook({ api, supabase, userId, fallbackAgentId, resolveAgent });
6386
6415
  // ── Register agent_end hook for server-side brain extraction ──
6387
6416
  registerBrainExtractionHook(api, supabase, userId, fallbackAgentId);
6388
- // ── CYCLE 10 PROBE — verify subagent_* hook payload shapes ─────────────
6389
- // Probe-only build (4.50.0-probe.x). Logs full payload on first fire of
6390
- // each native subagent hook + triggers a one-shot subagent.run 8s after
6391
- // init so we capture spawning → spawned → ended lifecycle. Removed in
6392
- // 4.50.0 production release.
6393
- registerCycle10Probe(api);
6394
6417
  // ── Count and log ──
6395
6418
  const toolCount = 16;
6396
6419
  const callerName = getCallingAgentName(api);
@@ -6425,22 +6448,21 @@ function registerBrainExtractionHook(api, supabase, userId, fallbackAgentId) {
6425
6448
  return;
6426
6449
  }
6427
6450
  // Find last user + last assistant message
6451
+ // BUG 12 fix: extractMessageText handles structured content arrays
6452
+ // (OpenClaw 2026.5+ delivers `[{type:'text',text:'X'},{type:'thinking',...}]`,
6453
+ // not plain strings). Prior inline expression returned the array, which
6454
+ // typeof-checked to "object" → both lastUser/lastAssistant became "".
6428
6455
  let lastUser = "";
6429
6456
  let lastAssistant = "";
6430
6457
  for (let i = messages.length - 1; i >= 0; i--) {
6431
6458
  const msg = messages[i];
6432
6459
  const role = msg?.role || msg?.message?.role;
6433
- const text = typeof msg === "string" ? msg
6434
- : msg?.text || msg?.content || msg?.message?.content
6435
- ?.filter?.((c) => c.type === "text")
6436
- ?.map?.((c) => c.text)
6437
- ?.join?.("\n")
6438
- || (typeof msg?.message?.content === "string" ? msg.message.content : "");
6460
+ const text = extractMessageText(msg);
6439
6461
  if (!lastAssistant && (role === "assistant" || role === "model")) {
6440
- lastAssistant = typeof text === "string" ? text : "";
6462
+ lastAssistant = text;
6441
6463
  }
6442
6464
  if (!lastUser && role === "user") {
6443
- lastUser = typeof text === "string" ? text : "";
6465
+ lastUser = text;
6444
6466
  }
6445
6467
  if (lastUser && lastAssistant)
6446
6468
  break;
@@ -6753,102 +6775,6 @@ function registerBrainExtractionHook(api, supabase, userId, fallbackAgentId) {
6753
6775
  api.logger.debug?.("[ofiere] Could not register agent_end hook — may not be supported in this OpenClaw version");
6754
6776
  }
6755
6777
  }
6756
- // ─────────────────────────────────────────────────────────────────────────────
6757
- // CYCLE 10 PROBE — REMOVE/DISABLE BEFORE PRODUCTION 4.50.x
6758
- // ─────────────────────────────────────────────────────────────────────────────
6759
- // One-shot diagnostic that logs the full payload shape of every native
6760
- // subagent_* hook, then triggers a single api.runtime.subagent.run so the
6761
- // lifecycle fires end-to-end. Used to verify field names before the real
6762
- // subagent_ended handler is written. Activated by env var
6763
- // OFIERE_CYCLE10_PROBE=1; otherwise inert.
6764
- //
6765
- // Captured in cycle 10 BUGSHOOT under hook key [ofiere-probe].
6766
- // ─────────────────────────────────────────────────────────────────────────────
6767
- function registerCycle10Probe(api) {
6768
- const safeStringify = (obj, max = 4000) => {
6769
- try {
6770
- const seen = new WeakSet();
6771
- const out = JSON.stringify(obj, (_k, v) => {
6772
- if (typeof v === "object" && v !== null) {
6773
- if (seen.has(v))
6774
- return "[Circular]";
6775
- seen.add(v);
6776
- }
6777
- if (typeof v === "function")
6778
- return `[Function ${v.name || "anon"}]`;
6779
- return v;
6780
- }, 2);
6781
- return out.length > max ? out.slice(0, max) + "...[truncated]" : out;
6782
- }
6783
- catch (e) {
6784
- return `[stringify-fail: ${e instanceof Error ? e.message : String(e)}]`;
6785
- }
6786
- };
6787
- const hookNames = [
6788
- "subagent_spawning",
6789
- "subagent_spawned",
6790
- "subagent_delivery_target",
6791
- "subagent_ended",
6792
- ];
6793
- for (const hook of hookNames) {
6794
- try {
6795
- api.on?.(hook, (event, ctx) => {
6796
- try {
6797
- api.logger.info?.(`[ofiere-probe] ${hook} event=${safeStringify(event, 2000)} ctx=${safeStringify(ctx, 2000)}`);
6798
- }
6799
- catch (e) {
6800
- api.logger.warn?.(`[ofiere-probe] ${hook} log failed: ${e instanceof Error ? e.message : String(e)}`);
6801
- }
6802
- });
6803
- api.logger.info?.(`[ofiere-probe] ${hook} hook registered`);
6804
- }
6805
- catch (e) {
6806
- api.logger.warn?.(`[ofiere-probe] ${hook} hook register failed: ${e instanceof Error ? e.message : String(e)}`);
6807
- }
6808
- }
6809
- // One-shot spawn 8s after init so the gateway is fully up.
6810
- setTimeout(async () => {
6811
- const sessionKey = `agent:celia:subagent:probe-${Date.now().toString(36).slice(-6)}`;
6812
- api.logger.info?.(`[ofiere-probe] firing api.runtime.subagent.run sessionKey=${sessionKey}`);
6813
- try {
6814
- const runtime = api?.runtime;
6815
- if (!runtime?.subagent?.run) {
6816
- api.logger.error?.(`[ofiere-probe] api.runtime.subagent.run NOT AVAILABLE on this OpenClaw version. ` +
6817
- `runtime keys: ${runtime ? Object.keys(runtime).join(",") : "(no runtime)"}`);
6818
- return;
6819
- }
6820
- const result = await runtime.subagent.run({
6821
- sessionKey,
6822
- message: "Reply with exactly the text PROBE-OK and nothing else.",
6823
- deliver: false,
6824
- });
6825
- api.logger.info?.(`[ofiere-probe] subagent.run returned ${safeStringify(result, 1000)}`);
6826
- // Try waitForRun + getSessionMessages immediately so we capture the
6827
- // full transcript shape even before subagent_ended fires.
6828
- if (result?.runId && runtime.subagent.waitForRun) {
6829
- try {
6830
- const waitResult = await runtime.subagent.waitForRun({ runId: result.runId, timeoutMs: 30000 });
6831
- api.logger.info?.(`[ofiere-probe] waitForRun returned ${safeStringify(waitResult, 2000)}`);
6832
- }
6833
- catch (e) {
6834
- api.logger.warn?.(`[ofiere-probe] waitForRun threw: ${e instanceof Error ? e.message : String(e)}`);
6835
- }
6836
- }
6837
- if (runtime.subagent.getSessionMessages) {
6838
- try {
6839
- const msgsResult = await runtime.subagent.getSessionMessages({ sessionKey, limit: 10 });
6840
- api.logger.info?.(`[ofiere-probe] getSessionMessages returned ${safeStringify(msgsResult, 4000)}`);
6841
- }
6842
- catch (e) {
6843
- api.logger.warn?.(`[ofiere-probe] getSessionMessages threw: ${e instanceof Error ? e.message : String(e)}`);
6844
- }
6845
- }
6846
- }
6847
- catch (e) {
6848
- api.logger.error?.(`[ofiere-probe] subagent.run threw: ${e instanceof Error ? `${e.name}: ${e.message}\n${e.stack}` : String(e)}`);
6849
- }
6850
- }, 8000);
6851
- }
6852
6778
  // ── Brain Context Bootstrap Injection ──────────────────────────────────────
6853
6779
  // Injects the calling agent's active memories into the system prompt via
6854
6780
  // api.on("before_prompt_build"). Uses the TMT hierarchy:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofiere-openclaw-plugin",
3
- "version": "4.50.0-probe.2",
3
+ "version": "4.50.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/tools.ts CHANGED
@@ -36,6 +36,32 @@ function ok(data: unknown): ToolResult {
36
36
  };
37
37
  }
38
38
 
39
+ // Extract plain text from a message that may be a plain string, a `{text}`
40
+ // envelope, or — under OpenClaw 2026.5+ — a structured content array such as
41
+ // `[{type:'text', text:'...'}, {type:'thinking', thinking:'...'}]`.
42
+ //
43
+ // BUG 12 fix (cycle 7b BUGSHOOT-5 → cycle 10 pivot): the prior inline
44
+ // expression returned the array value itself instead of joining the text
45
+ // parts because `||` short-circuited on the truthy array. Result: every
46
+ // staff dispatch persisted empty content. Centralising the logic in one
47
+ // helper avoids the same bug recurring at every call site.
48
+ function extractMessageText(msg: any): string {
49
+ if (typeof msg === "string") return msg;
50
+ if (typeof msg?.text === "string") return msg.text;
51
+ if (typeof msg?.content === "string") return msg.content;
52
+ if (typeof msg?.message?.content === "string") return msg.message.content;
53
+ const arr = Array.isArray(msg?.content) ? msg.content
54
+ : Array.isArray(msg?.message?.content) ? msg.message.content
55
+ : null;
56
+ if (arr) {
57
+ return arr
58
+ .filter((c: any) => c?.type === "text" && typeof c?.text === "string")
59
+ .map((c: any) => c.text)
60
+ .join("\n");
61
+ }
62
+ return "";
63
+ }
64
+
39
65
  function err(message: string): ToolResult {
40
66
  return {
41
67
  content: [{ type: "text" as const, text: `Error: ${message}` }],
@@ -6536,13 +6562,6 @@ export function registerTools(
6536
6562
  // ── Register agent_end hook for server-side brain extraction ──
6537
6563
  registerBrainExtractionHook(api, supabase, userId, fallbackAgentId);
6538
6564
 
6539
- // ── CYCLE 10 PROBE — verify subagent_* hook payload shapes ─────────────
6540
- // Probe-only build (4.50.0-probe.x). Logs full payload on first fire of
6541
- // each native subagent hook + triggers a one-shot subagent.run 8s after
6542
- // init so we capture spawning → spawned → ended lifecycle. Removed in
6543
- // 4.50.0 production release.
6544
- registerCycle10Probe(api);
6545
-
6546
6565
  // ── Count and log ──
6547
6566
  const toolCount = 16;
6548
6567
  const callerName = getCallingAgentName(api);
@@ -6586,23 +6605,22 @@ function registerBrainExtractionHook(
6586
6605
  }
6587
6606
 
6588
6607
  // Find last user + last assistant message
6608
+ // BUG 12 fix: extractMessageText handles structured content arrays
6609
+ // (OpenClaw 2026.5+ delivers `[{type:'text',text:'X'},{type:'thinking',...}]`,
6610
+ // not plain strings). Prior inline expression returned the array, which
6611
+ // typeof-checked to "object" → both lastUser/lastAssistant became "".
6589
6612
  let lastUser = "";
6590
6613
  let lastAssistant = "";
6591
6614
  for (let i = messages.length - 1; i >= 0; i--) {
6592
6615
  const msg = messages[i];
6593
6616
  const role = msg?.role || msg?.message?.role;
6594
- const text = typeof msg === "string" ? msg
6595
- : msg?.text || msg?.content || msg?.message?.content
6596
- ?.filter?.((c: any) => c.type === "text")
6597
- ?.map?.((c: any) => c.text)
6598
- ?.join?.("\n")
6599
- || (typeof msg?.message?.content === "string" ? msg.message.content : "");
6617
+ const text = extractMessageText(msg);
6600
6618
 
6601
6619
  if (!lastAssistant && (role === "assistant" || role === "model")) {
6602
- lastAssistant = typeof text === "string" ? text : "";
6620
+ lastAssistant = text;
6603
6621
  }
6604
6622
  if (!lastUser && role === "user") {
6605
- lastUser = typeof text === "string" ? text : "";
6623
+ lastUser = text;
6606
6624
  }
6607
6625
  if (lastUser && lastAssistant) break;
6608
6626
  }
@@ -6932,105 +6950,6 @@ function registerBrainExtractionHook(
6932
6950
  }
6933
6951
  }
6934
6952
 
6935
- // ─────────────────────────────────────────────────────────────────────────────
6936
- // CYCLE 10 PROBE — REMOVE/DISABLE BEFORE PRODUCTION 4.50.x
6937
- // ─────────────────────────────────────────────────────────────────────────────
6938
- // One-shot diagnostic that logs the full payload shape of every native
6939
- // subagent_* hook, then triggers a single api.runtime.subagent.run so the
6940
- // lifecycle fires end-to-end. Used to verify field names before the real
6941
- // subagent_ended handler is written. Activated by env var
6942
- // OFIERE_CYCLE10_PROBE=1; otherwise inert.
6943
- //
6944
- // Captured in cycle 10 BUGSHOOT under hook key [ofiere-probe].
6945
- // ─────────────────────────────────────────────────────────────────────────────
6946
-
6947
- function registerCycle10Probe(api: any): void {
6948
- const safeStringify = (obj: unknown, max = 4000): string => {
6949
- try {
6950
- const seen = new WeakSet<object>();
6951
- const out = JSON.stringify(obj, (_k, v) => {
6952
- if (typeof v === "object" && v !== null) {
6953
- if (seen.has(v)) return "[Circular]";
6954
- seen.add(v);
6955
- }
6956
- if (typeof v === "function") return `[Function ${v.name || "anon"}]`;
6957
- return v;
6958
- }, 2);
6959
- return out.length > max ? out.slice(0, max) + "...[truncated]" : out;
6960
- } catch (e) {
6961
- return `[stringify-fail: ${e instanceof Error ? e.message : String(e)}]`;
6962
- }
6963
- };
6964
-
6965
- const hookNames = [
6966
- "subagent_spawning",
6967
- "subagent_spawned",
6968
- "subagent_delivery_target",
6969
- "subagent_ended",
6970
- ];
6971
- for (const hook of hookNames) {
6972
- try {
6973
- api.on?.(hook, (event: any, ctx: any) => {
6974
- try {
6975
- api.logger.info?.(
6976
- `[ofiere-probe] ${hook} event=${safeStringify(event, 2000)} ctx=${safeStringify(ctx, 2000)}`,
6977
- );
6978
- } catch (e) {
6979
- api.logger.warn?.(`[ofiere-probe] ${hook} log failed: ${e instanceof Error ? e.message : String(e)}`);
6980
- }
6981
- });
6982
- api.logger.info?.(`[ofiere-probe] ${hook} hook registered`);
6983
- } catch (e) {
6984
- api.logger.warn?.(`[ofiere-probe] ${hook} hook register failed: ${e instanceof Error ? e.message : String(e)}`);
6985
- }
6986
- }
6987
-
6988
- // One-shot spawn 8s after init so the gateway is fully up.
6989
- setTimeout(async () => {
6990
- const sessionKey = `agent:celia:subagent:probe-${Date.now().toString(36).slice(-6)}`;
6991
- api.logger.info?.(`[ofiere-probe] firing api.runtime.subagent.run sessionKey=${sessionKey}`);
6992
- try {
6993
- const runtime = (api as any)?.runtime;
6994
- if (!runtime?.subagent?.run) {
6995
- api.logger.error?.(
6996
- `[ofiere-probe] api.runtime.subagent.run NOT AVAILABLE on this OpenClaw version. ` +
6997
- `runtime keys: ${runtime ? Object.keys(runtime).join(",") : "(no runtime)"}`,
6998
- );
6999
- return;
7000
- }
7001
- const result = await runtime.subagent.run({
7002
- sessionKey,
7003
- message: "Reply with exactly the text PROBE-OK and nothing else.",
7004
- deliver: false,
7005
- });
7006
- api.logger.info?.(`[ofiere-probe] subagent.run returned ${safeStringify(result, 1000)}`);
7007
-
7008
- // Try waitForRun + getSessionMessages immediately so we capture the
7009
- // full transcript shape even before subagent_ended fires.
7010
- if (result?.runId && runtime.subagent.waitForRun) {
7011
- try {
7012
- const waitResult = await runtime.subagent.waitForRun({ runId: result.runId, timeoutMs: 30000 });
7013
- api.logger.info?.(`[ofiere-probe] waitForRun returned ${safeStringify(waitResult, 2000)}`);
7014
- } catch (e) {
7015
- api.logger.warn?.(`[ofiere-probe] waitForRun threw: ${e instanceof Error ? e.message : String(e)}`);
7016
- }
7017
- }
7018
- if (runtime.subagent.getSessionMessages) {
7019
- try {
7020
- const msgsResult = await runtime.subagent.getSessionMessages({ sessionKey, limit: 10 });
7021
- api.logger.info?.(`[ofiere-probe] getSessionMessages returned ${safeStringify(msgsResult, 4000)}`);
7022
- } catch (e) {
7023
- api.logger.warn?.(`[ofiere-probe] getSessionMessages threw: ${e instanceof Error ? e.message : String(e)}`);
7024
- }
7025
- }
7026
- } catch (e) {
7027
- api.logger.error?.(
7028
- `[ofiere-probe] subagent.run threw: ${e instanceof Error ? `${e.name}: ${e.message}\n${e.stack}` : String(e)}`,
7029
- );
7030
- }
7031
- }, 8000);
7032
- }
7033
-
7034
6953
  // ── Brain Context Bootstrap Injection ──────────────────────────────────────
7035
6954
  // Injects the calling agent's active memories into the system prompt via
7036
6955
  // api.on("before_prompt_build"). Uses the TMT hierarchy: