@syengup/friday-channel-next 0.1.19 → 0.1.21
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/index.js +4 -4
- package/dist/src/history/normalize-message.js +13 -4
- package/index.ts +7 -6
- package/install.js +18 -29
- package/package.json +1 -1
- package/src/history/normalize-message.test.ts +30 -0
- package/src/history/normalize-message.ts +12 -3
package/dist/index.js
CHANGED
|
@@ -10,6 +10,8 @@ import { forwardAgentEventRaw, getLastRegisteredFridayDeviceId, resolveFridayDev
|
|
|
10
10
|
import { setFridayAgentForwardRuntime } from "./src/agent-forward-runtime.js";
|
|
11
11
|
import { getOpenClawAgentRunContext } from "./src/agent-run-context-bridge.js";
|
|
12
12
|
import { accumulateRunUsage } from "./src/agent/run-usage-accumulator.js";
|
|
13
|
+
import { createFridayNextLogger } from "./src/logging.js";
|
|
14
|
+
const hookLogger = createFridayNextLogger("hook");
|
|
13
15
|
export { fridayNextChannelPlugin } from "./src/channel.js";
|
|
14
16
|
export { setFridayNextRuntime } from "./src/runtime.js";
|
|
15
17
|
/** `api.on` returns void — register tool hooks at most once per process. */
|
|
@@ -136,8 +138,7 @@ export default defineChannelPluginEntry({
|
|
|
136
138
|
const deviceId = deviceIdFromToolContext(ctx);
|
|
137
139
|
const runId = ctx.runId ?? "(unknown)";
|
|
138
140
|
const logLine = (detail) => {
|
|
139
|
-
|
|
140
|
-
console.error(`[Friday-HOOK] [${ts}] [TOOL_CALL] toolName=${event.toolName} runId=${runId} deviceId=${deviceId ?? "(unknown)"} detail=${detail}`);
|
|
141
|
+
hookLogger.debug(`[TOOL_CALL] toolName=${event.toolName} runId=${runId} deviceId=${deviceId ?? "(unknown)"} detail=${detail}`);
|
|
141
142
|
};
|
|
142
143
|
if (!deviceId) {
|
|
143
144
|
logLine("SKIP_no_deviceId");
|
|
@@ -163,8 +164,7 @@ export default defineChannelPluginEntry({
|
|
|
163
164
|
const deviceId = deviceIdFromToolContext(ctx);
|
|
164
165
|
const runId = ctx.runId ?? "(unknown)";
|
|
165
166
|
const logLine = (detail) => {
|
|
166
|
-
|
|
167
|
-
console.error(`[Friday-HOOK] [${ts}] [TOOL_DONE] toolName=${event.toolName} runId=${runId} deviceId=${deviceId ?? "(unknown)"} detail=${detail}`);
|
|
167
|
+
hookLogger.debug(`[TOOL_DONE] toolName=${event.toolName} runId=${runId} deviceId=${deviceId ?? "(unknown)"} detail=${detail}`);
|
|
168
168
|
};
|
|
169
169
|
if (!deviceId) {
|
|
170
170
|
logLine("SKIP_no_deviceId");
|
|
@@ -180,16 +180,25 @@ export function normalizeHistoryMessage(raw, index) {
|
|
|
180
180
|
}
|
|
181
181
|
if (role === "toolResult") {
|
|
182
182
|
const split = splitMediaLines(parsed.text);
|
|
183
|
+
const toolName = readString(record.toolName);
|
|
184
|
+
// Canvas snapshots come back as base64 image blocks on the `canvas` tool result so the *agent*
|
|
185
|
+
// can "see" the rendered page — they must never surface as chat attachments on history rebuild.
|
|
186
|
+
// The streaming deliver path already drops the temp-file form (see isCanvasSnapshotMediaPath in
|
|
187
|
+
// http/handlers/messages.ts); this is the transcript-rebuild counterpart. The canvas tool has no
|
|
188
|
+
// other image-returning action, so all images on a canvas result are snapshots.
|
|
189
|
+
const isCanvasResult = toolName === "canvas";
|
|
190
|
+
const images = isCanvasResult ? [] : parsed.images;
|
|
191
|
+
const mediaPaths = isCanvasResult ? [] : split.paths;
|
|
183
192
|
const toolResult = {
|
|
184
193
|
...(readString(record.toolCallId) ? { toolCallId: readString(record.toolCallId) } : {}),
|
|
185
|
-
...(
|
|
194
|
+
...(toolName ? { toolName } : {}),
|
|
186
195
|
...(record.isError === true ? { isError: true } : {}),
|
|
187
196
|
...(split.text ? { text: split.text } : {}),
|
|
188
|
-
...(
|
|
197
|
+
...(images.length ? { images } : {}),
|
|
189
198
|
};
|
|
190
199
|
message.toolResult = toolResult;
|
|
191
|
-
if (
|
|
192
|
-
message.mediaPaths =
|
|
200
|
+
if (mediaPaths.length)
|
|
201
|
+
message.mediaPaths = mediaPaths;
|
|
193
202
|
return message;
|
|
194
203
|
}
|
|
195
204
|
const split = splitMediaLines(parsed.text);
|
package/index.ts
CHANGED
|
@@ -17,6 +17,9 @@ import {
|
|
|
17
17
|
import { setFridayAgentForwardRuntime } from "./src/agent-forward-runtime.js";
|
|
18
18
|
import { getOpenClawAgentRunContext } from "./src/agent-run-context-bridge.js";
|
|
19
19
|
import { accumulateRunUsage } from "./src/agent/run-usage-accumulator.js";
|
|
20
|
+
import { createFridayNextLogger } from "./src/logging.js";
|
|
21
|
+
|
|
22
|
+
const hookLogger = createFridayNextLogger("hook");
|
|
20
23
|
|
|
21
24
|
export { fridayNextChannelPlugin } from "./src/channel.js";
|
|
22
25
|
export { setFridayNextRuntime } from "./src/runtime.js";
|
|
@@ -148,9 +151,8 @@ export default defineChannelPluginEntry({
|
|
|
148
151
|
const runId = ctx.runId ?? "(unknown)";
|
|
149
152
|
|
|
150
153
|
const logLine = (detail: string) => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
`[Friday-HOOK] [${ts}] [TOOL_CALL] toolName=${event.toolName} runId=${runId} deviceId=${deviceId ?? "(unknown)"} detail=${detail}`,
|
|
154
|
+
hookLogger.debug(
|
|
155
|
+
`[TOOL_CALL] toolName=${event.toolName} runId=${runId} deviceId=${deviceId ?? "(unknown)"} detail=${detail}`,
|
|
154
156
|
);
|
|
155
157
|
};
|
|
156
158
|
|
|
@@ -180,9 +182,8 @@ export default defineChannelPluginEntry({
|
|
|
180
182
|
const runId = ctx.runId ?? "(unknown)";
|
|
181
183
|
|
|
182
184
|
const logLine = (detail: string) => {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
`[Friday-HOOK] [${ts}] [TOOL_DONE] toolName=${event.toolName} runId=${runId} deviceId=${deviceId ?? "(unknown)"} detail=${detail}`,
|
|
185
|
+
hookLogger.debug(
|
|
186
|
+
`[TOOL_DONE] toolName=${event.toolName} runId=${runId} deviceId=${deviceId ?? "(unknown)"} detail=${detail}`,
|
|
186
187
|
);
|
|
187
188
|
};
|
|
188
189
|
|
package/install.js
CHANGED
|
@@ -96,7 +96,6 @@ if (!hasOpenclaw()) {
|
|
|
96
96
|
// --------------- install plugin package ---------------
|
|
97
97
|
|
|
98
98
|
log("Installing Friday Next channel plugin...");
|
|
99
|
-
let installed = false;
|
|
100
99
|
|
|
101
100
|
try {
|
|
102
101
|
const out = execSync(
|
|
@@ -104,7 +103,6 @@ try {
|
|
|
104
103
|
{ encoding: "utf8", stdio: "pipe", timeout: 120000 }
|
|
105
104
|
);
|
|
106
105
|
if (out.trim()) console.log(out.trim());
|
|
107
|
-
installed = true;
|
|
108
106
|
log("Plugin registered with install record — auto-upgrade enabled.");
|
|
109
107
|
|
|
110
108
|
// Remove old manual install to avoid "duplicate plugin id" warning.
|
|
@@ -115,25 +113,9 @@ try {
|
|
|
115
113
|
}
|
|
116
114
|
} catch (e) {
|
|
117
115
|
const msg = (e.stderr || e.stdout || e.message || "").toString();
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// --------------- fallback: manual install ---------------
|
|
123
|
-
|
|
124
|
-
if (!installed) {
|
|
125
|
-
const PKG = has("npm") ? "npm" : has("pnpm") ? "pnpm" : null;
|
|
126
|
-
if (!PKG) {
|
|
127
|
-
err("npm is required for manual install. Install Node.js first.");
|
|
128
|
-
process.exit(1);
|
|
129
|
-
}
|
|
130
|
-
warn("Manual install complete, but auto-upgrade is NOT available.");
|
|
131
|
-
warn("To enable auto-upgrade later, run: openclaw plugins install @syengup/friday-channel-next --force");
|
|
132
|
-
// Clean up legacy dir even in fallback to avoid duplicate warnings
|
|
133
|
-
if (existsSync(join(USER_HOME, ".openclaw", "extensions", "friday-channel-next"))) {
|
|
134
|
-
warn("Legacy install detected. Remove it to avoid duplicate warnings:");
|
|
135
|
-
warn(" rm -rf ~/.openclaw/extensions/friday-channel-next");
|
|
136
|
-
}
|
|
116
|
+
err("Plugin install failed: " + (msg.trim().split("\n").pop() || "unknown error"));
|
|
117
|
+
err("Fix the error above and re-run: npx -y @syengup/friday-channel-next");
|
|
118
|
+
process.exit(1);
|
|
137
119
|
}
|
|
138
120
|
|
|
139
121
|
// --------------- configure OpenClaw ---------------
|
|
@@ -326,17 +308,24 @@ async function verifyGateway(url, token, retries = 30) {
|
|
|
326
308
|
log("Verifying gateway...");
|
|
327
309
|
const verified = await verifyGateway(verifyUrl, gatewayToken);
|
|
328
310
|
|
|
329
|
-
// --------------- show connection info ---------------
|
|
330
|
-
|
|
331
311
|
const BOLD_YELLOW = (s) => `\x1b[1;33m${s}\x1b[0m`;
|
|
332
312
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
313
|
+
// Hard gate: if the gateway didn't verify, the install did NOT succeed — stop here
|
|
314
|
+
// with a non-zero exit and do NOT print the QR / URL / "complete" block, so a failure
|
|
315
|
+
// can never look like a success.
|
|
316
|
+
if (!verified) {
|
|
317
|
+
log("--------------------------------------------------");
|
|
318
|
+
err("Installation FAILED: the Friday Next gateway did not come up.");
|
|
319
|
+
err("Diagnose with: openclaw gateway status");
|
|
320
|
+
err("Then restart: openclaw gateway restart");
|
|
321
|
+
err("And re-run: npx -y @syengup/friday-channel-next");
|
|
322
|
+
process.exit(1);
|
|
339
323
|
}
|
|
324
|
+
|
|
325
|
+
// --------------- show connection info ---------------
|
|
326
|
+
|
|
327
|
+
log("--------------------------------------------------");
|
|
328
|
+
log("Installation complete! Friday Next channel is now active.");
|
|
340
329
|
log("");
|
|
341
330
|
|
|
342
331
|
// --------------- QR code ---------------
|
package/package.json
CHANGED
|
@@ -108,6 +108,36 @@ describe("normalizeHistoryMessage", () => {
|
|
|
108
108
|
});
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
+
it("drops base64 image blocks from a canvas snapshot toolResult (agent-only, never a chat attachment)", () => {
|
|
112
|
+
const out = normalizeHistoryMessage(
|
|
113
|
+
{
|
|
114
|
+
role: "toolResult",
|
|
115
|
+
toolCallId: "tc-canvas",
|
|
116
|
+
toolName: "canvas",
|
|
117
|
+
content: [{ type: "image", mimeType: "image/jpeg", data: "BASE64SNAPSHOT" }],
|
|
118
|
+
...meta("entry-canvas", 5),
|
|
119
|
+
},
|
|
120
|
+
0,
|
|
121
|
+
);
|
|
122
|
+
expect(out?.role).toBe("toolResult");
|
|
123
|
+
expect(out?.toolResult).toEqual({ toolCallId: "tc-canvas", toolName: "canvas" });
|
|
124
|
+
expect(out?.toolResult?.images).toBeUndefined();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("keeps image blocks on non-canvas toolResults", () => {
|
|
128
|
+
const out = normalizeHistoryMessage(
|
|
129
|
+
{
|
|
130
|
+
role: "toolResult",
|
|
131
|
+
toolCallId: "tc-img",
|
|
132
|
+
toolName: "image_generation",
|
|
133
|
+
content: [{ type: "image", mimeType: "image/png", data: "REALIMG" }],
|
|
134
|
+
...meta("entry-img", 6),
|
|
135
|
+
},
|
|
136
|
+
0,
|
|
137
|
+
);
|
|
138
|
+
expect(out?.toolResult?.images).toEqual([{ mimeType: "image/png", data: "REALIMG" }]);
|
|
139
|
+
});
|
|
140
|
+
|
|
111
141
|
it("strips MEDIA: lines from text into mediaPaths", () => {
|
|
112
142
|
const out = normalizeHistoryMessage(
|
|
113
143
|
{
|
|
@@ -253,15 +253,24 @@ export function normalizeHistoryMessage(
|
|
|
253
253
|
|
|
254
254
|
if (role === "toolResult") {
|
|
255
255
|
const split = splitMediaLines(parsed.text);
|
|
256
|
+
const toolName = readString(record.toolName);
|
|
257
|
+
// Canvas snapshots come back as base64 image blocks on the `canvas` tool result so the *agent*
|
|
258
|
+
// can "see" the rendered page — they must never surface as chat attachments on history rebuild.
|
|
259
|
+
// The streaming deliver path already drops the temp-file form (see isCanvasSnapshotMediaPath in
|
|
260
|
+
// http/handlers/messages.ts); this is the transcript-rebuild counterpart. The canvas tool has no
|
|
261
|
+
// other image-returning action, so all images on a canvas result are snapshots.
|
|
262
|
+
const isCanvasResult = toolName === "canvas";
|
|
263
|
+
const images = isCanvasResult ? [] : parsed.images;
|
|
264
|
+
const mediaPaths = isCanvasResult ? [] : split.paths;
|
|
256
265
|
const toolResult: FridayHistoryToolResult = {
|
|
257
266
|
...(readString(record.toolCallId) ? { toolCallId: readString(record.toolCallId) } : {}),
|
|
258
|
-
...(
|
|
267
|
+
...(toolName ? { toolName } : {}),
|
|
259
268
|
...(record.isError === true ? { isError: true } : {}),
|
|
260
269
|
...(split.text ? { text: split.text } : {}),
|
|
261
|
-
...(
|
|
270
|
+
...(images.length ? { images } : {}),
|
|
262
271
|
};
|
|
263
272
|
message.toolResult = toolResult;
|
|
264
|
-
if (
|
|
273
|
+
if (mediaPaths.length) message.mediaPaths = mediaPaths;
|
|
265
274
|
return message;
|
|
266
275
|
}
|
|
267
276
|
|