chatccc 0.2.231 → 0.2.233
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/.agents/skills/create-chatccc-feishu-app/SKILL.md +85 -85
- package/.claude/skills/create-chatccc-feishu-app/SKILL.md +85 -85
- package/.cursor/skills/create-chatccc-feishu-app/SKILL.md +85 -85
- package/README.md +90 -90
- package/agent-prompts/claude_specific.md +45 -45
- package/agent-prompts/codex_specific.md +2 -2
- package/agent-prompts/cursor_specific.md +13 -13
- package/bin/cccagent.mjs +17 -17
- package/im-skills/feishu-skill/receive-send-file.md +63 -63
- package/im-skills/feishu-skill/receive-send-image.md +24 -24
- package/im-skills/feishu-skill/skill.md +3 -3
- package/im-skills/wechat-file-skill/receive-send-file.md +38 -38
- package/im-skills/wechat-file-skill/send-file.mjs +83 -83
- package/im-skills/wechat-file-skill/skill.md +10 -10
- package/im-skills/wechat-image-skill/skill.md +10 -10
- package/im-skills/wechat-video-skill/receive-send-video.md +38 -38
- package/im-skills/wechat-video-skill/send-video.mjs +79 -79
- package/im-skills/wechat-video-skill/skill.md +10 -10
- package/package.json +1 -1
- package/scripts/postinstall-sharp-check.mjs +58 -58
- package/src/__tests__/agent-activity.test.ts +86 -86
- package/src/__tests__/agent-delegate-task-rpc.test.ts +165 -165
- package/src/__tests__/agent-reload-config-rpc.test.ts +99 -99
- package/src/__tests__/builtin-chat-session.test.ts +449 -393
- package/src/__tests__/builtin-cli-json.test.ts +39 -39
- package/src/__tests__/builtin-config.test.ts +26 -26
- package/src/__tests__/builtin-context.test.ts +319 -202
- package/src/__tests__/builtin-file-tools.test.ts +240 -240
- package/src/__tests__/builtin-permissions.test.ts +211 -211
- package/src/__tests__/builtin-session-search.test.ts +228 -0
- package/src/__tests__/builtin-session-select.test.ts +116 -116
- package/src/__tests__/builtin-sigint.test.ts +56 -56
- package/src/__tests__/builtin-skills.test.ts +284 -284
- package/src/__tests__/builtin-web-tools.test.ts +220 -220
- package/src/__tests__/card-action-routing.test.ts +18 -18
- package/src/__tests__/cardkit.test.ts +60 -60
- package/src/__tests__/ccc-adapter.test.ts +170 -170
- package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -89
- package/src/__tests__/chatgpt-subscription.test.ts +135 -135
- package/src/__tests__/chrome-devtools-guard.test.ts +165 -165
- package/src/__tests__/claude-adapter.test.ts +614 -614
- package/src/__tests__/claude-raw-stream-log.test.ts +87 -87
- package/src/__tests__/codex-adapter.test.ts +58 -58
- package/src/__tests__/codex-raw-stream-log.test.ts +170 -170
- package/src/__tests__/codex-reset-actions.test.ts +146 -146
- package/src/__tests__/crash-logging.test.ts +95 -1
- package/src/__tests__/cursor-adapter.test.ts +268 -268
- package/src/__tests__/feishu-api.test.ts +60 -60
- package/src/__tests__/feishu-avatar.test.ts +164 -164
- package/src/__tests__/feishu-message-ingress.test.ts +138 -138
- package/src/__tests__/feishu-platform.test.ts +22 -22
- package/src/__tests__/format-message.test.ts +47 -47
- package/src/__tests__/jsonl-stream.test.ts +79 -79
- package/src/__tests__/package-files.test.ts +24 -24
- package/src/__tests__/privacy.test.ts +198 -198
- package/src/__tests__/progress-reducer.test.ts +121 -121
- package/src/__tests__/raw-stream-log.test.ts +106 -106
- package/src/__tests__/response-stall.test.ts +49 -49
- package/src/__tests__/restart.test.ts +55 -10
- package/src/__tests__/session.test.ts +199 -199
- package/src/__tests__/shared-prefix.test.ts +36 -36
- package/src/__tests__/sim-platform.test.ts +16 -16
- package/src/__tests__/startup-lifecycle.test.ts +231 -231
- package/src/__tests__/stop-session.test.ts +34 -34
- package/src/__tests__/stream-state.test.ts +42 -42
- package/src/__tests__/terminal-error.test.ts +54 -54
- package/src/__tests__/terminal-renderer.test.ts +247 -247
- package/src/__tests__/update-command-guard.test.ts +144 -144
- package/src/__tests__/web-ui.test.ts +326 -326
- package/src/adapters/adapter-interface.ts +33 -33
- package/src/adapters/ccc-adapter.ts +141 -141
- package/src/adapters/claude-adapter.ts +620 -620
- package/src/adapters/claude-session-meta-store.ts +120 -120
- package/src/adapters/codex-adapter.ts +426 -426
- package/src/adapters/cursor-adapter.ts +681 -681
- package/src/adapters/jsonl-stream.ts +157 -157
- package/src/adapters/raw-stream-log.ts +124 -124
- package/src/adapters/resource-monitor.ts +140 -140
- package/src/agent-activity.ts +175 -175
- package/src/agent-delegate-task-rpc.ts +153 -153
- package/src/agent-delegate-task.ts +91 -91
- package/src/agent-reload-config-rpc.ts +34 -34
- package/src/agent-stop-stuck.ts +129 -129
- package/src/builtin/cli.ts +6 -0
- package/src/builtin/config.ts +84 -84
- package/src/builtin/context.ts +465 -391
- package/src/builtin/file-log.ts +38 -38
- package/src/builtin/file-tools.ts +34 -0
- package/src/builtin/index.ts +24 -23
- package/src/builtin/proc-tree-kill.ts +61 -61
- package/src/builtin/progress/cards-helpers.ts +76 -76
- package/src/builtin/progress/reducer.ts +113 -113
- package/src/builtin/progress/terminal-renderer.ts +294 -294
- package/src/builtin/progress/view.ts +77 -77
- package/src/builtin/raw-stream-log.ts +124 -124
- package/src/builtin/session-search.ts +363 -0
- package/src/builtin/session-select.ts +48 -48
- package/src/builtin/sigint.ts +50 -50
- package/src/builtin/skills.ts +205 -205
- package/src/builtin/web-tools.ts +313 -313
- package/src/card-action-routing.ts +14 -14
- package/src/chatgpt-subscription-rpc.ts +27 -27
- package/src/chatgpt-subscription.ts +299 -299
- package/src/chrome-devtools-guard.ts +318 -318
- package/src/codex-reset-actions.ts +184 -184
- package/src/feishu-api.ts +193 -193
- package/src/feishu-message-ingress.ts +195 -195
- package/src/feishu-platform.ts +20 -20
- package/src/format-message.ts +293 -293
- package/src/index.ts +6 -1
- package/src/litellm-proxy.ts +374 -374
- package/src/orchestrator.ts +36 -13
- package/src/platform-adapter.ts +6 -6
- package/src/privacy.ts +118 -118
- package/src/progress/reducer.ts +113 -113
- package/src/progress/terminal-renderer.ts +294 -294
- package/src/progress/view.ts +77 -77
- package/src/response-stall.ts +28 -28
- package/src/runtime-reload.ts +34 -34
- package/src/session-chat-binding.ts +82 -82
- package/src/session-name.ts +8 -8
- package/src/session.ts +201 -201
- package/src/shared-prefix.ts +29 -29
- package/src/shared.ts +44 -0
- package/src/sim-platform.ts +20 -20
- package/src/startup-lifecycle.ts +250 -250
- package/src/stream-state.ts +21 -21
- package/src/terminal-error.ts +129 -129
- package/src/turn-cards.ts +117 -117
- package/src/update-command-guard.ts +165 -165
package/src/session.ts
CHANGED
|
@@ -41,14 +41,14 @@ import { createCccAdapter } from "./adapters/ccc-adapter.ts";
|
|
|
41
41
|
import { killProcessTree } from "./adapters/proc-tree-kill.ts";
|
|
42
42
|
import { resourceMonitor, registerProcess, unregisterProcess } from "./adapters/resource-monitor.ts";
|
|
43
43
|
import { buildImSkillsPromptCached, exportSkillSubDocs, clearImSkillsPromptCache } from "./im-skills.ts";
|
|
44
|
-
import type { PlatformAdapter } from "./platform-adapter.ts";
|
|
45
|
-
import { hasResponseStalled, observeResponseProgress } from "./response-stall.ts";
|
|
46
|
-
import {
|
|
47
|
-
classifyTerminalError,
|
|
48
|
-
formatTerminalErrorNotice,
|
|
49
|
-
formatTerminalErrorReason,
|
|
50
|
-
type TerminalErrorInfo,
|
|
51
|
-
} from "./terminal-error.ts";
|
|
44
|
+
import type { PlatformAdapter } from "./platform-adapter.ts";
|
|
45
|
+
import { hasResponseStalled, observeResponseProgress } from "./response-stall.ts";
|
|
46
|
+
import {
|
|
47
|
+
classifyTerminalError,
|
|
48
|
+
formatTerminalErrorNotice,
|
|
49
|
+
formatTerminalErrorReason,
|
|
50
|
+
type TerminalErrorInfo,
|
|
51
|
+
} from "./terminal-error.ts";
|
|
52
52
|
import {
|
|
53
53
|
MAX_PROCESSED,
|
|
54
54
|
clearFeishuMessageLedgerMemory,
|
|
@@ -181,8 +181,8 @@ const DEFAULT_FINAL_RESPONSE_CLOSE_TIMEOUT_MS = 10_000;
|
|
|
181
181
|
export const RESPONSE_STALL_RECOVERY_PROMPT = "完成了吗?如果没完成继续";
|
|
182
182
|
export const RESPONSE_STALL_RECOVERY_NOTICE =
|
|
183
183
|
`检测到会话停滞,正在自动确认并继续。\n\n${RESPONSE_STALL_RECOVERY_PROMPT}`;
|
|
184
|
-
export const RESPONSE_STALL_RECOVERY_EXHAUSTED_NOTICE =
|
|
185
|
-
"⚠️ 自动续跑仍连续 3 分钟没有生成新回复,本次不再自动继续。";
|
|
184
|
+
export const RESPONSE_STALL_RECOVERY_EXHAUSTED_NOTICE =
|
|
185
|
+
"⚠️ 自动续跑仍连续 3 分钟没有生成新回复,本次不再自动继续。";
|
|
186
186
|
const RESPONSE_STALL_RECOVERY_DELAY_MS = 200;
|
|
187
187
|
let processMonitorIntervalMs = DEFAULT_PROCESS_MONITOR_INTERVAL_MS;
|
|
188
188
|
let responseStallTimeoutMs = DEFAULT_RESPONSE_STALL_TIMEOUT_MS;
|
|
@@ -311,18 +311,18 @@ function scheduleFinalResponseCloseGuard(
|
|
|
311
311
|
runningPrompt.finalResponseCloseTimer = handle;
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
-
function formatTerminalHeader(
|
|
315
|
-
status: "running" | "done" | "stopped" | "error" | "auto_ended",
|
|
316
|
-
terminalError?: TerminalErrorInfo,
|
|
317
|
-
): {
|
|
314
|
+
function formatTerminalHeader(
|
|
315
|
+
status: "running" | "done" | "stopped" | "error" | "auto_ended",
|
|
316
|
+
terminalError?: TerminalErrorInfo,
|
|
317
|
+
): {
|
|
318
318
|
title: string;
|
|
319
319
|
template?: string;
|
|
320
320
|
} {
|
|
321
321
|
if (status === "auto_ended") return { title: "已自动结束 · 3分钟无新内容", template: "orange" };
|
|
322
322
|
if (status === "stopped") return { title: "已停止", template: "red" };
|
|
323
|
-
if (status === "error") {
|
|
324
|
-
return { title: terminalError ? `异常结束 · ${terminalError.title}` : "异常结束", template: "red" };
|
|
325
|
-
}
|
|
323
|
+
if (status === "error") {
|
|
324
|
+
return { title: terminalError ? `异常结束 · ${terminalError.title}` : "异常结束", template: "red" };
|
|
325
|
+
}
|
|
326
326
|
return { title: "完成" };
|
|
327
327
|
}
|
|
328
328
|
|
|
@@ -330,59 +330,59 @@ function turnFinalStatus(status: "running" | "done" | "stopped" | "error" | "aut
|
|
|
330
330
|
return status === "stopped" || status === "error" || status === "auto_ended" ? "stopped" : "done";
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
-
function formatAutoEndedReply(finalReply: string): string {
|
|
334
|
-
const reason = "⚠️ 已自动结束:生成回复阶段连续 3 分钟没有字符变化。";
|
|
333
|
+
function formatAutoEndedReply(finalReply: string): string {
|
|
334
|
+
const reason = "⚠️ 已自动结束:生成回复阶段连续 3 分钟没有字符变化。";
|
|
335
335
|
return finalReply
|
|
336
336
|
? `${reason}以下回复可能不完整。\n\n${finalReply}`
|
|
337
337
|
: `${reason}本轮没有可发送的回复内容。`;
|
|
338
338
|
}
|
|
339
339
|
|
|
340
340
|
/**
|
|
341
|
-
* 只监控明确的回复生成阶段。启动、压缩、思考、工具调用和搜索都有各自的
|
|
342
|
-
* 状态或资源保护,不能把它们消耗的时间算入回复停滞窗口。
|
|
341
|
+
* 只监控明确的回复生成阶段。启动、压缩、思考、工具调用和搜索都有各自的
|
|
342
|
+
* 状态或资源保护,不能把它们消耗的时间算入回复停滞窗口。
|
|
343
343
|
*/
|
|
344
|
-
function monitorsOutputProgress(kind: AgentActivityKind): boolean {
|
|
345
|
-
return kind === "responding";
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function formatTerminalReply(
|
|
349
|
-
status: "running" | "done" | "stopped" | "error" | "auto_ended",
|
|
350
|
-
finalReply: string,
|
|
351
|
-
terminalError?: TerminalErrorInfo,
|
|
352
|
-
): string | null {
|
|
353
|
-
if (status === "auto_ended") return formatAutoEndedReply(finalReply);
|
|
354
|
-
if (status === "error") {
|
|
355
|
-
const error = terminalError ?? {
|
|
356
|
-
kind: "unknown" as const,
|
|
357
|
-
title: "原因未记录",
|
|
358
|
-
message: "当前状态中没有可用的错误详情,请查看运行日志。",
|
|
359
|
-
occurredAt: Date.now(),
|
|
360
|
-
};
|
|
361
|
-
return formatTerminalErrorNotice(error, finalReply);
|
|
362
|
-
}
|
|
363
|
-
return finalReply || null;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
function formatTerminalCardContent(state: {
|
|
367
|
-
status: "running" | "done" | "stopped" | "error" | "auto_ended";
|
|
368
|
-
accumulatedContent: string;
|
|
369
|
-
finalReply: string;
|
|
370
|
-
terminalError?: TerminalErrorInfo;
|
|
371
|
-
}): string {
|
|
372
|
-
const content = state.accumulatedContent + state.finalReply;
|
|
373
|
-
if (state.status !== "error") return content;
|
|
374
|
-
|
|
375
|
-
const error = state.terminalError ?? {
|
|
376
|
-
kind: "unknown" as const,
|
|
377
|
-
title: "原因未记录",
|
|
378
|
-
message: "当前状态中没有可用的错误详情,请查看运行日志。",
|
|
379
|
-
occurredAt: Date.now(),
|
|
380
|
-
};
|
|
381
|
-
const reason = formatTerminalErrorReason(error);
|
|
382
|
-
return content.trim()
|
|
383
|
-
? `${content}\n\n${reason}\n以上内容可能不完整。`
|
|
384
|
-
: reason;
|
|
385
|
-
}
|
|
344
|
+
function monitorsOutputProgress(kind: AgentActivityKind): boolean {
|
|
345
|
+
return kind === "responding";
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function formatTerminalReply(
|
|
349
|
+
status: "running" | "done" | "stopped" | "error" | "auto_ended",
|
|
350
|
+
finalReply: string,
|
|
351
|
+
terminalError?: TerminalErrorInfo,
|
|
352
|
+
): string | null {
|
|
353
|
+
if (status === "auto_ended") return formatAutoEndedReply(finalReply);
|
|
354
|
+
if (status === "error") {
|
|
355
|
+
const error = terminalError ?? {
|
|
356
|
+
kind: "unknown" as const,
|
|
357
|
+
title: "原因未记录",
|
|
358
|
+
message: "当前状态中没有可用的错误详情,请查看运行日志。",
|
|
359
|
+
occurredAt: Date.now(),
|
|
360
|
+
};
|
|
361
|
+
return formatTerminalErrorNotice(error, finalReply);
|
|
362
|
+
}
|
|
363
|
+
return finalReply || null;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function formatTerminalCardContent(state: {
|
|
367
|
+
status: "running" | "done" | "stopped" | "error" | "auto_ended";
|
|
368
|
+
accumulatedContent: string;
|
|
369
|
+
finalReply: string;
|
|
370
|
+
terminalError?: TerminalErrorInfo;
|
|
371
|
+
}): string {
|
|
372
|
+
const content = state.accumulatedContent + state.finalReply;
|
|
373
|
+
if (state.status !== "error") return content;
|
|
374
|
+
|
|
375
|
+
const error = state.terminalError ?? {
|
|
376
|
+
kind: "unknown" as const,
|
|
377
|
+
title: "原因未记录",
|
|
378
|
+
message: "当前状态中没有可用的错误详情,请查看运行日志。",
|
|
379
|
+
occurredAt: Date.now(),
|
|
380
|
+
};
|
|
381
|
+
const reason = formatTerminalErrorReason(error);
|
|
382
|
+
return content.trim()
|
|
383
|
+
? `${content}\n\n${reason}\n以上内容可能不完整。`
|
|
384
|
+
: reason;
|
|
385
|
+
}
|
|
386
386
|
|
|
387
387
|
function isCardKitSequenceConflict(err: unknown): boolean {
|
|
388
388
|
return err instanceof Error && err.message.includes("300317");
|
|
@@ -948,16 +948,16 @@ export function accumulateBlockContent(
|
|
|
948
948
|
// 覆盖而非追加:适配器已保证这是一段完整最终文本(如 Cursor 流末快照)
|
|
949
949
|
state.finalCompleteText = block.text;
|
|
950
950
|
break;
|
|
951
|
-
case "compact_boundary": {
|
|
951
|
+
case "compact_boundary": {
|
|
952
952
|
const triggerLabel = block.trigger === "manual" ? "手动" : "自动"; // 手动 / 自动
|
|
953
953
|
state.accumulatedContent +=
|
|
954
954
|
`\n\n🔄 上下文压缩(${triggerLabel}): **${block.pre_tokens}** → **${block.post_tokens}** tokens\n`; // 🔄 / →
|
|
955
|
-
break;
|
|
956
|
-
}
|
|
957
|
-
case "agent_status":
|
|
958
|
-
break;
|
|
959
|
-
}
|
|
960
|
-
}
|
|
955
|
+
break;
|
|
956
|
+
}
|
|
957
|
+
case "agent_status":
|
|
958
|
+
break;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
961
|
|
|
962
962
|
// ---------------------------------------------------------------------------
|
|
963
963
|
// switchChatBinding — /newh、/session N 共用的事务式"切换 chat 绑定"
|
|
@@ -1146,7 +1146,7 @@ export async function initClaudeSession(tool: string, overrideCwd?: string, chat
|
|
|
1146
1146
|
return { sessionId, cwd };
|
|
1147
1147
|
}
|
|
1148
1148
|
|
|
1149
|
-
export async function resumeAndPrompt(
|
|
1149
|
+
export async function resumeAndPrompt(
|
|
1150
1150
|
sessionId: string,
|
|
1151
1151
|
userText: string,
|
|
1152
1152
|
platform: PlatformAdapter,
|
|
@@ -1154,25 +1154,25 @@ export async function resumeAndPrompt(
|
|
|
1154
1154
|
msgTimestamp: number,
|
|
1155
1155
|
tool: string,
|
|
1156
1156
|
traceId?: string,
|
|
1157
|
-
): Promise<SessionRunOutcome> {
|
|
1158
|
-
return runAgentSession(sessionId, userText, platform, chatId, msgTimestamp, tool, traceId);
|
|
1159
|
-
}
|
|
1157
|
+
): Promise<SessionRunOutcome> {
|
|
1158
|
+
return runAgentSession(sessionId, userText, platform, chatId, msgTimestamp, tool, traceId);
|
|
1159
|
+
}
|
|
1160
1160
|
|
|
1161
1161
|
// ---------------------------------------------------------------------------
|
|
1162
1162
|
// runAgentSession — session 中心的 agent prompt(文件持久化 + display 解耦)
|
|
1163
1163
|
// ---------------------------------------------------------------------------
|
|
1164
1164
|
|
|
1165
|
-
interface RunAgentSessionOptions {
|
|
1165
|
+
interface RunAgentSessionOptions {
|
|
1166
1166
|
/**
|
|
1167
1167
|
* 标记本轮是 response-stall 后的唯一一次内部续跑。若本轮再次因相同原因
|
|
1168
1168
|
* 停滞,不再递归创建第三轮,避免服务异常期间无限消耗 token。
|
|
1169
1169
|
*/
|
|
1170
1170
|
autoRecovery?: boolean;
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
export type SessionRunOutcome = "busy" | "done" | "stopped" | "error" | "auto_ended";
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
export type SessionRunOutcome = "busy" | "done" | "stopped" | "error" | "auto_ended";
|
|
1174
1174
|
|
|
1175
|
-
export async function runAgentSession(
|
|
1175
|
+
export async function runAgentSession(
|
|
1176
1176
|
sessionId: string,
|
|
1177
1177
|
userText: string,
|
|
1178
1178
|
platform: PlatformAdapter,
|
|
@@ -1181,7 +1181,7 @@ export async function runAgentSession(
|
|
|
1181
1181
|
tool: string,
|
|
1182
1182
|
traceId?: string,
|
|
1183
1183
|
options: RunAgentSessionOptions = {},
|
|
1184
|
-
): Promise<SessionRunOutcome> {
|
|
1184
|
+
): Promise<SessionRunOutcome> {
|
|
1185
1185
|
const tid = traceId ?? "";
|
|
1186
1186
|
|
|
1187
1187
|
// runAgentSession 是飞书用户消息、队列消息与内部自动恢复共同使用的唯一
|
|
@@ -1208,7 +1208,7 @@ export async function runAgentSession(
|
|
|
1208
1208
|
? "当前正在生成回复中,请等待完成后再发送消息。也可以发送 /stop 结束,已完成的步骤不会丢失。"
|
|
1209
1209
|
: "该会话正在生成回复中,请等待完成后再发送消息。也可以发送 /stop 结束,已完成的步骤不会丢失。";
|
|
1210
1210
|
await platform.sendText(_chatId, busyMsg).catch(() => {});
|
|
1211
|
-
return "busy";
|
|
1211
|
+
return "busy";
|
|
1212
1212
|
}
|
|
1213
1213
|
|
|
1214
1214
|
// 立即标记活跃,确保 /sessions、isSessionRunning 等查询在异步准备阶段就能看到运行状态。
|
|
@@ -1336,7 +1336,7 @@ export async function runAgentSession(
|
|
|
1336
1336
|
// 再开始缓存问题对应的任务"。
|
|
1337
1337
|
const prevState = await readStreamState(sessionId);
|
|
1338
1338
|
if (prevState && prevState.status !== "running") {
|
|
1339
|
-
const prevTerminalReply = formatTerminalReply(prevState.status, prevState.finalReply, prevState.terminalError);
|
|
1339
|
+
const prevTerminalReply = formatTerminalReply(prevState.status, prevState.finalReply, prevState.terminalError);
|
|
1340
1340
|
const displayChatId = pickDisplayChat(sessionId);
|
|
1341
1341
|
if (displayChatId) {
|
|
1342
1342
|
const pp = platformForChat(displayChatId);
|
|
@@ -1355,8 +1355,8 @@ export async function runAgentSession(
|
|
|
1355
1355
|
setSessionChatAvatar(pp, displayChatId, prevState.tool, "idle", sessionId).catch(() => {});
|
|
1356
1356
|
} else {
|
|
1357
1357
|
const nextSeq = display.sequence + 1;
|
|
1358
|
-
const { title: headerTitle, template: headerTemplate } = formatTerminalHeader(prevState.status, prevState.terminalError);
|
|
1359
|
-
const cardContent = truncateContent(formatTerminalCardContent(prevState)) || " ";
|
|
1358
|
+
const { title: headerTitle, template: headerTemplate } = formatTerminalHeader(prevState.status, prevState.terminalError);
|
|
1359
|
+
const cardContent = truncateContent(formatTerminalCardContent(prevState)) || " ";
|
|
1360
1360
|
const doneCard = buildProgressCard(progressView({ text: cardContent, status: "done", showStop: false, headerTitle, headerTemplate }));
|
|
1361
1361
|
await pp.cardUpdate(display.cardId, doneCard, nextSeq).catch(err => {
|
|
1362
1362
|
console.error(`[${ts()}] [DISPLAY] prevState final cardUpdate failed: ${(err as Error).message}`);
|
|
@@ -1449,21 +1449,21 @@ export async function runAgentSession(
|
|
|
1449
1449
|
|
|
1450
1450
|
let lastFileWrite = Date.now();
|
|
1451
1451
|
const FILE_WRITE_INTERVAL_MS = 2000;
|
|
1452
|
-
const toolCallMap = new Map<string, { name: string; input: unknown }>();
|
|
1453
|
-
let streamErrored = false;
|
|
1454
|
-
let streamTerminalError: TerminalErrorInfo | undefined;
|
|
1455
|
-
let runOutcome: SessionRunOutcome = "error";
|
|
1456
|
-
|
|
1457
|
-
const runningPrompt = activePrompts.get(sessionId);
|
|
1458
|
-
if (runningPrompt) {
|
|
1459
|
-
// 在消费第一个事件前建立阶段感知的零字符基线;启动阶段不计时,只有后续
|
|
1460
|
-
// 收到明确的 responding 状态后才会启动三分钟回复停滞保护。
|
|
1461
|
-
runningPrompt.responseProgress = observeResponseProgress(
|
|
1462
|
-
undefined,
|
|
1463
|
-
monitorsOutputProgress(activityTracker.activity.kind),
|
|
1464
|
-
0,
|
|
1465
|
-
activityTracker.activity.startedAt,
|
|
1466
|
-
);
|
|
1452
|
+
const toolCallMap = new Map<string, { name: string; input: unknown }>();
|
|
1453
|
+
let streamErrored = false;
|
|
1454
|
+
let streamTerminalError: TerminalErrorInfo | undefined;
|
|
1455
|
+
let runOutcome: SessionRunOutcome = "error";
|
|
1456
|
+
|
|
1457
|
+
const runningPrompt = activePrompts.get(sessionId);
|
|
1458
|
+
if (runningPrompt) {
|
|
1459
|
+
// 在消费第一个事件前建立阶段感知的零字符基线;启动阶段不计时,只有后续
|
|
1460
|
+
// 收到明确的 responding 状态后才会启动三分钟回复停滞保护。
|
|
1461
|
+
runningPrompt.responseProgress = observeResponseProgress(
|
|
1462
|
+
undefined,
|
|
1463
|
+
monitorsOutputProgress(activityTracker.activity.kind),
|
|
1464
|
+
0,
|
|
1465
|
+
activityTracker.activity.startedAt,
|
|
1466
|
+
);
|
|
1467
1467
|
|
|
1468
1468
|
const checkResponseStall = async () => {
|
|
1469
1469
|
const current = activePrompts.get(sessionId);
|
|
@@ -1533,7 +1533,7 @@ export async function runAgentSession(
|
|
|
1533
1533
|
current.controller.abort();
|
|
1534
1534
|
await killProcessTree(current.processPid);
|
|
1535
1535
|
console.warn(
|
|
1536
|
-
`[${ts()}] [RESPONSE-STALL] Session ${sessionId} auto-ended after 3 minutes without reply progress`,
|
|
1536
|
+
`[${ts()}] [RESPONSE-STALL] Session ${sessionId} auto-ended after 3 minutes without reply progress`,
|
|
1537
1537
|
);
|
|
1538
1538
|
};
|
|
1539
1539
|
|
|
@@ -1625,10 +1625,10 @@ export async function runAgentSession(
|
|
|
1625
1625
|
});
|
|
1626
1626
|
}
|
|
1627
1627
|
}
|
|
1628
|
-
} catch (streamErr) {
|
|
1629
|
-
streamErrored = true;
|
|
1630
|
-
streamTerminalError = classifyTerminalError(streamErr);
|
|
1631
|
-
console.error(`[${ts()}] [STREAM] Error in stream loop for ${sessionId}: ${(streamErr as Error).message}`);
|
|
1628
|
+
} catch (streamErr) {
|
|
1629
|
+
streamErrored = true;
|
|
1630
|
+
streamTerminalError = classifyTerminalError(streamErr);
|
|
1631
|
+
console.error(`[${ts()}] [STREAM] Error in stream loop for ${sessionId}: ${(streamErr as Error).message}`);
|
|
1632
1632
|
} finally {
|
|
1633
1633
|
// 标记 prompt 结束
|
|
1634
1634
|
resourceMonitor.off("stuck", onResourceStuck);
|
|
@@ -1676,24 +1676,24 @@ export async function runAgentSession(
|
|
|
1676
1676
|
: wasStopped
|
|
1677
1677
|
? "stopped"
|
|
1678
1678
|
: "done";
|
|
1679
|
-
const finalReply = pickFinalReply(state).trim();
|
|
1680
|
-
const terminalError = streamTerminalError
|
|
1681
|
-
?? (wasAbnormalExit
|
|
1682
|
-
? {
|
|
1683
|
-
kind: "process" as const,
|
|
1684
|
-
title: "Agent 进程意外退出",
|
|
1685
|
-
message: "Agent CLI 进程已退出。若回复不完整,请重新发送上一条指令。",
|
|
1686
|
-
occurredAt: Date.now(),
|
|
1687
|
-
}
|
|
1688
|
-
: wasResourceStuck
|
|
1689
|
-
? {
|
|
1690
|
-
kind: "resource" as const,
|
|
1691
|
-
title: "Agent 进程失去响应",
|
|
1692
|
-
message: "Agent 进程长时间没有 CPU 或内存变化,已被强制停止。若回复不完整,请重新发送上一条指令。",
|
|
1693
|
-
occurredAt: Date.now(),
|
|
1694
|
-
}
|
|
1695
|
-
: undefined);
|
|
1696
|
-
runOutcome = finalStatus;
|
|
1679
|
+
const finalReply = pickFinalReply(state).trim();
|
|
1680
|
+
const terminalError = streamTerminalError
|
|
1681
|
+
?? (wasAbnormalExit
|
|
1682
|
+
? {
|
|
1683
|
+
kind: "process" as const,
|
|
1684
|
+
title: "Agent 进程意外退出",
|
|
1685
|
+
message: "Agent CLI 进程已退出。若回复不完整,请重新发送上一条指令。",
|
|
1686
|
+
occurredAt: Date.now(),
|
|
1687
|
+
}
|
|
1688
|
+
: wasResourceStuck
|
|
1689
|
+
? {
|
|
1690
|
+
kind: "resource" as const,
|
|
1691
|
+
title: "Agent 进程失去响应",
|
|
1692
|
+
message: "Agent 进程长时间没有 CPU 或内存变化,已被强制停止。若回复不完整,请重新发送上一条指令。",
|
|
1693
|
+
occurredAt: Date.now(),
|
|
1694
|
+
}
|
|
1695
|
+
: undefined);
|
|
1696
|
+
runOutcome = finalStatus;
|
|
1697
1697
|
|
|
1698
1698
|
// stop-stuck-loop 接口可能在 fire-and-forget 中已写入带 final_reply 的
|
|
1699
1699
|
// stream state,finally 不应覆盖它。同时保留 stuckAt 标记,防止
|
|
@@ -1723,9 +1723,9 @@ export async function runAgentSession(
|
|
|
1723
1723
|
cwd,
|
|
1724
1724
|
tool,
|
|
1725
1725
|
...(preserveStuckAt ? { stuckAt: preserveStuckAt } : {}),
|
|
1726
|
-
...(autoEndedAt !== undefined ? { autoEndedAt } : {}),
|
|
1727
|
-
...(terminalError ? { terminalError } : {}),
|
|
1728
|
-
});
|
|
1726
|
+
...(autoEndedAt !== undefined ? { autoEndedAt } : {}),
|
|
1727
|
+
...(terminalError ? { terminalError } : {}),
|
|
1728
|
+
});
|
|
1729
1729
|
|
|
1730
1730
|
// display loop 下一轮会读到最终状态并发送消息
|
|
1731
1731
|
|
|
@@ -1796,9 +1796,9 @@ export async function runAgentSession(
|
|
|
1796
1796
|
autoRecoveryTarget = { chatId: activeAutoEnded, platform: pp };
|
|
1797
1797
|
}
|
|
1798
1798
|
}
|
|
1799
|
-
console.warn(`[${ts()}] Session ${sessionId} auto-ended after stalled response output (content chunks: ${state.chunkCount})`);
|
|
1799
|
+
console.warn(`[${ts()}] Session ${sessionId} auto-ended after stalled response output (content chunks: ${state.chunkCount})`);
|
|
1800
1800
|
if (tid) logTrace(tid, "SESSION_END", { sessionId, outcome: "response_stall", chunks: state.chunkCount });
|
|
1801
|
-
} else if (wasAbnormalExit) {
|
|
1801
|
+
} else if (wasAbnormalExit) {
|
|
1802
1802
|
for (const cid of finalizationChatIds) {
|
|
1803
1803
|
const finfo = sessionInfoMap.get(cid);
|
|
1804
1804
|
await recordSessionRegistry({
|
|
@@ -1813,55 +1813,55 @@ export async function runAgentSession(
|
|
|
1813
1813
|
}
|
|
1814
1814
|
const activeErr = getLastActiveChat(sessionId) ?? finalizationChatIds[0];
|
|
1815
1815
|
if (activeErr) setSessionChatAvatar(platform, activeErr, tool, "idle", sessionId).catch(() => {});
|
|
1816
|
-
console.log(`[${ts()}] Session ${sessionId} process exited unexpectedly (content chunks: ${state.chunkCount})`);
|
|
1817
|
-
if (tid) logTrace(tid, "SESSION_END", { sessionId, outcome: "process_missing", chunks: state.chunkCount });
|
|
1818
|
-
} else if (streamErrored || wasResourceStuck) {
|
|
1819
|
-
for (const cid of finalizationChatIds) {
|
|
1820
|
-
const finfo = sessionInfoMap.get(cid);
|
|
1821
|
-
await recordSessionRegistry({
|
|
1822
|
-
chatId: cid,
|
|
1823
|
-
sessionId,
|
|
1824
|
-
tool,
|
|
1825
|
-
turnCount: finfo?.turnCount ?? nextTurnCount,
|
|
1826
|
-
lastContextTokens: finfo?.lastContextTokens ?? nextContextTokens,
|
|
1827
|
-
startTime: finfo?.startTime ?? now,
|
|
1828
|
-
running: false,
|
|
1829
|
-
});
|
|
1830
|
-
}
|
|
1831
|
-
const activeErr = getLastActiveChat(sessionId) ?? finalizationChatIds[0];
|
|
1832
|
-
if (activeErr) {
|
|
1833
|
-
const pp = platformForChat(activeErr) ?? platform;
|
|
1834
|
-
const terminalState = await readStreamState(sessionId);
|
|
1835
|
-
if (
|
|
1836
|
-
terminalError
|
|
1837
|
-
&& !displayCards.has(activeErr)
|
|
1838
|
-
&& (!terminalState || !isFinalReplySentForTurn(terminalState))
|
|
1839
|
-
) {
|
|
1840
|
-
await sendFinalReplyTextOnce(
|
|
1841
|
-
pp,
|
|
1842
|
-
activeErr,
|
|
1843
|
-
sessionId,
|
|
1844
|
-
nextTurnCount,
|
|
1845
|
-
formatTerminalErrorNotice(terminalError, finalReplyToWrite),
|
|
1846
|
-
);
|
|
1847
|
-
}
|
|
1848
|
-
setSessionChatAvatar(pp, activeErr, tool, "idle", sessionId).catch(() => {});
|
|
1849
|
-
}
|
|
1850
|
-
const errorOutcome = wasResourceStuck ? "resource_stuck" : "stream_error";
|
|
1851
|
-
console.error(
|
|
1852
|
-
`[${ts()}] Session ${sessionId} ended with ${errorOutcome}` +
|
|
1853
|
-
`${terminalError ? ` (${terminalError.kind}: ${terminalError.title})` : ""} (content chunks: ${state.chunkCount})`,
|
|
1854
|
-
);
|
|
1855
|
-
if (tid) {
|
|
1856
|
-
logTrace(tid, "SESSION_END", {
|
|
1857
|
-
sessionId,
|
|
1858
|
-
outcome: errorOutcome,
|
|
1859
|
-
errorKind: terminalError?.kind,
|
|
1860
|
-
errorTitle: terminalError?.title,
|
|
1861
|
-
chunks: state.chunkCount,
|
|
1862
|
-
});
|
|
1863
|
-
}
|
|
1864
|
-
} else {
|
|
1816
|
+
console.log(`[${ts()}] Session ${sessionId} process exited unexpectedly (content chunks: ${state.chunkCount})`);
|
|
1817
|
+
if (tid) logTrace(tid, "SESSION_END", { sessionId, outcome: "process_missing", chunks: state.chunkCount });
|
|
1818
|
+
} else if (streamErrored || wasResourceStuck) {
|
|
1819
|
+
for (const cid of finalizationChatIds) {
|
|
1820
|
+
const finfo = sessionInfoMap.get(cid);
|
|
1821
|
+
await recordSessionRegistry({
|
|
1822
|
+
chatId: cid,
|
|
1823
|
+
sessionId,
|
|
1824
|
+
tool,
|
|
1825
|
+
turnCount: finfo?.turnCount ?? nextTurnCount,
|
|
1826
|
+
lastContextTokens: finfo?.lastContextTokens ?? nextContextTokens,
|
|
1827
|
+
startTime: finfo?.startTime ?? now,
|
|
1828
|
+
running: false,
|
|
1829
|
+
});
|
|
1830
|
+
}
|
|
1831
|
+
const activeErr = getLastActiveChat(sessionId) ?? finalizationChatIds[0];
|
|
1832
|
+
if (activeErr) {
|
|
1833
|
+
const pp = platformForChat(activeErr) ?? platform;
|
|
1834
|
+
const terminalState = await readStreamState(sessionId);
|
|
1835
|
+
if (
|
|
1836
|
+
terminalError
|
|
1837
|
+
&& !displayCards.has(activeErr)
|
|
1838
|
+
&& (!terminalState || !isFinalReplySentForTurn(terminalState))
|
|
1839
|
+
) {
|
|
1840
|
+
await sendFinalReplyTextOnce(
|
|
1841
|
+
pp,
|
|
1842
|
+
activeErr,
|
|
1843
|
+
sessionId,
|
|
1844
|
+
nextTurnCount,
|
|
1845
|
+
formatTerminalErrorNotice(terminalError, finalReplyToWrite),
|
|
1846
|
+
);
|
|
1847
|
+
}
|
|
1848
|
+
setSessionChatAvatar(pp, activeErr, tool, "idle", sessionId).catch(() => {});
|
|
1849
|
+
}
|
|
1850
|
+
const errorOutcome = wasResourceStuck ? "resource_stuck" : "stream_error";
|
|
1851
|
+
console.error(
|
|
1852
|
+
`[${ts()}] Session ${sessionId} ended with ${errorOutcome}` +
|
|
1853
|
+
`${terminalError ? ` (${terminalError.kind}: ${terminalError.title})` : ""} (content chunks: ${state.chunkCount})`,
|
|
1854
|
+
);
|
|
1855
|
+
if (tid) {
|
|
1856
|
+
logTrace(tid, "SESSION_END", {
|
|
1857
|
+
sessionId,
|
|
1858
|
+
outcome: errorOutcome,
|
|
1859
|
+
errorKind: terminalError?.kind,
|
|
1860
|
+
errorTitle: terminalError?.title,
|
|
1861
|
+
chunks: state.chunkCount,
|
|
1862
|
+
});
|
|
1863
|
+
}
|
|
1864
|
+
} else {
|
|
1865
1865
|
for (const cid of finalizationChatIds) {
|
|
1866
1866
|
const finfo = sessionInfoMap.get(cid);
|
|
1867
1867
|
await recordSessionRegistry({
|
|
@@ -1973,9 +1973,9 @@ export async function runAgentSession(
|
|
|
1973
1973
|
} finally {
|
|
1974
1974
|
clearSessionFinalizing(sessionId);
|
|
1975
1975
|
}
|
|
1976
|
-
}
|
|
1977
|
-
return runOutcome;
|
|
1978
|
-
}
|
|
1976
|
+
}
|
|
1977
|
+
return runOutcome;
|
|
1978
|
+
}
|
|
1979
1979
|
|
|
1980
1980
|
// ---------------------------------------------------------------------------
|
|
1981
1981
|
// startUnifiedDisplayLoop — 全局统一 display 循环,遍历 displayCards 更新卡片
|
|
@@ -2052,13 +2052,13 @@ export function startUnifiedDisplayLoop(): void {
|
|
|
2052
2052
|
if (activePrompts.has(sessionId)) continue;
|
|
2053
2053
|
|
|
2054
2054
|
const tail = "━━━ 回答结束 ━━━";
|
|
2055
|
-
const finalMsg = state.status === "auto_ended"
|
|
2056
|
-
? formatAutoEndedReply(remaining)
|
|
2057
|
-
: state.status === "error"
|
|
2058
|
-
? formatTerminalReply(state.status, remaining, state.terminalError) ?? tail
|
|
2059
|
-
: remaining
|
|
2060
|
-
? remaining + "\n" + tail
|
|
2061
|
-
: tail;
|
|
2055
|
+
const finalMsg = state.status === "auto_ended"
|
|
2056
|
+
? formatAutoEndedReply(remaining)
|
|
2057
|
+
: state.status === "error"
|
|
2058
|
+
? formatTerminalReply(state.status, remaining, state.terminalError) ?? tail
|
|
2059
|
+
: remaining
|
|
2060
|
+
? remaining + "\n" + tail
|
|
2061
|
+
: tail;
|
|
2062
2062
|
if (!isFinalReplySentForTurn(state)) {
|
|
2063
2063
|
await sendFinalReplyTextOnce(p, chatId, sessionId, state.turnCount, finalMsg);
|
|
2064
2064
|
}
|
|
@@ -2080,8 +2080,8 @@ export function startUnifiedDisplayLoop(): void {
|
|
|
2080
2080
|
let terminalCardUpdateAccepted = terminalCardAlreadyUpdated;
|
|
2081
2081
|
if (!terminalCardAlreadyUpdated) {
|
|
2082
2082
|
const nextSeq = display.sequence + 1;
|
|
2083
|
-
const { title: headerTitle, template: headerTemplate } = formatTerminalHeader(state.status, state.terminalError);
|
|
2084
|
-
const cardContent = truncateContent(formatTerminalCardContent(state)) || " ";
|
|
2083
|
+
const { title: headerTitle, template: headerTemplate } = formatTerminalHeader(state.status, state.terminalError);
|
|
2084
|
+
const cardContent = truncateContent(formatTerminalCardContent(state)) || " ";
|
|
2085
2085
|
const doneCard = buildProgressCard(progressView({ text: cardContent, status: "done", showStop: false, headerTitle, headerTemplate }));
|
|
2086
2086
|
await p.cardUpdate(display.cardId, doneCard, nextSeq).then(() => {
|
|
2087
2087
|
display.sequence = nextSeq;
|
|
@@ -2108,19 +2108,19 @@ export function startUnifiedDisplayLoop(): void {
|
|
|
2108
2108
|
}
|
|
2109
2109
|
|
|
2110
2110
|
let terminalTextDelivered = true;
|
|
2111
|
-
const terminalReply = formatTerminalReply(state.status, state.finalReply, state.terminalError);
|
|
2112
|
-
const errorWasDeliveredByCard =
|
|
2113
|
-
state.status === "error"
|
|
2114
|
-
&& !state.finalReply.trim()
|
|
2115
|
-
&& terminalCardUpdateAccepted;
|
|
2116
|
-
if (errorWasDeliveredByCard) {
|
|
2117
|
-
if (!isFinalReplySentForTurn(state)) {
|
|
2118
|
-
await markFinalReplySent(sessionId, state.turnCount);
|
|
2119
|
-
}
|
|
2120
|
-
} else if (terminalReply) {
|
|
2121
|
-
if (!isFinalReplySentForTurn(state)) {
|
|
2122
|
-
terminalTextDelivered = await sendFinalReplyTextOnce(p, chatId, sessionId, state.turnCount, terminalReply);
|
|
2123
|
-
}
|
|
2111
|
+
const terminalReply = formatTerminalReply(state.status, state.finalReply, state.terminalError);
|
|
2112
|
+
const errorWasDeliveredByCard =
|
|
2113
|
+
state.status === "error"
|
|
2114
|
+
&& !state.finalReply.trim()
|
|
2115
|
+
&& terminalCardUpdateAccepted;
|
|
2116
|
+
if (errorWasDeliveredByCard) {
|
|
2117
|
+
if (!isFinalReplySentForTurn(state)) {
|
|
2118
|
+
await markFinalReplySent(sessionId, state.turnCount);
|
|
2119
|
+
}
|
|
2120
|
+
} else if (terminalReply) {
|
|
2121
|
+
if (!isFinalReplySentForTurn(state)) {
|
|
2122
|
+
terminalTextDelivered = await sendFinalReplyTextOnce(p, chatId, sessionId, state.turnCount, terminalReply);
|
|
2123
|
+
}
|
|
2124
2124
|
} else if (state.accumulatedContent.trim()) {
|
|
2125
2125
|
const short = truncateContent(state.accumulatedContent, 30, 4000);
|
|
2126
2126
|
terminalTextDelivered = await p.sendText(chatId, `[生成过程]\n${short}`).then((ok) => ok !== false).catch(() => false);
|
package/src/shared-prefix.ts
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
export const ABD_PREFIX = "/abd";
|
|
2
|
-
|
|
3
|
-
export const ABD_APPEND_PROMPT =
|
|
4
|
-
"请从第一性原理出发挖掘我的真实需求。你觉得我的需求合理吗?开始实现之前有什么问题要问我的?";
|
|
5
|
-
|
|
6
|
-
export const ABD_HELP_LINE =
|
|
7
|
-
`发送 **${ABD_PREFIX}** 前缀消息,将以第一性原理追问真实需求、合理性和实现前问题`;
|
|
8
|
-
|
|
9
|
-
export interface SharedPrefixResult {
|
|
10
|
-
matched: boolean;
|
|
11
|
-
text: string;
|
|
12
|
-
body: string;
|
|
13
|
-
prefix?: typeof ABD_PREFIX;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function applySharedPrefix(text: string): SharedPrefixResult {
|
|
17
|
-
if (!text.toLowerCase().startsWith(ABD_PREFIX)) {
|
|
18
|
-
return { matched: false, text, body: text };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const body = text.slice(ABD_PREFIX.length).replace(/^\s+/, "");
|
|
22
|
-
const appendix = ["---", ABD_APPEND_PROMPT].join("\n");
|
|
23
|
-
return {
|
|
24
|
-
matched: true,
|
|
25
|
-
prefix: ABD_PREFIX,
|
|
26
|
-
body,
|
|
27
|
-
text: body ? [body, appendix].join("\n\n") : appendix,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
1
|
+
export const ABD_PREFIX = "/abd";
|
|
2
|
+
|
|
3
|
+
export const ABD_APPEND_PROMPT =
|
|
4
|
+
"请从第一性原理出发挖掘我的真实需求。你觉得我的需求合理吗?开始实现之前有什么问题要问我的?";
|
|
5
|
+
|
|
6
|
+
export const ABD_HELP_LINE =
|
|
7
|
+
`发送 **${ABD_PREFIX}** 前缀消息,将以第一性原理追问真实需求、合理性和实现前问题`;
|
|
8
|
+
|
|
9
|
+
export interface SharedPrefixResult {
|
|
10
|
+
matched: boolean;
|
|
11
|
+
text: string;
|
|
12
|
+
body: string;
|
|
13
|
+
prefix?: typeof ABD_PREFIX;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function applySharedPrefix(text: string): SharedPrefixResult {
|
|
17
|
+
if (!text.toLowerCase().startsWith(ABD_PREFIX)) {
|
|
18
|
+
return { matched: false, text, body: text };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const body = text.slice(ABD_PREFIX.length).replace(/^\s+/, "");
|
|
22
|
+
const appendix = ["---", ABD_APPEND_PROMPT].join("\n");
|
|
23
|
+
return {
|
|
24
|
+
matched: true,
|
|
25
|
+
prefix: ABD_PREFIX,
|
|
26
|
+
body,
|
|
27
|
+
text: body ? [body, appendix].join("\n\n") : appendix,
|
|
28
|
+
};
|
|
29
|
+
}
|