ofiere-openclaw-plugin 4.50.0-probe.1 → 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 +41 -114
- package/openclaw.plugin.json +23 -0
- package/package.json +1 -1
- package/src/tools.ts +38 -118
package/dist/src/tools.js
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
//
|
|
10
10
|
// This pattern keeps the tool count low (1 tool per domain)
|
|
11
11
|
// while supporting unlimited operations within each domain.
|
|
12
|
+
import * as fs from "node:fs";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import { spawnSync } from "node:child_process";
|
|
12
15
|
import { resolveAgentId } from "./agent-resolver.js";
|
|
13
16
|
import { handleProposeAttach, handleCommitAttach, registerAttachmentContextHook, } from "./attachments.js";
|
|
14
17
|
import { invalidateAgentTier } from "./agent-tier.js";
|
|
@@ -18,6 +21,35 @@ function ok(data) {
|
|
|
18
21
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
19
22
|
};
|
|
20
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
|
+
}
|
|
21
53
|
function err(message) {
|
|
22
54
|
return {
|
|
23
55
|
content: [{ type: "text", text: `Error: ${message}` }],
|
|
@@ -3059,10 +3091,8 @@ function registerPromptOps(api, supabase, userId) {
|
|
|
3059
3091
|
// META-TOOL 10: OFIERE_CONSTELLATION_OPS — Agent Architecture Builder
|
|
3060
3092
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
3061
3093
|
function registerConstellationOps(api, supabase, userId) {
|
|
3062
|
-
//
|
|
3063
|
-
|
|
3064
|
-
const path = require("path");
|
|
3065
|
-
const { spawnSync } = require("child_process");
|
|
3094
|
+
// fs/path/spawnSync now imported at module scope (ESM — `require` is undefined
|
|
3095
|
+
// when the plugin is loaded as compiled ESM dist/index.js under gateway 2026.5.x).
|
|
3066
3096
|
// OpenClaw stores data at /data/.openclaw/ inside the Docker container
|
|
3067
3097
|
// Fallback: check HOME/.openclaw/ for local dev
|
|
3068
3098
|
const OPENCLAW_ROOT = fs.existsSync("/data/.openclaw") ? "/data/.openclaw"
|
|
@@ -6384,12 +6414,6 @@ supabase, config) {
|
|
|
6384
6414
|
registerAttachmentContextHook({ api, supabase, userId, fallbackAgentId, resolveAgent });
|
|
6385
6415
|
// ── Register agent_end hook for server-side brain extraction ──
|
|
6386
6416
|
registerBrainExtractionHook(api, supabase, userId, fallbackAgentId);
|
|
6387
|
-
// ── CYCLE 10 PROBE — verify subagent_* hook payload shapes ─────────────
|
|
6388
|
-
// Probe-only build (4.50.0-probe.x). Logs full payload on first fire of
|
|
6389
|
-
// each native subagent hook + triggers a one-shot subagent.run 8s after
|
|
6390
|
-
// init so we capture spawning → spawned → ended lifecycle. Removed in
|
|
6391
|
-
// 4.50.0 production release.
|
|
6392
|
-
registerCycle10Probe(api);
|
|
6393
6417
|
// ── Count and log ──
|
|
6394
6418
|
const toolCount = 16;
|
|
6395
6419
|
const callerName = getCallingAgentName(api);
|
|
@@ -6424,22 +6448,21 @@ function registerBrainExtractionHook(api, supabase, userId, fallbackAgentId) {
|
|
|
6424
6448
|
return;
|
|
6425
6449
|
}
|
|
6426
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 "".
|
|
6427
6455
|
let lastUser = "";
|
|
6428
6456
|
let lastAssistant = "";
|
|
6429
6457
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6430
6458
|
const msg = messages[i];
|
|
6431
6459
|
const role = msg?.role || msg?.message?.role;
|
|
6432
|
-
const text =
|
|
6433
|
-
: msg?.text || msg?.content || msg?.message?.content
|
|
6434
|
-
?.filter?.((c) => c.type === "text")
|
|
6435
|
-
?.map?.((c) => c.text)
|
|
6436
|
-
?.join?.("\n")
|
|
6437
|
-
|| (typeof msg?.message?.content === "string" ? msg.message.content : "");
|
|
6460
|
+
const text = extractMessageText(msg);
|
|
6438
6461
|
if (!lastAssistant && (role === "assistant" || role === "model")) {
|
|
6439
|
-
lastAssistant =
|
|
6462
|
+
lastAssistant = text;
|
|
6440
6463
|
}
|
|
6441
6464
|
if (!lastUser && role === "user") {
|
|
6442
|
-
lastUser =
|
|
6465
|
+
lastUser = text;
|
|
6443
6466
|
}
|
|
6444
6467
|
if (lastUser && lastAssistant)
|
|
6445
6468
|
break;
|
|
@@ -6752,102 +6775,6 @@ function registerBrainExtractionHook(api, supabase, userId, fallbackAgentId) {
|
|
|
6752
6775
|
api.logger.debug?.("[ofiere] Could not register agent_end hook — may not be supported in this OpenClaw version");
|
|
6753
6776
|
}
|
|
6754
6777
|
}
|
|
6755
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
6756
|
-
// CYCLE 10 PROBE — REMOVE/DISABLE BEFORE PRODUCTION 4.50.x
|
|
6757
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
6758
|
-
// One-shot diagnostic that logs the full payload shape of every native
|
|
6759
|
-
// subagent_* hook, then triggers a single api.runtime.subagent.run so the
|
|
6760
|
-
// lifecycle fires end-to-end. Used to verify field names before the real
|
|
6761
|
-
// subagent_ended handler is written. Activated by env var
|
|
6762
|
-
// OFIERE_CYCLE10_PROBE=1; otherwise inert.
|
|
6763
|
-
//
|
|
6764
|
-
// Captured in cycle 10 BUGSHOOT under hook key [ofiere-probe].
|
|
6765
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
6766
|
-
function registerCycle10Probe(api) {
|
|
6767
|
-
const safeStringify = (obj, max = 4000) => {
|
|
6768
|
-
try {
|
|
6769
|
-
const seen = new WeakSet();
|
|
6770
|
-
const out = JSON.stringify(obj, (_k, v) => {
|
|
6771
|
-
if (typeof v === "object" && v !== null) {
|
|
6772
|
-
if (seen.has(v))
|
|
6773
|
-
return "[Circular]";
|
|
6774
|
-
seen.add(v);
|
|
6775
|
-
}
|
|
6776
|
-
if (typeof v === "function")
|
|
6777
|
-
return `[Function ${v.name || "anon"}]`;
|
|
6778
|
-
return v;
|
|
6779
|
-
}, 2);
|
|
6780
|
-
return out.length > max ? out.slice(0, max) + "...[truncated]" : out;
|
|
6781
|
-
}
|
|
6782
|
-
catch (e) {
|
|
6783
|
-
return `[stringify-fail: ${e instanceof Error ? e.message : String(e)}]`;
|
|
6784
|
-
}
|
|
6785
|
-
};
|
|
6786
|
-
const hookNames = [
|
|
6787
|
-
"subagent_spawning",
|
|
6788
|
-
"subagent_spawned",
|
|
6789
|
-
"subagent_delivery_target",
|
|
6790
|
-
"subagent_ended",
|
|
6791
|
-
];
|
|
6792
|
-
for (const hook of hookNames) {
|
|
6793
|
-
try {
|
|
6794
|
-
api.on?.(hook, (event, ctx) => {
|
|
6795
|
-
try {
|
|
6796
|
-
api.logger.info?.(`[ofiere-probe] ${hook} event=${safeStringify(event, 2000)} ctx=${safeStringify(ctx, 2000)}`);
|
|
6797
|
-
}
|
|
6798
|
-
catch (e) {
|
|
6799
|
-
api.logger.warn?.(`[ofiere-probe] ${hook} log failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
6800
|
-
}
|
|
6801
|
-
});
|
|
6802
|
-
api.logger.info?.(`[ofiere-probe] ${hook} hook registered`);
|
|
6803
|
-
}
|
|
6804
|
-
catch (e) {
|
|
6805
|
-
api.logger.warn?.(`[ofiere-probe] ${hook} hook register failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
6806
|
-
}
|
|
6807
|
-
}
|
|
6808
|
-
// One-shot spawn 8s after init so the gateway is fully up.
|
|
6809
|
-
setTimeout(async () => {
|
|
6810
|
-
const sessionKey = `agent:celia:subagent:probe-${Date.now().toString(36).slice(-6)}`;
|
|
6811
|
-
api.logger.info?.(`[ofiere-probe] firing api.runtime.subagent.run sessionKey=${sessionKey}`);
|
|
6812
|
-
try {
|
|
6813
|
-
const runtime = api?.runtime;
|
|
6814
|
-
if (!runtime?.subagent?.run) {
|
|
6815
|
-
api.logger.error?.(`[ofiere-probe] api.runtime.subagent.run NOT AVAILABLE on this OpenClaw version. ` +
|
|
6816
|
-
`runtime keys: ${runtime ? Object.keys(runtime).join(",") : "(no runtime)"}`);
|
|
6817
|
-
return;
|
|
6818
|
-
}
|
|
6819
|
-
const result = await runtime.subagent.run({
|
|
6820
|
-
sessionKey,
|
|
6821
|
-
message: "Reply with exactly the text PROBE-OK and nothing else.",
|
|
6822
|
-
deliver: false,
|
|
6823
|
-
});
|
|
6824
|
-
api.logger.info?.(`[ofiere-probe] subagent.run returned ${safeStringify(result, 1000)}`);
|
|
6825
|
-
// Try waitForRun + getSessionMessages immediately so we capture the
|
|
6826
|
-
// full transcript shape even before subagent_ended fires.
|
|
6827
|
-
if (result?.runId && runtime.subagent.waitForRun) {
|
|
6828
|
-
try {
|
|
6829
|
-
const waitResult = await runtime.subagent.waitForRun({ runId: result.runId, timeoutMs: 30000 });
|
|
6830
|
-
api.logger.info?.(`[ofiere-probe] waitForRun returned ${safeStringify(waitResult, 2000)}`);
|
|
6831
|
-
}
|
|
6832
|
-
catch (e) {
|
|
6833
|
-
api.logger.warn?.(`[ofiere-probe] waitForRun threw: ${e instanceof Error ? e.message : String(e)}`);
|
|
6834
|
-
}
|
|
6835
|
-
}
|
|
6836
|
-
if (runtime.subagent.getSessionMessages) {
|
|
6837
|
-
try {
|
|
6838
|
-
const msgsResult = await runtime.subagent.getSessionMessages({ sessionKey, limit: 10 });
|
|
6839
|
-
api.logger.info?.(`[ofiere-probe] getSessionMessages returned ${safeStringify(msgsResult, 4000)}`);
|
|
6840
|
-
}
|
|
6841
|
-
catch (e) {
|
|
6842
|
-
api.logger.warn?.(`[ofiere-probe] getSessionMessages threw: ${e instanceof Error ? e.message : String(e)}`);
|
|
6843
|
-
}
|
|
6844
|
-
}
|
|
6845
|
-
}
|
|
6846
|
-
catch (e) {
|
|
6847
|
-
api.logger.error?.(`[ofiere-probe] subagent.run threw: ${e instanceof Error ? `${e.name}: ${e.message}\n${e.stack}` : String(e)}`);
|
|
6848
|
-
}
|
|
6849
|
-
}, 8000);
|
|
6850
|
-
}
|
|
6851
6778
|
// ── Brain Context Bootstrap Injection ──────────────────────────────────────
|
|
6852
6779
|
// Injects the calling agent's active memories into the system prompt via
|
|
6853
6780
|
// api.on("before_prompt_build"). Uses the TMT hierarchy:
|
package/openclaw.plugin.json
CHANGED
|
@@ -31,6 +31,29 @@
|
|
|
31
31
|
"hooks": {
|
|
32
32
|
"allowConversationAccess": true
|
|
33
33
|
},
|
|
34
|
+
"contracts": {
|
|
35
|
+
"tools": [
|
|
36
|
+
"OFIERE_TASK_OPS",
|
|
37
|
+
"OFIERE_AGENT_OPS",
|
|
38
|
+
"OFIERE_PROJECT_OPS",
|
|
39
|
+
"OFIERE_SCHEDULE_OPS",
|
|
40
|
+
"OFIERE_KNOWLEDGE_OPS",
|
|
41
|
+
"OFIERE_WORKFLOW_OPS",
|
|
42
|
+
"OFIERE_NOTIFY_OPS",
|
|
43
|
+
"OFIERE_MEMORY_OPS",
|
|
44
|
+
"OFIERE_PROMPT_OPS",
|
|
45
|
+
"OFIERE_CONSTELLATION_OPS",
|
|
46
|
+
"OFIERE_FILE_OPS",
|
|
47
|
+
"OFIERE_PLAN_OPS",
|
|
48
|
+
"OFIERE_SOP_OPS",
|
|
49
|
+
"OFIERE_FRAMEWORK_OPS",
|
|
50
|
+
"OFIERE_BRAIN_OPS",
|
|
51
|
+
"OFIERE_TALENT_OPS"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"activation": {
|
|
55
|
+
"onStartup": true
|
|
56
|
+
},
|
|
34
57
|
"uiHints": {
|
|
35
58
|
"enabled": {
|
|
36
59
|
"label": "Enable Ofiere PM",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofiere-openclaw-plugin",
|
|
3
|
-
"version": "4.50.0
|
|
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
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
// while supporting unlimited operations within each domain.
|
|
12
12
|
|
|
13
13
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
14
|
+
import * as fs from "node:fs";
|
|
15
|
+
import * as path from "node:path";
|
|
16
|
+
import { spawnSync } from "node:child_process";
|
|
14
17
|
import type { OfiereConfig } from "./types.js";
|
|
15
18
|
import { resolveAgentId } from "./agent-resolver.js";
|
|
16
19
|
import {
|
|
@@ -33,6 +36,32 @@ function ok(data: unknown): ToolResult {
|
|
|
33
36
|
};
|
|
34
37
|
}
|
|
35
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
|
+
|
|
36
65
|
function err(message: string): ToolResult {
|
|
37
66
|
return {
|
|
38
67
|
content: [{ type: "text" as const, text: `Error: ${message}` }],
|
|
@@ -3246,10 +3275,8 @@ function registerConstellationOps(
|
|
|
3246
3275
|
supabase: SupabaseClient,
|
|
3247
3276
|
userId: string,
|
|
3248
3277
|
): void {
|
|
3249
|
-
//
|
|
3250
|
-
|
|
3251
|
-
const path = require("path");
|
|
3252
|
-
const { spawnSync } = require("child_process");
|
|
3278
|
+
// fs/path/spawnSync now imported at module scope (ESM — `require` is undefined
|
|
3279
|
+
// when the plugin is loaded as compiled ESM dist/index.js under gateway 2026.5.x).
|
|
3253
3280
|
|
|
3254
3281
|
// OpenClaw stores data at /data/.openclaw/ inside the Docker container
|
|
3255
3282
|
// Fallback: check HOME/.openclaw/ for local dev
|
|
@@ -6535,13 +6562,6 @@ export function registerTools(
|
|
|
6535
6562
|
// ── Register agent_end hook for server-side brain extraction ──
|
|
6536
6563
|
registerBrainExtractionHook(api, supabase, userId, fallbackAgentId);
|
|
6537
6564
|
|
|
6538
|
-
// ── CYCLE 10 PROBE — verify subagent_* hook payload shapes ─────────────
|
|
6539
|
-
// Probe-only build (4.50.0-probe.x). Logs full payload on first fire of
|
|
6540
|
-
// each native subagent hook + triggers a one-shot subagent.run 8s after
|
|
6541
|
-
// init so we capture spawning → spawned → ended lifecycle. Removed in
|
|
6542
|
-
// 4.50.0 production release.
|
|
6543
|
-
registerCycle10Probe(api);
|
|
6544
|
-
|
|
6545
6565
|
// ── Count and log ──
|
|
6546
6566
|
const toolCount = 16;
|
|
6547
6567
|
const callerName = getCallingAgentName(api);
|
|
@@ -6585,23 +6605,22 @@ function registerBrainExtractionHook(
|
|
|
6585
6605
|
}
|
|
6586
6606
|
|
|
6587
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 "".
|
|
6588
6612
|
let lastUser = "";
|
|
6589
6613
|
let lastAssistant = "";
|
|
6590
6614
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6591
6615
|
const msg = messages[i];
|
|
6592
6616
|
const role = msg?.role || msg?.message?.role;
|
|
6593
|
-
const text =
|
|
6594
|
-
: msg?.text || msg?.content || msg?.message?.content
|
|
6595
|
-
?.filter?.((c: any) => c.type === "text")
|
|
6596
|
-
?.map?.((c: any) => c.text)
|
|
6597
|
-
?.join?.("\n")
|
|
6598
|
-
|| (typeof msg?.message?.content === "string" ? msg.message.content : "");
|
|
6617
|
+
const text = extractMessageText(msg);
|
|
6599
6618
|
|
|
6600
6619
|
if (!lastAssistant && (role === "assistant" || role === "model")) {
|
|
6601
|
-
lastAssistant =
|
|
6620
|
+
lastAssistant = text;
|
|
6602
6621
|
}
|
|
6603
6622
|
if (!lastUser && role === "user") {
|
|
6604
|
-
lastUser =
|
|
6623
|
+
lastUser = text;
|
|
6605
6624
|
}
|
|
6606
6625
|
if (lastUser && lastAssistant) break;
|
|
6607
6626
|
}
|
|
@@ -6931,105 +6950,6 @@ function registerBrainExtractionHook(
|
|
|
6931
6950
|
}
|
|
6932
6951
|
}
|
|
6933
6952
|
|
|
6934
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
6935
|
-
// CYCLE 10 PROBE — REMOVE/DISABLE BEFORE PRODUCTION 4.50.x
|
|
6936
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
6937
|
-
// One-shot diagnostic that logs the full payload shape of every native
|
|
6938
|
-
// subagent_* hook, then triggers a single api.runtime.subagent.run so the
|
|
6939
|
-
// lifecycle fires end-to-end. Used to verify field names before the real
|
|
6940
|
-
// subagent_ended handler is written. Activated by env var
|
|
6941
|
-
// OFIERE_CYCLE10_PROBE=1; otherwise inert.
|
|
6942
|
-
//
|
|
6943
|
-
// Captured in cycle 10 BUGSHOOT under hook key [ofiere-probe].
|
|
6944
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
6945
|
-
|
|
6946
|
-
function registerCycle10Probe(api: any): void {
|
|
6947
|
-
const safeStringify = (obj: unknown, max = 4000): string => {
|
|
6948
|
-
try {
|
|
6949
|
-
const seen = new WeakSet<object>();
|
|
6950
|
-
const out = JSON.stringify(obj, (_k, v) => {
|
|
6951
|
-
if (typeof v === "object" && v !== null) {
|
|
6952
|
-
if (seen.has(v)) return "[Circular]";
|
|
6953
|
-
seen.add(v);
|
|
6954
|
-
}
|
|
6955
|
-
if (typeof v === "function") return `[Function ${v.name || "anon"}]`;
|
|
6956
|
-
return v;
|
|
6957
|
-
}, 2);
|
|
6958
|
-
return out.length > max ? out.slice(0, max) + "...[truncated]" : out;
|
|
6959
|
-
} catch (e) {
|
|
6960
|
-
return `[stringify-fail: ${e instanceof Error ? e.message : String(e)}]`;
|
|
6961
|
-
}
|
|
6962
|
-
};
|
|
6963
|
-
|
|
6964
|
-
const hookNames = [
|
|
6965
|
-
"subagent_spawning",
|
|
6966
|
-
"subagent_spawned",
|
|
6967
|
-
"subagent_delivery_target",
|
|
6968
|
-
"subagent_ended",
|
|
6969
|
-
];
|
|
6970
|
-
for (const hook of hookNames) {
|
|
6971
|
-
try {
|
|
6972
|
-
api.on?.(hook, (event: any, ctx: any) => {
|
|
6973
|
-
try {
|
|
6974
|
-
api.logger.info?.(
|
|
6975
|
-
`[ofiere-probe] ${hook} event=${safeStringify(event, 2000)} ctx=${safeStringify(ctx, 2000)}`,
|
|
6976
|
-
);
|
|
6977
|
-
} catch (e) {
|
|
6978
|
-
api.logger.warn?.(`[ofiere-probe] ${hook} log failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
6979
|
-
}
|
|
6980
|
-
});
|
|
6981
|
-
api.logger.info?.(`[ofiere-probe] ${hook} hook registered`);
|
|
6982
|
-
} catch (e) {
|
|
6983
|
-
api.logger.warn?.(`[ofiere-probe] ${hook} hook register failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
6984
|
-
}
|
|
6985
|
-
}
|
|
6986
|
-
|
|
6987
|
-
// One-shot spawn 8s after init so the gateway is fully up.
|
|
6988
|
-
setTimeout(async () => {
|
|
6989
|
-
const sessionKey = `agent:celia:subagent:probe-${Date.now().toString(36).slice(-6)}`;
|
|
6990
|
-
api.logger.info?.(`[ofiere-probe] firing api.runtime.subagent.run sessionKey=${sessionKey}`);
|
|
6991
|
-
try {
|
|
6992
|
-
const runtime = (api as any)?.runtime;
|
|
6993
|
-
if (!runtime?.subagent?.run) {
|
|
6994
|
-
api.logger.error?.(
|
|
6995
|
-
`[ofiere-probe] api.runtime.subagent.run NOT AVAILABLE on this OpenClaw version. ` +
|
|
6996
|
-
`runtime keys: ${runtime ? Object.keys(runtime).join(",") : "(no runtime)"}`,
|
|
6997
|
-
);
|
|
6998
|
-
return;
|
|
6999
|
-
}
|
|
7000
|
-
const result = await runtime.subagent.run({
|
|
7001
|
-
sessionKey,
|
|
7002
|
-
message: "Reply with exactly the text PROBE-OK and nothing else.",
|
|
7003
|
-
deliver: false,
|
|
7004
|
-
});
|
|
7005
|
-
api.logger.info?.(`[ofiere-probe] subagent.run returned ${safeStringify(result, 1000)}`);
|
|
7006
|
-
|
|
7007
|
-
// Try waitForRun + getSessionMessages immediately so we capture the
|
|
7008
|
-
// full transcript shape even before subagent_ended fires.
|
|
7009
|
-
if (result?.runId && runtime.subagent.waitForRun) {
|
|
7010
|
-
try {
|
|
7011
|
-
const waitResult = await runtime.subagent.waitForRun({ runId: result.runId, timeoutMs: 30000 });
|
|
7012
|
-
api.logger.info?.(`[ofiere-probe] waitForRun returned ${safeStringify(waitResult, 2000)}`);
|
|
7013
|
-
} catch (e) {
|
|
7014
|
-
api.logger.warn?.(`[ofiere-probe] waitForRun threw: ${e instanceof Error ? e.message : String(e)}`);
|
|
7015
|
-
}
|
|
7016
|
-
}
|
|
7017
|
-
if (runtime.subagent.getSessionMessages) {
|
|
7018
|
-
try {
|
|
7019
|
-
const msgsResult = await runtime.subagent.getSessionMessages({ sessionKey, limit: 10 });
|
|
7020
|
-
api.logger.info?.(`[ofiere-probe] getSessionMessages returned ${safeStringify(msgsResult, 4000)}`);
|
|
7021
|
-
} catch (e) {
|
|
7022
|
-
api.logger.warn?.(`[ofiere-probe] getSessionMessages threw: ${e instanceof Error ? e.message : String(e)}`);
|
|
7023
|
-
}
|
|
7024
|
-
}
|
|
7025
|
-
} catch (e) {
|
|
7026
|
-
api.logger.error?.(
|
|
7027
|
-
`[ofiere-probe] subagent.run threw: ${e instanceof Error ? `${e.name}: ${e.message}\n${e.stack}` : String(e)}`,
|
|
7028
|
-
);
|
|
7029
|
-
}
|
|
7030
|
-
}, 8000);
|
|
7031
|
-
}
|
|
7032
|
-
|
|
7033
6953
|
// ── Brain Context Bootstrap Injection ──────────────────────────────────────
|
|
7034
6954
|
// Injects the calling agent's active memories into the system prompt via
|
|
7035
6955
|
// api.on("before_prompt_build"). Uses the TMT hierarchy:
|