@syengup/friday-channel-next 1.0.0 → 1.0.1
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/friday-session.js +22 -0
- package/package.json +1 -1
- package/src/friday-session.forward-agent.test.ts +50 -0
- package/src/friday-session.ts +26 -2
- package/dist/src/http 2/middleware/auth.d.ts +0 -13
- package/dist/src/http 2/middleware/auth.js +0 -29
- package/dist/src/http 2/middleware/body.d.ts +0 -2
- package/dist/src/http 2/middleware/body.js +0 -24
- package/dist/src/http 2/middleware/cors.d.ts +0 -2
- package/dist/src/http 2/middleware/cors.js +0 -11
|
@@ -321,6 +321,28 @@ export function forwardAgentEventRaw(evt) {
|
|
|
321
321
|
if (typeof evt.stream === "string" && evt.stream.startsWith("codex_app_server")) {
|
|
322
322
|
codexRunIds.add(evt.runId);
|
|
323
323
|
}
|
|
324
|
+
// Codex app-server reasoning: newer OpenClaw cores stopped invoking the dispatch
|
|
325
|
+
// `onReasoningStream` callback (the A2 path in messages.ts) and instead stream the
|
|
326
|
+
// reasoning summary on the agent-event bus as `stream:"item" kind:"preamble"` with a
|
|
327
|
+
// cumulative `progressText` (source "codex-app-server"). The Friday app only renders
|
|
328
|
+
// `stream:"thinking"`, so translate it here — synthesize a thinking event reusing the
|
|
329
|
+
// cumulative→delta rewrite below. The raw preamble item is still forwarded but the app
|
|
330
|
+
// ignores unknown item kinds. (The onReasoningStream callback stays as a harmless
|
|
331
|
+
// fallback for cores that still fire it.)
|
|
332
|
+
if (evt.stream === "item" &&
|
|
333
|
+
evt.data.kind === "preamble" &&
|
|
334
|
+
evt.data.source === "codex-app-server") {
|
|
335
|
+
codexRunIds.add(evt.runId);
|
|
336
|
+
const reasoningText = typeof evt.data.progressText === "string" ? evt.data.progressText : "";
|
|
337
|
+
if (reasoningText) {
|
|
338
|
+
forwardAgentEventRaw({
|
|
339
|
+
runId: evt.runId,
|
|
340
|
+
stream: "thinking",
|
|
341
|
+
data: { text: reasoningText },
|
|
342
|
+
sessionKey: evt.sessionKey ?? sk,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
324
346
|
// Register sessionKey → runId so we can resolve parentRunId
|
|
325
347
|
if (sk && evt.stream === "lifecycle" && evt.data.phase === "start") {
|
|
326
348
|
registerSessionKeyForRun(sk, evt.runId);
|
package/package.json
CHANGED
|
@@ -139,6 +139,56 @@ describe("forwardAgentEventRaw (thinking delta rewrite)", () => {
|
|
|
139
139
|
expect(third.delta).toBe(t1);
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
+
it("translates a Codex preamble item (progressText) into streamed thinking deltas", () => {
|
|
143
|
+
// Newer OpenClaw cores stopped invoking dispatch onReasoningStream for Codex and instead
|
|
144
|
+
// stream the reasoning summary on the agent-event bus as item/preamble with cumulative
|
|
145
|
+
// progressText. We translate it back into stream:"thinking" so the app renders it.
|
|
146
|
+
forwardAgentEventRaw({
|
|
147
|
+
runId,
|
|
148
|
+
seq: 1,
|
|
149
|
+
stream: "item",
|
|
150
|
+
sessionKey,
|
|
151
|
+
data: { kind: "preamble", source: "codex-app-server", phase: "update", progressText: "先把" },
|
|
152
|
+
});
|
|
153
|
+
forwardAgentEventRaw({
|
|
154
|
+
runId,
|
|
155
|
+
seq: 2,
|
|
156
|
+
stream: "item",
|
|
157
|
+
sessionKey,
|
|
158
|
+
data: {
|
|
159
|
+
kind: "preamble",
|
|
160
|
+
source: "codex-app-server",
|
|
161
|
+
phase: "update",
|
|
162
|
+
progressText: "先把标准",
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const thinking = (sseEmitter.broadcastToRun as ReturnType<typeof vi.fn>).mock.calls
|
|
167
|
+
.map((c) => c[1].data)
|
|
168
|
+
.filter((d: { stream?: string }) => d.stream === "thinking");
|
|
169
|
+
expect(thinking).toHaveLength(2);
|
|
170
|
+
expect(thinking[0].data.text).toBe("先把");
|
|
171
|
+
expect(thinking[0].data.delta).toBe("先把");
|
|
172
|
+
expect(thinking[0].data.reasoningPrefixChars).toBe(0);
|
|
173
|
+
expect(thinking[1].data.text).toBe("先把标准");
|
|
174
|
+
expect(thinking[1].data.delta).toBe("标准");
|
|
175
|
+
expect(thinking[1].data.reasoningPrefixChars).toBe(2);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("does not translate preamble items from a non-Codex source", () => {
|
|
179
|
+
forwardAgentEventRaw({
|
|
180
|
+
runId,
|
|
181
|
+
seq: 1,
|
|
182
|
+
stream: "item",
|
|
183
|
+
sessionKey,
|
|
184
|
+
data: { kind: "preamble", source: "something-else", progressText: "x" },
|
|
185
|
+
});
|
|
186
|
+
const thinking = (sseEmitter.broadcastToRun as ReturnType<typeof vi.fn>).mock.calls
|
|
187
|
+
.map((c) => c[1].data)
|
|
188
|
+
.filter((d: { stream?: string }) => d.stream === "thinking");
|
|
189
|
+
expect(thinking).toHaveLength(0);
|
|
190
|
+
});
|
|
191
|
+
|
|
142
192
|
it("merges run metadata into lifecycle.end (model, tokens, context usage)", () => {
|
|
143
193
|
forwardAgentEventRaw({
|
|
144
194
|
runId,
|
package/src/friday-session.ts
CHANGED
|
@@ -373,6 +373,31 @@ export function forwardAgentEventRaw(evt: ForwardAgentEventArgs): void {
|
|
|
373
373
|
codexRunIds.add(evt.runId);
|
|
374
374
|
}
|
|
375
375
|
|
|
376
|
+
// Codex app-server reasoning: newer OpenClaw cores stopped invoking the dispatch
|
|
377
|
+
// `onReasoningStream` callback (the A2 path in messages.ts) and instead stream the
|
|
378
|
+
// reasoning summary on the agent-event bus as `stream:"item" kind:"preamble"` with a
|
|
379
|
+
// cumulative `progressText` (source "codex-app-server"). The Friday app only renders
|
|
380
|
+
// `stream:"thinking"`, so translate it here — synthesize a thinking event reusing the
|
|
381
|
+
// cumulative→delta rewrite below. The raw preamble item is still forwarded but the app
|
|
382
|
+
// ignores unknown item kinds. (The onReasoningStream callback stays as a harmless
|
|
383
|
+
// fallback for cores that still fire it.)
|
|
384
|
+
if (
|
|
385
|
+
evt.stream === "item" &&
|
|
386
|
+
evt.data.kind === "preamble" &&
|
|
387
|
+
evt.data.source === "codex-app-server"
|
|
388
|
+
) {
|
|
389
|
+
codexRunIds.add(evt.runId);
|
|
390
|
+
const reasoningText = typeof evt.data.progressText === "string" ? evt.data.progressText : "";
|
|
391
|
+
if (reasoningText) {
|
|
392
|
+
forwardAgentEventRaw({
|
|
393
|
+
runId: evt.runId,
|
|
394
|
+
stream: "thinking",
|
|
395
|
+
data: { text: reasoningText },
|
|
396
|
+
sessionKey: evt.sessionKey ?? sk,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
376
401
|
// Register sessionKey → runId so we can resolve parentRunId
|
|
377
402
|
if (sk && evt.stream === "lifecycle" && evt.data.phase === "start") {
|
|
378
403
|
registerSessionKeyForRun(sk, evt.runId);
|
|
@@ -469,8 +494,7 @@ export function forwardAgentEventRaw(evt: ForwardAgentEventArgs): void {
|
|
|
469
494
|
if (evt.stream === "lifecycle" && evt.data.phase === "start") {
|
|
470
495
|
const announced = parseAnnounceRunId(evt.runId);
|
|
471
496
|
if (announced) {
|
|
472
|
-
const entry =
|
|
473
|
-
lookupByChildSessionKey(announced.childSessionKey) ?? lookupByRunId(evt.runId);
|
|
497
|
+
const entry = lookupByChildSessionKey(announced.childSessionKey) ?? lookupByRunId(evt.runId);
|
|
474
498
|
sseEmitter.broadcast(
|
|
475
499
|
{
|
|
476
500
|
type: "subagent",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bearer token authentication middleware for Friday HTTP routes.
|
|
3
|
-
*
|
|
4
|
-
* Validates that the bearer token matches the gateway's configured auth token.
|
|
5
|
-
* This ensures plugin HTTP endpoints use the same token as gateway WS connections.
|
|
6
|
-
*/
|
|
7
|
-
import type { IncomingMessage } from "node:http";
|
|
8
|
-
/**
|
|
9
|
-
* Extract and validate bearer token from Authorization header.
|
|
10
|
-
* Returns the token only if it matches the gateway's configured auth token.
|
|
11
|
-
* Returns null if token is missing, malformed, or doesn't match.
|
|
12
|
-
*/
|
|
13
|
-
export declare function extractBearerToken(req: IncomingMessage): string | null;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bearer token authentication middleware for Friday HTTP routes.
|
|
3
|
-
*
|
|
4
|
-
* Validates that the bearer token matches the gateway's configured auth token.
|
|
5
|
-
* This ensures plugin HTTP endpoints use the same token as gateway WS connections.
|
|
6
|
-
*/
|
|
7
|
-
import { resolveFridayNextConfig } from "../../config.js";
|
|
8
|
-
import { getHostOpenClawConfigSnapshot } from "../../host-config.js";
|
|
9
|
-
import { getFridayNextRuntime } from "../../runtime.js";
|
|
10
|
-
/**
|
|
11
|
-
* Extract and validate bearer token from Authorization header.
|
|
12
|
-
* Returns the token only if it matches the gateway's configured auth token.
|
|
13
|
-
* Returns null if token is missing, malformed, or doesn't match.
|
|
14
|
-
*/
|
|
15
|
-
export function extractBearerToken(req) {
|
|
16
|
-
const auth = req.headers.authorization;
|
|
17
|
-
if (!auth || typeof auth !== "string")
|
|
18
|
-
return null;
|
|
19
|
-
const parts = auth.trim().split(/\s+/);
|
|
20
|
-
if (parts.length !== 2 || parts[0].toLowerCase() !== "bearer")
|
|
21
|
-
return null;
|
|
22
|
-
const token = parts[1];
|
|
23
|
-
// Validate token matches the gateway's configured auth token.
|
|
24
|
-
const cfg = getHostOpenClawConfigSnapshot(getFridayNextRuntime().config);
|
|
25
|
-
const runtimeConfig = resolveFridayNextConfig(cfg);
|
|
26
|
-
if (!runtimeConfig.authToken || token !== runtimeConfig.authToken)
|
|
27
|
-
return null;
|
|
28
|
-
return token;
|
|
29
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export async function readJsonBody(req, maxBytes = 2 * 1024 * 1024) {
|
|
2
|
-
return await new Promise((resolve) => {
|
|
3
|
-
const chunks = [];
|
|
4
|
-
let total = 0;
|
|
5
|
-
req.on("data", (chunk) => {
|
|
6
|
-
total += chunk.length;
|
|
7
|
-
if (total > maxBytes) {
|
|
8
|
-
resolve(null);
|
|
9
|
-
req.destroy();
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
chunks.push(chunk);
|
|
13
|
-
});
|
|
14
|
-
req.on("end", () => {
|
|
15
|
-
try {
|
|
16
|
-
resolve(JSON.parse(Buffer.concat(chunks).toString("utf-8")));
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
resolve(null);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
req.on("error", () => resolve(null));
|
|
23
|
-
});
|
|
24
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { resolveFridayNextConfig } from "../../config.js";
|
|
2
|
-
import { getHostOpenClawConfigSnapshot } from "../../host-config.js";
|
|
3
|
-
import { getFridayNextRuntime } from "../../runtime.js";
|
|
4
|
-
export function applyCorsHeaders(res) {
|
|
5
|
-
const cfg = resolveFridayNextConfig(getHostOpenClawConfigSnapshot(getFridayNextRuntime().config));
|
|
6
|
-
if (!cfg.corsEnabled)
|
|
7
|
-
return;
|
|
8
|
-
res.setHeader("Access-Control-Allow-Origin", cfg.corsAllowOrigin || "*");
|
|
9
|
-
res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Last-Event-ID");
|
|
10
|
-
res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
|
|
11
|
-
}
|