@keyou007/agent-bot 0.1.15-alpha.2 → 0.1.15-alpha.3
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/CHANGELOG.md +4 -0
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/dist/codex/CodexRuntime.js +19 -0
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/feishu/CardRenderer.js +7 -6
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +129 -52
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/state/StateStore.js +46 -0
- package/dist/state/StateStore.js.map +1 -1
- package/dist/state/migrations.js +11 -0
- package/dist/state/migrations.js.map +1 -1
- package/docs/technical-reference.md +2 -2
- package/docs/technical-reference.zh.md +2 -2
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/src/codex/CodexRuntime.ts +22 -0
- package/src/feishu/CardRenderer.ts +4 -3
- package/src/proxy/ProxySessionController.ts +143 -51
- package/src/runtime/types.ts +1 -0
- package/src/state/StateStore.ts +51 -0
- package/src/state/migrations.ts +11 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { promises as fs, type Dirent } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
3
|
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
4
5
|
import type { Logger } from "pino";
|
|
5
6
|
import { createProjectlessWorkspace, detectProjectlessWorkspace } from "../codex/ProjectlessWorkspace.js";
|
|
6
7
|
import { resolveUserPath } from "../config/paths.js";
|
|
@@ -48,6 +49,7 @@ import type {
|
|
|
48
49
|
} from "../runtime/types.js";
|
|
49
50
|
import {
|
|
50
51
|
StateStore,
|
|
52
|
+
type CardActionBinding,
|
|
51
53
|
type MessageReactionRecord,
|
|
52
54
|
type MessageReactionStatus,
|
|
53
55
|
type QueuedPromptRecord,
|
|
@@ -96,7 +98,8 @@ const MESSAGE_RECEIVED_REACTION = "OnIt";
|
|
|
96
98
|
const MESSAGE_COMPLETED_REACTION = "DONE";
|
|
97
99
|
const MESSAGE_FAILED_REACTION = "ERROR";
|
|
98
100
|
const MESSAGE_CANCELLED_REACTION = "CrossMark";
|
|
99
|
-
const SESSION_PAGE_SIZE =
|
|
101
|
+
const SESSION_PAGE_SIZE = 10;
|
|
102
|
+
const SESSION_PROMPT_PREVIEW_LENGTH = 50;
|
|
100
103
|
const DIRECTORY_PAGE_SIZE = 15;
|
|
101
104
|
const RESET_HISTORY_PAGE_SIZE = 10;
|
|
102
105
|
const MAX_LLM_TURN_RETRIES = 3;
|
|
@@ -749,8 +752,17 @@ export class ProxySessionController {
|
|
|
749
752
|
return;
|
|
750
753
|
}
|
|
751
754
|
if (!this.store.claimInboundEvent(action.actionId, "card_action")) return;
|
|
752
|
-
|
|
753
|
-
|
|
755
|
+
let resolvedAction: CardAction;
|
|
756
|
+
try {
|
|
757
|
+
resolvedAction = this.resolveCardActionBinding(action);
|
|
758
|
+
} catch (error) {
|
|
759
|
+
await this.sendError(action.contextKey, error);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const contextKey = this.cardActionContextKey(resolvedAction);
|
|
763
|
+
const scopedAction = contextKey === resolvedAction.contextKey
|
|
764
|
+
? resolvedAction
|
|
765
|
+
: { ...resolvedAction, contextKey };
|
|
754
766
|
const replyTarget = isThreadContextKey(contextKey) && action.messageId
|
|
755
767
|
? { messageId: action.messageId, replyInThread: true as const }
|
|
756
768
|
: undefined;
|
|
@@ -1663,6 +1675,16 @@ export class ProxySessionController {
|
|
|
1663
1675
|
: action.contextKey;
|
|
1664
1676
|
}
|
|
1665
1677
|
|
|
1678
|
+
private resolveCardActionBinding(action: CardAction): CardAction {
|
|
1679
|
+
if (typeof action.value.action === "string") return action;
|
|
1680
|
+
const token = typeof action.value.t === "string" ? action.value.t : undefined;
|
|
1681
|
+
if (!token) return action;
|
|
1682
|
+
if (!action.messageId) throw new Error("任务列表卡片缺少消息 ID,请重新发送 /sessions。");
|
|
1683
|
+
const value = this.store.getCardActionBinding(action.messageId, token);
|
|
1684
|
+
if (!value) throw new Error("任务列表卡片已失效,请重新发送 /sessions。");
|
|
1685
|
+
return { ...action, value };
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1666
1688
|
private async execute(
|
|
1667
1689
|
contextKey: string,
|
|
1668
1690
|
command: Command,
|
|
@@ -4997,13 +5019,34 @@ export class ProxySessionController {
|
|
|
4997
5019
|
const remoteHint = remoteErrors.length > 0
|
|
4998
5020
|
? `部分 Agent 的任务读取失败:${remoteErrors.join(";")}`
|
|
4999
5021
|
: undefined;
|
|
5000
|
-
|
|
5001
|
-
const
|
|
5002
|
-
|
|
5022
|
+
const latestLocalUserPrompts = new Map<string, string>();
|
|
5023
|
+
for (const session of localSessions) {
|
|
5024
|
+
const persisted = this.store.findLatestTurnSnapshotForSession(session.localSessionId);
|
|
5025
|
+
const prompt = latestUserPromptFromTurnView(turnViewSnapshot(persisted?.snapshot));
|
|
5026
|
+
if (prompt) latestLocalUserPrompts.set(session.localSessionId, prompt);
|
|
5027
|
+
}
|
|
5028
|
+
const latestRemoteUserPrompts = new Map<string, string>();
|
|
5029
|
+
for (const { agentName, session: remote } of remoteSessions) {
|
|
5030
|
+
const local = this.store.findSessionByRemoteSessionId(remote.id, undefined, agentName);
|
|
5031
|
+
if (!local) continue;
|
|
5032
|
+
const persisted = this.store.findLatestTurnSnapshotForSession(local.localSessionId);
|
|
5033
|
+
const prompt = latestUserPromptFromTurnView(turnViewSnapshot(persisted?.snapshot));
|
|
5034
|
+
if (prompt) latestRemoteUserPrompts.set(agentRemoteKey(agentName, remote.id), prompt);
|
|
5035
|
+
}
|
|
5036
|
+
|
|
5037
|
+
const globallyOrderedEntries = orderTaskListByRecency(
|
|
5038
|
+
mergeTaskList(
|
|
5039
|
+
localSessions,
|
|
5040
|
+
remoteSessions,
|
|
5041
|
+
context.currentSessionId,
|
|
5042
|
+
latestLocalUserPrompts,
|
|
5043
|
+
latestRemoteUserPrompts,
|
|
5044
|
+
)
|
|
5003
5045
|
.filter((entry) => !normalizedSearch
|
|
5004
5046
|
|| [entry.id, entry.title, entry.cwd, entry.agentName]
|
|
5005
5047
|
.some((value) => value.toLowerCase().includes(normalizedSearch))),
|
|
5006
5048
|
);
|
|
5049
|
+
const entries = groupTaskListPagesByProject(globallyOrderedEntries, SESSION_PAGE_SIZE);
|
|
5007
5050
|
const activeCount = entries.filter((entry) => entry.active).length;
|
|
5008
5051
|
const lastKnownPage = Math.max(0, Math.ceil(entries.length / SESSION_PAGE_SIZE) - 1);
|
|
5009
5052
|
const page = remoteHasMore ? requestedPage : Math.min(requestedPage, lastKnownPage);
|
|
@@ -5017,6 +5060,9 @@ export class ProxySessionController {
|
|
|
5017
5060
|
const hasPrevious = page > 0;
|
|
5018
5061
|
const hasNext = remoteHasMore || entries.length > offset + SESSION_PAGE_SIZE;
|
|
5019
5062
|
this.lastSessionListings.set(contextKey, entries.map((entry) => entry.reference));
|
|
5063
|
+
const cardActionBindings = new Map<string, CardActionBinding>();
|
|
5064
|
+
const bindCardAction = (action: TaskListCardAction): TaskListCardAction =>
|
|
5065
|
+
bindSessionCardAction(action, cardActionBindings);
|
|
5020
5066
|
const cardEntries = visibleEntries.map((entry, index): {
|
|
5021
5067
|
project: TaskProjectInfo;
|
|
5022
5068
|
projectActions: TaskListCardAction[];
|
|
@@ -5024,11 +5070,11 @@ export class ProxySessionController {
|
|
|
5024
5070
|
} => {
|
|
5025
5071
|
const project = taskProjectInfo(entry);
|
|
5026
5072
|
const projectActionSource = projectActionSources.get(project.key) ?? entry;
|
|
5027
|
-
const marker = entry.current ? "✅" : entry.active ? "🟢" : "
|
|
5073
|
+
const marker = entry.current ? "✅ " : entry.active ? "🟢 " : "";
|
|
5028
5074
|
const showStop = entry.status === "外部执行中"
|
|
5029
5075
|
&& entry.reference !== options.forceSwitchTaskId
|
|
5030
5076
|
&& entry.id !== options.forceSwitchTaskId;
|
|
5031
|
-
const actions: TaskListCardAction[] = entry.current ? [] : [{
|
|
5077
|
+
const actions: TaskListCardAction[] = entry.current ? [] : [bindCardAction({
|
|
5032
5078
|
text: showStop ? "Stop" : "Switch",
|
|
5033
5079
|
type: showStop ? "danger" as const : "default" as const,
|
|
5034
5080
|
value: {
|
|
@@ -5038,8 +5084,8 @@ export class ProxySessionController {
|
|
|
5038
5084
|
page: String(page),
|
|
5039
5085
|
contextKey,
|
|
5040
5086
|
},
|
|
5041
|
-
}];
|
|
5042
|
-
const projectActions: TaskListCardAction[] = [{
|
|
5087
|
+
})];
|
|
5088
|
+
const projectActions: TaskListCardAction[] = [bindCardAction({
|
|
5043
5089
|
text: "New",
|
|
5044
5090
|
value: {
|
|
5045
5091
|
action: "session_new",
|
|
@@ -5048,7 +5094,7 @@ export class ProxySessionController {
|
|
|
5048
5094
|
page: String(page),
|
|
5049
5095
|
contextKey,
|
|
5050
5096
|
},
|
|
5051
|
-
}, {
|
|
5097
|
+
}), bindCardAction({
|
|
5052
5098
|
text: "NewGroup",
|
|
5053
5099
|
value: {
|
|
5054
5100
|
action: "session_new_group",
|
|
@@ -5057,8 +5103,8 @@ export class ProxySessionController {
|
|
|
5057
5103
|
page: String(page),
|
|
5058
5104
|
contextKey,
|
|
5059
5105
|
},
|
|
5060
|
-
}];
|
|
5061
|
-
actions.push({
|
|
5106
|
+
})];
|
|
5107
|
+
actions.push(bindCardAction({
|
|
5062
5108
|
text: "Fork",
|
|
5063
5109
|
value: {
|
|
5064
5110
|
action: "session_fork",
|
|
@@ -5067,8 +5113,8 @@ export class ProxySessionController {
|
|
|
5067
5113
|
page: String(page),
|
|
5068
5114
|
contextKey,
|
|
5069
5115
|
},
|
|
5070
|
-
});
|
|
5071
|
-
actions.push({
|
|
5116
|
+
}));
|
|
5117
|
+
actions.push(bindCardAction({
|
|
5072
5118
|
text: "ForkGroup",
|
|
5073
5119
|
value: {
|
|
5074
5120
|
action: "session_fork_group",
|
|
@@ -5077,16 +5123,16 @@ export class ProxySessionController {
|
|
|
5077
5123
|
page: String(page),
|
|
5078
5124
|
contextKey,
|
|
5079
5125
|
},
|
|
5080
|
-
});
|
|
5081
|
-
actions.push({
|
|
5126
|
+
}));
|
|
5127
|
+
actions.push(bindCardAction({
|
|
5082
5128
|
text: "Status",
|
|
5083
5129
|
value: {
|
|
5084
5130
|
action: "session_status",
|
|
5085
5131
|
sessionId: entry.reference,
|
|
5086
5132
|
contextKey,
|
|
5087
5133
|
},
|
|
5088
|
-
});
|
|
5089
|
-
actions.push({
|
|
5134
|
+
}));
|
|
5135
|
+
actions.push(bindCardAction({
|
|
5090
5136
|
text: "Archive",
|
|
5091
5137
|
value: {
|
|
5092
5138
|
action: "session_archive",
|
|
@@ -5095,18 +5141,18 @@ export class ProxySessionController {
|
|
|
5095
5141
|
page: String(page),
|
|
5096
5142
|
contextKey,
|
|
5097
5143
|
},
|
|
5098
|
-
});
|
|
5144
|
+
}));
|
|
5099
5145
|
const title = truncateText(entry.title.replace(/\s+/gu, " ").trim() || "未命名任务", 56);
|
|
5146
|
+
const lastUserPrompt = sessionPromptPreview(entry.lastUserPrompt);
|
|
5100
5147
|
return {
|
|
5101
5148
|
project,
|
|
5102
5149
|
projectActions,
|
|
5103
5150
|
cardEntry: {
|
|
5104
5151
|
reference: entry.reference,
|
|
5105
|
-
summary: `${offset + index + 1}. ${marker}
|
|
5152
|
+
summary: `${offset + index + 1}. ${marker}${title} · ${entry.agentName}`,
|
|
5106
5153
|
detailLines: [
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
`**目录**:${cardCode(entry.cwd || "目录未知")}`,
|
|
5154
|
+
cardText(lastUserPrompt),
|
|
5155
|
+
`**更新时间**:${cardText(entry.updatedLabel)}`,
|
|
5110
5156
|
],
|
|
5111
5157
|
actions,
|
|
5112
5158
|
current: entry.current,
|
|
@@ -5136,11 +5182,11 @@ export class ProxySessionController {
|
|
|
5136
5182
|
...(remoteHint ? [remoteHint] : []),
|
|
5137
5183
|
`第 ${page + 1} 页 · 每页 ${SESSION_PAGE_SIZE} 个任务${hasNext ? "" : ` · 当前共 ${entries.length} 个任务`}`,
|
|
5138
5184
|
"",
|
|
5139
|
-
">
|
|
5185
|
+
"> 项目菜单:**New** 新建任务,**NewGroup** 新建群。",
|
|
5140
5186
|
"> 任务详情:**Switch** 切换,**Stop** 停止,**Fork** / **ForkGroup** 创建分支,**Status** 查看状态,**Archive** 归档。",
|
|
5141
5187
|
],
|
|
5142
5188
|
[
|
|
5143
|
-
...(hasPrevious ? [{
|
|
5189
|
+
...(hasPrevious ? [bindCardAction({
|
|
5144
5190
|
text: "Previous",
|
|
5145
5191
|
value: {
|
|
5146
5192
|
action: "session_page",
|
|
@@ -5148,8 +5194,8 @@ export class ProxySessionController {
|
|
|
5148
5194
|
page: String(page - 1),
|
|
5149
5195
|
contextKey,
|
|
5150
5196
|
},
|
|
5151
|
-
}] : []),
|
|
5152
|
-
...(hasNext ? [{
|
|
5197
|
+
})] : []),
|
|
5198
|
+
...(hasNext ? [bindCardAction({
|
|
5153
5199
|
text: "Next",
|
|
5154
5200
|
value: {
|
|
5155
5201
|
action: "session_page",
|
|
@@ -5157,13 +5203,20 @@ export class ProxySessionController {
|
|
|
5157
5203
|
page: String(page + 1),
|
|
5158
5204
|
contextKey,
|
|
5159
5205
|
},
|
|
5160
|
-
}] : []),
|
|
5206
|
+
})] : []),
|
|
5161
5207
|
],
|
|
5162
5208
|
);
|
|
5209
|
+
const bindings = [...cardActionBindings.values()];
|
|
5163
5210
|
if (options.updateMessageId) {
|
|
5211
|
+
this.store.upsertCardActionBindings(options.updateMessageId, bindings);
|
|
5164
5212
|
await this.outbound.updateInteractiveCard(contextKey, options.updateMessageId, card);
|
|
5213
|
+
this.store.retainCardActionBindings(options.updateMessageId, bindings.map((binding) => binding.token));
|
|
5165
5214
|
} else {
|
|
5166
|
-
await this.outbound.sendInteractiveCard(contextKey, card);
|
|
5215
|
+
const messageId = await this.outbound.sendInteractiveCard(contextKey, card);
|
|
5216
|
+
if (messageId) {
|
|
5217
|
+
this.store.upsertCardActionBindings(messageId, bindings);
|
|
5218
|
+
this.store.retainCardActionBindings(messageId, bindings.map((binding) => binding.token));
|
|
5219
|
+
}
|
|
5167
5220
|
}
|
|
5168
5221
|
}
|
|
5169
5222
|
|
|
@@ -5973,6 +6026,7 @@ interface UnifiedTaskListEntry {
|
|
|
5973
6026
|
status: string;
|
|
5974
6027
|
active: boolean;
|
|
5975
6028
|
current: boolean;
|
|
6029
|
+
lastUserPrompt?: string;
|
|
5976
6030
|
updatedAt: number;
|
|
5977
6031
|
updatedLabel: string;
|
|
5978
6032
|
}
|
|
@@ -5982,26 +6036,28 @@ interface TaskProjectInfo {
|
|
|
5982
6036
|
title: string;
|
|
5983
6037
|
}
|
|
5984
6038
|
|
|
5985
|
-
function
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
const
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6039
|
+
function orderTaskListByRecency(entries: UnifiedTaskListEntry[]): UnifiedTaskListEntry[] {
|
|
6040
|
+
return [...entries].sort(compareTaskListEntries);
|
|
6041
|
+
}
|
|
6042
|
+
|
|
6043
|
+
function groupTaskListPagesByProject(
|
|
6044
|
+
entries: UnifiedTaskListEntry[],
|
|
6045
|
+
pageSize: number,
|
|
6046
|
+
): UnifiedTaskListEntry[] {
|
|
6047
|
+
const result: UnifiedTaskListEntry[] = [];
|
|
6048
|
+
for (let offset = 0; offset < entries.length; offset += pageSize) {
|
|
6049
|
+
const groups = new Map<string, UnifiedTaskListEntry[]>();
|
|
6050
|
+
for (const entry of entries.slice(offset, offset + pageSize)) {
|
|
6051
|
+
const key = taskProjectInfo(entry).key;
|
|
6052
|
+
const group = groups.get(key);
|
|
6053
|
+
if (group) group.push(entry);
|
|
6054
|
+
else groups.set(key, [entry]);
|
|
6055
|
+
}
|
|
6056
|
+
for (const group of groups.values()) {
|
|
6057
|
+
result.push(...group.sort(compareTaskListEntries));
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
6060
|
+
return result;
|
|
6005
6061
|
}
|
|
6006
6062
|
|
|
6007
6063
|
function compareTaskListEntries(left: UnifiedTaskListEntry, right: UnifiedTaskListEntry): number {
|
|
@@ -6030,6 +6086,8 @@ function mergeTaskList(
|
|
|
6030
6086
|
localSessions: SessionRecord[],
|
|
6031
6087
|
remoteSessions: AgentRemoteSessionSummary[],
|
|
6032
6088
|
currentLocalSessionId?: string,
|
|
6089
|
+
latestLocalUserPrompts: ReadonlyMap<string, string> = new Map(),
|
|
6090
|
+
latestRemoteUserPrompts: ReadonlyMap<string, string> = new Map(),
|
|
6033
6091
|
): UnifiedTaskListEntry[] {
|
|
6034
6092
|
const localByRemoteId = new Map(
|
|
6035
6093
|
localSessions
|
|
@@ -6054,6 +6112,9 @@ function mergeTaskList(
|
|
|
6054
6112
|
status,
|
|
6055
6113
|
active,
|
|
6056
6114
|
current: Boolean(local && local.localSessionId === currentLocalSessionId),
|
|
6115
|
+
lastUserPrompt: remote.lastUserPrompt
|
|
6116
|
+
?? (local ? latestLocalUserPrompts.get(local.localSessionId) : undefined)
|
|
6117
|
+
?? latestRemoteUserPrompts.get(agentRemoteKey(agentName, remote.id)),
|
|
6057
6118
|
updatedAt: (recencyAt ?? 0) * 1_000,
|
|
6058
6119
|
updatedLabel: formatRemoteTime(recencyAt),
|
|
6059
6120
|
};
|
|
@@ -6073,6 +6134,7 @@ function mergeTaskList(
|
|
|
6073
6134
|
status: sessionStatusLabel(local.status),
|
|
6074
6135
|
active: local.status === "running",
|
|
6075
6136
|
current: local.localSessionId === currentLocalSessionId,
|
|
6137
|
+
lastUserPrompt: latestLocalUserPrompts.get(local.localSessionId),
|
|
6076
6138
|
updatedAt: Number.isNaN(updatedAt) ? 0 : updatedAt,
|
|
6077
6139
|
updatedLabel: formatStatusTime(local.updatedAt),
|
|
6078
6140
|
});
|
|
@@ -6102,6 +6164,20 @@ function parseRemoteSessionReference(reference: string): { agentName: string; re
|
|
|
6102
6164
|
}
|
|
6103
6165
|
}
|
|
6104
6166
|
|
|
6167
|
+
function bindSessionCardAction(
|
|
6168
|
+
action: TaskListCardAction,
|
|
6169
|
+
bindings: Map<string, CardActionBinding>,
|
|
6170
|
+
): TaskListCardAction {
|
|
6171
|
+
const serialized = JSON.stringify(action.value);
|
|
6172
|
+
const token = createHash("sha256").update(serialized).digest("hex").slice(0, 16);
|
|
6173
|
+
const existing = bindings.get(token);
|
|
6174
|
+
if (existing && JSON.stringify(existing.value) !== serialized) {
|
|
6175
|
+
throw new Error("任务列表卡片操作令牌发生冲突,请重试。");
|
|
6176
|
+
}
|
|
6177
|
+
bindings.set(token, { token, value: action.value });
|
|
6178
|
+
return { ...action, value: { t: token } };
|
|
6179
|
+
}
|
|
6180
|
+
|
|
6105
6181
|
function runtimeErrorMessage(error: unknown): string {
|
|
6106
6182
|
return error instanceof Error ? error.message : String(error);
|
|
6107
6183
|
}
|
|
@@ -6124,6 +6200,22 @@ function turnViewSnapshot(value: unknown): TurnViewState | undefined {
|
|
|
6124
6200
|
: undefined;
|
|
6125
6201
|
}
|
|
6126
6202
|
|
|
6203
|
+
function latestUserPromptFromTurnView(snapshot: TurnViewState | undefined): string | undefined {
|
|
6204
|
+
const latestUserActivity = [...(snapshot?.activities ?? [])]
|
|
6205
|
+
.reverse()
|
|
6206
|
+
.find((activity): activity is Extract<TurnActivity, { kind: "user" }> =>
|
|
6207
|
+
activity.kind === "user" && Boolean(activity.text.trim()));
|
|
6208
|
+
return latestUserActivity?.text.trim() || snapshot?.prompt?.trim() || undefined;
|
|
6209
|
+
}
|
|
6210
|
+
|
|
6211
|
+
function sessionPromptPreview(prompt: string | undefined): string {
|
|
6212
|
+
const normalized = prompt?.replace(/\s+/gu, " ").trim() || "暂无用户 Prompt";
|
|
6213
|
+
const characters = Array.from(normalized);
|
|
6214
|
+
return characters.length > SESSION_PROMPT_PREVIEW_LENGTH
|
|
6215
|
+
? `${characters.slice(0, SESSION_PROMPT_PREVIEW_LENGTH).join("")}...`
|
|
6216
|
+
: normalized;
|
|
6217
|
+
}
|
|
6218
|
+
|
|
6127
6219
|
function isTerminalTurnViewStatus(status: TurnViewState["status"]): boolean {
|
|
6128
6220
|
return status === "completed" || status === "cancelled" || status === "failed";
|
|
6129
6221
|
}
|
package/src/runtime/types.ts
CHANGED
|
@@ -123,6 +123,7 @@ export interface RemoteSessionSummary {
|
|
|
123
123
|
lastTurnId?: string;
|
|
124
124
|
lastCompletedTurnId?: string;
|
|
125
125
|
lastTurnStatus?: "completed" | "interrupted" | "failed" | "inProgress";
|
|
126
|
+
lastUserPrompt?: string;
|
|
126
127
|
lastActivity?: string;
|
|
127
128
|
finalResponse?: string;
|
|
128
129
|
lastError?: string;
|
package/src/state/StateStore.ts
CHANGED
|
@@ -75,6 +75,11 @@ export interface GoalCardDeliveryRecord {
|
|
|
75
75
|
updatedAt: string;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
export interface CardActionBinding {
|
|
79
|
+
token: string;
|
|
80
|
+
value: Record<string, string>;
|
|
81
|
+
}
|
|
82
|
+
|
|
78
83
|
export interface TurnRuntimeOriginRecord {
|
|
79
84
|
turnId: string;
|
|
80
85
|
localSessionId: string;
|
|
@@ -289,6 +294,52 @@ export class StateStore {
|
|
|
289
294
|
return rows.map(goalCardDeliveryFromRow);
|
|
290
295
|
}
|
|
291
296
|
|
|
297
|
+
upsertCardActionBindings(messageId: string, bindings: CardActionBinding[]): void {
|
|
298
|
+
if (bindings.length === 0) return;
|
|
299
|
+
const createdAt = new Date().toISOString();
|
|
300
|
+
const upsert = this.db.prepare(`
|
|
301
|
+
INSERT INTO card_action_bindings (message_id, token, value_json, created_at)
|
|
302
|
+
VALUES (?, ?, ?, ?)
|
|
303
|
+
ON CONFLICT(message_id, token) DO UPDATE SET
|
|
304
|
+
value_json = excluded.value_json,
|
|
305
|
+
created_at = excluded.created_at
|
|
306
|
+
`);
|
|
307
|
+
this.db.transaction(() => {
|
|
308
|
+
for (const binding of bindings) {
|
|
309
|
+
upsert.run(messageId, binding.token, JSON.stringify(binding.value), createdAt);
|
|
310
|
+
}
|
|
311
|
+
})();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
retainCardActionBindings(messageId: string, tokens: string[]): void {
|
|
315
|
+
if (tokens.length === 0) {
|
|
316
|
+
this.db.prepare("DELETE FROM card_action_bindings WHERE message_id = ?").run(messageId);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const placeholders = tokens.map(() => "?").join(", ");
|
|
320
|
+
this.db.prepare(`
|
|
321
|
+
DELETE FROM card_action_bindings
|
|
322
|
+
WHERE message_id = ? AND token NOT IN (${placeholders})
|
|
323
|
+
`).run(messageId, ...tokens);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
getCardActionBinding(messageId: string, token: string): Record<string, unknown> | undefined {
|
|
327
|
+
const row = this.db.prepare(`
|
|
328
|
+
SELECT value_json
|
|
329
|
+
FROM card_action_bindings
|
|
330
|
+
WHERE message_id = ? AND token = ?
|
|
331
|
+
`).get(messageId, token) as { value_json: string } | undefined;
|
|
332
|
+
if (!row) return undefined;
|
|
333
|
+
try {
|
|
334
|
+
const value = JSON.parse(row.value_json) as unknown;
|
|
335
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
336
|
+
? value as Record<string, unknown>
|
|
337
|
+
: undefined;
|
|
338
|
+
} catch {
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
292
343
|
close(): void {
|
|
293
344
|
this.db.close();
|
|
294
345
|
}
|
package/src/state/migrations.ts
CHANGED
|
@@ -184,4 +184,15 @@ export const migrations = [
|
|
|
184
184
|
CREATE INDEX IF NOT EXISTS idx_goal_card_deliveries_session
|
|
185
185
|
ON goal_card_deliveries(local_session_id);
|
|
186
186
|
`,
|
|
187
|
+
`
|
|
188
|
+
CREATE TABLE IF NOT EXISTS card_action_bindings (
|
|
189
|
+
message_id TEXT NOT NULL,
|
|
190
|
+
token TEXT NOT NULL,
|
|
191
|
+
value_json TEXT NOT NULL,
|
|
192
|
+
created_at TEXT NOT NULL,
|
|
193
|
+
PRIMARY KEY (message_id, token)
|
|
194
|
+
);
|
|
195
|
+
CREATE INDEX IF NOT EXISTS idx_card_action_bindings_created
|
|
196
|
+
ON card_action_bindings(created_at);
|
|
197
|
+
`,
|
|
187
198
|
] as const;
|