codex-to-im 1.0.29 → 1.0.31
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/cli.mjs +206 -1
- package/dist/daemon.mjs +53 -20
- package/dist/ui-server.mjs +708 -85
- package/package.json +1 -1
- package/references/setup-guides.md +1 -1
- package/scripts/doctor.sh +9 -9
package/dist/cli.mjs
CHANGED
|
@@ -20,6 +20,14 @@ var DEFAULT_WORKSPACE_ROOT = path.join(os.homedir(), "cx2im");
|
|
|
20
20
|
var CTI_HOME = process.env.CTI_HOME || DEFAULT_CTI_HOME;
|
|
21
21
|
var CONFIG_PATH = path.join(CTI_HOME, "config.env");
|
|
22
22
|
var CONFIG_V2_PATH = path.join(CTI_HOME, "config.v2.json");
|
|
23
|
+
function expandHomePath(value) {
|
|
24
|
+
if (!value) return value;
|
|
25
|
+
if (value === "~") return os.homedir();
|
|
26
|
+
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
27
|
+
return path.join(os.homedir(), value.slice(2));
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
23
31
|
function parseEnvFile(content) {
|
|
24
32
|
const entries = /* @__PURE__ */ new Map();
|
|
25
33
|
for (const line of content.split("\n")) {
|
|
@@ -43,6 +51,175 @@ function loadRawConfigEnv() {
|
|
|
43
51
|
return /* @__PURE__ */ new Map();
|
|
44
52
|
}
|
|
45
53
|
}
|
|
54
|
+
function splitCsv(value) {
|
|
55
|
+
if (!value) return void 0;
|
|
56
|
+
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
57
|
+
}
|
|
58
|
+
function parsePositiveInt(value) {
|
|
59
|
+
if (!value) return void 0;
|
|
60
|
+
const parsed = Number(value);
|
|
61
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
|
|
62
|
+
return Math.floor(parsed);
|
|
63
|
+
}
|
|
64
|
+
function parseSandboxMode(value) {
|
|
65
|
+
if (value === "read-only" || value === "workspace-write" || value === "danger-full-access") {
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
function parseReasoningEffort(value) {
|
|
71
|
+
if (value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh") {
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
function normalizeFeishuSite(value) {
|
|
77
|
+
const normalized = (value || "").trim().replace(/\/+$/, "").toLowerCase();
|
|
78
|
+
if (!normalized) return "feishu";
|
|
79
|
+
if (normalized === "lark") return "lark";
|
|
80
|
+
if (normalized === "feishu") return "feishu";
|
|
81
|
+
if (normalized.includes("open.larksuite.com")) return "lark";
|
|
82
|
+
return "feishu";
|
|
83
|
+
}
|
|
84
|
+
function nowIso() {
|
|
85
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
86
|
+
}
|
|
87
|
+
function ensureConfigDir() {
|
|
88
|
+
fs.mkdirSync(CTI_HOME, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
function readConfigV2File() {
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(fs.readFileSync(CONFIG_V2_PATH, "utf-8"));
|
|
93
|
+
if (parsed && parsed.schemaVersion === 2 && parsed.runtime && Array.isArray(parsed.channels)) {
|
|
94
|
+
return parsed;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
} catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function writeConfigV2File(config) {
|
|
102
|
+
ensureConfigDir();
|
|
103
|
+
const tmpPath = CONFIG_V2_PATH + ".tmp";
|
|
104
|
+
fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
105
|
+
fs.renameSync(tmpPath, CONFIG_V2_PATH);
|
|
106
|
+
}
|
|
107
|
+
function defaultAliasForProvider(provider) {
|
|
108
|
+
return provider === "feishu" ? "\u98DE\u4E66" : "\u5FAE\u4FE1";
|
|
109
|
+
}
|
|
110
|
+
function buildDefaultChannelId(provider) {
|
|
111
|
+
return `${provider}-default`;
|
|
112
|
+
}
|
|
113
|
+
function migrateLegacyEnvToV2(env) {
|
|
114
|
+
const rawRuntime = env.get("CTI_RUNTIME") || "codex";
|
|
115
|
+
const runtime = ["claude", "codex", "auto"].includes(rawRuntime) ? rawRuntime : "codex";
|
|
116
|
+
const enabledChannels = splitCsv(env.get("CTI_ENABLED_CHANNELS")) ?? ["feishu"];
|
|
117
|
+
const timestamp = nowIso();
|
|
118
|
+
const channels = [];
|
|
119
|
+
const hasFeishuConfig = Boolean(
|
|
120
|
+
env.get("CTI_FEISHU_APP_ID") || env.get("CTI_FEISHU_APP_SECRET") || env.get("CTI_FEISHU_ALLOWED_USERS") || enabledChannels.includes("feishu")
|
|
121
|
+
);
|
|
122
|
+
if (hasFeishuConfig) {
|
|
123
|
+
channels.push({
|
|
124
|
+
id: buildDefaultChannelId("feishu"),
|
|
125
|
+
alias: defaultAliasForProvider("feishu"),
|
|
126
|
+
provider: "feishu",
|
|
127
|
+
enabled: enabledChannels.includes("feishu"),
|
|
128
|
+
createdAt: timestamp,
|
|
129
|
+
updatedAt: timestamp,
|
|
130
|
+
config: {
|
|
131
|
+
appId: env.get("CTI_FEISHU_APP_ID") || void 0,
|
|
132
|
+
appSecret: env.get("CTI_FEISHU_APP_SECRET") || void 0,
|
|
133
|
+
site: normalizeFeishuSite(env.get("CTI_FEISHU_SITE") || env.get("CTI_FEISHU_DOMAIN")),
|
|
134
|
+
allowedUsers: splitCsv(env.get("CTI_FEISHU_ALLOWED_USERS")),
|
|
135
|
+
streamingEnabled: env.has("CTI_FEISHU_STREAMING_ENABLED") ? env.get("CTI_FEISHU_STREAMING_ENABLED") === "true" : true,
|
|
136
|
+
feedbackMarkdownEnabled: env.has("CTI_FEISHU_COMMAND_MARKDOWN_ENABLED") ? env.get("CTI_FEISHU_COMMAND_MARKDOWN_ENABLED") === "true" : true
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
const hasWeixinConfig = Boolean(
|
|
141
|
+
env.get("CTI_WEIXIN_BASE_URL") || env.get("CTI_WEIXIN_CDN_BASE_URL") || env.get("CTI_WEIXIN_MEDIA_ENABLED") || enabledChannels.includes("weixin")
|
|
142
|
+
);
|
|
143
|
+
if (hasWeixinConfig) {
|
|
144
|
+
channels.push({
|
|
145
|
+
id: buildDefaultChannelId("weixin"),
|
|
146
|
+
alias: defaultAliasForProvider("weixin"),
|
|
147
|
+
provider: "weixin",
|
|
148
|
+
enabled: enabledChannels.includes("weixin"),
|
|
149
|
+
createdAt: timestamp,
|
|
150
|
+
updatedAt: timestamp,
|
|
151
|
+
config: {
|
|
152
|
+
baseUrl: env.get("CTI_WEIXIN_BASE_URL") || void 0,
|
|
153
|
+
cdnBaseUrl: env.get("CTI_WEIXIN_CDN_BASE_URL") || void 0,
|
|
154
|
+
mediaEnabled: env.has("CTI_WEIXIN_MEDIA_ENABLED") ? env.get("CTI_WEIXIN_MEDIA_ENABLED") === "true" : void 0,
|
|
155
|
+
feedbackMarkdownEnabled: env.has("CTI_WEIXIN_COMMAND_MARKDOWN_ENABLED") ? env.get("CTI_WEIXIN_COMMAND_MARKDOWN_ENABLED") === "true" : false
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
schemaVersion: 2,
|
|
161
|
+
runtime: {
|
|
162
|
+
provider: runtime,
|
|
163
|
+
defaultWorkspaceRoot: expandHomePath(env.get("CTI_DEFAULT_WORKSPACE_ROOT")) || void 0,
|
|
164
|
+
defaultModel: env.get("CTI_DEFAULT_MODEL") || void 0,
|
|
165
|
+
defaultMode: env.get("CTI_DEFAULT_MODE") || "code",
|
|
166
|
+
historyMessageLimit: parsePositiveInt(env.get("CTI_HISTORY_MESSAGE_LIMIT")) ?? 8,
|
|
167
|
+
codexSkipGitRepoCheck: env.has("CTI_CODEX_SKIP_GIT_REPO_CHECK") ? env.get("CTI_CODEX_SKIP_GIT_REPO_CHECK") === "true" : true,
|
|
168
|
+
codexSandboxMode: parseSandboxMode(env.get("CTI_CODEX_SANDBOX_MODE")) ?? "workspace-write",
|
|
169
|
+
codexReasoningEffort: parseReasoningEffort(env.get("CTI_CODEX_REASONING_EFFORT")) ?? "medium",
|
|
170
|
+
uiAllowLan: env.get("CTI_UI_ALLOW_LAN") === "true",
|
|
171
|
+
uiAccessToken: env.get("CTI_UI_ACCESS_TOKEN") || void 0,
|
|
172
|
+
autoApprove: env.get("CTI_AUTO_APPROVE") === "true"
|
|
173
|
+
},
|
|
174
|
+
channels
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function expandConfig(v2) {
|
|
178
|
+
return {
|
|
179
|
+
schemaVersion: 2,
|
|
180
|
+
channels: v2.channels,
|
|
181
|
+
runtime: v2.runtime.provider,
|
|
182
|
+
enabledChannels: Array.from(new Set(
|
|
183
|
+
v2.channels.filter((channel) => channel.enabled).map((channel) => channel.provider)
|
|
184
|
+
)),
|
|
185
|
+
defaultWorkspaceRoot: v2.runtime.defaultWorkspaceRoot,
|
|
186
|
+
defaultModel: v2.runtime.defaultModel,
|
|
187
|
+
defaultMode: v2.runtime.defaultMode || "code",
|
|
188
|
+
historyMessageLimit: v2.runtime.historyMessageLimit ?? 8,
|
|
189
|
+
codexSkipGitRepoCheck: v2.runtime.codexSkipGitRepoCheck ?? true,
|
|
190
|
+
codexSandboxMode: v2.runtime.codexSandboxMode ?? "workspace-write",
|
|
191
|
+
codexReasoningEffort: v2.runtime.codexReasoningEffort ?? "medium",
|
|
192
|
+
uiAllowLan: v2.runtime.uiAllowLan === true,
|
|
193
|
+
uiAccessToken: v2.runtime.uiAccessToken || void 0,
|
|
194
|
+
autoApprove: v2.runtime.autoApprove === true
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function loadConfig() {
|
|
198
|
+
const current = readConfigV2File();
|
|
199
|
+
if (current) return expandConfig(current);
|
|
200
|
+
const legacyEnv = loadRawConfigEnv();
|
|
201
|
+
if (legacyEnv.size > 0) {
|
|
202
|
+
const migrated = migrateLegacyEnvToV2(legacyEnv);
|
|
203
|
+
writeConfigV2File(migrated);
|
|
204
|
+
return expandConfig(migrated);
|
|
205
|
+
}
|
|
206
|
+
const empty = {
|
|
207
|
+
schemaVersion: 2,
|
|
208
|
+
runtime: {
|
|
209
|
+
provider: "codex",
|
|
210
|
+
defaultWorkspaceRoot: DEFAULT_WORKSPACE_ROOT,
|
|
211
|
+
defaultMode: "code",
|
|
212
|
+
historyMessageLimit: 8,
|
|
213
|
+
codexSkipGitRepoCheck: true,
|
|
214
|
+
codexSandboxMode: "workspace-write",
|
|
215
|
+
codexReasoningEffort: "medium",
|
|
216
|
+
uiAllowLan: false,
|
|
217
|
+
autoApprove: false
|
|
218
|
+
},
|
|
219
|
+
channels: []
|
|
220
|
+
};
|
|
221
|
+
return expandConfig(empty);
|
|
222
|
+
}
|
|
46
223
|
|
|
47
224
|
// src/service-manager.ts
|
|
48
225
|
var moduleDir = path2.dirname(fileURLToPath(import.meta.url));
|
|
@@ -308,6 +485,27 @@ function buildDaemonEnv() {
|
|
|
308
485
|
delete env.CLAUDECODE;
|
|
309
486
|
return env;
|
|
310
487
|
}
|
|
488
|
+
function describeBridgeStartupPreflightFailure(channels) {
|
|
489
|
+
const configured = Array.isArray(channels) ? channels : [];
|
|
490
|
+
if (configured.length === 0) {
|
|
491
|
+
return "\u672A\u914D\u7F6E\u4EFB\u4F55\u901A\u9053\u5B9E\u4F8B\u3002\u8BF7\u5148\u5728 Web \u63A7\u5236\u53F0\u521B\u5EFA\u5E76\u4FDD\u5B58\u81F3\u5C11\u4E00\u4E2A\u98DE\u4E66\u6216\u5FAE\u4FE1\u901A\u9053\uFF0C\u7136\u540E\u518D\u542F\u52A8\u6865\u63A5\u670D\u52A1\u3002";
|
|
492
|
+
}
|
|
493
|
+
const enabled = configured.filter((channel) => channel.enabled !== false);
|
|
494
|
+
if (enabled.length === 0) {
|
|
495
|
+
return "\u5F53\u524D\u6240\u6709\u901A\u9053\u5B9E\u4F8B\u90FD\u5DF2\u7981\u7528\u3002\u8BF7\u5148\u542F\u7528\u81F3\u5C11\u4E00\u4E2A\u901A\u9053\u5B9E\u4F8B\uFF0C\u7136\u540E\u518D\u542F\u52A8\u6865\u63A5\u670D\u52A1\u3002";
|
|
496
|
+
}
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
function describeBridgeActivationFailure(status, channels) {
|
|
500
|
+
const statusReason = status.lastExitReason?.trim();
|
|
501
|
+
if (statusReason) return statusReason;
|
|
502
|
+
const preflightFailure = describeBridgeStartupPreflightFailure(channels);
|
|
503
|
+
if (preflightFailure) return preflightFailure;
|
|
504
|
+
const enabled = (channels || []).filter((channel) => channel.enabled !== false);
|
|
505
|
+
if (enabled.length === 0) return null;
|
|
506
|
+
const labels = enabled.map((channel) => channel.alias?.trim() || channel.id).join("\u3001");
|
|
507
|
+
return `\u6CA1\u6709\u4EFB\u4F55\u901A\u9053\u9002\u914D\u5668\u542F\u52A8\u6210\u529F\u3002\u8BF7\u68C0\u67E5\u901A\u9053\u914D\u7F6E\u3001\u51ED\u636E\u548C\u65E5\u5FD7\u3002\u5F53\u524D\u5DF2\u542F\u7528\u901A\u9053\uFF1A${labels}`;
|
|
508
|
+
}
|
|
311
509
|
async function waitForBridgeRunning(timeoutMs = 2e4) {
|
|
312
510
|
const startedAt = Date.now();
|
|
313
511
|
while (Date.now() - startedAt < timeoutMs) {
|
|
@@ -340,6 +538,11 @@ async function startBridge() {
|
|
|
340
538
|
if (current.running && extraAlivePids.length > 0) {
|
|
341
539
|
await stopBridge();
|
|
342
540
|
}
|
|
541
|
+
const config = loadConfig();
|
|
542
|
+
const preflightFailure = describeBridgeStartupPreflightFailure(config.channels);
|
|
543
|
+
if (preflightFailure) {
|
|
544
|
+
throw new Error(preflightFailure);
|
|
545
|
+
}
|
|
343
546
|
const daemonEntry = path2.join(packageRoot, "dist", "daemon.mjs");
|
|
344
547
|
if (!fs2.existsSync(daemonEntry)) {
|
|
345
548
|
throw new Error(`Daemon bundle not found at ${daemonEntry}. Run npm run build first.`);
|
|
@@ -356,7 +559,9 @@ async function startBridge() {
|
|
|
356
559
|
child.unref();
|
|
357
560
|
const status = await waitForBridgeRunning();
|
|
358
561
|
if (!status.running) {
|
|
359
|
-
throw new Error(
|
|
562
|
+
throw new Error(
|
|
563
|
+
describeBridgeActivationFailure(status, config.channels) || "Bridge failed to report running=true."
|
|
564
|
+
);
|
|
360
565
|
}
|
|
361
566
|
return status;
|
|
362
567
|
}
|
package/dist/daemon.mjs
CHANGED
|
@@ -7818,11 +7818,8 @@ function readJson(filePath, fallback) {
|
|
|
7818
7818
|
function getAccountRecency(account) {
|
|
7819
7819
|
return account.lastLoginAt ?? account.updatedAt ?? account.createdAt;
|
|
7820
7820
|
}
|
|
7821
|
-
function
|
|
7822
|
-
|
|
7823
|
-
return { accounts, removedAccountIds: [] };
|
|
7824
|
-
}
|
|
7825
|
-
const sorted = [...accounts].sort((a, b) => {
|
|
7821
|
+
function sortAccountsByRecency(accounts) {
|
|
7822
|
+
return [...accounts].sort((a, b) => {
|
|
7826
7823
|
const recencyDiff = getAccountRecency(b).localeCompare(getAccountRecency(a));
|
|
7827
7824
|
if (recencyDiff !== 0) return recencyDiff;
|
|
7828
7825
|
const updatedDiff = b.updatedAt.localeCompare(a.updatedAt);
|
|
@@ -7831,16 +7828,14 @@ function normalizeAccounts(accounts) {
|
|
|
7831
7828
|
if (createdDiff !== 0) return createdDiff;
|
|
7832
7829
|
return 0;
|
|
7833
7830
|
});
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
)
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
removedAccountIds
|
|
7843
|
-
};
|
|
7831
|
+
}
|
|
7832
|
+
function normalizeAccounts(accounts) {
|
|
7833
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
7834
|
+
for (const account of sortAccountsByRecency(accounts)) {
|
|
7835
|
+
if (!account?.accountId || deduped.has(account.accountId)) continue;
|
|
7836
|
+
deduped.set(account.accountId, account);
|
|
7837
|
+
}
|
|
7838
|
+
return Array.from(deduped.values());
|
|
7844
7839
|
}
|
|
7845
7840
|
function readStoredAccounts() {
|
|
7846
7841
|
ensureDir(DATA_DIR);
|
|
@@ -7848,7 +7843,7 @@ function readStoredAccounts() {
|
|
|
7848
7843
|
return Array.isArray(raw) ? raw : [];
|
|
7849
7844
|
}
|
|
7850
7845
|
function readAccounts() {
|
|
7851
|
-
return normalizeAccounts(readStoredAccounts())
|
|
7846
|
+
return normalizeAccounts(readStoredAccounts());
|
|
7852
7847
|
}
|
|
7853
7848
|
function readContextTokens() {
|
|
7854
7849
|
ensureDir(DATA_DIR);
|
|
@@ -7862,7 +7857,7 @@ function contextKey(accountId, peerUserId) {
|
|
|
7862
7857
|
return `${accountId}::${peerUserId}`;
|
|
7863
7858
|
}
|
|
7864
7859
|
function listWeixinAccounts() {
|
|
7865
|
-
return readAccounts()
|
|
7860
|
+
return sortAccountsByRecency(readAccounts());
|
|
7866
7861
|
}
|
|
7867
7862
|
function getWeixinAccount(accountId) {
|
|
7868
7863
|
return readAccounts().find((account) => account.accountId === accountId);
|
|
@@ -14234,6 +14229,22 @@ var WeixinAdapter = class extends BaseChannelAdapter {
|
|
|
14234
14229
|
}
|
|
14235
14230
|
}
|
|
14236
14231
|
validateConfig() {
|
|
14232
|
+
const configuredAccountId = this.configuredAccountId;
|
|
14233
|
+
const accounts = listWeixinAccounts();
|
|
14234
|
+
const enabledAccounts = accounts.filter((account) => account.enabled && account.token);
|
|
14235
|
+
if (configuredAccountId) {
|
|
14236
|
+
const configured = getWeixinAccount(configuredAccountId);
|
|
14237
|
+
if (!configured) {
|
|
14238
|
+
return `Linked WeChat account ${configuredAccountId} not found`;
|
|
14239
|
+
}
|
|
14240
|
+
if (!configured.enabled || !configured.token) {
|
|
14241
|
+
return `Linked WeChat account ${configuredAccountId} is disabled or missing token`;
|
|
14242
|
+
}
|
|
14243
|
+
return null;
|
|
14244
|
+
}
|
|
14245
|
+
if (enabledAccounts.length > 1) {
|
|
14246
|
+
return "Multiple linked WeChat accounts detected. Please select a WeChat account for this channel.";
|
|
14247
|
+
}
|
|
14237
14248
|
return null;
|
|
14238
14249
|
}
|
|
14239
14250
|
isAuthorized(_userId, _chatId) {
|
|
@@ -15370,7 +15381,7 @@ function bindStoreToSdkSession(store, channelType, chatId, sdkSessionId, opts) {
|
|
|
15370
15381
|
import fs5 from "node:fs";
|
|
15371
15382
|
import path6 from "node:path";
|
|
15372
15383
|
var DRAFT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
15373
|
-
var MAX_HIDDEN_DRAFT_SESSIONS =
|
|
15384
|
+
var MAX_HIDDEN_DRAFT_SESSIONS = 64;
|
|
15374
15385
|
var INTERNAL_SESSION_ROOT = path6.join(CTI_HOME, "runtime", "internal-sessions");
|
|
15375
15386
|
var DRAFT_SESSION_PREFIX = "Draft";
|
|
15376
15387
|
var HISTORY_SESSION_PREFIX = "History Summary";
|
|
@@ -18164,6 +18175,29 @@ function buildMirrorTitle(threadTitle, markdown = false) {
|
|
|
18164
18175
|
function buildMirrorSpeakerLabel(label, markdown = false) {
|
|
18165
18176
|
return markdown ? `**${label}:**` : `${label}:`;
|
|
18166
18177
|
}
|
|
18178
|
+
var MIRROR_USER_WRAPPER_LABELS = /* @__PURE__ */ new Map([
|
|
18179
|
+
["# Review findings:", "Review findings"],
|
|
18180
|
+
["# Context from my IDE setup:", "IDE setup"],
|
|
18181
|
+
["# Files mentioned by the user:", "Files"]
|
|
18182
|
+
]);
|
|
18183
|
+
var MIRROR_USER_REQUEST_MARKER = "## My request for Codex:";
|
|
18184
|
+
function formatMirrorUserText(text2) {
|
|
18185
|
+
const normalized = (text2 || "").replace(/\r\n?/g, "\n").trim();
|
|
18186
|
+
if (!normalized) return null;
|
|
18187
|
+
const lines = normalized.split("\n");
|
|
18188
|
+
const firstNonEmptyIndex = lines.findIndex((line) => line.trim().length > 0);
|
|
18189
|
+
if (firstNonEmptyIndex < 0) return null;
|
|
18190
|
+
const wrapperLabel = MIRROR_USER_WRAPPER_LABELS.get(lines[firstNonEmptyIndex].trim());
|
|
18191
|
+
if (!wrapperLabel) return normalized;
|
|
18192
|
+
const requestMarkerIndex = lines.findIndex(
|
|
18193
|
+
(line, index) => index > firstNonEmptyIndex && line.trim() === MIRROR_USER_REQUEST_MARKER
|
|
18194
|
+
);
|
|
18195
|
+
if (requestMarkerIndex < 0) return normalized;
|
|
18196
|
+
const requestBody = lines.slice(requestMarkerIndex + 1).join("\n").trim();
|
|
18197
|
+
if (!requestBody) return normalized;
|
|
18198
|
+
return `\uFF08\u57FA\u4E8E ${wrapperLabel}\uFF09
|
|
18199
|
+
${requestBody}`;
|
|
18200
|
+
}
|
|
18167
18201
|
function formatMirrorSpeakerBlock(label, text2, markdown = false, forceLabel = false) {
|
|
18168
18202
|
const normalized = (text2 || "").trim();
|
|
18169
18203
|
if (!normalized) {
|
|
@@ -18342,7 +18376,7 @@ function createMirrorTurnState(sessionId, timestamp, turnId) {
|
|
|
18342
18376
|
};
|
|
18343
18377
|
}
|
|
18344
18378
|
function appendMirrorUserText(turnState, chunk) {
|
|
18345
|
-
const normalized = chunk
|
|
18379
|
+
const normalized = formatMirrorUserText(chunk);
|
|
18346
18380
|
if (!normalized) return;
|
|
18347
18381
|
if (!turnState.userText) {
|
|
18348
18382
|
turnState.userText = normalized;
|
|
@@ -18416,7 +18450,6 @@ function consumeMirrorRecords(subscription, records) {
|
|
|
18416
18450
|
subscription.pendingTurn.lastActivityAt = record.timestamp;
|
|
18417
18451
|
}
|
|
18418
18452
|
}
|
|
18419
|
-
startMirrorStreaming(subscription, subscription.pendingTurn);
|
|
18420
18453
|
continue;
|
|
18421
18454
|
}
|
|
18422
18455
|
if (record.type === "task_complete") {
|