@ccpocket/bridge 1.53.0 → 1.53.2
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/auto-rename.d.ts +2 -0
- package/dist/auto-rename.js +5 -1
- package/dist/auto-rename.js.map +1 -1
- package/dist/sessions-index.js +243 -27
- package/dist/sessions-index.js.map +1 -1
- package/dist/websocket.d.ts +1 -0
- package/dist/websocket.js +51 -0
- package/dist/websocket.js.map +1 -1
- package/package.json +1 -1
package/dist/auto-rename.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Provider, ServerMessage } from "./parser.js";
|
|
2
|
+
export declare const AUTO_RENAME_PROMPT_PREFIX = "Write a concise name for this coding-agent session.";
|
|
2
3
|
export interface AutoRenameTranscript {
|
|
3
4
|
userText: string;
|
|
4
5
|
assistantText?: string;
|
|
@@ -11,5 +12,6 @@ export interface AutoRenameOptions {
|
|
|
11
12
|
}
|
|
12
13
|
export declare function buildAutoRenameTranscript(history: readonly ServerMessage[]): AutoRenameTranscript | null;
|
|
13
14
|
export declare function buildAutoRenamePrompt(transcript: AutoRenameTranscript): string;
|
|
15
|
+
export declare function isAutoRenamePromptText(text: string): boolean;
|
|
14
16
|
export declare function sanitizeAutoRenameName(output: string): string | null;
|
|
15
17
|
export declare function generateAutoRenameName(options: AutoRenameOptions): string | null;
|
package/dist/auto-rename.js
CHANGED
|
@@ -3,7 +3,8 @@ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { CODEX_ASSIST_MODEL } from "./codex-assist.js";
|
|
6
|
-
const
|
|
6
|
+
export const AUTO_RENAME_PROMPT_PREFIX = "Write a concise name for this coding-agent session.";
|
|
7
|
+
const AUTO_RENAME_PROMPT = `${AUTO_RENAME_PROMPT_PREFIX}
|
|
7
8
|
|
|
8
9
|
Rules:
|
|
9
10
|
- Output only the name. No quotes, JSON, markdown, or explanation.
|
|
@@ -45,6 +46,9 @@ export function buildAutoRenamePrompt(transcript) {
|
|
|
45
46
|
}
|
|
46
47
|
return `${AUTO_RENAME_PROMPT}\n\nTranscript:\n${sections.join("\n\n")}`;
|
|
47
48
|
}
|
|
49
|
+
export function isAutoRenamePromptText(text) {
|
|
50
|
+
return text.trimStart().startsWith(AUTO_RENAME_PROMPT_PREFIX);
|
|
51
|
+
}
|
|
48
52
|
export function sanitizeAutoRenameName(output) {
|
|
49
53
|
const line = output
|
|
50
54
|
.split("\n")
|
package/dist/auto-rename.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-rename.js","sourceRoot":"","sources":["../src/auto-rename.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,kBAAkB,GAAG;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"auto-rename.js","sourceRoot":"","sources":["../src/auto-rename.ts"],"names":[],"mappings":"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"}
|
package/dist/sessions-index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { readdir, readFile, writeFile, appendFile, stat, open } from "node:fs/promises";
|
|
2
2
|
import { createReadStream } from "node:fs";
|
|
3
3
|
import { createInterface } from "node:readline";
|
|
4
|
-
import { basename, join } from "node:path";
|
|
4
|
+
import { basename, extname, join } from "node:path";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
|
+
import { isAutoRenamePromptText } from "./auto-rename.js";
|
|
7
|
+
import { CODEX_ASSIST_MODEL } from "./codex-assist.js";
|
|
6
8
|
function createRecentSessionsPerfStats() {
|
|
7
9
|
return {
|
|
8
10
|
claudeProjectDirs: 0,
|
|
@@ -119,6 +121,9 @@ const RE_SYSTEM_INJECTED = /^<(?:local-command-caveat|local-command-std(?:err|ou
|
|
|
119
121
|
function isSystemInjectedText(text) {
|
|
120
122
|
return RE_SYSTEM_INJECTED.test(text) || text.startsWith("Base directory for this skill:");
|
|
121
123
|
}
|
|
124
|
+
function isCodexAutoRenameSession(firstPrompt, model) {
|
|
125
|
+
return model === CODEX_ASSIST_MODEL && isAutoRenamePromptText(firstPrompt);
|
|
126
|
+
}
|
|
122
127
|
/** Extract user prompt text from a parsed JSONL entry. */
|
|
123
128
|
function extractUserPromptText(entry) {
|
|
124
129
|
const message = entry.message;
|
|
@@ -147,6 +152,7 @@ function parseFromChunks(sessionId, head, tail) {
|
|
|
147
152
|
let headFoundFirstPrompt = false;
|
|
148
153
|
let headFoundProjectPath = false;
|
|
149
154
|
let headFoundGitBranch = false;
|
|
155
|
+
let isInternalAutoRename = false;
|
|
150
156
|
// --- Scan head lines ---
|
|
151
157
|
const headLines = head.split("\n");
|
|
152
158
|
for (const line of headLines) {
|
|
@@ -200,6 +206,10 @@ function parseFromChunks(sessionId, head, tail) {
|
|
|
200
206
|
const entry = JSON.parse(line);
|
|
201
207
|
const text = extractUserPromptText(entry);
|
|
202
208
|
if (text && !isSystemInjectedText(text)) {
|
|
209
|
+
if (isAutoRenamePromptText(text)) {
|
|
210
|
+
isInternalAutoRename = true;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
203
213
|
firstPrompt = text;
|
|
204
214
|
headFoundFirstPrompt = true;
|
|
205
215
|
}
|
|
@@ -207,6 +217,14 @@ function parseFromChunks(sessionId, head, tail) {
|
|
|
207
217
|
catch { /* skip */ }
|
|
208
218
|
}
|
|
209
219
|
}
|
|
220
|
+
if (isInternalAutoRename) {
|
|
221
|
+
return {
|
|
222
|
+
entry: null,
|
|
223
|
+
headFoundFirstPrompt,
|
|
224
|
+
headFoundProjectPath,
|
|
225
|
+
headFoundGitBranch,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
210
228
|
// --- Scan tail lines (if separate from head) ---
|
|
211
229
|
if (tail) {
|
|
212
230
|
const tailLines = tail.split("\n");
|
|
@@ -288,6 +306,14 @@ function parseFromChunks(sessionId, head, tail) {
|
|
|
288
306
|
headFoundGitBranch,
|
|
289
307
|
};
|
|
290
308
|
}
|
|
309
|
+
if (isAutoRenamePromptText(firstPrompt)) {
|
|
310
|
+
return {
|
|
311
|
+
entry: null,
|
|
312
|
+
headFoundFirstPrompt,
|
|
313
|
+
headFoundProjectPath,
|
|
314
|
+
headFoundGitBranch,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
291
317
|
return {
|
|
292
318
|
entry: {
|
|
293
319
|
sessionId,
|
|
@@ -361,6 +387,9 @@ async function parseClaudeJsonlFileFast(sessionId, filePath) {
|
|
|
361
387
|
if (!result.firstPrompt && missing.firstPrompt) {
|
|
362
388
|
result.firstPrompt = missing.firstPrompt;
|
|
363
389
|
}
|
|
390
|
+
if (isAutoRenamePromptText(result.firstPrompt)) {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
364
393
|
if (missing.projectPath) {
|
|
365
394
|
result.projectPath = missing.projectPath;
|
|
366
395
|
if (missing.rawCwd && missing.rawCwd !== missing.projectPath) {
|
|
@@ -377,6 +406,8 @@ async function parseClaudeJsonlFileFast(sessionId, filePath) {
|
|
|
377
406
|
return result;
|
|
378
407
|
}
|
|
379
408
|
async function hydrateClaudeIndexedEntry(dirPath, entry) {
|
|
409
|
+
if (isAutoRenamePromptText(entry.firstPrompt ?? ""))
|
|
410
|
+
return null;
|
|
380
411
|
const rawProjectPath = entry.projectPath ?? "";
|
|
381
412
|
const normalizedPath = normalizeWorktreePath(rawProjectPath);
|
|
382
413
|
const base = {
|
|
@@ -404,6 +435,8 @@ async function hydrateClaudeIndexedEntry(dirPath, entry) {
|
|
|
404
435
|
const parsed = await parseClaudeJsonlFileFast(entry.sessionId, fallbackPath);
|
|
405
436
|
if (!parsed)
|
|
406
437
|
return base;
|
|
438
|
+
if (isAutoRenamePromptText(parsed.firstPrompt))
|
|
439
|
+
return null;
|
|
407
440
|
return {
|
|
408
441
|
...base,
|
|
409
442
|
firstPrompt: base.firstPrompt || parsed.firstPrompt,
|
|
@@ -677,8 +710,11 @@ export async function getAllRecentSessions(options = {}) {
|
|
|
677
710
|
continue;
|
|
678
711
|
}
|
|
679
712
|
indexedIds.add(entry.sessionId);
|
|
680
|
-
|
|
681
|
-
|
|
713
|
+
const hydrated = await hydrateClaudeIndexedEntry(dirPath, entry);
|
|
714
|
+
if (hydrated) {
|
|
715
|
+
result.entries.push(hydrated);
|
|
716
|
+
result.indexEntries += 1;
|
|
717
|
+
}
|
|
682
718
|
}
|
|
683
719
|
if (!includeOnlyNamedClaude) {
|
|
684
720
|
const scanned = await scanJsonlDir(dirPath, {
|
|
@@ -981,6 +1017,8 @@ function parseCodexSessionJsonl(raw, fallbackSessionId) {
|
|
|
981
1017
|
}
|
|
982
1018
|
if (model === "codex-auto-review")
|
|
983
1019
|
return null;
|
|
1020
|
+
if (isCodexAutoRenameSession(firstPrompt, model))
|
|
1021
|
+
return null;
|
|
984
1022
|
if (!projectPath || !hasMessages)
|
|
985
1023
|
return null;
|
|
986
1024
|
summary = lastAssistantText || summary;
|
|
@@ -2066,46 +2104,221 @@ async function extractCodexMessageImages(sessionId, messageUuid) {
|
|
|
2066
2104
|
catch {
|
|
2067
2105
|
return [];
|
|
2068
2106
|
}
|
|
2069
|
-
// Codex doesn't have per-message UUIDs in the same way.
|
|
2070
|
-
//
|
|
2071
|
-
//
|
|
2107
|
+
// Codex doesn't have per-message UUIDs in the same way. Newer app history
|
|
2108
|
+
// uses a stable turn ordinal (codex:user-turn:N); older builds encoded the
|
|
2109
|
+
// JSONL line index (codex-line:N).
|
|
2072
2110
|
const lineIndex = messageUuid.startsWith("codex-line-")
|
|
2073
2111
|
? parseInt(messageUuid.slice("codex-line-".length), 10)
|
|
2074
2112
|
: -1;
|
|
2075
|
-
if (lineIndex < 0)
|
|
2076
|
-
return [];
|
|
2077
2113
|
const lines = raw.split("\n");
|
|
2078
|
-
if (lineIndex >=
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2114
|
+
if (lineIndex >= 0) {
|
|
2115
|
+
if (lineIndex >= lines.length)
|
|
2116
|
+
return [];
|
|
2117
|
+
const line = lines[lineIndex];
|
|
2118
|
+
if (!line?.trim())
|
|
2119
|
+
return [];
|
|
2120
|
+
let entry;
|
|
2121
|
+
try {
|
|
2122
|
+
entry = JSON.parse(line);
|
|
2123
|
+
}
|
|
2124
|
+
catch {
|
|
2125
|
+
return [];
|
|
2126
|
+
}
|
|
2127
|
+
if (entry.type !== "event_msg")
|
|
2128
|
+
return [];
|
|
2129
|
+
const payload = asObject(entry.payload);
|
|
2130
|
+
if (!payload || payload.type !== "user_message")
|
|
2131
|
+
return [];
|
|
2132
|
+
return extractCodexUserMessagePayloadImages(payload);
|
|
2086
2133
|
}
|
|
2087
|
-
|
|
2134
|
+
const turnMatch = messageUuid.match(/^codex:user-turn:(\d+)$/);
|
|
2135
|
+
const targetOrdinal = turnMatch ? Number(turnMatch[1]) : -1;
|
|
2136
|
+
if (!Number.isInteger(targetOrdinal) || targetOrdinal <= 0)
|
|
2088
2137
|
return [];
|
|
2138
|
+
const responseItemImagesByOrdinal = collectCodexUserResponseItemImagesByOrdinal(lines);
|
|
2139
|
+
let ordinal = 0;
|
|
2140
|
+
for (const line of lines) {
|
|
2141
|
+
if (!line.trim())
|
|
2142
|
+
continue;
|
|
2143
|
+
let entry;
|
|
2144
|
+
try {
|
|
2145
|
+
entry = JSON.parse(line);
|
|
2146
|
+
}
|
|
2147
|
+
catch {
|
|
2148
|
+
continue;
|
|
2149
|
+
}
|
|
2150
|
+
if (entry.type !== "event_msg")
|
|
2151
|
+
continue;
|
|
2152
|
+
const payload = asObject(entry.payload);
|
|
2153
|
+
if (!payload || payload.type !== "user_message")
|
|
2154
|
+
continue;
|
|
2155
|
+
if (!codexUserMessagePayloadHasDisplayContent(payload))
|
|
2156
|
+
continue;
|
|
2157
|
+
ordinal += 1;
|
|
2158
|
+
if (ordinal === targetOrdinal) {
|
|
2159
|
+
const images = await extractCodexUserMessagePayloadImages(payload);
|
|
2160
|
+
return images.length > 0
|
|
2161
|
+
? images
|
|
2162
|
+
: (responseItemImagesByOrdinal.get(targetOrdinal) ?? []);
|
|
2163
|
+
}
|
|
2089
2164
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
if (!payload || payload.type !== "user_message")
|
|
2094
|
-
return [];
|
|
2165
|
+
return [];
|
|
2166
|
+
}
|
|
2167
|
+
async function extractCodexUserMessagePayloadImages(payload) {
|
|
2095
2168
|
const images = [];
|
|
2096
|
-
// Parse payload.images (Data URI format: "data:image/png;base64,...")
|
|
2097
2169
|
if (Array.isArray(payload.images)) {
|
|
2098
2170
|
for (const img of payload.images) {
|
|
2099
|
-
if (typeof img
|
|
2171
|
+
if (typeof img === "string") {
|
|
2172
|
+
const match = img.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
2173
|
+
if (match) {
|
|
2174
|
+
images.push({ base64: match[2], mimeType: match[1] });
|
|
2175
|
+
}
|
|
2100
2176
|
continue;
|
|
2101
|
-
const match = img.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
2102
|
-
if (match) {
|
|
2103
|
-
images.push({ base64: match[2], mimeType: match[1] });
|
|
2104
2177
|
}
|
|
2178
|
+
const item = asObject(img);
|
|
2179
|
+
if (!item)
|
|
2180
|
+
continue;
|
|
2181
|
+
const base64 = typeof item.base64 === "string"
|
|
2182
|
+
? item.base64
|
|
2183
|
+
: typeof item.data === "string"
|
|
2184
|
+
? item.data
|
|
2185
|
+
: undefined;
|
|
2186
|
+
const mimeType = typeof item.mimeType === "string"
|
|
2187
|
+
? item.mimeType
|
|
2188
|
+
: typeof item.mime_type === "string"
|
|
2189
|
+
? item.mime_type
|
|
2190
|
+
: typeof item.media_type === "string"
|
|
2191
|
+
? item.media_type
|
|
2192
|
+
: undefined;
|
|
2193
|
+
if (base64 && mimeType) {
|
|
2194
|
+
images.push({ base64, mimeType });
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
if (Array.isArray(payload.local_images)) {
|
|
2199
|
+
for (const imagePath of payload.local_images) {
|
|
2200
|
+
if (typeof imagePath !== "string" || imagePath.length === 0)
|
|
2201
|
+
continue;
|
|
2202
|
+
const image = await readLocalImageAsBase64(imagePath);
|
|
2203
|
+
if (image)
|
|
2204
|
+
images.push(image);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
return images;
|
|
2208
|
+
}
|
|
2209
|
+
function collectCodexUserResponseItemImagesByOrdinal(lines) {
|
|
2210
|
+
const imagesByOrdinal = new Map();
|
|
2211
|
+
let ordinal = 0;
|
|
2212
|
+
for (const line of lines) {
|
|
2213
|
+
if (!line.trim())
|
|
2214
|
+
continue;
|
|
2215
|
+
let entry;
|
|
2216
|
+
try {
|
|
2217
|
+
entry = JSON.parse(line);
|
|
2218
|
+
}
|
|
2219
|
+
catch {
|
|
2220
|
+
continue;
|
|
2221
|
+
}
|
|
2222
|
+
if (entry.type !== "response_item")
|
|
2223
|
+
continue;
|
|
2224
|
+
const payload = asObject(entry.payload);
|
|
2225
|
+
if (!payload ||
|
|
2226
|
+
payload.type !== "message" ||
|
|
2227
|
+
payload.role !== "user" ||
|
|
2228
|
+
!codexUserResponseItemHasDisplayContent(payload)) {
|
|
2229
|
+
continue;
|
|
2230
|
+
}
|
|
2231
|
+
ordinal += 1;
|
|
2232
|
+
const images = extractCodexUserResponseItemImages(payload);
|
|
2233
|
+
if (images.length > 0) {
|
|
2234
|
+
imagesByOrdinal.set(ordinal, images);
|
|
2105
2235
|
}
|
|
2106
2236
|
}
|
|
2237
|
+
return imagesByOrdinal;
|
|
2238
|
+
}
|
|
2239
|
+
function codexUserResponseItemHasDisplayContent(payload) {
|
|
2240
|
+
const texts = [];
|
|
2241
|
+
let hasImage = false;
|
|
2242
|
+
for (const item of arrayValue(payload.content)) {
|
|
2243
|
+
const content = asObject(item);
|
|
2244
|
+
if (!content)
|
|
2245
|
+
continue;
|
|
2246
|
+
if (content.type === "input_image") {
|
|
2247
|
+
hasImage = true;
|
|
2248
|
+
continue;
|
|
2249
|
+
}
|
|
2250
|
+
if (content.type !== "input_text" || typeof content.text !== "string") {
|
|
2251
|
+
continue;
|
|
2252
|
+
}
|
|
2253
|
+
texts.push(content.text);
|
|
2254
|
+
}
|
|
2255
|
+
const userText = texts
|
|
2256
|
+
.filter((text) => !isCodexImageWrapperText(text.trim()))
|
|
2257
|
+
.join("\n")
|
|
2258
|
+
.trim();
|
|
2259
|
+
if (userText && isCodexInjectedUserContext(userText))
|
|
2260
|
+
return false;
|
|
2261
|
+
return userText.length > 0 || hasImage;
|
|
2262
|
+
}
|
|
2263
|
+
function extractCodexUserResponseItemImages(payload) {
|
|
2264
|
+
const images = [];
|
|
2265
|
+
for (const item of arrayValue(payload.content)) {
|
|
2266
|
+
const content = asObject(item);
|
|
2267
|
+
if (!content || content.type !== "input_image")
|
|
2268
|
+
continue;
|
|
2269
|
+
const imageUrl = typeof content.image_url === "string"
|
|
2270
|
+
? content.image_url
|
|
2271
|
+
: typeof content.url === "string"
|
|
2272
|
+
? content.url
|
|
2273
|
+
: undefined;
|
|
2274
|
+
const image = extractDataUriImage(imageUrl);
|
|
2275
|
+
if (image)
|
|
2276
|
+
images.push(image);
|
|
2277
|
+
}
|
|
2107
2278
|
return images;
|
|
2108
2279
|
}
|
|
2280
|
+
function extractDataUriImage(value) {
|
|
2281
|
+
const match = value?.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
2282
|
+
return match ? { base64: match[2], mimeType: match[1] } : null;
|
|
2283
|
+
}
|
|
2284
|
+
function isCodexImageWrapperText(text) {
|
|
2285
|
+
return /^<image(?:\s[^>]*)?>$/.test(text) || text === "</image>";
|
|
2286
|
+
}
|
|
2287
|
+
async function readLocalImageAsBase64(imagePath) {
|
|
2288
|
+
const mimeType = mimeTypeForLocalImagePath(imagePath);
|
|
2289
|
+
if (!mimeType)
|
|
2290
|
+
return null;
|
|
2291
|
+
try {
|
|
2292
|
+
const buffer = await readFile(imagePath);
|
|
2293
|
+
return { base64: buffer.toString("base64"), mimeType };
|
|
2294
|
+
}
|
|
2295
|
+
catch {
|
|
2296
|
+
return null;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
function mimeTypeForLocalImagePath(imagePath) {
|
|
2300
|
+
switch (extname(imagePath).toLowerCase()) {
|
|
2301
|
+
case ".png":
|
|
2302
|
+
return "image/png";
|
|
2303
|
+
case ".jpg":
|
|
2304
|
+
case ".jpeg":
|
|
2305
|
+
return "image/jpeg";
|
|
2306
|
+
case ".gif":
|
|
2307
|
+
return "image/gif";
|
|
2308
|
+
case ".webp":
|
|
2309
|
+
return "image/webp";
|
|
2310
|
+
default:
|
|
2311
|
+
return null;
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
function codexUserMessagePayloadHasDisplayContent(payload) {
|
|
2315
|
+
const message = typeof payload.message === "string" ? payload.message : "";
|
|
2316
|
+
const images = Array.isArray(payload.images) ? payload.images.length : 0;
|
|
2317
|
+
const localImages = Array.isArray(payload.local_images)
|
|
2318
|
+
? payload.local_images.length
|
|
2319
|
+
: 0;
|
|
2320
|
+
return message.trim().length > 0 || images + localImages > 0;
|
|
2321
|
+
}
|
|
2109
2322
|
export async function getCodexSessionHistory(threadId) {
|
|
2110
2323
|
const jsonlPath = await findCodexSessionJsonlPath(threadId);
|
|
2111
2324
|
if (!jsonlPath)
|
|
@@ -2212,6 +2425,9 @@ export async function getCodexSessionHistory(threadId) {
|
|
|
2212
2425
|
continue;
|
|
2213
2426
|
}
|
|
2214
2427
|
if (payload.role === "user") {
|
|
2428
|
+
if (content.some((item) => item.type === "input_image")) {
|
|
2429
|
+
continue;
|
|
2430
|
+
}
|
|
2215
2431
|
const text = content
|
|
2216
2432
|
.filter((item) => item.type === "input_text" && typeof item.text === "string")
|
|
2217
2433
|
.map((item) => item.text)
|