workspacecord 1.1.1 → 1.1.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/dist/{archive-manager-XM3UPOY2.js → archive-manager-RW36JGUV.js} +4 -4
- package/dist/{attachment-cli-4TGLBBUO.js → attachment-cli-MF7XZ4WT.js} +2 -2
- package/dist/{chunk-GMYN4SYT.js → chunk-4KQ7OSK7.js} +3 -3
- package/dist/{chunk-FXOGIVVO.js → chunk-7GTUWAQR.js} +374 -1156
- package/dist/{chunk-QNMCFWNT.js → chunk-AGB4GP4G.js} +1314 -881
- package/dist/{chunk-LBHCSIOK.js → chunk-BFINJJYL.js} +31 -6
- package/dist/{chunk-UEX7U2KW.js → chunk-IVXCJA5I.js} +902 -232
- package/dist/chunk-WON3DPE4.js +5092 -0
- package/dist/{chunk-COXPTYH5.js → chunk-WP6YJVAE.js} +4 -4
- package/dist/{chunk-54DP53ZK.js → chunk-ZO62NAYX.js} +29 -3
- package/dist/cli-framework-7E5MKPMM.js +18 -0
- package/dist/cli.js +9 -9
- package/dist/{codex-launcher-VDQ5VZPT.js → codex-launcher-ZBQ5VL6L.js} +1 -1
- package/dist/{codex-provider-NYI7KBGO.js → codex-provider-Q4Z6UKO6.js} +1 -1
- package/dist/{config-cli-RQR2ZRQ5.js → config-cli-7JEV3WYY.js} +2 -2
- package/dist/{panel-adapter-DAFBW5CV.js → panel-adapter-U75WXDLB.js} +4 -3
- package/dist/{project-cli-6P6ZWDR6.js → project-cli-ZXMHOFUJ.js} +2 -2
- package/dist/{project-registry-OEVPECMS.js → project-registry-ED6P5ZTM.js} +2 -2
- package/dist/{session-local-registration-RIO5EPZ5.js → session-local-registration-MISPPGXF.js} +3 -3
- package/dist/{setup-KOS7SRSL.js → setup-ZFVMMNT2.js} +1 -1
- package/package.json +3 -3
- package/dist/chunk-RK6EIZOL.js +0 -589
- package/dist/cli-framework-V2GGW6TO.js +0 -18
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
cleanupSessionAttachments
|
|
4
|
+
} from "./chunk-BFINJJYL.js";
|
|
5
|
+
import {
|
|
6
|
+
STATE_COLORS,
|
|
7
|
+
STATE_LABELS,
|
|
8
|
+
debouncedSaveSession,
|
|
9
|
+
gateService,
|
|
3
10
|
getAllSessions,
|
|
4
11
|
getSession,
|
|
12
|
+
getSessionController,
|
|
5
13
|
getSessionPermissionSummary,
|
|
14
|
+
mapPlatformEventToState,
|
|
6
15
|
setCurrentInteractionMessage,
|
|
7
16
|
setStatusCardBinding,
|
|
17
|
+
stateMachine,
|
|
18
|
+
toPlatformEvent,
|
|
8
19
|
updateSession
|
|
9
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-WON3DPE4.js";
|
|
10
21
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from "./chunk-
|
|
22
|
+
config,
|
|
23
|
+
truncate
|
|
24
|
+
} from "./chunk-IVXCJA5I.js";
|
|
14
25
|
|
|
15
26
|
// ../bot/src/discord/status-card-projection-renderer.ts
|
|
16
27
|
var StatusCardProjectionRenderer = class {
|
|
@@ -24,7 +35,9 @@ var StatusCardProjectionRenderer = class {
|
|
|
24
35
|
phase: projection.phase,
|
|
25
36
|
remoteHumanControl: context.remoteHumanControl,
|
|
26
37
|
provider: context.provider,
|
|
27
|
-
permissionsSummary: context.permissionsSummary
|
|
38
|
+
permissionsSummary: context.permissionsSummary,
|
|
39
|
+
verbose: context.verbose,
|
|
40
|
+
monitorGoal: context.monitorGoal
|
|
28
41
|
});
|
|
29
42
|
} catch (error) {
|
|
30
43
|
console.error(`\u72B6\u6001\u5361\u66F4\u65B0\u5931\u8D25 (${sessionId}):`, error);
|
|
@@ -64,1001 +77,6 @@ var StatusCardProjectionRenderer = class {
|
|
|
64
77
|
import {
|
|
65
78
|
EmbedBuilder
|
|
66
79
|
} from "discord.js";
|
|
67
|
-
|
|
68
|
-
// ../state/src/types.ts
|
|
69
|
-
var STATE_PRIORITY = {
|
|
70
|
-
error: 9,
|
|
71
|
-
awaiting_human: 8,
|
|
72
|
-
stalled: 7,
|
|
73
|
-
summarizing: 6,
|
|
74
|
-
working: 5,
|
|
75
|
-
thinking: 4,
|
|
76
|
-
completed: 3,
|
|
77
|
-
idle: 2,
|
|
78
|
-
offline: 1
|
|
79
|
-
};
|
|
80
|
-
var STATE_LABELS = {
|
|
81
|
-
idle: "\u5F85\u547D",
|
|
82
|
-
thinking: "\u6B63\u5728\u601D\u8003",
|
|
83
|
-
working: "\u6B63\u5728\u6267\u884C",
|
|
84
|
-
awaiting_human: "\u7B49\u5F85\u4EBA\u5DE5\u5904\u7406",
|
|
85
|
-
summarizing: "\u6B63\u5728\u6574\u7406\u4E0A\u4E0B\u6587",
|
|
86
|
-
completed: "\u672C\u8F6E\u5DF2\u5B8C\u6210",
|
|
87
|
-
error: "\u51FA\u73B0\u5F02\u5E38",
|
|
88
|
-
stalled: "\u7591\u4F3C\u5361\u4F4F",
|
|
89
|
-
offline: "\u5DF2\u79BB\u7EBF"
|
|
90
|
-
};
|
|
91
|
-
var STATE_COLORS = {
|
|
92
|
-
idle: 8421504,
|
|
93
|
-
// 灰色
|
|
94
|
-
thinking: 3447003,
|
|
95
|
-
// 蓝色
|
|
96
|
-
working: 3066993,
|
|
97
|
-
// 绿色
|
|
98
|
-
awaiting_human: 15965202,
|
|
99
|
-
// 橙色
|
|
100
|
-
summarizing: 10181046,
|
|
101
|
-
// 紫色
|
|
102
|
-
completed: 2600544,
|
|
103
|
-
// 深绿
|
|
104
|
-
error: 15158332,
|
|
105
|
-
// 红色
|
|
106
|
-
stalled: 15105570,
|
|
107
|
-
// 深橙
|
|
108
|
-
offline: 9807270
|
|
109
|
-
// 浅灰
|
|
110
|
-
};
|
|
111
|
-
var PLATFORM_EVENT_TO_STATE = {
|
|
112
|
-
session_started: "idle",
|
|
113
|
-
session_idle: "idle",
|
|
114
|
-
thinking_started: "thinking",
|
|
115
|
-
work_started: "working",
|
|
116
|
-
awaiting_human: "awaiting_human",
|
|
117
|
-
human_resolved: "working",
|
|
118
|
-
compaction_started: "summarizing",
|
|
119
|
-
completed: "completed",
|
|
120
|
-
errored: "error",
|
|
121
|
-
stalled: "stalled",
|
|
122
|
-
session_ended: "offline"
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// ../state/src/state-projections.ts
|
|
126
|
-
function toProjection(state) {
|
|
127
|
-
return {
|
|
128
|
-
state: state.displayState,
|
|
129
|
-
stateSource: state.stateSource,
|
|
130
|
-
confidence: state.confidence,
|
|
131
|
-
updatedAt: state.updatedAt,
|
|
132
|
-
turn: state.turn,
|
|
133
|
-
phase: state.phase,
|
|
134
|
-
humanResolved: state.humanResolved
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
function toPanelProjection(state) {
|
|
138
|
-
return {
|
|
139
|
-
...toProjection(state),
|
|
140
|
-
isWaitingHuman: state.lifecycle === "waiting_human" || state.gate === "pending" || state.displayState === "awaiting_human",
|
|
141
|
-
isCompleted: state.displayState === "completed" || state.lifecycle === "completed",
|
|
142
|
-
isError: state.displayState === "error" || state.lifecycle === "error",
|
|
143
|
-
isStalled: state.displayState === "stalled"
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
function resolveDisplayState(sessions) {
|
|
147
|
-
let best = "idle";
|
|
148
|
-
let bestPri = 0;
|
|
149
|
-
for (const state of sessions) {
|
|
150
|
-
const pri = STATE_PRIORITY[state.displayState] || 0;
|
|
151
|
-
if (pri > bestPri) {
|
|
152
|
-
best = state.displayState;
|
|
153
|
-
bestPri = pri;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return best;
|
|
157
|
-
}
|
|
158
|
-
function getStateLabel(state) {
|
|
159
|
-
return STATE_LABELS[state] || state;
|
|
160
|
-
}
|
|
161
|
-
function getStateColor(state) {
|
|
162
|
-
return STATE_COLORS[state] || 8421504;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// ../state/src/state-event-mapper.ts
|
|
166
|
-
function mapEventToTransition(event, currentState, context) {
|
|
167
|
-
const mappedState = PLATFORM_EVENT_TO_STATE[event.type];
|
|
168
|
-
if (!mappedState) return null;
|
|
169
|
-
if (event.type === "session_idle" && !context.isSessionIdleTransitionAllowed(event, currentState)) {
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
const allowTransition = event.type === "human_resolved" || event.type === "completed" || event.type === "session_ended" || context.shouldTransition(
|
|
173
|
-
currentState.displayState,
|
|
174
|
-
mappedState,
|
|
175
|
-
currentState.stateSource,
|
|
176
|
-
event.stateSource ?? "formal"
|
|
177
|
-
);
|
|
178
|
-
if (!allowTransition) {
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
const lifecycle = mapLifecycle(event, currentState, mappedState);
|
|
182
|
-
const execution = mapExecution(event, currentState, mappedState, lifecycle);
|
|
183
|
-
const gate = mapGate(event, currentState);
|
|
184
|
-
const turn = resolveTurn(event, currentState);
|
|
185
|
-
const humanResolved = resolveHumanResolved(event, currentState);
|
|
186
|
-
const phaseLabel = event.metadata?.phase ?? getStateLabel(mappedState);
|
|
187
|
-
return {
|
|
188
|
-
updates: {
|
|
189
|
-
lifecycle,
|
|
190
|
-
execution,
|
|
191
|
-
gate
|
|
192
|
-
},
|
|
193
|
-
metadata: {
|
|
194
|
-
displayState: mappedState,
|
|
195
|
-
stateSource: event.stateSource ?? "formal",
|
|
196
|
-
confidence: event.confidence,
|
|
197
|
-
updatedAt: event.timestamp,
|
|
198
|
-
turn,
|
|
199
|
-
phase: phaseLabel,
|
|
200
|
-
humanResolved
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
function mapLifecycle(event, current, mappedState) {
|
|
205
|
-
switch (event.type) {
|
|
206
|
-
case "session_started":
|
|
207
|
-
case "session_idle":
|
|
208
|
-
case "thinking_started":
|
|
209
|
-
case "work_started":
|
|
210
|
-
case "human_resolved":
|
|
211
|
-
case "compaction_started":
|
|
212
|
-
return "active";
|
|
213
|
-
case "awaiting_human":
|
|
214
|
-
return "waiting_human";
|
|
215
|
-
case "completed":
|
|
216
|
-
return "completed";
|
|
217
|
-
case "errored":
|
|
218
|
-
return "error";
|
|
219
|
-
case "stalled":
|
|
220
|
-
return current.lifecycle === "active" ? "paused" : current.lifecycle;
|
|
221
|
-
case "session_ended":
|
|
222
|
-
return current.lifecycle === "initializing" ? "initializing" : "paused";
|
|
223
|
-
default:
|
|
224
|
-
return mapLifecycleFromState(mappedState, current.lifecycle);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
function mapLifecycleFromState(mappedState, fallback) {
|
|
228
|
-
switch (mappedState) {
|
|
229
|
-
case "idle":
|
|
230
|
-
case "thinking":
|
|
231
|
-
case "working":
|
|
232
|
-
case "summarizing":
|
|
233
|
-
return "active";
|
|
234
|
-
case "awaiting_human":
|
|
235
|
-
return "waiting_human";
|
|
236
|
-
case "completed":
|
|
237
|
-
return "completed";
|
|
238
|
-
case "error":
|
|
239
|
-
return "error";
|
|
240
|
-
case "stalled":
|
|
241
|
-
return "paused";
|
|
242
|
-
case "offline":
|
|
243
|
-
return fallback === "initializing" ? "initializing" : "paused";
|
|
244
|
-
default:
|
|
245
|
-
return fallback;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
function mapExecution(event, current, mappedState, lifecycle) {
|
|
249
|
-
if (lifecycle !== "active") return null;
|
|
250
|
-
switch (event.type) {
|
|
251
|
-
case "session_started":
|
|
252
|
-
case "session_idle":
|
|
253
|
-
return "idle";
|
|
254
|
-
case "thinking_started":
|
|
255
|
-
return "thinking";
|
|
256
|
-
case "work_started":
|
|
257
|
-
return "tool_executing";
|
|
258
|
-
case "human_resolved":
|
|
259
|
-
return "tool_executing";
|
|
260
|
-
case "compaction_started":
|
|
261
|
-
return "thinking";
|
|
262
|
-
default:
|
|
263
|
-
return mapExecutionFromState(mappedState, current.execution);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
function mapExecutionFromState(mappedState, fallback) {
|
|
267
|
-
switch (mappedState) {
|
|
268
|
-
case "idle":
|
|
269
|
-
return "idle";
|
|
270
|
-
case "thinking":
|
|
271
|
-
case "summarizing":
|
|
272
|
-
return "thinking";
|
|
273
|
-
case "working":
|
|
274
|
-
return "tool_executing";
|
|
275
|
-
default:
|
|
276
|
-
return fallback;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
function mapGate(event, current) {
|
|
280
|
-
switch (event.type) {
|
|
281
|
-
case "awaiting_human":
|
|
282
|
-
return "pending";
|
|
283
|
-
case "human_resolved":
|
|
284
|
-
return event.metadata?.action === "reject" ? "rejected" : "approved";
|
|
285
|
-
case "session_idle":
|
|
286
|
-
if (event.metadata?.action === "reject" && current.gate === "pending") {
|
|
287
|
-
return "rejected";
|
|
288
|
-
}
|
|
289
|
-
return current.gate;
|
|
290
|
-
case "session_ended":
|
|
291
|
-
return current.gate === "pending" ? "invalidated" : current.gate;
|
|
292
|
-
default:
|
|
293
|
-
return current.gate;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
function resolveTurn(event, current) {
|
|
297
|
-
if (event.type === "session_started" && current.turn <= 0) {
|
|
298
|
-
return 1;
|
|
299
|
-
}
|
|
300
|
-
return current.turn;
|
|
301
|
-
}
|
|
302
|
-
function resolveHumanResolved(event, current) {
|
|
303
|
-
if (event.type === "awaiting_human") return false;
|
|
304
|
-
if (event.type === "human_resolved") return true;
|
|
305
|
-
if (event.type === "session_idle" && event.metadata?.action === "reject") return true;
|
|
306
|
-
return current.humanResolved;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// ../state/src/state-machine.ts
|
|
310
|
-
var LIFECYCLE_TRANSITIONS = {
|
|
311
|
-
initializing: ["active", "waiting_human", "paused", "completed", "error"],
|
|
312
|
-
active: ["waiting_human", "paused", "completed", "error"],
|
|
313
|
-
waiting_human: ["active", "paused", "error"],
|
|
314
|
-
paused: ["active", "completed", "error"],
|
|
315
|
-
completed: ["active"],
|
|
316
|
-
error: ["active", "completed"]
|
|
317
|
-
};
|
|
318
|
-
var EXECUTION_TRANSITIONS = {
|
|
319
|
-
idle: ["thinking", "tool_executing"],
|
|
320
|
-
thinking: ["tool_executing", "streaming_output", "idle"],
|
|
321
|
-
tool_executing: ["thinking", "streaming_output", "idle"],
|
|
322
|
-
streaming_output: ["idle", "thinking"]
|
|
323
|
-
};
|
|
324
|
-
var StateMachine = class {
|
|
325
|
-
sessions = /* @__PURE__ */ new Map();
|
|
326
|
-
transitionHistory = /* @__PURE__ */ new Map();
|
|
327
|
-
completedTimers = /* @__PURE__ */ new Map();
|
|
328
|
-
completedTimerTokens = /* @__PURE__ */ new Map();
|
|
329
|
-
completedTimerSequence = 0;
|
|
330
|
-
getState(sessionId) {
|
|
331
|
-
const existing = this.sessions.get(sessionId);
|
|
332
|
-
if (existing) return existing;
|
|
333
|
-
const defaultState = this.createDefaultState();
|
|
334
|
-
this.sessions.set(sessionId, defaultState);
|
|
335
|
-
return defaultState;
|
|
336
|
-
}
|
|
337
|
-
getSnapshot(sessionId) {
|
|
338
|
-
return toProjection(this.getState(sessionId));
|
|
339
|
-
}
|
|
340
|
-
getPanelProjection(sessionId) {
|
|
341
|
-
return toPanelProjection(this.getState(sessionId));
|
|
342
|
-
}
|
|
343
|
-
transition(sessionId, event, updates, metadata = {}) {
|
|
344
|
-
const current = this.getState(sessionId);
|
|
345
|
-
const timestamp = metadata.updatedAt ?? Date.now();
|
|
346
|
-
const target = {
|
|
347
|
-
...current,
|
|
348
|
-
lifecycle: updates.lifecycle ?? current.lifecycle,
|
|
349
|
-
execution: updates.execution !== void 0 ? updates.execution : current.execution,
|
|
350
|
-
gate: updates.gate !== void 0 ? updates.gate : current.gate,
|
|
351
|
-
displayState: metadata.displayState ?? current.displayState,
|
|
352
|
-
stateSource: metadata.stateSource ?? current.stateSource,
|
|
353
|
-
confidence: metadata.confidence ?? current.confidence,
|
|
354
|
-
updatedAt: timestamp,
|
|
355
|
-
turn: metadata.turn ?? current.turn,
|
|
356
|
-
phase: metadata.phase ?? current.phase,
|
|
357
|
-
humanResolved: metadata.humanResolved ?? current.humanResolved
|
|
358
|
-
};
|
|
359
|
-
if (updates.lifecycle && updates.lifecycle !== current.lifecycle) {
|
|
360
|
-
const allowed = LIFECYCLE_TRANSITIONS[current.lifecycle];
|
|
361
|
-
if (!allowed.includes(updates.lifecycle)) {
|
|
362
|
-
return {
|
|
363
|
-
success: false,
|
|
364
|
-
state: current,
|
|
365
|
-
error: `\u975E\u6CD5\u751F\u547D\u5468\u671F\u8F6C\u6362: ${current.lifecycle} -> ${updates.lifecycle}`
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
if (updates.execution !== void 0 && updates.execution !== current.execution) {
|
|
370
|
-
if (target.lifecycle !== "active" && updates.execution !== null) {
|
|
371
|
-
return {
|
|
372
|
-
success: false,
|
|
373
|
-
state: current,
|
|
374
|
-
error: `\u6267\u884C\u72B6\u6001\u4EC5\u5728 lifecycle=active \u65F6\u6709\u6548\uFF0C\u5F53\u524D lifecycle=${target.lifecycle}`
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
if (current.execution && updates.execution) {
|
|
378
|
-
const allowed = EXECUTION_TRANSITIONS[current.execution];
|
|
379
|
-
if (!allowed.includes(updates.execution)) {
|
|
380
|
-
return {
|
|
381
|
-
success: false,
|
|
382
|
-
state: current,
|
|
383
|
-
error: `\u975E\u6CD5\u6267\u884C\u72B6\u6001\u8F6C\u6362: ${current.execution} -> ${updates.execution}`
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
if (target.lifecycle !== "active" && target.execution !== null) {
|
|
389
|
-
target.execution = null;
|
|
390
|
-
}
|
|
391
|
-
if (this.isSameState(current, target)) {
|
|
392
|
-
return { success: true, state: current };
|
|
393
|
-
}
|
|
394
|
-
this.sessions.set(sessionId, target);
|
|
395
|
-
const transition = {
|
|
396
|
-
from: { ...current },
|
|
397
|
-
to: { ...target },
|
|
398
|
-
event,
|
|
399
|
-
timestamp,
|
|
400
|
-
sessionId
|
|
401
|
-
};
|
|
402
|
-
const history = this.transitionHistory.get(sessionId) || [];
|
|
403
|
-
history.push(transition);
|
|
404
|
-
if (history.length > 100) {
|
|
405
|
-
history.shift();
|
|
406
|
-
}
|
|
407
|
-
this.transitionHistory.set(sessionId, history);
|
|
408
|
-
console.log(
|
|
409
|
-
`[state-machine] ${sessionId} | ${event} | lifecycle: ${current.lifecycle} -> ${target.lifecycle} | execution: ${current.execution} -> ${target.execution} | gate: ${current.gate} -> ${target.gate} | display: ${current.displayState} -> ${target.displayState}`
|
|
410
|
-
);
|
|
411
|
-
return { success: true, state: target };
|
|
412
|
-
}
|
|
413
|
-
getTransitionHistory(sessionId) {
|
|
414
|
-
return this.transitionHistory.get(sessionId) || [];
|
|
415
|
-
}
|
|
416
|
-
resolveDisplayState() {
|
|
417
|
-
return resolveDisplayState(this.sessions.values());
|
|
418
|
-
}
|
|
419
|
-
shouldTransition(from, to, fromSource = "formal", toSource = "formal") {
|
|
420
|
-
const fromPri = STATE_PRIORITY[from] || 0;
|
|
421
|
-
const toPri = STATE_PRIORITY[to] || 0;
|
|
422
|
-
if (fromSource === "formal" && toSource === "inferred") {
|
|
423
|
-
return toPri > fromPri;
|
|
424
|
-
}
|
|
425
|
-
return toPri >= fromPri;
|
|
426
|
-
}
|
|
427
|
-
getStateLabel(state) {
|
|
428
|
-
return getStateLabel(state);
|
|
429
|
-
}
|
|
430
|
-
getStateColor(state) {
|
|
431
|
-
return getStateColor(state);
|
|
432
|
-
}
|
|
433
|
-
setTurn(sessionId, turn, event = "turn_set") {
|
|
434
|
-
const result = this.transition(sessionId, event, {}, { turn, humanResolved: false });
|
|
435
|
-
return toProjection(result.state);
|
|
436
|
-
}
|
|
437
|
-
incrementTurn(sessionId) {
|
|
438
|
-
const current = this.getState(sessionId);
|
|
439
|
-
const result = this.transition(sessionId, "turn_incremented", {}, {
|
|
440
|
-
turn: current.turn + 1,
|
|
441
|
-
humanResolved: false
|
|
442
|
-
});
|
|
443
|
-
return toProjection(result.state);
|
|
444
|
-
}
|
|
445
|
-
advanceTurnToIdle(sessionId) {
|
|
446
|
-
const current = this.getState(sessionId);
|
|
447
|
-
const incremented = this.transition(sessionId, "turn_incremented", {}, {
|
|
448
|
-
turn: current.turn + 1,
|
|
449
|
-
humanResolved: false
|
|
450
|
-
});
|
|
451
|
-
const baseState = incremented.success ? incremented.state : current;
|
|
452
|
-
const settled = this.transition(
|
|
453
|
-
sessionId,
|
|
454
|
-
"turn_completed",
|
|
455
|
-
{ lifecycle: "active", execution: "idle", gate: null },
|
|
456
|
-
{
|
|
457
|
-
displayState: "idle",
|
|
458
|
-
stateSource: "formal",
|
|
459
|
-
confidence: "high",
|
|
460
|
-
phase: getStateLabel("idle"),
|
|
461
|
-
humanResolved: false,
|
|
462
|
-
turn: baseState.turn
|
|
463
|
-
}
|
|
464
|
-
);
|
|
465
|
-
return toProjection(settled.success ? settled.state : baseState);
|
|
466
|
-
}
|
|
467
|
-
applyPlatformEvent(event) {
|
|
468
|
-
const current = this.getState(event.sessionId);
|
|
469
|
-
const mapped = mapEventToTransition(event, current, {
|
|
470
|
-
shouldTransition: this.shouldTransition.bind(this),
|
|
471
|
-
isSessionIdleTransitionAllowed: this.isSessionIdleTransitionAllowed.bind(this)
|
|
472
|
-
});
|
|
473
|
-
if (!mapped) return toProjection(current);
|
|
474
|
-
const mappedState = PLATFORM_EVENT_TO_STATE[event.type];
|
|
475
|
-
const shouldResetCompletedTimer = event.type === "session_started" || event.type === "session_ended" || event.type !== "completed" && mappedState !== "completed" && current.displayState === "completed";
|
|
476
|
-
if (shouldResetCompletedTimer) {
|
|
477
|
-
this.clearCompletedTimer(event.sessionId);
|
|
478
|
-
}
|
|
479
|
-
const result = this.transition(
|
|
480
|
-
event.sessionId,
|
|
481
|
-
event.type,
|
|
482
|
-
mapped.updates,
|
|
483
|
-
mapped.metadata
|
|
484
|
-
);
|
|
485
|
-
if (!result.success) {
|
|
486
|
-
return toProjection(current);
|
|
487
|
-
}
|
|
488
|
-
if (mappedState === "completed") {
|
|
489
|
-
this.clearCompletedTimer(event.sessionId);
|
|
490
|
-
const timerToken = ++this.completedTimerSequence;
|
|
491
|
-
const completedTurn = result.state.turn;
|
|
492
|
-
const timer = setTimeout(() => {
|
|
493
|
-
this.applyPlatformEvent({
|
|
494
|
-
type: "session_idle",
|
|
495
|
-
sessionId: event.sessionId,
|
|
496
|
-
source: event.source,
|
|
497
|
-
stateSource: "formal",
|
|
498
|
-
confidence: "high",
|
|
499
|
-
timestamp: Date.now(),
|
|
500
|
-
metadata: {
|
|
501
|
-
phase: "\u5F85\u547D",
|
|
502
|
-
idleTimerToken: timerToken,
|
|
503
|
-
turn: completedTurn
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
this.clearCompletedTimer(event.sessionId);
|
|
507
|
-
}, 3e3);
|
|
508
|
-
this.completedTimers.set(event.sessionId, timer);
|
|
509
|
-
this.completedTimerTokens.set(event.sessionId, timerToken);
|
|
510
|
-
}
|
|
511
|
-
return toProjection(result.state);
|
|
512
|
-
}
|
|
513
|
-
clearSession(sessionId) {
|
|
514
|
-
this.sessions.delete(sessionId);
|
|
515
|
-
this.transitionHistory.delete(sessionId);
|
|
516
|
-
this.clearCompletedTimer(sessionId);
|
|
517
|
-
}
|
|
518
|
-
getSessionCount() {
|
|
519
|
-
return this.sessions.size;
|
|
520
|
-
}
|
|
521
|
-
createDefaultState() {
|
|
522
|
-
return {
|
|
523
|
-
lifecycle: "initializing",
|
|
524
|
-
execution: null,
|
|
525
|
-
gate: null,
|
|
526
|
-
displayState: "idle",
|
|
527
|
-
stateSource: "formal",
|
|
528
|
-
confidence: "high",
|
|
529
|
-
updatedAt: Date.now(),
|
|
530
|
-
turn: 0,
|
|
531
|
-
phase: STATE_LABELS.idle,
|
|
532
|
-
humanResolved: false
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
isSameState(a, b) {
|
|
536
|
-
return a.lifecycle === b.lifecycle && a.execution === b.execution && a.gate === b.gate && a.displayState === b.displayState && a.stateSource === b.stateSource && a.confidence === b.confidence && a.updatedAt === b.updatedAt && a.turn === b.turn && a.phase === b.phase && a.humanResolved === b.humanResolved;
|
|
537
|
-
}
|
|
538
|
-
clearCompletedTimer(sessionId) {
|
|
539
|
-
const completedTimer = this.completedTimers.get(sessionId);
|
|
540
|
-
if (completedTimer) {
|
|
541
|
-
clearTimeout(completedTimer);
|
|
542
|
-
this.completedTimers.delete(sessionId);
|
|
543
|
-
}
|
|
544
|
-
this.completedTimerTokens.delete(sessionId);
|
|
545
|
-
}
|
|
546
|
-
isSessionIdleTransitionAllowed(event, current) {
|
|
547
|
-
if (current.displayState === "completed") {
|
|
548
|
-
return true;
|
|
549
|
-
}
|
|
550
|
-
const idleTimerToken = this.readNumericMetadata(event.metadata, "idleTimerToken");
|
|
551
|
-
if (idleTimerToken === void 0) {
|
|
552
|
-
return false;
|
|
553
|
-
}
|
|
554
|
-
const activeTimerToken = this.completedTimerTokens.get(event.sessionId);
|
|
555
|
-
if (activeTimerToken !== idleTimerToken) {
|
|
556
|
-
return false;
|
|
557
|
-
}
|
|
558
|
-
const timerTurn = this.readNumericMetadata(event.metadata, "turn");
|
|
559
|
-
if (timerTurn !== void 0 && timerTurn !== current.turn) {
|
|
560
|
-
return false;
|
|
561
|
-
}
|
|
562
|
-
return true;
|
|
563
|
-
}
|
|
564
|
-
readNumericMetadata(metadata, field) {
|
|
565
|
-
const value = metadata?.[field];
|
|
566
|
-
return typeof value === "number" ? value : void 0;
|
|
567
|
-
}
|
|
568
|
-
};
|
|
569
|
-
var stateMachine = new StateMachine();
|
|
570
|
-
|
|
571
|
-
// ../state/src/event-normalizer.ts
|
|
572
|
-
var CLAUDE_EVENT_MAP = {
|
|
573
|
-
session_init: "idle",
|
|
574
|
-
text_delta: "thinking",
|
|
575
|
-
tool_start: "working",
|
|
576
|
-
tool_result: "working",
|
|
577
|
-
ask_user: "awaiting_human",
|
|
578
|
-
result: "completed",
|
|
579
|
-
error: "error"
|
|
580
|
-
};
|
|
581
|
-
function normalizeClaudeEvent(event, sessionId) {
|
|
582
|
-
const state = CLAUDE_EVENT_MAP[event.type];
|
|
583
|
-
if (!state) return null;
|
|
584
|
-
const platformType = mapToPlatformType(event.type);
|
|
585
|
-
if (!platformType) return null;
|
|
586
|
-
return {
|
|
587
|
-
type: platformType,
|
|
588
|
-
sessionId,
|
|
589
|
-
source: "claude",
|
|
590
|
-
stateSource: "formal",
|
|
591
|
-
confidence: "high",
|
|
592
|
-
metadata: event,
|
|
593
|
-
timestamp: Date.now()
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
function mapToPlatformType(eventType) {
|
|
597
|
-
const mapping = {
|
|
598
|
-
session_init: "session_started",
|
|
599
|
-
text_delta: "thinking_started",
|
|
600
|
-
tool_start: "work_started",
|
|
601
|
-
ask_user: "awaiting_human",
|
|
602
|
-
result: "completed",
|
|
603
|
-
error: "errored"
|
|
604
|
-
};
|
|
605
|
-
return mapping[eventType] || null;
|
|
606
|
-
}
|
|
607
|
-
function normalizeCodexEvent(eventKey, sessionId, extra) {
|
|
608
|
-
const byEvent = mapCodexToPlatformType(eventKey);
|
|
609
|
-
const byState = mapCodexStateToPlatformType(extra.observedState);
|
|
610
|
-
const preferStateOverride = extra.observedState === "codex-permission";
|
|
611
|
-
const platformType = preferStateOverride ? byState ?? byEvent : byEvent ?? byState;
|
|
612
|
-
if (!platformType) return null;
|
|
613
|
-
const isPermissionEvent = eventKey === "codex-permission" || extra.observedState === "codex-permission";
|
|
614
|
-
const inferredFromState = Boolean(byState && !byEvent);
|
|
615
|
-
const stateSource = isPermissionEvent || inferredFromState ? "inferred" : "formal";
|
|
616
|
-
return {
|
|
617
|
-
type: platformType,
|
|
618
|
-
sessionId,
|
|
619
|
-
source: "codex",
|
|
620
|
-
stateSource,
|
|
621
|
-
confidence: isPermissionEvent ? "medium" : "high",
|
|
622
|
-
metadata: { eventKey, ...extra },
|
|
623
|
-
timestamp: Date.now()
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
|
-
function mapCodexToPlatformType(eventKey) {
|
|
627
|
-
const mapping = {
|
|
628
|
-
"session_meta": "session_started",
|
|
629
|
-
"event_msg:task_started": "thinking_started",
|
|
630
|
-
"event_msg:user_message": "thinking_started",
|
|
631
|
-
"response_item:function_call": "work_started",
|
|
632
|
-
"response_item:custom_tool_call": "work_started",
|
|
633
|
-
"response_item:web_search_call": "work_started",
|
|
634
|
-
"event_msg:exec_command_start": "work_started",
|
|
635
|
-
"codex-permission": "awaiting_human",
|
|
636
|
-
"event_msg:task_complete": "completed",
|
|
637
|
-
"event_msg:context_compacted": "compaction_started",
|
|
638
|
-
"event_msg:turn_aborted": "session_idle",
|
|
639
|
-
"codex-turn-end": "completed",
|
|
640
|
-
"event_msg:error": "errored",
|
|
641
|
-
"stale-cleanup": "session_ended"
|
|
642
|
-
};
|
|
643
|
-
return mapping[eventKey] || null;
|
|
644
|
-
}
|
|
645
|
-
function mapCodexStateToPlatformType(state) {
|
|
646
|
-
if (!state) return null;
|
|
647
|
-
const mapping = {
|
|
648
|
-
thinking: "thinking_started",
|
|
649
|
-
working: "work_started",
|
|
650
|
-
sweeping: "compaction_started",
|
|
651
|
-
"codex-permission": "awaiting_human",
|
|
652
|
-
attention: "completed",
|
|
653
|
-
idle: "session_idle",
|
|
654
|
-
error: "errored",
|
|
655
|
-
sleeping: "session_ended"
|
|
656
|
-
};
|
|
657
|
-
return mapping[state] ?? null;
|
|
658
|
-
}
|
|
659
|
-
function isPlatformEvent(input) {
|
|
660
|
-
if (!input || typeof input !== "object") return false;
|
|
661
|
-
const obj = input;
|
|
662
|
-
if (typeof obj.type !== "string") return false;
|
|
663
|
-
if (typeof obj.sessionId !== "string") return false;
|
|
664
|
-
if (obj.source !== "claude" && obj.source !== "codex") return false;
|
|
665
|
-
if (obj.confidence !== "high" && obj.confidence !== "medium" && obj.confidence !== "low") {
|
|
666
|
-
return false;
|
|
667
|
-
}
|
|
668
|
-
return typeof obj.timestamp === "number";
|
|
669
|
-
}
|
|
670
|
-
function toPlatformEvent(event, sessionId, source = "claude") {
|
|
671
|
-
if (isPlatformEvent(event)) {
|
|
672
|
-
return {
|
|
673
|
-
...event,
|
|
674
|
-
sessionId: event.sessionId || sessionId,
|
|
675
|
-
source: event.source || source,
|
|
676
|
-
stateSource: event.stateSource ?? "formal",
|
|
677
|
-
timestamp: event.timestamp || Date.now()
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
if (source === "codex") {
|
|
681
|
-
return null;
|
|
682
|
-
}
|
|
683
|
-
return normalizeClaudeEvent(event, sessionId);
|
|
684
|
-
}
|
|
685
|
-
function mapPlatformEventToState(type) {
|
|
686
|
-
return PLATFORM_EVENT_TO_STATE[type] ?? null;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// ../state/src/human-gate.ts
|
|
690
|
-
var HumanGateRegistry = class {
|
|
691
|
-
gates = /* @__PURE__ */ new Map();
|
|
692
|
-
store;
|
|
693
|
-
constructor() {
|
|
694
|
-
this.store = new Store("gates.json");
|
|
695
|
-
}
|
|
696
|
-
/** Load persisted gates from disk. Call during startup before using the registry. */
|
|
697
|
-
async init() {
|
|
698
|
-
try {
|
|
699
|
-
const data = await this.store.read();
|
|
700
|
-
if (data && Array.isArray(data)) {
|
|
701
|
-
for (const record of data) {
|
|
702
|
-
this.gates.set(record.id, record);
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
} catch (err) {
|
|
706
|
-
console.error("[HumanGateRegistry] Failed to load persisted gates:", err);
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
async _save() {
|
|
710
|
-
return this.store.write(Array.from(this.gates.values())).catch((err) => {
|
|
711
|
-
console.error("[HumanGateRegistry] Failed to persist gates:", err);
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
// 创建新门控
|
|
715
|
-
create(params) {
|
|
716
|
-
const id = this.generateId();
|
|
717
|
-
const record = {
|
|
718
|
-
...params,
|
|
719
|
-
id,
|
|
720
|
-
version: 1,
|
|
721
|
-
createdAt: Date.now(),
|
|
722
|
-
status: "pending"
|
|
723
|
-
};
|
|
724
|
-
this.gates.set(id, record);
|
|
725
|
-
this._save();
|
|
726
|
-
return record;
|
|
727
|
-
}
|
|
728
|
-
// 获取门控记录
|
|
729
|
-
get(id) {
|
|
730
|
-
return this.gates.get(id);
|
|
731
|
-
}
|
|
732
|
-
// 获取会话的所有门控
|
|
733
|
-
getBySession(sessionId) {
|
|
734
|
-
return Array.from(this.gates.values()).filter((g) => g.sessionId === sessionId);
|
|
735
|
-
}
|
|
736
|
-
// 获取会话的活跃门控(pending 状态)
|
|
737
|
-
getActiveBySession(sessionId) {
|
|
738
|
-
return this.getBySession(sessionId).filter((g) => g.status === "pending");
|
|
739
|
-
}
|
|
740
|
-
// CAS 更新:使用乐观锁保证原子性
|
|
741
|
-
update(id, expectedVersion, updates) {
|
|
742
|
-
const current = this.gates.get(id);
|
|
743
|
-
if (!current) {
|
|
744
|
-
return {
|
|
745
|
-
success: false,
|
|
746
|
-
error: "not_found",
|
|
747
|
-
message: `Gate ${id} not found`
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
if (current.version !== expectedVersion) {
|
|
751
|
-
return {
|
|
752
|
-
success: false,
|
|
753
|
-
error: "version_conflict",
|
|
754
|
-
message: `Version conflict: expected ${expectedVersion}, got ${current.version}`
|
|
755
|
-
};
|
|
756
|
-
}
|
|
757
|
-
if (updates.status && !this.isValidTransition(current.status, updates.status)) {
|
|
758
|
-
return {
|
|
759
|
-
success: false,
|
|
760
|
-
error: "invalid_transition",
|
|
761
|
-
message: `Invalid transition: ${current.status} -> ${updates.status}`
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
const updated = {
|
|
765
|
-
...current,
|
|
766
|
-
...updates,
|
|
767
|
-
version: current.version + 1
|
|
768
|
-
};
|
|
769
|
-
this.gates.set(id, updated);
|
|
770
|
-
this._save();
|
|
771
|
-
return {
|
|
772
|
-
success: true,
|
|
773
|
-
record: updated
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
// 批量失效门控(用于重启后清理)
|
|
777
|
-
invalidateAll(reason = "restart") {
|
|
778
|
-
let count = 0;
|
|
779
|
-
for (const [id, gate] of this.gates.entries()) {
|
|
780
|
-
if (gate.status === "pending") {
|
|
781
|
-
const updated = {
|
|
782
|
-
...gate,
|
|
783
|
-
status: "invalidated",
|
|
784
|
-
resolvedAt: Date.now(),
|
|
785
|
-
resolvedBy: reason,
|
|
786
|
-
version: gate.version + 1
|
|
787
|
-
};
|
|
788
|
-
this.gates.set(id, updated);
|
|
789
|
-
count++;
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
this._save();
|
|
793
|
-
return count;
|
|
794
|
-
}
|
|
795
|
-
// 删除门控记录
|
|
796
|
-
delete(id) {
|
|
797
|
-
const result = this.gates.delete(id);
|
|
798
|
-
if (result) this._save();
|
|
799
|
-
return result;
|
|
800
|
-
}
|
|
801
|
-
// 清理过期门控(超过指定时间未解决)
|
|
802
|
-
cleanupExpired(maxAgeMs = 5 * 60 * 1e3) {
|
|
803
|
-
const now = Date.now();
|
|
804
|
-
let count = 0;
|
|
805
|
-
for (const [id, gate] of this.gates.entries()) {
|
|
806
|
-
if (gate.status === "pending" && now - gate.createdAt >= maxAgeMs) {
|
|
807
|
-
const updated = {
|
|
808
|
-
...gate,
|
|
809
|
-
status: "expired",
|
|
810
|
-
resolvedAt: now,
|
|
811
|
-
resolvedBy: "timeout",
|
|
812
|
-
version: gate.version + 1
|
|
813
|
-
};
|
|
814
|
-
this.gates.set(id, updated);
|
|
815
|
-
count++;
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
this._save();
|
|
819
|
-
return count;
|
|
820
|
-
}
|
|
821
|
-
// 归档已解决的门控(保留最近 N 条)
|
|
822
|
-
archiveResolved(keepCount = 100) {
|
|
823
|
-
const resolved = Array.from(this.gates.values()).filter((g) => g.status !== "pending").sort((a, b) => (b.resolvedAt || 0) - (a.resolvedAt || 0));
|
|
824
|
-
if (resolved.length <= keepCount) {
|
|
825
|
-
return 0;
|
|
826
|
-
}
|
|
827
|
-
const toArchive = resolved.slice(keepCount);
|
|
828
|
-
for (const gate of toArchive) {
|
|
829
|
-
this.gates.delete(gate.id);
|
|
830
|
-
}
|
|
831
|
-
this._save();
|
|
832
|
-
return toArchive.length;
|
|
833
|
-
}
|
|
834
|
-
// 获取所有门控(用于调试)
|
|
835
|
-
getAll() {
|
|
836
|
-
return Array.from(this.gates.values());
|
|
837
|
-
}
|
|
838
|
-
// 获取统计信息
|
|
839
|
-
getStats() {
|
|
840
|
-
const all = this.getAll();
|
|
841
|
-
return {
|
|
842
|
-
total: all.length,
|
|
843
|
-
pending: all.filter((g) => g.status === "pending").length,
|
|
844
|
-
approved: all.filter((g) => g.status === "approved").length,
|
|
845
|
-
rejected: all.filter((g) => g.status === "rejected").length,
|
|
846
|
-
expired: all.filter((g) => g.status === "expired").length,
|
|
847
|
-
invalidated: all.filter((g) => g.status === "invalidated").length
|
|
848
|
-
};
|
|
849
|
-
}
|
|
850
|
-
// 验证状态转换合法性
|
|
851
|
-
isValidTransition(from, to) {
|
|
852
|
-
const validTransitions = {
|
|
853
|
-
pending: ["approved", "rejected", "expired", "invalidated"],
|
|
854
|
-
approved: [],
|
|
855
|
-
// 终态
|
|
856
|
-
rejected: [],
|
|
857
|
-
// 终态
|
|
858
|
-
expired: [],
|
|
859
|
-
// 终态
|
|
860
|
-
invalidated: []
|
|
861
|
-
// 终态
|
|
862
|
-
};
|
|
863
|
-
return validTransitions[from]?.includes(to) ?? false;
|
|
864
|
-
}
|
|
865
|
-
// 生成唯一 ID
|
|
866
|
-
generateId() {
|
|
867
|
-
return `gate_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
868
|
-
}
|
|
869
|
-
};
|
|
870
|
-
var humanGateRegistry = new HumanGateRegistry();
|
|
871
|
-
|
|
872
|
-
// ../state/src/gate-coordinator.ts
|
|
873
|
-
var GateCoordinator = class {
|
|
874
|
-
registry;
|
|
875
|
-
receiptHandles = /* @__PURE__ */ new Map();
|
|
876
|
-
timeoutTimers = /* @__PURE__ */ new Map();
|
|
877
|
-
constructor(registry) {
|
|
878
|
-
this.registry = registry;
|
|
879
|
-
}
|
|
880
|
-
/**
|
|
881
|
-
* 创建新的人工门控
|
|
882
|
-
*/
|
|
883
|
-
createGate(params) {
|
|
884
|
-
const record = this.registry.create(params);
|
|
885
|
-
if (record.supportsRemoteDecision && record.isBlocking) {
|
|
886
|
-
this.setupTimeout(record.id);
|
|
887
|
-
}
|
|
888
|
-
return record;
|
|
889
|
-
}
|
|
890
|
-
/**
|
|
891
|
-
* 注册回执句柄(仅内存,不持久化)
|
|
892
|
-
*/
|
|
893
|
-
registerReceiptHandle(gateId, handle) {
|
|
894
|
-
const record = this.registry.get(gateId);
|
|
895
|
-
if (!record || record.status !== "pending") {
|
|
896
|
-
handle.reject("\u95E8\u63A7\u4E0D\u5B58\u5728\u6216\u5DF2\u5904\u7406");
|
|
897
|
-
return;
|
|
898
|
-
}
|
|
899
|
-
this.receiptHandles.set(gateId, { ...handle, gateId });
|
|
900
|
-
}
|
|
901
|
-
/**
|
|
902
|
-
* 绑定 Discord 交互卡消息 ID
|
|
903
|
-
*/
|
|
904
|
-
bindDiscordMessage(gateId, discordMessageId) {
|
|
905
|
-
const record = this.registry.get(gateId);
|
|
906
|
-
if (!record) {
|
|
907
|
-
return false;
|
|
908
|
-
}
|
|
909
|
-
const result = this.registry.update(gateId, record.version, { discordMessageId });
|
|
910
|
-
return result.success;
|
|
911
|
-
}
|
|
912
|
-
/**
|
|
913
|
-
* 尝试通过 Discord 解决门控(CAS 更新)
|
|
914
|
-
*/
|
|
915
|
-
async resolveFromDiscord(gateId, action) {
|
|
916
|
-
const record = this.registry.get(gateId);
|
|
917
|
-
if (!record) {
|
|
918
|
-
return { success: false, message: "\u95E8\u63A7\u4E0D\u5B58\u5728", handledByReceipt: false };
|
|
919
|
-
}
|
|
920
|
-
if (record.status !== "pending") {
|
|
921
|
-
return { success: false, message: "\u95E8\u63A7\u5DF2\u88AB\u5904\u7406", handledByReceipt: false };
|
|
922
|
-
}
|
|
923
|
-
const result = this.registry.update(record.id, record.version, {
|
|
924
|
-
status: action === "approve" ? "approved" : "rejected",
|
|
925
|
-
resolvedAt: Date.now(),
|
|
926
|
-
resolvedBy: "discord",
|
|
927
|
-
resolvedAction: action
|
|
928
|
-
});
|
|
929
|
-
if (!result.success) {
|
|
930
|
-
return { success: false, message: result.message, handledByReceipt: false };
|
|
931
|
-
}
|
|
932
|
-
this.clearTimeout(gateId);
|
|
933
|
-
const handle = this.receiptHandles.get(gateId);
|
|
934
|
-
let handledByReceipt = false;
|
|
935
|
-
if (handle) {
|
|
936
|
-
handle.resolve(action, "discord");
|
|
937
|
-
this.receiptHandles.delete(gateId);
|
|
938
|
-
handledByReceipt = true;
|
|
939
|
-
}
|
|
940
|
-
return { success: true, handledByReceipt };
|
|
941
|
-
}
|
|
942
|
-
/**
|
|
943
|
-
* 通知终端已处理(由钩子调用)
|
|
944
|
-
*/
|
|
945
|
-
notifyTerminalResolved(gateId, action) {
|
|
946
|
-
const record = this.registry.get(gateId);
|
|
947
|
-
if (!record) {
|
|
948
|
-
return { success: false, message: "\u95E8\u63A7\u4E0D\u5B58\u5728", handledByReceipt: false };
|
|
949
|
-
}
|
|
950
|
-
if (record.status !== "pending") {
|
|
951
|
-
return { success: false, message: "\u95E8\u63A7\u5DF2\u88AB\u5904\u7406", handledByReceipt: false };
|
|
952
|
-
}
|
|
953
|
-
const result = this.registry.update(record.id, record.version, {
|
|
954
|
-
status: action === "approve" ? "approved" : "rejected",
|
|
955
|
-
resolvedAt: Date.now(),
|
|
956
|
-
resolvedBy: "terminal",
|
|
957
|
-
resolvedAction: action
|
|
958
|
-
});
|
|
959
|
-
if (!result.success) {
|
|
960
|
-
return { success: false, message: result.message, handledByReceipt: false };
|
|
961
|
-
}
|
|
962
|
-
this.clearTimeout(gateId);
|
|
963
|
-
const handle = this.receiptHandles.get(gateId);
|
|
964
|
-
let handledByReceipt = false;
|
|
965
|
-
if (handle) {
|
|
966
|
-
handle.resolve(action, "terminal");
|
|
967
|
-
this.receiptHandles.delete(gateId);
|
|
968
|
-
handledByReceipt = true;
|
|
969
|
-
}
|
|
970
|
-
return { success: true, handledByReceipt };
|
|
971
|
-
}
|
|
972
|
-
/**
|
|
973
|
-
* 设置超时定时器(5 分钟)
|
|
974
|
-
*/
|
|
975
|
-
setupTimeout(gateId) {
|
|
976
|
-
const timer = setTimeout(() => {
|
|
977
|
-
this.handleTimeout(gateId);
|
|
978
|
-
}, 5 * 60 * 1e3);
|
|
979
|
-
this.timeoutTimers.set(gateId, timer);
|
|
980
|
-
}
|
|
981
|
-
/**
|
|
982
|
-
* 清理超时定时器
|
|
983
|
-
*/
|
|
984
|
-
clearTimeout(gateId) {
|
|
985
|
-
const timer = this.timeoutTimers.get(gateId);
|
|
986
|
-
if (timer) {
|
|
987
|
-
clearTimeout(timer);
|
|
988
|
-
this.timeoutTimers.delete(gateId);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
/**
|
|
992
|
-
* 处理超时
|
|
993
|
-
*/
|
|
994
|
-
handleTimeout(gateId) {
|
|
995
|
-
const record = this.registry.get(gateId);
|
|
996
|
-
if (!record || record.status !== "pending") {
|
|
997
|
-
return;
|
|
998
|
-
}
|
|
999
|
-
this.registry.update(record.id, record.version, {
|
|
1000
|
-
status: "expired",
|
|
1001
|
-
resolvedAt: Date.now(),
|
|
1002
|
-
resolvedBy: "timeout"
|
|
1003
|
-
});
|
|
1004
|
-
const handle = this.receiptHandles.get(gateId);
|
|
1005
|
-
if (handle) {
|
|
1006
|
-
handle.reject("\u5BA1\u6279\u8D85\u65F6\uFF085 \u5206\u949F\uFF09");
|
|
1007
|
-
this.receiptHandles.delete(gateId);
|
|
1008
|
-
}
|
|
1009
|
-
this.timeoutTimers.delete(gateId);
|
|
1010
|
-
}
|
|
1011
|
-
/**
|
|
1012
|
-
* 重启时失效所有待处理门控
|
|
1013
|
-
*/
|
|
1014
|
-
invalidateAllOnRestart() {
|
|
1015
|
-
const count = this.registry.invalidateAll("restart");
|
|
1016
|
-
console.log(`[GateCoordinator] Invalidated ${count} pending gates on restart`);
|
|
1017
|
-
const toUpdate = [];
|
|
1018
|
-
for (const gate of this.registry.getAll()) {
|
|
1019
|
-
if (gate.status === "invalidated" && gate.discordMessageId) {
|
|
1020
|
-
toUpdate.push({
|
|
1021
|
-
gateId: gate.id,
|
|
1022
|
-
discordMessageId: gate.discordMessageId
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
this.receiptHandles.clear();
|
|
1027
|
-
for (const timer of this.timeoutTimers.values()) {
|
|
1028
|
-
clearTimeout(timer);
|
|
1029
|
-
}
|
|
1030
|
-
this.timeoutTimers.clear();
|
|
1031
|
-
return toUpdate;
|
|
1032
|
-
}
|
|
1033
|
-
/**
|
|
1034
|
-
* 获取门控记录
|
|
1035
|
-
*/
|
|
1036
|
-
getGate(gateId) {
|
|
1037
|
-
return this.registry.get(gateId);
|
|
1038
|
-
}
|
|
1039
|
-
/**
|
|
1040
|
-
* 获取会话的活跃门控
|
|
1041
|
-
*/
|
|
1042
|
-
getActiveGateForSession(sessionId) {
|
|
1043
|
-
const active = this.registry.getActiveBySession(sessionId);
|
|
1044
|
-
return active[0];
|
|
1045
|
-
}
|
|
1046
|
-
/**
|
|
1047
|
-
* 清理过期门控
|
|
1048
|
-
*/
|
|
1049
|
-
cleanupExpired() {
|
|
1050
|
-
return this.registry.cleanupExpired();
|
|
1051
|
-
}
|
|
1052
|
-
/**
|
|
1053
|
-
* 归档已解决门控,保留最近 N 条
|
|
1054
|
-
*/
|
|
1055
|
-
archiveResolved(keepCount = 100) {
|
|
1056
|
-
return this.registry.archiveResolved(keepCount);
|
|
1057
|
-
}
|
|
1058
|
-
};
|
|
1059
|
-
var gateCoordinator = new GateCoordinator(humanGateRegistry);
|
|
1060
|
-
|
|
1061
|
-
// ../bot/src/discord/status-card.ts
|
|
1062
80
|
var StatusCard = class {
|
|
1063
81
|
messageId = null;
|
|
1064
82
|
channel;
|
|
@@ -1149,6 +167,15 @@ var StatusCard = class {
|
|
|
1149
167
|
embed.addFields({ name: "\u9636\u6BB5", value: sanitizedPhase, inline: true });
|
|
1150
168
|
}
|
|
1151
169
|
}
|
|
170
|
+
if (data.verbose !== void 0) {
|
|
171
|
+
embed.addFields({ name: "\u8F93\u51FA", value: data.verbose ? "\u{1F50A} \u8BE6\u7EC6" : "\u{1F507} \u7CBE\u7B80", inline: true });
|
|
172
|
+
}
|
|
173
|
+
if (data.monitorGoal) {
|
|
174
|
+
embed.addFields({ name: "\u76D1\u63A7\u76EE\u6807", value: truncate(data.monitorGoal, 150) });
|
|
175
|
+
}
|
|
176
|
+
if (data.monitorIteration !== void 0 && data.maxMonitorIterations !== void 0) {
|
|
177
|
+
embed.addFields({ name: "\u8FED\u4EE3", value: `${data.monitorIteration}/${data.maxMonitorIterations}`, inline: true });
|
|
178
|
+
}
|
|
1152
179
|
if (data.permissionsSummary) {
|
|
1153
180
|
embed.addFields({ name: "\u6743\u9650", value: data.permissionsSummary, inline: false });
|
|
1154
181
|
}
|
|
@@ -1295,6 +322,34 @@ async function sendAckReaction(message, reaction) {
|
|
|
1295
322
|
} catch {
|
|
1296
323
|
}
|
|
1297
324
|
}
|
|
325
|
+
var SEND_MAX_RETRIES = 3;
|
|
326
|
+
function isDiscordRateLimitError(err) {
|
|
327
|
+
if (!err || typeof err !== "object") return false;
|
|
328
|
+
const e = err;
|
|
329
|
+
return e.code === 429 || e.status === 429 || e.httpStatus === 429;
|
|
330
|
+
}
|
|
331
|
+
function sleep(ms) {
|
|
332
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
333
|
+
}
|
|
334
|
+
async function sendWithBackoff(channel, payload) {
|
|
335
|
+
let attempt = 0;
|
|
336
|
+
while (true) {
|
|
337
|
+
try {
|
|
338
|
+
const message = await channel.send(payload);
|
|
339
|
+
return message.id;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
if (!isDiscordRateLimitError(error) || attempt >= SEND_MAX_RETRIES) {
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
const delayMs = 250 * 2 ** attempt;
|
|
345
|
+
console.warn(
|
|
346
|
+
`[Delivery] Discord rate-limited (attempt ${attempt + 1}/${SEND_MAX_RETRIES}), retrying in ${delayMs}ms`
|
|
347
|
+
);
|
|
348
|
+
await sleep(delayMs);
|
|
349
|
+
attempt++;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
1298
353
|
async function sendChunk(channel, chunk, options = {}) {
|
|
1299
354
|
const payload = { content: chunk };
|
|
1300
355
|
if (options.files?.length) payload.files = options.files;
|
|
@@ -1302,16 +357,14 @@ async function sendChunk(channel, chunk, options = {}) {
|
|
|
1302
357
|
payload.reply = { messageReference: options.replyToMessageId };
|
|
1303
358
|
}
|
|
1304
359
|
try {
|
|
1305
|
-
|
|
1306
|
-
return message.id;
|
|
360
|
+
return await sendWithBackoff(channel, payload);
|
|
1307
361
|
} catch (error) {
|
|
1308
362
|
if (!options.replyToMessageId) {
|
|
1309
363
|
throw error;
|
|
1310
364
|
}
|
|
1311
365
|
const fallbackPayload = { content: chunk };
|
|
1312
366
|
if (options.files?.length) fallbackPayload.files = options.files;
|
|
1313
|
-
|
|
1314
|
-
return fallbackMessage.id;
|
|
367
|
+
return sendWithBackoff(channel, fallbackPayload);
|
|
1315
368
|
}
|
|
1316
369
|
}
|
|
1317
370
|
async function deliver(channel, plan) {
|
|
@@ -1381,8 +434,7 @@ var DigestDelivery = class {
|
|
|
1381
434
|
nextMessageIds.push(message.id);
|
|
1382
435
|
}
|
|
1383
436
|
for (const staleId of [...replacedMessageIds, ...this.messageIds.slice(chunks.length)]) {
|
|
1384
|
-
await this.channel.messages.delete(staleId).catch(() => {
|
|
1385
|
-
});
|
|
437
|
+
await this.channel.messages.delete(staleId).catch((e) => console.warn(`[DigestDelivery] Failed to delete message: ${e.message}`));
|
|
1386
438
|
}
|
|
1387
439
|
this.messageIds = nextMessageIds;
|
|
1388
440
|
this.chunks = [...chunks];
|
|
@@ -1401,8 +453,7 @@ var DigestDelivery = class {
|
|
|
1401
453
|
}
|
|
1402
454
|
} catch (error) {
|
|
1403
455
|
for (const messageId of newMessageIds) {
|
|
1404
|
-
await this.channel.messages.delete(messageId).catch(() => {
|
|
1405
|
-
});
|
|
456
|
+
await this.channel.messages.delete(messageId).catch((e) => console.warn(`[DigestDelivery] Failed to delete message: ${e.message}`));
|
|
1406
457
|
}
|
|
1407
458
|
throw error;
|
|
1408
459
|
}
|
|
@@ -1506,6 +557,10 @@ var InteractionCard = class {
|
|
|
1506
557
|
async show(sessionId, turn, detail, options = {}) {
|
|
1507
558
|
const customIdBase = `awaiting_human:${sessionId}:${turn}`;
|
|
1508
559
|
const embed = new EmbedBuilder3().setTitle("\u23F8\uFE0F \u7B49\u5F85\u4EBA\u5DE5\u5904\u7406").setDescription(detail).setColor(16755200).setTimestamp();
|
|
560
|
+
embed.addFields(
|
|
561
|
+
{ name: "\u8F6E\u6B21", value: `#${turn}`, inline: true },
|
|
562
|
+
{ name: "\u7B49\u5F85\u81EA", value: `<t:${Math.floor(Date.now() / 1e3)}:R>`, inline: true }
|
|
563
|
+
);
|
|
1509
564
|
let components = [];
|
|
1510
565
|
if (options.remoteHumanControl !== false) {
|
|
1511
566
|
const row = new ActionRowBuilder().addComponents(
|
|
@@ -1607,7 +662,7 @@ var SessionPanelComponent = class {
|
|
|
1607
662
|
}
|
|
1608
663
|
this.digestQueue.push({ kind: item.kind, text });
|
|
1609
664
|
if (this.digestQueue.length > MAX_DIGEST_QUEUE_SIZE) {
|
|
1610
|
-
this.digestQueue
|
|
665
|
+
this.digestQueue = this.digestQueue.slice(-MAX_DIGEST_QUEUE_SIZE);
|
|
1611
666
|
}
|
|
1612
667
|
}
|
|
1613
668
|
getDigestQueue() {
|
|
@@ -1634,6 +689,97 @@ var SessionPanelComponent = class {
|
|
|
1634
689
|
}
|
|
1635
690
|
};
|
|
1636
691
|
|
|
692
|
+
// ../engine/src/session-context.ts
|
|
693
|
+
var EMPTY_PROJECTION = Object.freeze({
|
|
694
|
+
turn: 0,
|
|
695
|
+
humanResolved: false,
|
|
696
|
+
updatedAt: 0
|
|
697
|
+
});
|
|
698
|
+
function safeGetSessionController(sessionId) {
|
|
699
|
+
try {
|
|
700
|
+
const fn = getSessionController;
|
|
701
|
+
return typeof fn === "function" ? fn(sessionId) : void 0;
|
|
702
|
+
} catch {
|
|
703
|
+
return void 0;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
function safeDebouncedSaveSession() {
|
|
707
|
+
try {
|
|
708
|
+
const fn = debouncedSaveSession;
|
|
709
|
+
if (typeof fn === "function") fn();
|
|
710
|
+
} catch {
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function safeGetProjection(sessionId) {
|
|
714
|
+
try {
|
|
715
|
+
const sm = stateMachine;
|
|
716
|
+
if (sm && typeof sm.getSnapshot === "function") {
|
|
717
|
+
return sm.getSnapshot(sessionId);
|
|
718
|
+
}
|
|
719
|
+
} catch {
|
|
720
|
+
}
|
|
721
|
+
return EMPTY_PROJECTION;
|
|
722
|
+
}
|
|
723
|
+
var SessionSupervisor = class {
|
|
724
|
+
contexts = /* @__PURE__ */ new Map();
|
|
725
|
+
get(sessionId) {
|
|
726
|
+
const live = getSession(sessionId);
|
|
727
|
+
const cached = this.contexts.get(sessionId);
|
|
728
|
+
if (!live) {
|
|
729
|
+
if (cached) this.contexts.delete(sessionId);
|
|
730
|
+
return void 0;
|
|
731
|
+
}
|
|
732
|
+
if (cached) {
|
|
733
|
+
this.syncContext(cached);
|
|
734
|
+
return cached;
|
|
735
|
+
}
|
|
736
|
+
const ctx = this.build(live);
|
|
737
|
+
this.contexts.set(sessionId, ctx);
|
|
738
|
+
return ctx;
|
|
739
|
+
}
|
|
740
|
+
/** 枚举现有 context。用于 supervisor 层面的扫描(健康检查、空闲回收)。 */
|
|
741
|
+
all() {
|
|
742
|
+
return Array.from(this.contexts.values());
|
|
743
|
+
}
|
|
744
|
+
/** 释放某个 session 的 context(endSession 调用时触发)。 */
|
|
745
|
+
release(sessionId) {
|
|
746
|
+
this.contexts.delete(sessionId);
|
|
747
|
+
}
|
|
748
|
+
releaseAll() {
|
|
749
|
+
this.contexts.clear();
|
|
750
|
+
}
|
|
751
|
+
build(session) {
|
|
752
|
+
const ctx = {
|
|
753
|
+
sessionId: session.id,
|
|
754
|
+
session,
|
|
755
|
+
controller: safeGetSessionController(session.id),
|
|
756
|
+
projection: safeGetProjection(session.id),
|
|
757
|
+
save: () => safeDebouncedSaveSession(),
|
|
758
|
+
refresh: () => this.syncContext(ctx)
|
|
759
|
+
};
|
|
760
|
+
return ctx;
|
|
761
|
+
}
|
|
762
|
+
syncContext(ctx) {
|
|
763
|
+
const live = getSession(ctx.sessionId);
|
|
764
|
+
if (live) {
|
|
765
|
+
ctx.session = live;
|
|
766
|
+
}
|
|
767
|
+
ctx.controller = safeGetSessionController(
|
|
768
|
+
ctx.sessionId
|
|
769
|
+
);
|
|
770
|
+
ctx.projection = safeGetProjection(
|
|
771
|
+
ctx.sessionId
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
var sessionSupervisor = new SessionSupervisor();
|
|
776
|
+
function getSessionContext(sessionId) {
|
|
777
|
+
return sessionSupervisor.get(sessionId);
|
|
778
|
+
}
|
|
779
|
+
function getSessionView(sessionId) {
|
|
780
|
+
return sessionSupervisor.get(sessionId)?.session;
|
|
781
|
+
}
|
|
782
|
+
|
|
1637
783
|
// ../bot/src/monitoring/performance-tracker.ts
|
|
1638
784
|
import { performance } from "perf_hooks";
|
|
1639
785
|
import { memoryUsage, cpuUsage } from "process";
|
|
@@ -1778,6 +924,100 @@ var PerformanceTracker = class {
|
|
|
1778
924
|
};
|
|
1779
925
|
var performanceTracker = new PerformanceTracker();
|
|
1780
926
|
|
|
927
|
+
// ../bot/src/panel/panel-state.ts
|
|
928
|
+
var sessionPanels = /* @__PURE__ */ new Map();
|
|
929
|
+
var sessionInitializationPromises = /* @__PURE__ */ new Map();
|
|
930
|
+
function getPanel(sessionId) {
|
|
931
|
+
return sessionPanels.get(sessionId);
|
|
932
|
+
}
|
|
933
|
+
function setPanel(sessionId, panel) {
|
|
934
|
+
sessionPanels.set(sessionId, panel);
|
|
935
|
+
}
|
|
936
|
+
function deletePanel(sessionId) {
|
|
937
|
+
sessionPanels.delete(sessionId);
|
|
938
|
+
}
|
|
939
|
+
function getAllPanels() {
|
|
940
|
+
return sessionPanels.entries();
|
|
941
|
+
}
|
|
942
|
+
function getPanelCount() {
|
|
943
|
+
return sessionPanels.size;
|
|
944
|
+
}
|
|
945
|
+
function getInitializationPromise(sessionId) {
|
|
946
|
+
return sessionInitializationPromises.get(sessionId);
|
|
947
|
+
}
|
|
948
|
+
function setInitializationPromise(sessionId, promise) {
|
|
949
|
+
sessionInitializationPromises.set(sessionId, promise);
|
|
950
|
+
}
|
|
951
|
+
function deleteInitializationPromise(sessionId) {
|
|
952
|
+
sessionInitializationPromises.delete(sessionId);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// ../bot/src/panel/panel-relocation.ts
|
|
956
|
+
var RELOCATION_TIMEOUT_MS = 15e3;
|
|
957
|
+
async function relocateSessionPanelToBottom(sessionId, channel, initialize) {
|
|
958
|
+
let panel = getPanel(sessionId);
|
|
959
|
+
if (!panel && channel) {
|
|
960
|
+
const session = getSessionView(sessionId);
|
|
961
|
+
const initPromise = initialize(sessionId, channel, {
|
|
962
|
+
statusCardMessageId: session?.statusCardMessageId,
|
|
963
|
+
initialTurn: session?.currentTurn || 1
|
|
964
|
+
});
|
|
965
|
+
let timeoutHandle;
|
|
966
|
+
const timeout = new Promise((_, reject) => {
|
|
967
|
+
timeoutHandle = setTimeout(
|
|
968
|
+
() => reject(new Error("Panel initialization timeout")),
|
|
969
|
+
RELOCATION_TIMEOUT_MS
|
|
970
|
+
);
|
|
971
|
+
});
|
|
972
|
+
try {
|
|
973
|
+
await Promise.race([initPromise, timeout]);
|
|
974
|
+
} finally {
|
|
975
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
976
|
+
}
|
|
977
|
+
panel = getPanel(sessionId);
|
|
978
|
+
}
|
|
979
|
+
if (!panel) return;
|
|
980
|
+
let statusRelocation = null;
|
|
981
|
+
try {
|
|
982
|
+
statusRelocation = await panel.statusCard.recreateAtBottom();
|
|
983
|
+
} catch (error) {
|
|
984
|
+
console.warn(`\u72B6\u6001\u6D88\u606F\u8FC1\u79FB\u5931\u8D25 (${sessionId})\uFF1A`, error);
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
let digestRelocation = { oldMessageIds: [], newMessageIds: [] };
|
|
988
|
+
try {
|
|
989
|
+
digestRelocation = await panel.summaryHandler.relocateDigestToBottom();
|
|
990
|
+
} catch (error) {
|
|
991
|
+
console.warn(`\u6458\u8981\u6D88\u606F\u8FC1\u79FB\u5931\u8D25 (${sessionId})\uFF1A`, error);
|
|
992
|
+
if (statusRelocation?.oldMessageId && statusRelocation.newMessageId) {
|
|
993
|
+
panel.statusCard.adopt(statusRelocation.oldMessageId);
|
|
994
|
+
await panel.channel.messages.delete(statusRelocation.newMessageId).catch(
|
|
995
|
+
(e) => console.warn(
|
|
996
|
+
`[PanelAdapter] Failed to cleanup new status card (${sessionId}): ${e.message}`
|
|
997
|
+
)
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
if (statusRelocation?.newMessageId) {
|
|
1003
|
+
setStatusCardBinding(sessionId, { messageId: statusRelocation.newMessageId });
|
|
1004
|
+
}
|
|
1005
|
+
if (statusRelocation?.oldMessageId) {
|
|
1006
|
+
await panel.channel.messages.delete(statusRelocation.oldMessageId).catch(
|
|
1007
|
+
(e) => console.warn(
|
|
1008
|
+
`[PanelAdapter] Failed to delete old status card (${sessionId}): ${e.message}`
|
|
1009
|
+
)
|
|
1010
|
+
);
|
|
1011
|
+
}
|
|
1012
|
+
for (const messageId of digestRelocation.oldMessageIds) {
|
|
1013
|
+
await panel.channel.messages.delete(messageId).catch(
|
|
1014
|
+
(e) => console.warn(
|
|
1015
|
+
`[PanelAdapter] Failed to delete old digest (${sessionId}): ${e.message}`
|
|
1016
|
+
)
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1781
1021
|
// ../engine/src/output/answer-store.ts
|
|
1782
1022
|
var pendingAnswersStore = /* @__PURE__ */ new Map();
|
|
1783
1023
|
var questionCountStore = /* @__PURE__ */ new Map();
|
|
@@ -1851,15 +1091,82 @@ async function notifyUnmanagedCodexHint(client, sessionId, channelId) {
|
|
|
1851
1091
|
}
|
|
1852
1092
|
}
|
|
1853
1093
|
|
|
1094
|
+
// ../bot/src/panel/panel-performance.ts
|
|
1095
|
+
var SESSION_INACTIVE_TIMEOUT_MS = 36e5;
|
|
1096
|
+
function createCleanupSessionPanel(renderer) {
|
|
1097
|
+
return function cleanupSessionPanel2(sessionId) {
|
|
1098
|
+
const panel = getPanel(sessionId);
|
|
1099
|
+
if (panel) panel.cleanup();
|
|
1100
|
+
deletePanel(sessionId);
|
|
1101
|
+
renderer.clear(sessionId);
|
|
1102
|
+
stateMachine.clearSession(sessionId);
|
|
1103
|
+
clearPendingAnswers(sessionId);
|
|
1104
|
+
cleanupSessionDeliveryState(sessionId);
|
|
1105
|
+
clearCodexHint(sessionId);
|
|
1106
|
+
cleanupSessionAttachments(sessionId).catch(
|
|
1107
|
+
(e) => console.warn(
|
|
1108
|
+
`[PanelAdapter] Failed to cleanup attachments (${sessionId}): ${e.message}`
|
|
1109
|
+
)
|
|
1110
|
+
);
|
|
1111
|
+
const activeGate = gateService.getActiveGateForSession(sessionId);
|
|
1112
|
+
if (activeGate) {
|
|
1113
|
+
gateService.resolveFromDiscord(activeGate.id, "reject").catch(
|
|
1114
|
+
(e) => console.warn(
|
|
1115
|
+
`[PanelAdapter] Failed to invalidate gate on cleanup (${sessionId}): ${e.message}`
|
|
1116
|
+
)
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
function createCleanupInactiveSessions(renderer) {
|
|
1122
|
+
return function cleanupInactiveSessions2() {
|
|
1123
|
+
const now = Date.now();
|
|
1124
|
+
for (const [sessionId, panel] of getAllPanels()) {
|
|
1125
|
+
if (now - panel.getLastActivity() > SESSION_INACTIVE_TIMEOUT_MS) {
|
|
1126
|
+
panel.cleanup();
|
|
1127
|
+
deletePanel(sessionId);
|
|
1128
|
+
renderer.clear(sessionId);
|
|
1129
|
+
console.log(`\u6E05\u7406\u5931\u6D3B\u4F1A\u8BDD\u72B6\u6001\u6295\u5F71: ${sessionId}`);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
function createGetPerformanceStats() {
|
|
1135
|
+
return function getPerformanceStats2() {
|
|
1136
|
+
let projectionCount = 0;
|
|
1137
|
+
for (const [, panel] of getAllPanels()) {
|
|
1138
|
+
if (panel.getCachedProjection() !== null) projectionCount++;
|
|
1139
|
+
}
|
|
1140
|
+
return {
|
|
1141
|
+
discoveryLatency: performanceTracker.getMetricStats("session_discovery_latency"),
|
|
1142
|
+
updateLatency: performanceTracker.getMetricStats("state_update_latency"),
|
|
1143
|
+
activeSessions: getPanelCount(),
|
|
1144
|
+
projectionCount
|
|
1145
|
+
};
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
var cleanupInterval = null;
|
|
1149
|
+
function startPerformanceMonitoring(onTick) {
|
|
1150
|
+
if (cleanupInterval) return;
|
|
1151
|
+
cleanupInterval = setInterval(() => {
|
|
1152
|
+
onTick();
|
|
1153
|
+
performanceTracker.takeSnapshot();
|
|
1154
|
+
performanceTracker.cleanup();
|
|
1155
|
+
}, 6e4);
|
|
1156
|
+
}
|
|
1157
|
+
function stopPerformanceMonitoring() {
|
|
1158
|
+
if (cleanupInterval) {
|
|
1159
|
+
clearInterval(cleanupInterval);
|
|
1160
|
+
cleanupInterval = null;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
function generatePerformanceReport() {
|
|
1164
|
+
return performanceTracker.generateReport();
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1854
1167
|
// ../bot/src/panel-adapter.ts
|
|
1855
|
-
var sessionPanels = /* @__PURE__ */ new Map();
|
|
1856
|
-
var sessionInitializationPromises = /* @__PURE__ */ new Map();
|
|
1857
1168
|
var BATCH_UPDATE_DELAY_MS = 500;
|
|
1858
1169
|
var statusCardProjectionRenderer = new StatusCardProjectionRenderer();
|
|
1859
|
-
var SESSION_INACTIVE_TIMEOUT_MS = 36e5;
|
|
1860
|
-
function getPanel(sessionId) {
|
|
1861
|
-
return sessionPanels.get(sessionId);
|
|
1862
|
-
}
|
|
1863
1170
|
function getSessionProjection(sessionId) {
|
|
1864
1171
|
return stateMachine.getSnapshot(sessionId);
|
|
1865
1172
|
}
|
|
@@ -1872,25 +1179,25 @@ function ensureProjectionTurn(sessionId, turn = 1, event = "turn_bootstrap") {
|
|
|
1872
1179
|
}
|
|
1873
1180
|
function cacheProjection(sessionId, projection) {
|
|
1874
1181
|
const panel = getPanel(sessionId);
|
|
1875
|
-
if (panel)
|
|
1876
|
-
panel.updateProjection(projection);
|
|
1877
|
-
}
|
|
1182
|
+
if (panel) panel.updateProjection(projection);
|
|
1878
1183
|
}
|
|
1879
|
-
|
|
1184
|
+
stateMachine.registerTurnStatePersister((sessionId, projection) => {
|
|
1880
1185
|
updateSession(sessionId, {
|
|
1881
1186
|
currentTurn: projection.turn,
|
|
1882
1187
|
humanResolved: projection.humanResolved
|
|
1883
1188
|
});
|
|
1884
|
-
}
|
|
1189
|
+
});
|
|
1885
1190
|
function createStatusCardProjectionContext(sessionId) {
|
|
1886
1191
|
const panel = getPanel(sessionId);
|
|
1887
1192
|
if (!panel) return void 0;
|
|
1888
|
-
const session =
|
|
1193
|
+
const session = getSessionView(sessionId);
|
|
1889
1194
|
return {
|
|
1890
1195
|
statusCard: panel.statusCard,
|
|
1891
1196
|
remoteHumanControl: session?.remoteHumanControl,
|
|
1892
1197
|
provider: session?.provider,
|
|
1893
|
-
permissionsSummary: session ? getSessionPermissionSummary(session) : void 0
|
|
1198
|
+
permissionsSummary: session ? getSessionPermissionSummary(session) : void 0,
|
|
1199
|
+
verbose: session?.verbose,
|
|
1200
|
+
monitorGoal: session?.monitorGoal
|
|
1894
1201
|
};
|
|
1895
1202
|
}
|
|
1896
1203
|
async function renderProjectionToStatusCard(sessionId, projection) {
|
|
@@ -1910,17 +1217,16 @@ async function scheduleProjectionRender(sessionId, projection, updateKey) {
|
|
|
1910
1217
|
);
|
|
1911
1218
|
}
|
|
1912
1219
|
function resolveProviderSource(sessionId, fallback = "claude") {
|
|
1913
|
-
const session =
|
|
1220
|
+
const session = getSessionView(sessionId);
|
|
1914
1221
|
return session?.provider === "codex" ? "codex" : fallback;
|
|
1915
1222
|
}
|
|
1916
1223
|
async function initializeSessionPanel(sessionId, channel, options = {}) {
|
|
1917
1224
|
performanceTracker.startSessionDiscovery(sessionId);
|
|
1918
|
-
|
|
1919
|
-
if (existing) {
|
|
1225
|
+
if (getPanel(sessionId)) {
|
|
1920
1226
|
performanceTracker.endSessionDiscovery(sessionId, { cached: true });
|
|
1921
1227
|
return;
|
|
1922
1228
|
}
|
|
1923
|
-
const pendingInitialization =
|
|
1229
|
+
const pendingInitialization = getInitializationPromise(sessionId);
|
|
1924
1230
|
if (pendingInitialization) {
|
|
1925
1231
|
await pendingInitialization;
|
|
1926
1232
|
performanceTracker.endSessionDiscovery(sessionId, { cached: true });
|
|
@@ -1928,7 +1234,7 @@ async function initializeSessionPanel(sessionId, channel, options = {}) {
|
|
|
1928
1234
|
}
|
|
1929
1235
|
const initialization = (async () => {
|
|
1930
1236
|
const panel = new SessionPanelComponent(sessionId, channel);
|
|
1931
|
-
const session =
|
|
1237
|
+
const session = getSessionView(sessionId);
|
|
1932
1238
|
await panel.initialize({
|
|
1933
1239
|
statusCardMessageId: options.statusCardMessageId,
|
|
1934
1240
|
initialTurn: options.initialTurn,
|
|
@@ -1937,7 +1243,7 @@ async function initializeSessionPanel(sessionId, channel, options = {}) {
|
|
|
1937
1243
|
provider: session?.provider,
|
|
1938
1244
|
permissionsSummary: session ? getSessionPermissionSummary(session) : void 0
|
|
1939
1245
|
});
|
|
1940
|
-
|
|
1246
|
+
setPanel(sessionId, panel);
|
|
1941
1247
|
setStatusCardBinding(sessionId, {
|
|
1942
1248
|
messageId: panel.getMessageId() ?? options.statusCardMessageId
|
|
1943
1249
|
});
|
|
@@ -1948,24 +1254,22 @@ async function initializeSessionPanel(sessionId, channel, options = {}) {
|
|
|
1948
1254
|
);
|
|
1949
1255
|
cacheProjection(sessionId, projection);
|
|
1950
1256
|
})();
|
|
1951
|
-
|
|
1257
|
+
setInitializationPromise(sessionId, initialization);
|
|
1952
1258
|
try {
|
|
1953
1259
|
await initialization;
|
|
1954
1260
|
performanceTracker.endSessionDiscovery(sessionId, { cached: false });
|
|
1955
1261
|
} finally {
|
|
1956
|
-
|
|
1262
|
+
deleteInitializationPromise(sessionId);
|
|
1957
1263
|
}
|
|
1958
1264
|
}
|
|
1959
1265
|
async function registerExistingStatusCard(sessionId, channel, statusCardMessageId) {
|
|
1960
|
-
await initializeSessionPanel(sessionId, channel, {
|
|
1961
|
-
statusCardMessageId
|
|
1962
|
-
});
|
|
1266
|
+
await initializeSessionPanel(sessionId, channel, { statusCardMessageId });
|
|
1963
1267
|
}
|
|
1964
1268
|
async function updateSessionState(sessionId, event, options = {}) {
|
|
1965
1269
|
const updateKey = `${sessionId}:state`;
|
|
1966
1270
|
performanceTracker.startStateUpdate(updateKey);
|
|
1967
1271
|
if (!getPanel(sessionId) && options.channel) {
|
|
1968
|
-
const session2 =
|
|
1272
|
+
const session2 = getSessionView(sessionId);
|
|
1969
1273
|
await initializeSessionPanel(sessionId, options.channel, {
|
|
1970
1274
|
statusCardMessageId: session2?.statusCardMessageId,
|
|
1971
1275
|
initialTurn: session2?.currentTurn || 1
|
|
@@ -1980,15 +1284,20 @@ async function updateSessionState(sessionId, event, options = {}) {
|
|
|
1980
1284
|
performanceTracker.endStateUpdate(updateKey, { skipped: true });
|
|
1981
1285
|
return null;
|
|
1982
1286
|
}
|
|
1983
|
-
const session =
|
|
1287
|
+
const session = getSessionView(sessionId);
|
|
1984
1288
|
if (platformEvent.source === "codex" && platformEvent.type === "completed" && session?.isGenerating) {
|
|
1985
1289
|
performanceTracker.endStateUpdate(updateKey, { skipped: true });
|
|
1986
1290
|
return getSessionProjection(sessionId);
|
|
1987
1291
|
}
|
|
1292
|
+
const previousProjection = getSessionProjection(sessionId);
|
|
1988
1293
|
const projection = stateMachine.applyPlatformEvent(platformEvent);
|
|
1989
1294
|
cacheProjection(sessionId, projection);
|
|
1990
|
-
|
|
1991
|
-
|
|
1295
|
+
const stateChanged = projection.state !== previousProjection.state || projection.turn !== previousProjection.turn || projection.phase !== previousProjection.phase;
|
|
1296
|
+
if (stateChanged) {
|
|
1297
|
+
await scheduleProjectionRender(sessionId, projection, updateKey);
|
|
1298
|
+
} else {
|
|
1299
|
+
performanceTracker.endStateUpdate(updateKey, { skipped: true });
|
|
1300
|
+
}
|
|
1992
1301
|
return projection;
|
|
1993
1302
|
}
|
|
1994
1303
|
async function handleResultEvent(sessionId, event, textContent, attachments = []) {
|
|
@@ -2010,7 +1319,7 @@ async function handleResultEvent(sessionId, event, textContent, attachments = []
|
|
|
2010
1319
|
metadata: { from: "result" }
|
|
2011
1320
|
});
|
|
2012
1321
|
} else if (!event.success) {
|
|
2013
|
-
const session =
|
|
1322
|
+
const session = getSessionView(sessionId);
|
|
2014
1323
|
const failureText = textContent.trim() || event.errors.join("\n").trim() || "\u4EFB\u52A1\u5931\u8D25";
|
|
2015
1324
|
await panel.summaryHandler.sendTurnFailure(
|
|
2016
1325
|
failureText,
|
|
@@ -2028,7 +1337,7 @@ async function handleResultEvent(sessionId, event, textContent, attachments = []
|
|
|
2028
1337
|
});
|
|
2029
1338
|
} else {
|
|
2030
1339
|
const beforeProjection = getSessionProjection(sessionId);
|
|
2031
|
-
const session =
|
|
1340
|
+
const session = getSessionView(sessionId);
|
|
2032
1341
|
await panel.summaryHandler.sendTurnSummary(
|
|
2033
1342
|
textContent,
|
|
2034
1343
|
beforeProjection.turn,
|
|
@@ -2036,7 +1345,6 @@ async function handleResultEvent(sessionId, event, textContent, attachments = []
|
|
|
2036
1345
|
attachments
|
|
2037
1346
|
);
|
|
2038
1347
|
const projectionAfterTurn = stateMachine.advanceTurnToIdle(sessionId);
|
|
2039
|
-
persistTurnState(sessionId, projectionAfterTurn);
|
|
2040
1348
|
await renderProjectionToStatusCard(sessionId, projectionAfterTurn);
|
|
2041
1349
|
cacheProjection(sessionId, projectionAfterTurn);
|
|
2042
1350
|
}
|
|
@@ -2044,15 +1352,17 @@ async function handleResultEvent(sessionId, event, textContent, attachments = []
|
|
|
2044
1352
|
async function handleAwaitingHuman(sessionId, detail, options = {}) {
|
|
2045
1353
|
const panel = getPanel(sessionId);
|
|
2046
1354
|
if (!panel) return null;
|
|
2047
|
-
const session =
|
|
1355
|
+
const session = getSessionView(sessionId);
|
|
2048
1356
|
if (!panel.checkInteractionCooldown()) {
|
|
2049
|
-
console.warn(
|
|
1357
|
+
console.warn(
|
|
1358
|
+
`\u4EA4\u4E92\u5361\u521B\u5EFA\u9650\u6D41 (${sessionId}): \u8DDD\u4E0A\u6B21\u521B\u5EFA\u4EC5 ${panel.getTimeSinceLastInteraction()}ms`
|
|
1359
|
+
);
|
|
2050
1360
|
return null;
|
|
2051
1361
|
}
|
|
2052
1362
|
const projection = ensureProjectionTurn(sessionId, 1, "turn_bootstrap");
|
|
2053
1363
|
const provider = session?.provider ?? resolveProviderSource(sessionId);
|
|
2054
1364
|
const remoteHumanControl = session?.remoteHumanControl !== false;
|
|
2055
|
-
const gate =
|
|
1365
|
+
const gate = gateService.createGate({
|
|
2056
1366
|
sessionId,
|
|
2057
1367
|
provider,
|
|
2058
1368
|
type: "binary_approval",
|
|
@@ -2074,7 +1384,7 @@ async function handleAwaitingHuman(sessionId, detail, options = {}) {
|
|
|
2074
1384
|
remoteHumanControl,
|
|
2075
1385
|
provider
|
|
2076
1386
|
});
|
|
2077
|
-
|
|
1387
|
+
gateService.bindDiscordMessage(gate.id, messageId);
|
|
2078
1388
|
panel.recordInteractionCardTime();
|
|
2079
1389
|
cacheProjection(sessionId, getSessionProjection(sessionId));
|
|
2080
1390
|
updateSession(sessionId, {
|
|
@@ -2086,63 +1396,21 @@ async function handleAwaitingHuman(sessionId, detail, options = {}) {
|
|
|
2086
1396
|
setCurrentInteractionMessage(sessionId, messageId);
|
|
2087
1397
|
return messageId;
|
|
2088
1398
|
}
|
|
2089
|
-
async function
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
initialTurn: session?.currentTurn || 1
|
|
2096
|
-
});
|
|
2097
|
-
panel = getPanel(sessionId);
|
|
2098
|
-
}
|
|
2099
|
-
if (!panel) return;
|
|
2100
|
-
let statusRelocation = null;
|
|
2101
|
-
try {
|
|
2102
|
-
statusRelocation = await panel.statusCard.recreateAtBottom();
|
|
2103
|
-
} catch (error) {
|
|
2104
|
-
console.warn(`\u72B6\u6001\u6D88\u606F\u8FC1\u79FB\u5931\u8D25 (${sessionId})\uFF1A`, error);
|
|
2105
|
-
return;
|
|
2106
|
-
}
|
|
2107
|
-
let digestRelocation = { oldMessageIds: [], newMessageIds: [] };
|
|
2108
|
-
try {
|
|
2109
|
-
digestRelocation = await panel.summaryHandler.relocateDigestToBottom();
|
|
2110
|
-
} catch (error) {
|
|
2111
|
-
console.warn(`\u6458\u8981\u6D88\u606F\u8FC1\u79FB\u5931\u8D25 (${sessionId})\uFF1A`, error);
|
|
2112
|
-
if (statusRelocation?.oldMessageId && statusRelocation.newMessageId) {
|
|
2113
|
-
panel.statusCard.adopt(statusRelocation.oldMessageId);
|
|
2114
|
-
await panel.channel.messages.delete(statusRelocation.newMessageId).catch(() => {
|
|
2115
|
-
});
|
|
2116
|
-
}
|
|
2117
|
-
return;
|
|
2118
|
-
}
|
|
2119
|
-
if (statusRelocation?.newMessageId) {
|
|
2120
|
-
setStatusCardBinding(sessionId, {
|
|
2121
|
-
messageId: statusRelocation.newMessageId
|
|
2122
|
-
});
|
|
2123
|
-
}
|
|
2124
|
-
if (statusRelocation?.oldMessageId) {
|
|
2125
|
-
await panel.channel.messages.delete(statusRelocation.oldMessageId).catch(() => {
|
|
2126
|
-
});
|
|
2127
|
-
}
|
|
2128
|
-
for (const messageId of digestRelocation.oldMessageIds) {
|
|
2129
|
-
await panel.channel.messages.delete(messageId).catch(() => {
|
|
2130
|
-
});
|
|
2131
|
-
}
|
|
1399
|
+
async function relocateSessionPanelToBottom2(sessionId, channel) {
|
|
1400
|
+
await relocateSessionPanelToBottom(
|
|
1401
|
+
sessionId,
|
|
1402
|
+
channel,
|
|
1403
|
+
(sid, ch, options) => initializeSessionPanel(sid, ch, options)
|
|
1404
|
+
);
|
|
2132
1405
|
}
|
|
2133
1406
|
function queueDigest(sessionId, item) {
|
|
2134
|
-
|
|
2135
|
-
if (!panel) return;
|
|
2136
|
-
panel.queueDigest(item);
|
|
1407
|
+
getPanel(sessionId)?.queueDigest(item);
|
|
2137
1408
|
}
|
|
2138
1409
|
function getDigestQueue(sessionId) {
|
|
2139
|
-
|
|
2140
|
-
if (!panel) return [];
|
|
2141
|
-
return panel.getDigestQueue();
|
|
1410
|
+
return getPanel(sessionId)?.getDigestQueue() ?? [];
|
|
2142
1411
|
}
|
|
2143
1412
|
function clearDigestQueue(sessionId) {
|
|
2144
|
-
|
|
2145
|
-
if (panel) panel.clearDigestQueue();
|
|
1413
|
+
getPanel(sessionId)?.clearDigestQueue();
|
|
2146
1414
|
}
|
|
2147
1415
|
async function flushDigest(sessionId) {
|
|
2148
1416
|
const panel = getPanel(sessionId);
|
|
@@ -2158,66 +1426,16 @@ function mapPlatformEventTypeToUnifiedState(type) {
|
|
|
2158
1426
|
function getStateMachine() {
|
|
2159
1427
|
return stateMachine;
|
|
2160
1428
|
}
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
stateMachine.clearSession(sessionId);
|
|
2167
|
-
clearPendingAnswers(sessionId);
|
|
2168
|
-
cleanupSessionDeliveryState(sessionId);
|
|
2169
|
-
clearCodexHint(sessionId);
|
|
2170
|
-
const activeGate = gateCoordinator.getActiveGateForSession(sessionId);
|
|
2171
|
-
if (activeGate) {
|
|
2172
|
-
gateCoordinator.resolveFromDiscord(activeGate.id, "reject").catch(() => {
|
|
2173
|
-
});
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
function cleanupInactiveSessions() {
|
|
2177
|
-
const now = Date.now();
|
|
2178
|
-
for (const [sessionId, panel] of sessionPanels) {
|
|
2179
|
-
if (now - panel.getLastActivity() > SESSION_INACTIVE_TIMEOUT_MS) {
|
|
2180
|
-
panel.cleanup();
|
|
2181
|
-
sessionPanels.delete(sessionId);
|
|
2182
|
-
statusCardProjectionRenderer.clear(sessionId);
|
|
2183
|
-
console.log(`\u6E05\u7406\u5931\u6D3B\u4F1A\u8BDD\u72B6\u6001\u6295\u5F71: ${sessionId}`);
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2187
|
-
function getPerformanceStats() {
|
|
2188
|
-
let projectionCount = 0;
|
|
2189
|
-
for (const panel of sessionPanels.values()) {
|
|
2190
|
-
if (panel.getCachedProjection() !== null) projectionCount++;
|
|
2191
|
-
}
|
|
2192
|
-
return {
|
|
2193
|
-
discoveryLatency: performanceTracker.getMetricStats("session_discovery_latency"),
|
|
2194
|
-
updateLatency: performanceTracker.getMetricStats("state_update_latency"),
|
|
2195
|
-
activeSessions: sessionPanels.size,
|
|
2196
|
-
projectionCount
|
|
2197
|
-
};
|
|
2198
|
-
}
|
|
2199
|
-
var cleanupInterval = null;
|
|
2200
|
-
function startPerformanceMonitoring() {
|
|
2201
|
-
if (cleanupInterval) return;
|
|
2202
|
-
cleanupInterval = setInterval(() => {
|
|
2203
|
-
cleanupInactiveSessions();
|
|
2204
|
-
performanceTracker.takeSnapshot();
|
|
2205
|
-
performanceTracker.cleanup();
|
|
2206
|
-
}, 6e4);
|
|
2207
|
-
}
|
|
2208
|
-
function stopPerformanceMonitoring() {
|
|
2209
|
-
if (cleanupInterval) {
|
|
2210
|
-
clearInterval(cleanupInterval);
|
|
2211
|
-
cleanupInterval = null;
|
|
2212
|
-
}
|
|
2213
|
-
}
|
|
2214
|
-
function generatePerformanceReport() {
|
|
2215
|
-
return performanceTracker.generateReport();
|
|
1429
|
+
var cleanupSessionPanel = createCleanupSessionPanel(statusCardProjectionRenderer);
|
|
1430
|
+
var cleanupInactiveSessions = createCleanupInactiveSessions(statusCardProjectionRenderer);
|
|
1431
|
+
var getPerformanceStats = createGetPerformanceStats();
|
|
1432
|
+
function startPerformanceMonitoring2() {
|
|
1433
|
+
startPerformanceMonitoring(() => cleanupInactiveSessions());
|
|
2216
1434
|
}
|
|
2217
1435
|
|
|
2218
1436
|
export {
|
|
2219
|
-
|
|
2220
|
-
|
|
1437
|
+
getSessionContext,
|
|
1438
|
+
getSessionView,
|
|
2221
1439
|
setPendingAnswer,
|
|
2222
1440
|
getPendingAnswers,
|
|
2223
1441
|
getQuestionCount,
|
|
@@ -2227,13 +1445,15 @@ export {
|
|
|
2227
1445
|
deliver,
|
|
2228
1446
|
cleanupOldMessages,
|
|
2229
1447
|
notifyUnmanagedCodexHint,
|
|
1448
|
+
stopPerformanceMonitoring,
|
|
1449
|
+
generatePerformanceReport,
|
|
2230
1450
|
getSessionProjection,
|
|
2231
1451
|
initializeSessionPanel,
|
|
2232
1452
|
registerExistingStatusCard,
|
|
2233
1453
|
updateSessionState,
|
|
2234
1454
|
handleResultEvent,
|
|
2235
1455
|
handleAwaitingHuman,
|
|
2236
|
-
relocateSessionPanelToBottom,
|
|
1456
|
+
relocateSessionPanelToBottom2 as relocateSessionPanelToBottom,
|
|
2237
1457
|
queueDigest,
|
|
2238
1458
|
getDigestQueue,
|
|
2239
1459
|
clearDigestQueue,
|
|
@@ -2243,7 +1463,5 @@ export {
|
|
|
2243
1463
|
cleanupSessionPanel,
|
|
2244
1464
|
cleanupInactiveSessions,
|
|
2245
1465
|
getPerformanceStats,
|
|
2246
|
-
startPerformanceMonitoring
|
|
2247
|
-
stopPerformanceMonitoring,
|
|
2248
|
-
generatePerformanceReport
|
|
1466
|
+
startPerformanceMonitoring2 as startPerformanceMonitoring
|
|
2249
1467
|
};
|