codeksei 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -661
- package/README.en.md +109 -47
- package/README.md +79 -58
- package/bin/cyberboss.js +1 -1
- package/package.json +86 -86
- package/scripts/open_shared_wechat_thread.sh +77 -77
- package/scripts/open_wechat_thread.sh +108 -108
- package/scripts/shared-common.js +144 -144
- package/scripts/shared-open.js +14 -14
- package/scripts/shared-start.js +5 -5
- package/scripts/shared-status.js +27 -27
- package/scripts/show_shared_status.sh +45 -45
- package/scripts/start_shared_app_server.sh +52 -52
- package/scripts/start_shared_wechat.sh +94 -94
- package/scripts/timeline-screenshot.sh +14 -14
- package/src/adapters/channel/weixin/account-store.js +99 -99
- package/src/adapters/channel/weixin/api-v2.js +50 -50
- package/src/adapters/channel/weixin/api.js +169 -169
- package/src/adapters/channel/weixin/context-token-store.js +84 -84
- package/src/adapters/channel/weixin/index.js +618 -604
- package/src/adapters/channel/weixin/legacy.js +579 -566
- package/src/adapters/channel/weixin/media-mime.js +22 -22
- package/src/adapters/channel/weixin/media-receive.js +370 -370
- package/src/adapters/channel/weixin/media-send.js +102 -102
- package/src/adapters/channel/weixin/message-utils-v2.js +282 -282
- package/src/adapters/channel/weixin/message-utils.js +199 -199
- package/src/adapters/channel/weixin/redact.js +41 -41
- package/src/adapters/channel/weixin/reminder-queue-store.js +101 -101
- package/src/adapters/channel/weixin/sync-buffer-store.js +35 -35
- package/src/adapters/runtime/codex/events.js +215 -215
- package/src/adapters/runtime/codex/index.js +109 -104
- package/src/adapters/runtime/codex/message-utils.js +95 -95
- package/src/adapters/runtime/codex/model-catalog.js +106 -106
- package/src/adapters/runtime/codex/protocol-leak-monitor.js +75 -75
- package/src/adapters/runtime/codex/rpc-client.js +339 -339
- package/src/adapters/runtime/codex/session-store.js +286 -286
- package/src/app/channel-send-file-cli.js +57 -57
- package/src/app/diary-write-cli.js +236 -88
- package/src/app/note-sync-cli.js +2 -2
- package/src/app/reminder-write-cli.js +215 -210
- package/src/app/review-cli.js +7 -5
- package/src/app/system-checkin-poller.js +64 -64
- package/src/app/system-send-cli.js +129 -129
- package/src/app/timeline-event-cli.js +28 -25
- package/src/app/timeline-screenshot-cli.js +103 -100
- package/src/core/app.js +1763 -1763
- package/src/core/branding.js +2 -1
- package/src/core/command-registry.js +381 -369
- package/src/core/config.js +30 -14
- package/src/core/default-targets.js +163 -163
- package/src/core/durable-note-schema.js +9 -8
- package/src/core/instructions-template.js +17 -16
- package/src/core/note-sync.js +8 -7
- package/src/core/path-utils.js +54 -0
- package/src/core/project-radar.js +11 -10
- package/src/core/review.js +48 -50
- package/src/core/stream-delivery.js +1162 -983
- package/src/core/system-message-dispatcher.js +68 -68
- package/src/core/system-message-queue-store.js +128 -128
- package/src/core/thread-state-store.js +96 -96
- package/src/core/timeline-screenshot-queue-store.js +134 -134
- package/src/core/timezone.js +436 -0
- package/src/core/workspace-bootstrap.js +9 -1
- package/src/index.js +148 -146
- package/src/integrations/timeline/index.js +130 -74
- package/src/integrations/timeline/state-sync.js +240 -0
- package/templates/weixin-instructions.md +12 -38
- package/templates/weixin-operations.md +29 -31
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
class SystemMessageDispatcher {
|
|
2
|
-
constructor({ queueStore, config, accountId }) {
|
|
3
|
-
this.queueStore = queueStore;
|
|
4
|
-
this.config = config;
|
|
5
|
-
this.accountId = accountId;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
hasPending() {
|
|
9
|
-
return this.queueStore.hasPendingForAccount(this.accountId);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
drainPending() {
|
|
13
|
-
return this.queueStore.drainForAccount(this.accountId);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
requeue(message) {
|
|
17
|
-
return this.queueStore.enqueue(message);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
resolveWorkspaceRoot(message) {
|
|
21
|
-
return normalizeText(message?.workspaceRoot) || normalizeText(this.config.workspaceRoot);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
buildPreparedMessage(message, contextToken = "") {
|
|
25
|
-
return {
|
|
26
|
-
provider: "system",
|
|
27
|
-
workspaceId: this.config.workspaceId,
|
|
28
|
-
accountId: this.accountId,
|
|
29
|
-
chatId: message.senderId,
|
|
30
|
-
threadKey: `system:${message.senderId}`,
|
|
31
|
-
senderId: message.senderId,
|
|
32
|
-
messageId: message.id,
|
|
33
|
-
text: buildSystemInboundText(message?.text, this.config),
|
|
34
|
-
attachments: [],
|
|
35
|
-
command: "message",
|
|
36
|
-
contextToken,
|
|
37
|
-
receivedAt: normalizeIsoTime(message?.createdAt) || new Date().toISOString(),
|
|
38
|
-
workspaceRoot: this.resolveWorkspaceRoot(message),
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function buildSystemInboundText(text, config = {}) {
|
|
44
|
-
const body = normalizeText(text);
|
|
45
|
-
const userName = normalizeText(config?.userName) || "用户";
|
|
46
|
-
if (!body) {
|
|
47
|
-
return `System trigger.\nThis message is not visible to ${userName}.`;
|
|
48
|
-
}
|
|
49
|
-
return `System trigger.\nThis message is not visible to ${userName}.\n${body}`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function normalizeIsoTime(value) {
|
|
53
|
-
const normalized = normalizeText(value);
|
|
54
|
-
if (!normalized) {
|
|
55
|
-
return "";
|
|
56
|
-
}
|
|
57
|
-
const parsed = Date.parse(normalized);
|
|
58
|
-
if (!Number.isFinite(parsed)) {
|
|
59
|
-
return "";
|
|
60
|
-
}
|
|
61
|
-
return new Date(parsed).toISOString();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function normalizeText(value) {
|
|
65
|
-
return typeof value === "string" ? value.trim() : "";
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
module.exports = { SystemMessageDispatcher };
|
|
1
|
+
class SystemMessageDispatcher {
|
|
2
|
+
constructor({ queueStore, config, accountId }) {
|
|
3
|
+
this.queueStore = queueStore;
|
|
4
|
+
this.config = config;
|
|
5
|
+
this.accountId = accountId;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
hasPending() {
|
|
9
|
+
return this.queueStore.hasPendingForAccount(this.accountId);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
drainPending() {
|
|
13
|
+
return this.queueStore.drainForAccount(this.accountId);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
requeue(message) {
|
|
17
|
+
return this.queueStore.enqueue(message);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
resolveWorkspaceRoot(message) {
|
|
21
|
+
return normalizeText(message?.workspaceRoot) || normalizeText(this.config.workspaceRoot);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
buildPreparedMessage(message, contextToken = "") {
|
|
25
|
+
return {
|
|
26
|
+
provider: "system",
|
|
27
|
+
workspaceId: this.config.workspaceId,
|
|
28
|
+
accountId: this.accountId,
|
|
29
|
+
chatId: message.senderId,
|
|
30
|
+
threadKey: `system:${message.senderId}`,
|
|
31
|
+
senderId: message.senderId,
|
|
32
|
+
messageId: message.id,
|
|
33
|
+
text: buildSystemInboundText(message?.text, this.config),
|
|
34
|
+
attachments: [],
|
|
35
|
+
command: "message",
|
|
36
|
+
contextToken,
|
|
37
|
+
receivedAt: normalizeIsoTime(message?.createdAt) || new Date().toISOString(),
|
|
38
|
+
workspaceRoot: this.resolveWorkspaceRoot(message),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildSystemInboundText(text, config = {}) {
|
|
44
|
+
const body = normalizeText(text);
|
|
45
|
+
const userName = normalizeText(config?.userName) || "用户";
|
|
46
|
+
if (!body) {
|
|
47
|
+
return `System trigger.\nThis message is not visible to ${userName}.`;
|
|
48
|
+
}
|
|
49
|
+
return `System trigger.\nThis message is not visible to ${userName}.\n${body}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function normalizeIsoTime(value) {
|
|
53
|
+
const normalized = normalizeText(value);
|
|
54
|
+
if (!normalized) {
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
const parsed = Date.parse(normalized);
|
|
58
|
+
if (!Number.isFinite(parsed)) {
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
return new Date(parsed).toISOString();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function normalizeText(value) {
|
|
65
|
+
return typeof value === "string" ? value.trim() : "";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = { SystemMessageDispatcher };
|
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
|
|
4
|
-
class SystemMessageQueueStore {
|
|
5
|
-
constructor({ filePath }) {
|
|
6
|
-
this.filePath = filePath;
|
|
7
|
-
this.state = { messages: [] };
|
|
8
|
-
this.ensureParentDirectory();
|
|
9
|
-
this.load();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
ensureParentDirectory() {
|
|
13
|
-
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
load() {
|
|
17
|
-
try {
|
|
18
|
-
const raw = fs.readFileSync(this.filePath, "utf8");
|
|
19
|
-
const parsed = JSON.parse(raw);
|
|
20
|
-
const messages = Array.isArray(parsed?.messages) ? parsed.messages : [];
|
|
21
|
-
this.state = {
|
|
22
|
-
messages: messages
|
|
23
|
-
.map(normalizeSystemMessage)
|
|
24
|
-
.filter(Boolean)
|
|
25
|
-
.sort(compareSystemMessages),
|
|
26
|
-
};
|
|
27
|
-
} catch {
|
|
28
|
-
this.state = { messages: [] };
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
save() {
|
|
33
|
-
fs.writeFileSync(this.filePath, JSON.stringify(this.state, null, 2));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
enqueue(message) {
|
|
37
|
-
this.load();
|
|
38
|
-
const normalized = normalizeSystemMessage(message);
|
|
39
|
-
if (!normalized) {
|
|
40
|
-
throw new Error("invalid system message");
|
|
41
|
-
}
|
|
42
|
-
this.state.messages.push(normalized);
|
|
43
|
-
this.state.messages.sort(compareSystemMessages);
|
|
44
|
-
this.save();
|
|
45
|
-
return normalized;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
drainForAccount(accountId) {
|
|
49
|
-
this.load();
|
|
50
|
-
const normalizedAccountId = normalizeText(accountId);
|
|
51
|
-
const drained = [];
|
|
52
|
-
const pending = [];
|
|
53
|
-
|
|
54
|
-
for (const message of this.state.messages) {
|
|
55
|
-
if (message.accountId === normalizedAccountId) {
|
|
56
|
-
drained.push(message);
|
|
57
|
-
} else {
|
|
58
|
-
pending.push(message);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (drained.length) {
|
|
63
|
-
this.state.messages = pending;
|
|
64
|
-
this.save();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return drained;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
hasPendingForAccount(accountId) {
|
|
71
|
-
this.load();
|
|
72
|
-
const normalizedAccountId = normalizeText(accountId);
|
|
73
|
-
return this.state.messages.some((message) => message.accountId === normalizedAccountId);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function normalizeSystemMessage(message) {
|
|
78
|
-
if (!message || typeof message !== "object") {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const id = normalizeText(message.id);
|
|
83
|
-
const accountId = normalizeText(message.accountId);
|
|
84
|
-
const senderId = normalizeText(message.senderId);
|
|
85
|
-
const workspaceRoot = normalizeText(message.workspaceRoot);
|
|
86
|
-
const text = normalizeText(message.text);
|
|
87
|
-
const createdAt = normalizeIsoTime(message.createdAt);
|
|
88
|
-
|
|
89
|
-
if (!id || !accountId || !senderId || !workspaceRoot || !text) {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
id,
|
|
95
|
-
accountId,
|
|
96
|
-
senderId,
|
|
97
|
-
workspaceRoot,
|
|
98
|
-
text,
|
|
99
|
-
createdAt: createdAt || new Date().toISOString(),
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function normalizeIsoTime(value) {
|
|
104
|
-
const normalized = normalizeText(value);
|
|
105
|
-
if (!normalized) {
|
|
106
|
-
return "";
|
|
107
|
-
}
|
|
108
|
-
const parsed = Date.parse(normalized);
|
|
109
|
-
if (!Number.isFinite(parsed)) {
|
|
110
|
-
return "";
|
|
111
|
-
}
|
|
112
|
-
return new Date(parsed).toISOString();
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function compareSystemMessages(left, right) {
|
|
116
|
-
const leftTime = Date.parse(left?.createdAt || "") || 0;
|
|
117
|
-
const rightTime = Date.parse(right?.createdAt || "") || 0;
|
|
118
|
-
if (leftTime !== rightTime) {
|
|
119
|
-
return leftTime - rightTime;
|
|
120
|
-
}
|
|
121
|
-
return String(left?.id || "").localeCompare(String(right?.id || ""));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function normalizeText(value) {
|
|
125
|
-
return typeof value === "string" ? value.trim() : "";
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
module.exports = { SystemMessageQueueStore };
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
class SystemMessageQueueStore {
|
|
5
|
+
constructor({ filePath }) {
|
|
6
|
+
this.filePath = filePath;
|
|
7
|
+
this.state = { messages: [] };
|
|
8
|
+
this.ensureParentDirectory();
|
|
9
|
+
this.load();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
ensureParentDirectory() {
|
|
13
|
+
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
load() {
|
|
17
|
+
try {
|
|
18
|
+
const raw = fs.readFileSync(this.filePath, "utf8");
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
const messages = Array.isArray(parsed?.messages) ? parsed.messages : [];
|
|
21
|
+
this.state = {
|
|
22
|
+
messages: messages
|
|
23
|
+
.map(normalizeSystemMessage)
|
|
24
|
+
.filter(Boolean)
|
|
25
|
+
.sort(compareSystemMessages),
|
|
26
|
+
};
|
|
27
|
+
} catch {
|
|
28
|
+
this.state = { messages: [] };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
save() {
|
|
33
|
+
fs.writeFileSync(this.filePath, JSON.stringify(this.state, null, 2));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
enqueue(message) {
|
|
37
|
+
this.load();
|
|
38
|
+
const normalized = normalizeSystemMessage(message);
|
|
39
|
+
if (!normalized) {
|
|
40
|
+
throw new Error("invalid system message");
|
|
41
|
+
}
|
|
42
|
+
this.state.messages.push(normalized);
|
|
43
|
+
this.state.messages.sort(compareSystemMessages);
|
|
44
|
+
this.save();
|
|
45
|
+
return normalized;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
drainForAccount(accountId) {
|
|
49
|
+
this.load();
|
|
50
|
+
const normalizedAccountId = normalizeText(accountId);
|
|
51
|
+
const drained = [];
|
|
52
|
+
const pending = [];
|
|
53
|
+
|
|
54
|
+
for (const message of this.state.messages) {
|
|
55
|
+
if (message.accountId === normalizedAccountId) {
|
|
56
|
+
drained.push(message);
|
|
57
|
+
} else {
|
|
58
|
+
pending.push(message);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (drained.length) {
|
|
63
|
+
this.state.messages = pending;
|
|
64
|
+
this.save();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return drained;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
hasPendingForAccount(accountId) {
|
|
71
|
+
this.load();
|
|
72
|
+
const normalizedAccountId = normalizeText(accountId);
|
|
73
|
+
return this.state.messages.some((message) => message.accountId === normalizedAccountId);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeSystemMessage(message) {
|
|
78
|
+
if (!message || typeof message !== "object") {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const id = normalizeText(message.id);
|
|
83
|
+
const accountId = normalizeText(message.accountId);
|
|
84
|
+
const senderId = normalizeText(message.senderId);
|
|
85
|
+
const workspaceRoot = normalizeText(message.workspaceRoot);
|
|
86
|
+
const text = normalizeText(message.text);
|
|
87
|
+
const createdAt = normalizeIsoTime(message.createdAt);
|
|
88
|
+
|
|
89
|
+
if (!id || !accountId || !senderId || !workspaceRoot || !text) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
id,
|
|
95
|
+
accountId,
|
|
96
|
+
senderId,
|
|
97
|
+
workspaceRoot,
|
|
98
|
+
text,
|
|
99
|
+
createdAt: createdAt || new Date().toISOString(),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function normalizeIsoTime(value) {
|
|
104
|
+
const normalized = normalizeText(value);
|
|
105
|
+
if (!normalized) {
|
|
106
|
+
return "";
|
|
107
|
+
}
|
|
108
|
+
const parsed = Date.parse(normalized);
|
|
109
|
+
if (!Number.isFinite(parsed)) {
|
|
110
|
+
return "";
|
|
111
|
+
}
|
|
112
|
+
return new Date(parsed).toISOString();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function compareSystemMessages(left, right) {
|
|
116
|
+
const leftTime = Date.parse(left?.createdAt || "") || 0;
|
|
117
|
+
const rightTime = Date.parse(right?.createdAt || "") || 0;
|
|
118
|
+
if (leftTime !== rightTime) {
|
|
119
|
+
return leftTime - rightTime;
|
|
120
|
+
}
|
|
121
|
+
return String(left?.id || "").localeCompare(String(right?.id || ""));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function normalizeText(value) {
|
|
125
|
+
return typeof value === "string" ? value.trim() : "";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = { SystemMessageQueueStore };
|
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
class ThreadStateStore {
|
|
2
|
-
constructor() {
|
|
3
|
-
this.stateByThreadId = new Map();
|
|
4
|
-
this.latestUsage = null;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
applyRuntimeEvent(event) {
|
|
8
|
-
if (event?.type === "runtime.usage.updated") {
|
|
9
|
-
this.latestUsage = {
|
|
10
|
-
...event.payload,
|
|
11
|
-
updatedAt: new Date().toISOString(),
|
|
12
|
-
};
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
if (!event || !event.payload || !event.payload.threadId) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const threadId = event.payload.threadId;
|
|
20
|
-
const current = this.stateByThreadId.get(threadId) || createEmptyThreadState(threadId);
|
|
21
|
-
const next = {
|
|
22
|
-
...current,
|
|
23
|
-
updatedAt: new Date().toISOString(),
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
switch (event.type) {
|
|
27
|
-
case "runtime.turn.started":
|
|
28
|
-
next.status = "running";
|
|
29
|
-
next.turnId = event.payload.turnId || next.turnId;
|
|
30
|
-
next.lastError = "";
|
|
31
|
-
break;
|
|
32
|
-
case "runtime.reply.delta":
|
|
33
|
-
next.status = "running";
|
|
34
|
-
next.turnId = event.payload.turnId || next.turnId;
|
|
35
|
-
next.lastReplyText = event.payload.text || next.lastReplyText;
|
|
36
|
-
break;
|
|
37
|
-
case "runtime.reply.completed":
|
|
38
|
-
next.status = "running";
|
|
39
|
-
next.turnId = event.payload.turnId || next.turnId;
|
|
40
|
-
next.lastReplyText = event.payload.text || next.lastReplyText;
|
|
41
|
-
break;
|
|
42
|
-
case "runtime.approval.requested":
|
|
43
|
-
next.status = "waiting_approval";
|
|
44
|
-
next.pendingApproval = {
|
|
45
|
-
requestId: event.payload.requestId ?? null,
|
|
46
|
-
reason: event.payload.reason || "",
|
|
47
|
-
command: event.payload.command || "",
|
|
48
|
-
commandTokens: Array.isArray(event.payload.commandTokens) ? event.payload.commandTokens : [],
|
|
49
|
-
};
|
|
50
|
-
break;
|
|
51
|
-
case "runtime.turn.completed":
|
|
52
|
-
next.status = "idle";
|
|
53
|
-
next.turnId = event.payload.turnId || next.turnId;
|
|
54
|
-
next.pendingApproval = null;
|
|
55
|
-
break;
|
|
56
|
-
case "runtime.turn.failed":
|
|
57
|
-
next.status = "failed";
|
|
58
|
-
next.turnId = event.payload.turnId || next.turnId;
|
|
59
|
-
next.lastError = event.payload.text || "执行失败";
|
|
60
|
-
next.pendingApproval = null;
|
|
61
|
-
break;
|
|
62
|
-
default:
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
this.stateByThreadId.set(threadId, next);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
getThreadState(threadId) {
|
|
70
|
-
return this.stateByThreadId.get(threadId) || null;
|
|
71
|
-
}
|
|
72
|
-
|
|
1
|
+
class ThreadStateStore {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.stateByThreadId = new Map();
|
|
4
|
+
this.latestUsage = null;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
applyRuntimeEvent(event) {
|
|
8
|
+
if (event?.type === "runtime.usage.updated") {
|
|
9
|
+
this.latestUsage = {
|
|
10
|
+
...event.payload,
|
|
11
|
+
updatedAt: new Date().toISOString(),
|
|
12
|
+
};
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (!event || !event.payload || !event.payload.threadId) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const threadId = event.payload.threadId;
|
|
20
|
+
const current = this.stateByThreadId.get(threadId) || createEmptyThreadState(threadId);
|
|
21
|
+
const next = {
|
|
22
|
+
...current,
|
|
23
|
+
updatedAt: new Date().toISOString(),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
switch (event.type) {
|
|
27
|
+
case "runtime.turn.started":
|
|
28
|
+
next.status = "running";
|
|
29
|
+
next.turnId = event.payload.turnId || next.turnId;
|
|
30
|
+
next.lastError = "";
|
|
31
|
+
break;
|
|
32
|
+
case "runtime.reply.delta":
|
|
33
|
+
next.status = "running";
|
|
34
|
+
next.turnId = event.payload.turnId || next.turnId;
|
|
35
|
+
next.lastReplyText = event.payload.text || next.lastReplyText;
|
|
36
|
+
break;
|
|
37
|
+
case "runtime.reply.completed":
|
|
38
|
+
next.status = "running";
|
|
39
|
+
next.turnId = event.payload.turnId || next.turnId;
|
|
40
|
+
next.lastReplyText = event.payload.text || next.lastReplyText;
|
|
41
|
+
break;
|
|
42
|
+
case "runtime.approval.requested":
|
|
43
|
+
next.status = "waiting_approval";
|
|
44
|
+
next.pendingApproval = {
|
|
45
|
+
requestId: event.payload.requestId ?? null,
|
|
46
|
+
reason: event.payload.reason || "",
|
|
47
|
+
command: event.payload.command || "",
|
|
48
|
+
commandTokens: Array.isArray(event.payload.commandTokens) ? event.payload.commandTokens : [],
|
|
49
|
+
};
|
|
50
|
+
break;
|
|
51
|
+
case "runtime.turn.completed":
|
|
52
|
+
next.status = "idle";
|
|
53
|
+
next.turnId = event.payload.turnId || next.turnId;
|
|
54
|
+
next.pendingApproval = null;
|
|
55
|
+
break;
|
|
56
|
+
case "runtime.turn.failed":
|
|
57
|
+
next.status = "failed";
|
|
58
|
+
next.turnId = event.payload.turnId || next.turnId;
|
|
59
|
+
next.lastError = event.payload.text || "执行失败";
|
|
60
|
+
next.pendingApproval = null;
|
|
61
|
+
break;
|
|
62
|
+
default:
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.stateByThreadId.set(threadId, next);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getThreadState(threadId) {
|
|
70
|
+
return this.stateByThreadId.get(threadId) || null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
73
|
resolveApproval(threadId, status = "running") {
|
|
74
74
|
const current = this.stateByThreadId.get(threadId);
|
|
75
75
|
if (!current) {
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
78
|
-
const next = {
|
|
79
|
-
...current,
|
|
80
|
-
status,
|
|
81
|
-
pendingApproval: null,
|
|
82
|
-
updatedAt: new Date().toISOString(),
|
|
83
|
-
};
|
|
78
|
+
const next = {
|
|
79
|
+
...current,
|
|
80
|
+
status,
|
|
81
|
+
pendingApproval: null,
|
|
82
|
+
updatedAt: new Date().toISOString(),
|
|
83
|
+
};
|
|
84
84
|
this.stateByThreadId.set(threadId, next);
|
|
85
85
|
return next;
|
|
86
86
|
}
|
|
@@ -106,25 +106,25 @@ class ThreadStateStore {
|
|
|
106
106
|
this.stateByThreadId.set(normalizedThreadId, next);
|
|
107
107
|
return next;
|
|
108
108
|
}
|
|
109
|
-
|
|
110
|
-
snapshot() {
|
|
111
|
-
return Array.from(this.stateByThreadId.values()).map((entry) => ({ ...entry }));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
getLatestUsage() {
|
|
115
|
-
return this.latestUsage ? { ...this.latestUsage } : null;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
109
|
+
|
|
110
|
+
snapshot() {
|
|
111
|
+
return Array.from(this.stateByThreadId.values()).map((entry) => ({ ...entry }));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
getLatestUsage() {
|
|
115
|
+
return this.latestUsage ? { ...this.latestUsage } : null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
119
|
function createEmptyThreadState(threadId) {
|
|
120
|
-
return {
|
|
121
|
-
threadId,
|
|
122
|
-
turnId: "",
|
|
123
|
-
status: "idle",
|
|
124
|
-
lastReplyText: "",
|
|
125
|
-
lastError: "",
|
|
126
|
-
pendingApproval: null,
|
|
127
|
-
updatedAt: new Date().toISOString(),
|
|
120
|
+
return {
|
|
121
|
+
threadId,
|
|
122
|
+
turnId: "",
|
|
123
|
+
status: "idle",
|
|
124
|
+
lastReplyText: "",
|
|
125
|
+
lastError: "",
|
|
126
|
+
pendingApproval: null,
|
|
127
|
+
updatedAt: new Date().toISOString(),
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
130
|
|