@ai-setting/roy-agent-cli 1.7.56 → 1.7.58
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/bin/roy-agent.js +863 -105
- package/dist/index.js +863 -105
- package/dist/roy-agent-linux-x64/bin/roy-agent +2 -2
- package/package.json +1 -1
package/dist/bin/roy-agent.js
CHANGED
|
@@ -365,25 +365,46 @@ var exports_stream_output_service = {};
|
|
|
365
365
|
__export(exports_stream_output_service, {
|
|
366
366
|
streamAbortSignal: () => streamAbortSignal,
|
|
367
367
|
resetStreamAbort: () => resetStreamAbort,
|
|
368
|
+
isStreamAborted: () => isStreamAborted,
|
|
368
369
|
hasNumericUsage: () => hasNumericUsage,
|
|
369
370
|
abortStream: () => abortStream,
|
|
371
|
+
__resetAllStreamAbortForTest: () => __resetAllStreamAbortForTest,
|
|
370
372
|
StreamOutputService: () => StreamOutputService,
|
|
371
373
|
COLORS: () => COLORS
|
|
372
374
|
});
|
|
373
375
|
import chalk2 from "chalk";
|
|
374
376
|
import { createLogger as createLogger2, TracedAs as TracedAs2 } from "@ai-setting/roy-agent-core";
|
|
375
|
-
function resetStreamAbort() {
|
|
376
|
-
|
|
377
|
+
function resetStreamAbort(scope) {
|
|
378
|
+
if (scope === undefined) {
|
|
379
|
+
streamAbortSignal.aborted = false;
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
abortedRuns.delete(scope);
|
|
383
|
+
}
|
|
384
|
+
function abortStream(scope) {
|
|
385
|
+
if (scope === undefined) {
|
|
386
|
+
streamAbortSignal.aborted = true;
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
abortedRuns.add(scope);
|
|
390
|
+
}
|
|
391
|
+
function isStreamAborted(scope) {
|
|
392
|
+
if (streamAbortSignal.aborted)
|
|
393
|
+
return true;
|
|
394
|
+
if (scope === undefined)
|
|
395
|
+
return false;
|
|
396
|
+
return abortedRuns.has(scope);
|
|
377
397
|
}
|
|
378
|
-
function
|
|
379
|
-
streamAbortSignal.aborted =
|
|
398
|
+
function __resetAllStreamAbortForTest() {
|
|
399
|
+
streamAbortSignal.aborted = false;
|
|
400
|
+
abortedRuns.clear();
|
|
380
401
|
}
|
|
381
402
|
function hasNumericUsage(usage) {
|
|
382
403
|
if (!usage)
|
|
383
404
|
return false;
|
|
384
405
|
return typeof usage.promptTokens === "number" || typeof usage.completionTokens === "number" || typeof usage.totalTokens === "number" || typeof usage.cacheRead === "number" || typeof usage.cacheWrite === "number" || typeof usage.promptTokensTotal === "number";
|
|
385
406
|
}
|
|
386
|
-
var logger2, COLORS, streamAbortSignal, StreamOutputService;
|
|
407
|
+
var logger2, COLORS, streamAbortSignal, abortedRuns, StreamOutputService;
|
|
387
408
|
var init_stream_output_service = __esm(() => {
|
|
388
409
|
init_markdown_renderer();
|
|
389
410
|
logger2 = createLogger2("stream-output");
|
|
@@ -406,6 +427,7 @@ var init_stream_output_service = __esm(() => {
|
|
|
406
427
|
streamAbortSignal = {
|
|
407
428
|
aborted: false
|
|
408
429
|
};
|
|
430
|
+
abortedRuns = new Set;
|
|
409
431
|
StreamOutputService = class StreamOutputService {
|
|
410
432
|
fullText = "";
|
|
411
433
|
fullReasoning = "";
|
|
@@ -421,6 +443,9 @@ var init_stream_output_service = __esm(() => {
|
|
|
421
443
|
reasoningClosedByText = false;
|
|
422
444
|
chunkCallback = undefined;
|
|
423
445
|
silent = false;
|
|
446
|
+
sessionFilter = undefined;
|
|
447
|
+
acceptsSidelessEvents = true;
|
|
448
|
+
runId = undefined;
|
|
424
449
|
constructor(options = {}) {
|
|
425
450
|
this.options = options;
|
|
426
451
|
this.silent = options.silent ?? false;
|
|
@@ -435,6 +460,15 @@ var init_stream_output_service = __esm(() => {
|
|
|
435
460
|
setChunkCallback(cb) {
|
|
436
461
|
this.chunkCallback = cb;
|
|
437
462
|
}
|
|
463
|
+
setSessionFilter(sessionId) {
|
|
464
|
+
this.sessionFilter = sessionId;
|
|
465
|
+
}
|
|
466
|
+
setAcceptsSidelessEvents(accepts) {
|
|
467
|
+
this.acceptsSidelessEvents = accepts;
|
|
468
|
+
}
|
|
469
|
+
setRunId(runId) {
|
|
470
|
+
this.runId = runId;
|
|
471
|
+
}
|
|
438
472
|
emitChunk(event) {
|
|
439
473
|
if (!this.chunkCallback)
|
|
440
474
|
return;
|
|
@@ -455,7 +489,8 @@ var init_stream_output_service = __esm(() => {
|
|
|
455
489
|
console.log(message);
|
|
456
490
|
}
|
|
457
491
|
checkAbort() {
|
|
458
|
-
|
|
492
|
+
const aborted = this.runId !== undefined ? isStreamAborted(this.runId) : streamAbortSignal.aborted;
|
|
493
|
+
if (aborted) {
|
|
459
494
|
this.logStdout(`
|
|
460
495
|
${COLORS.system("[已中断]")}
|
|
461
496
|
`);
|
|
@@ -463,7 +498,25 @@ ${COLORS.system("[已中断]")}
|
|
|
463
498
|
}
|
|
464
499
|
return false;
|
|
465
500
|
}
|
|
501
|
+
extractEventSessionId(event) {
|
|
502
|
+
const fromMeta = event?.metadata?.sessionId;
|
|
503
|
+
if (typeof fromMeta === "string" && fromMeta.length > 0)
|
|
504
|
+
return fromMeta;
|
|
505
|
+
const fromPayload = event?.payload?.metadata?.sessionId;
|
|
506
|
+
if (typeof fromPayload === "string" && fromPayload.length > 0)
|
|
507
|
+
return fromPayload;
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
466
510
|
handleEvent(event) {
|
|
511
|
+
if (this.sessionFilter !== undefined) {
|
|
512
|
+
const eventSessionId = this.extractEventSessionId(event);
|
|
513
|
+
if (eventSessionId === undefined) {
|
|
514
|
+
if (!this.acceptsSidelessEvents)
|
|
515
|
+
return;
|
|
516
|
+
} else if (eventSessionId !== this.sessionFilter) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
467
520
|
if (this.checkAbort()) {
|
|
468
521
|
return;
|
|
469
522
|
}
|
|
@@ -9586,7 +9639,7 @@ class CliQuietModeService {
|
|
|
9586
9639
|
// package.json
|
|
9587
9640
|
var package_default = {
|
|
9588
9641
|
name: "@ai-setting/roy-agent-cli",
|
|
9589
|
-
version: "1.7.
|
|
9642
|
+
version: "1.7.58",
|
|
9590
9643
|
type: "module",
|
|
9591
9644
|
description: "CLI for roy-agent - Non-interactive command execution",
|
|
9592
9645
|
main: "./dist/index.js",
|
|
@@ -11239,6 +11292,9 @@ class QueryExecutor {
|
|
|
11239
11292
|
showToolResults: false
|
|
11240
11293
|
};
|
|
11241
11294
|
_pendingContextConfig = null;
|
|
11295
|
+
_pendingSessionScope = undefined;
|
|
11296
|
+
_pendingAcceptsSideless = undefined;
|
|
11297
|
+
_sidelessPolicyExplicit = false;
|
|
11242
11298
|
_pendingChunkCallback = undefined;
|
|
11243
11299
|
failureNotifier;
|
|
11244
11300
|
constructor(options) {
|
|
@@ -11277,6 +11333,14 @@ class QueryExecutor {
|
|
|
11277
11333
|
this.streamService.setChunkCallback(this._pendingChunkCallback);
|
|
11278
11334
|
this._pendingChunkCallback = undefined;
|
|
11279
11335
|
}
|
|
11336
|
+
if (this._pendingAcceptsSideless !== undefined) {
|
|
11337
|
+
this.streamService.setAcceptsSidelessEvents(this._pendingAcceptsSideless);
|
|
11338
|
+
this._pendingAcceptsSideless = undefined;
|
|
11339
|
+
}
|
|
11340
|
+
if (this._pendingSessionScope !== undefined) {
|
|
11341
|
+
this.streamService.setSessionFilter(this._pendingSessionScope);
|
|
11342
|
+
this.streamService.setRunId(this._pendingSessionScope);
|
|
11343
|
+
}
|
|
11280
11344
|
if (this._pendingContextConfig) {
|
|
11281
11345
|
this.streamService.setContextInfo(this._pendingContextConfig.contextWindow, this._pendingContextConfig.contextWindow * this._pendingContextConfig.thresholdRatio);
|
|
11282
11346
|
this._pendingContextConfig = null;
|
|
@@ -11302,6 +11366,18 @@ class QueryExecutor {
|
|
|
11302
11366
|
this._pendingChunkCallback = cb;
|
|
11303
11367
|
}
|
|
11304
11368
|
}
|
|
11369
|
+
setSessionScope(sessionId) {
|
|
11370
|
+
this._pendingSessionScope = sessionId;
|
|
11371
|
+
if (this.streamService) {
|
|
11372
|
+
this.streamService.setSessionFilter(sessionId);
|
|
11373
|
+
this.streamService.setRunId(sessionId);
|
|
11374
|
+
}
|
|
11375
|
+
}
|
|
11376
|
+
setAcceptsSidelessEvents(accepts) {
|
|
11377
|
+
this._pendingAcceptsSideless = accepts;
|
|
11378
|
+
this._sidelessPolicyExplicit = true;
|
|
11379
|
+
this.streamService?.setAcceptsSidelessEvents(accepts);
|
|
11380
|
+
}
|
|
11305
11381
|
handleEvent(event) {
|
|
11306
11382
|
if (event.type === "context.threshold_exceeded" && !this.quiet) {
|
|
11307
11383
|
const payload = event.payload;
|
|
@@ -11322,6 +11398,13 @@ class QueryExecutor {
|
|
|
11322
11398
|
showToolResults: streamOptions?.showToolResults ?? this.streamOptions.showToolResults
|
|
11323
11399
|
};
|
|
11324
11400
|
this.subscribeToEvents(options);
|
|
11401
|
+
if (sessionId) {
|
|
11402
|
+
this.streamService?.setSessionFilter(sessionId);
|
|
11403
|
+
this.streamService?.setRunId(sessionId);
|
|
11404
|
+
if (!this._sidelessPolicyExplicit) {
|
|
11405
|
+
this.streamService?.setAcceptsSidelessEvents(true);
|
|
11406
|
+
}
|
|
11407
|
+
}
|
|
11325
11408
|
this.streamService?.reset();
|
|
11326
11409
|
if (!this.quiet) {
|
|
11327
11410
|
this.output.info(`执行: ${message}`);
|
|
@@ -11696,8 +11779,268 @@ import {
|
|
|
11696
11779
|
} from "@ai-setting/roy-agent-core";
|
|
11697
11780
|
|
|
11698
11781
|
// src/commands/shared/event-handler.ts
|
|
11782
|
+
import {
|
|
11783
|
+
getEnvContext,
|
|
11784
|
+
runWithEnvContextAsync
|
|
11785
|
+
} from "@ai-setting/roy-agent-core/env";
|
|
11699
11786
|
import { TracedAs as TracedAs5 } from "@ai-setting/roy-agent-core";
|
|
11700
11787
|
import { notifyReactFailure as notifyReactFailure2 } from "@ai-setting/roy-agent-core/env/error-reply";
|
|
11788
|
+
|
|
11789
|
+
// src/commands/shared/telemetry-notice.ts
|
|
11790
|
+
import { spawn } from "node:child_process";
|
|
11791
|
+
import { normalizeLarkAtMentions } from "@ai-setting/roy-agent-core/env/lark-mention-utils";
|
|
11792
|
+
import { deriveIdempotencyKey } from "@ai-setting/roy-agent-core/env/event-source/idempotency-key";
|
|
11793
|
+
var TELEMETRY_EVENT_TYPES = new Set([
|
|
11794
|
+
"task.background.started",
|
|
11795
|
+
"task.background.progress"
|
|
11796
|
+
]);
|
|
11797
|
+
function isTelemetryEvent(type) {
|
|
11798
|
+
if (!type)
|
|
11799
|
+
return false;
|
|
11800
|
+
return TELEMETRY_EVENT_TYPES.has(type);
|
|
11801
|
+
}
|
|
11802
|
+
var MAX_TITLE_CHARS = 40;
|
|
11803
|
+
function pad2(n) {
|
|
11804
|
+
return n < 10 ? `0${n}` : String(n);
|
|
11805
|
+
}
|
|
11806
|
+
function formatElapsed(ms) {
|
|
11807
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
11808
|
+
return "0m";
|
|
11809
|
+
const totalMin = Math.floor(ms / 60000);
|
|
11810
|
+
if (totalMin < 60)
|
|
11811
|
+
return `${totalMin}m`;
|
|
11812
|
+
const totalHour = Math.floor(totalMin / 60);
|
|
11813
|
+
const min = totalMin % 60;
|
|
11814
|
+
if (totalHour < 24) {
|
|
11815
|
+
return min === 0 ? `${totalHour}h` : `${totalHour}h${min}m`;
|
|
11816
|
+
}
|
|
11817
|
+
const day = Math.floor(totalHour / 24);
|
|
11818
|
+
const hour = totalHour % 24;
|
|
11819
|
+
return hour === 0 ? `${day}d` : `${day}d${hour}h`;
|
|
11820
|
+
}
|
|
11821
|
+
function formatClock(ts) {
|
|
11822
|
+
const d = new Date(ts);
|
|
11823
|
+
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
|
|
11824
|
+
}
|
|
11825
|
+
function formatTimeoutLabel(ms) {
|
|
11826
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
11827
|
+
return "0m";
|
|
11828
|
+
return formatElapsed(ms);
|
|
11829
|
+
}
|
|
11830
|
+
function truncateTitle(raw) {
|
|
11831
|
+
const s = raw.trim();
|
|
11832
|
+
if (s.length <= MAX_TITLE_CHARS)
|
|
11833
|
+
return s;
|
|
11834
|
+
return `${s.slice(0, MAX_TITLE_CHARS)}…`;
|
|
11835
|
+
}
|
|
11836
|
+
function pickString(v) {
|
|
11837
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
11838
|
+
}
|
|
11839
|
+
function pickNumber(v) {
|
|
11840
|
+
return typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
11841
|
+
}
|
|
11842
|
+
function buildTelemetryNoticeText(type, payload, now = Date.now()) {
|
|
11843
|
+
const p = payload ?? {};
|
|
11844
|
+
const taskId = pickNumber(p.associatedTaskId);
|
|
11845
|
+
const bgId = pickString(p.backgroundTaskId);
|
|
11846
|
+
const desc = truncateTitle(pickString(p.description) ?? "后台任务");
|
|
11847
|
+
let label;
|
|
11848
|
+
if (taskId !== undefined)
|
|
11849
|
+
label = `Task #${taskId}`;
|
|
11850
|
+
else if (bgId)
|
|
11851
|
+
label = bgId;
|
|
11852
|
+
else
|
|
11853
|
+
label = "后台任务";
|
|
11854
|
+
const head = type === "task.background.started" ? `\uD83D\uDD14 ${label}「${desc}」已启动` : `\uD83D\uDD04 ${label}「${desc}」后台运行中`;
|
|
11855
|
+
const parts = [];
|
|
11856
|
+
const startedAt = pickNumber(p.startedAt);
|
|
11857
|
+
const timeoutMs = pickNumber(p.timeoutMs);
|
|
11858
|
+
if (type !== "task.background.started" && startedAt !== undefined) {
|
|
11859
|
+
parts.push(`已跑 ${formatElapsed(now - startedAt)}`);
|
|
11860
|
+
}
|
|
11861
|
+
if (startedAt !== undefined && timeoutMs !== undefined) {
|
|
11862
|
+
parts.push(`预计 ${formatClock(startedAt + timeoutMs)} 超时(timeout ${formatTimeoutLabel(timeoutMs)})`);
|
|
11863
|
+
}
|
|
11864
|
+
let text = head;
|
|
11865
|
+
if (parts.length > 0)
|
|
11866
|
+
text += `
|
|
11867
|
+
${parts.join(",")}`;
|
|
11868
|
+
const progressMessage = pickString(p.progressMessage);
|
|
11869
|
+
if (progressMessage)
|
|
11870
|
+
text += `
|
|
11871
|
+
${progressMessage}`;
|
|
11872
|
+
return text;
|
|
11873
|
+
}
|
|
11874
|
+
var DEFAULT_SEND_TIMEOUT_MS = 5000;
|
|
11875
|
+
var MAX_IDEMPOTENCY_KEY_LEN = 50;
|
|
11876
|
+
function deriveTelemetryIdempotencyKey(eventId, chatId) {
|
|
11877
|
+
const anchor = pickString(eventId) ?? `ts-${Date.now()}-${Math.random()}`;
|
|
11878
|
+
return deriveIdempotencyKey(["telemetry", anchor, chatId]);
|
|
11879
|
+
}
|
|
11880
|
+
function buildLarkSendArgs(input) {
|
|
11881
|
+
const { chatId, text, profile, idempotencyKey } = input;
|
|
11882
|
+
const args = [
|
|
11883
|
+
"im",
|
|
11884
|
+
"+messages-send",
|
|
11885
|
+
"--chat-id",
|
|
11886
|
+
chatId,
|
|
11887
|
+
"--markdown",
|
|
11888
|
+
normalizeLarkAtMentions(text),
|
|
11889
|
+
"--as",
|
|
11890
|
+
"bot"
|
|
11891
|
+
];
|
|
11892
|
+
if (idempotencyKey) {
|
|
11893
|
+
args.push("--idempotency-key", idempotencyKey.slice(0, MAX_IDEMPOTENCY_KEY_LEN));
|
|
11894
|
+
}
|
|
11895
|
+
if (profile)
|
|
11896
|
+
args.unshift("--profile", profile);
|
|
11897
|
+
return args;
|
|
11898
|
+
}
|
|
11899
|
+
function sendLarkChatMarkdown(options) {
|
|
11900
|
+
const { chatId, text, profile, idempotencyKey } = options;
|
|
11901
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_SEND_TIMEOUT_MS;
|
|
11902
|
+
if (!chatId) {
|
|
11903
|
+
return Promise.reject(new Error("sendLarkChatMarkdown: empty chatId"));
|
|
11904
|
+
}
|
|
11905
|
+
return new Promise((resolve, reject) => {
|
|
11906
|
+
let settled = false;
|
|
11907
|
+
let timer;
|
|
11908
|
+
const finish = (err) => {
|
|
11909
|
+
if (settled)
|
|
11910
|
+
return;
|
|
11911
|
+
settled = true;
|
|
11912
|
+
if (timer)
|
|
11913
|
+
clearTimeout(timer);
|
|
11914
|
+
if (err)
|
|
11915
|
+
reject(err);
|
|
11916
|
+
else
|
|
11917
|
+
resolve();
|
|
11918
|
+
};
|
|
11919
|
+
const args = buildLarkSendArgs({ chatId, text, profile, idempotencyKey });
|
|
11920
|
+
let proc;
|
|
11921
|
+
try {
|
|
11922
|
+
proc = spawn("lark-cli", args, {
|
|
11923
|
+
shell: false,
|
|
11924
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
11925
|
+
});
|
|
11926
|
+
} catch (err) {
|
|
11927
|
+
finish(err instanceof Error ? err : new Error(String(err)));
|
|
11928
|
+
return;
|
|
11929
|
+
}
|
|
11930
|
+
let stderr = "";
|
|
11931
|
+
proc.stderr?.on("data", (chunk) => {
|
|
11932
|
+
stderr += chunk.toString();
|
|
11933
|
+
});
|
|
11934
|
+
timer = setTimeout(() => {
|
|
11935
|
+
try {
|
|
11936
|
+
proc.kill("SIGKILL");
|
|
11937
|
+
} catch {}
|
|
11938
|
+
finish(new Error(`lark-cli timed out after ${timeoutMs}ms`));
|
|
11939
|
+
}, timeoutMs);
|
|
11940
|
+
timer.unref?.();
|
|
11941
|
+
proc.on("close", (code) => {
|
|
11942
|
+
if (code === 0)
|
|
11943
|
+
finish();
|
|
11944
|
+
else
|
|
11945
|
+
finish(new Error(`lark-cli exited with code ${code}: ${stderr}`));
|
|
11946
|
+
});
|
|
11947
|
+
proc.on("error", (err) => finish(err));
|
|
11948
|
+
});
|
|
11949
|
+
}
|
|
11950
|
+
function targetFromEventMetadata(metadata) {
|
|
11951
|
+
if (!metadata)
|
|
11952
|
+
return;
|
|
11953
|
+
const replyChannel = metadata.replyChannel;
|
|
11954
|
+
const chatId = pickString(replyChannel?.chatId) ?? pickString(metadata.chatId);
|
|
11955
|
+
if (!chatId)
|
|
11956
|
+
return;
|
|
11957
|
+
const profile = pickString(replyChannel?.profile) ?? pickString(metadata.profile);
|
|
11958
|
+
return profile ? { chatId, profile } : { chatId };
|
|
11959
|
+
}
|
|
11960
|
+
async function targetFromTriggerEvent(eventStore, triggerEventId) {
|
|
11961
|
+
if (!eventStore || !triggerEventId)
|
|
11962
|
+
return;
|
|
11963
|
+
let persisted;
|
|
11964
|
+
try {
|
|
11965
|
+
persisted = await eventStore.getEventById(triggerEventId);
|
|
11966
|
+
} catch {
|
|
11967
|
+
return;
|
|
11968
|
+
}
|
|
11969
|
+
if (!persisted || typeof persisted !== "object")
|
|
11970
|
+
return;
|
|
11971
|
+
const metadata = persisted.metadata;
|
|
11972
|
+
if (!metadata || typeof metadata !== "object")
|
|
11973
|
+
return;
|
|
11974
|
+
return targetFromEventMetadata(metadata);
|
|
11975
|
+
}
|
|
11976
|
+
|
|
11977
|
+
// src/commands/shared/self-echo-filter.ts
|
|
11978
|
+
var BOT_SENDER_TYPES = new Set(["bot", "app"]);
|
|
11979
|
+
function pickString2(v) {
|
|
11980
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
11981
|
+
}
|
|
11982
|
+
function extractSenderIdentity(event) {
|
|
11983
|
+
if (!event)
|
|
11984
|
+
return {};
|
|
11985
|
+
const metadata = event.metadata;
|
|
11986
|
+
const payload = event.payload;
|
|
11987
|
+
const payloadMeta = payload?.metadata;
|
|
11988
|
+
const rawEvent = payload?.rawEvent;
|
|
11989
|
+
const innerEvent = rawEvent?.event;
|
|
11990
|
+
const innerSender = innerEvent?.sender;
|
|
11991
|
+
const topSender = rawEvent?.sender;
|
|
11992
|
+
const senderType = pickString2(metadata?.senderType) ?? pickString2(payloadMeta?.senderType) ?? pickString2(rawEvent?.sender_type) ?? pickString2(innerSender?.sender_type) ?? pickString2(topSender?.sender_type) ?? pickString2(payload?.senderType);
|
|
11993
|
+
const nestedSenderId = innerSender?.sender_id ?? topSender?.sender_id;
|
|
11994
|
+
const senderId = pickString2(metadata?.senderId) ?? pickString2(payloadMeta?.senderId) ?? pickString2(payload?.senderId) ?? pickString2(rawEvent?.sender_id) ?? (typeof nestedSenderId === "string" ? pickString2(nestedSenderId) : pickString2(nestedSenderId?.open_id) ?? pickString2(nestedSenderId?.user_id) ?? pickString2(nestedSenderId?.union_id));
|
|
11995
|
+
return {
|
|
11996
|
+
...senderType ? { senderType } : {},
|
|
11997
|
+
...senderId ? { senderId } : {}
|
|
11998
|
+
};
|
|
11999
|
+
}
|
|
12000
|
+
function isSelfEchoEvent(event, options = {}) {
|
|
12001
|
+
const { senderType, senderId } = extractSenderIdentity(event);
|
|
12002
|
+
if (options.enabled === false) {
|
|
12003
|
+
return {
|
|
12004
|
+
skip: false,
|
|
12005
|
+
reason: "disabled",
|
|
12006
|
+
...senderType ? { senderType } : {},
|
|
12007
|
+
...senderId ? { senderId } : {}
|
|
12008
|
+
};
|
|
12009
|
+
}
|
|
12010
|
+
if (senderType && BOT_SENDER_TYPES.has(senderType.toLowerCase())) {
|
|
12011
|
+
return {
|
|
12012
|
+
skip: true,
|
|
12013
|
+
reason: "bot-sender",
|
|
12014
|
+
...senderType ? { senderType } : {},
|
|
12015
|
+
...senderId ? { senderId } : {}
|
|
12016
|
+
};
|
|
12017
|
+
}
|
|
12018
|
+
if (senderId && options.selfBotIds && options.selfBotIds.length > 0) {
|
|
12019
|
+
if (options.selfBotIds.includes(senderId)) {
|
|
12020
|
+
return {
|
|
12021
|
+
skip: true,
|
|
12022
|
+
reason: "self-bot-id",
|
|
12023
|
+
...senderType ? { senderType } : {},
|
|
12024
|
+
senderId
|
|
12025
|
+
};
|
|
12026
|
+
}
|
|
12027
|
+
}
|
|
12028
|
+
return {
|
|
12029
|
+
skip: false,
|
|
12030
|
+
...senderType ? { senderType } : {},
|
|
12031
|
+
...senderId ? { senderId } : {}
|
|
12032
|
+
};
|
|
12033
|
+
}
|
|
12034
|
+
var SELF_ECHO_FILTER_ENABLED_ENV = "ROY_AGENT_SELF_ECHO_FILTER";
|
|
12035
|
+
function readSelfEchoFilterEnabled(env = process.env) {
|
|
12036
|
+
const raw = env[SELF_ECHO_FILTER_ENABLED_ENV];
|
|
12037
|
+
if (raw === undefined)
|
|
12038
|
+
return true;
|
|
12039
|
+
const v = raw.trim().toLowerCase();
|
|
12040
|
+
return !(v === "0" || v === "false" || v === "off" || v === "no");
|
|
12041
|
+
}
|
|
12042
|
+
|
|
12043
|
+
// src/commands/shared/event-handler.ts
|
|
11701
12044
|
function parseLarkPerTaskChatId(contextJson) {
|
|
11702
12045
|
if (!contextJson)
|
|
11703
12046
|
return;
|
|
@@ -11717,6 +12060,8 @@ var NOTIFY_V2_TERMINAL_TYPES = new Set([
|
|
|
11717
12060
|
]);
|
|
11718
12061
|
|
|
11719
12062
|
class EventHandler {
|
|
12063
|
+
static DEFAULT_LANE_KEY = "__default__";
|
|
12064
|
+
static DEFAULT_MAX_CONCURRENT_SESSIONS = 4;
|
|
11720
12065
|
env;
|
|
11721
12066
|
eventTypes;
|
|
11722
12067
|
onEvent;
|
|
@@ -11724,15 +12069,39 @@ class EventHandler {
|
|
|
11724
12069
|
formatter;
|
|
11725
12070
|
isIdle;
|
|
11726
12071
|
idleCheckInterval;
|
|
11727
|
-
|
|
11728
|
-
|
|
12072
|
+
isSessionIdleFn = null;
|
|
12073
|
+
maxConcurrentSessions;
|
|
12074
|
+
lanes = new Map;
|
|
12075
|
+
runningLanes = new Set;
|
|
12076
|
+
laneTails = new Map;
|
|
12077
|
+
get isProcessing() {
|
|
12078
|
+
return this.runningLanes.size > 0;
|
|
12079
|
+
}
|
|
12080
|
+
chatSessionCache = new Map;
|
|
12081
|
+
inFlightCount = 0;
|
|
12082
|
+
legacyQueue = [];
|
|
12083
|
+
get queue() {
|
|
12084
|
+
return this.legacyQueue;
|
|
12085
|
+
}
|
|
11729
12086
|
isStopped = false;
|
|
11730
12087
|
unsubscribe = null;
|
|
12088
|
+
activeSlots = 0;
|
|
12089
|
+
slotWaiters = [];
|
|
12090
|
+
sessionChains = new Map;
|
|
11731
12091
|
sessionComponent = null;
|
|
11732
12092
|
activeEventSessionId = null;
|
|
11733
12093
|
resolvedSessionByTrigger = new Map;
|
|
11734
12094
|
eventStore = null;
|
|
11735
12095
|
getHandlerForEsType = null;
|
|
12096
|
+
telemetrySender = (notice) => sendLarkChatMarkdown(notice);
|
|
12097
|
+
selfEchoFilter = {
|
|
12098
|
+
enabled: true,
|
|
12099
|
+
selfBotIds: []
|
|
12100
|
+
};
|
|
12101
|
+
selfEchoDroppedCount = 0;
|
|
12102
|
+
getSelfEchoDroppedCount() {
|
|
12103
|
+
return this.selfEchoDroppedCount;
|
|
12104
|
+
}
|
|
11736
12105
|
constructor(options) {
|
|
11737
12106
|
this.env = options.env;
|
|
11738
12107
|
this.eventTypes = options.eventTypes;
|
|
@@ -11740,10 +12109,18 @@ class EventHandler {
|
|
|
11740
12109
|
this.onEventWithEnv = options.onEventWithEnv;
|
|
11741
12110
|
this.formatter = options.formatter ?? new EventMessageFormatter;
|
|
11742
12111
|
this.isIdle = options.isIdle ?? (() => true);
|
|
12112
|
+
this.isSessionIdleFn = options.isSessionIdle ?? null;
|
|
12113
|
+
const maxSessions = options.maxConcurrentSessions;
|
|
12114
|
+
this.maxConcurrentSessions = typeof maxSessions === "number" && Number.isFinite(maxSessions) && maxSessions >= 1 ? Math.floor(maxSessions) : EventHandler.DEFAULT_MAX_CONCURRENT_SESSIONS;
|
|
11743
12115
|
this.idleCheckInterval = options.idleCheckInterval ?? 100;
|
|
11744
12116
|
this.sessionComponent = options.sessionComponent ?? null;
|
|
11745
12117
|
this.eventStore = options.eventStore ?? null;
|
|
11746
12118
|
this.getHandlerForEsType = options.getHandlerForEsType ?? null;
|
|
12119
|
+
this.telemetrySender = options.telemetrySender ?? ((notice) => sendLarkChatMarkdown(notice));
|
|
12120
|
+
this.selfEchoFilter = {
|
|
12121
|
+
enabled: options.selfEchoFilter?.enabled ?? readSelfEchoFilterEnabled(),
|
|
12122
|
+
selfBotIds: options.selfEchoFilter?.selfBotIds ?? []
|
|
12123
|
+
};
|
|
11747
12124
|
}
|
|
11748
12125
|
start() {
|
|
11749
12126
|
this.isStopped = false;
|
|
@@ -11777,17 +12154,58 @@ class EventHandler {
|
|
|
11777
12154
|
this.unsubscribe();
|
|
11778
12155
|
this.unsubscribe = null;
|
|
11779
12156
|
}
|
|
11780
|
-
this.
|
|
11781
|
-
this.
|
|
12157
|
+
this.lanes.clear();
|
|
12158
|
+
this.legacyQueue = [];
|
|
12159
|
+
this.laneTails.clear();
|
|
12160
|
+
const waiters = this.slotWaiters.splice(0, this.slotWaiters.length);
|
|
12161
|
+
for (const w of waiters)
|
|
12162
|
+
w();
|
|
12163
|
+
this.activeSlots = 0;
|
|
11782
12164
|
this.clearActiveSession();
|
|
11783
12165
|
}
|
|
11784
12166
|
async handleEvent(event) {
|
|
11785
12167
|
if (this.isStopped) {
|
|
11786
12168
|
return;
|
|
11787
12169
|
}
|
|
12170
|
+
if (this.selfEchoFilter.enabled) {
|
|
12171
|
+
const verdict = isSelfEchoEvent(event, {
|
|
12172
|
+
selfBotIds: this.selfEchoFilter.selfBotIds
|
|
12173
|
+
});
|
|
12174
|
+
if (verdict.skip) {
|
|
12175
|
+
this.selfEchoDroppedCount += 1;
|
|
12176
|
+
console.error(`[EventHandler] self-echo event dropped (reason=${verdict.reason}` + `, senderType=${verdict.senderType ?? "-"}` + `, senderId=${verdict.senderId ?? "-"}` + `, type=${event.type})`);
|
|
12177
|
+
return;
|
|
12178
|
+
}
|
|
12179
|
+
}
|
|
12180
|
+
if (isTelemetryEvent(event.type)) {
|
|
12181
|
+
this.sendTelemetryNotice(event);
|
|
12182
|
+
return;
|
|
12183
|
+
}
|
|
11788
12184
|
const formatted = await this.formatEvent(event);
|
|
11789
12185
|
this.enqueue(formatted);
|
|
11790
12186
|
}
|
|
12187
|
+
async sendTelemetryNotice(event) {
|
|
12188
|
+
try {
|
|
12189
|
+
const metadata = event.metadata;
|
|
12190
|
+
let target = targetFromEventMetadata(metadata);
|
|
12191
|
+
if (!target) {
|
|
12192
|
+
const triggerEventId = typeof metadata?.triggerEventId === "string" ? metadata.triggerEventId : undefined;
|
|
12193
|
+
target = await targetFromTriggerEvent(this.eventStore, triggerEventId);
|
|
12194
|
+
}
|
|
12195
|
+
if (!target) {
|
|
12196
|
+
return;
|
|
12197
|
+
}
|
|
12198
|
+
const text = buildTelemetryNoticeText(event.type, event.payload);
|
|
12199
|
+
await this.telemetrySender({
|
|
12200
|
+
chatId: target.chatId,
|
|
12201
|
+
...target.profile ? { profile: target.profile } : {},
|
|
12202
|
+
text,
|
|
12203
|
+
idempotencyKey: deriveTelemetryIdempotencyKey(event.id, target.chatId)
|
|
12204
|
+
});
|
|
12205
|
+
} catch (err) {
|
|
12206
|
+
console.error(`[EventHandler] telemetry notice failed (swallowed) type=${event.type}`, err);
|
|
12207
|
+
}
|
|
12208
|
+
}
|
|
11791
12209
|
async formatEvent(event) {
|
|
11792
12210
|
if (NOTIFY_V2_TERMINAL_TYPES.has(event.type) && event.metadata?.triggerEventId && this.eventStore && this.getHandlerForEsType) {
|
|
11793
12211
|
try {
|
|
@@ -11900,77 +12318,218 @@ class EventHandler {
|
|
|
11900
12318
|
await this.onEvent(formatted.message);
|
|
11901
12319
|
}
|
|
11902
12320
|
}
|
|
12321
|
+
laneKeyFor(item) {
|
|
12322
|
+
if (item.chatId)
|
|
12323
|
+
return item.chatId;
|
|
12324
|
+
if (item.triggerSessionId)
|
|
12325
|
+
return `trigger:${item.triggerSessionId}`;
|
|
12326
|
+
return EventHandler.DEFAULT_LANE_KEY;
|
|
12327
|
+
}
|
|
11903
12328
|
enqueue(formattedEvent) {
|
|
11904
12329
|
if (this.isStopped) {
|
|
11905
12330
|
return;
|
|
11906
12331
|
}
|
|
11907
|
-
|
|
11908
|
-
if (!
|
|
11909
|
-
|
|
12332
|
+
const item = { ...formattedEvent };
|
|
12333
|
+
if (!item.envContext) {
|
|
12334
|
+
item.envContext = getEnvContext();
|
|
11910
12335
|
}
|
|
12336
|
+
const laneKey = this.laneKeyFor(item);
|
|
12337
|
+
const lane = this.lanes.get(laneKey);
|
|
12338
|
+
if (lane) {
|
|
12339
|
+
lane.push(item);
|
|
12340
|
+
} else {
|
|
12341
|
+
this.lanes.set(laneKey, [item]);
|
|
12342
|
+
}
|
|
12343
|
+
this.scheduleLane(laneKey);
|
|
11911
12344
|
}
|
|
11912
12345
|
getQueueLength() {
|
|
11913
|
-
|
|
12346
|
+
let total = this.legacyQueue.length + this.inFlightCount;
|
|
12347
|
+
for (const lane of this.lanes.values()) {
|
|
12348
|
+
total += lane.length;
|
|
12349
|
+
}
|
|
12350
|
+
return total;
|
|
11914
12351
|
}
|
|
11915
12352
|
isQueueEmpty() {
|
|
11916
|
-
return this.
|
|
12353
|
+
return this.getQueueLength() === 0;
|
|
11917
12354
|
}
|
|
11918
|
-
|
|
11919
|
-
this.
|
|
11920
|
-
|
|
11921
|
-
|
|
11922
|
-
|
|
11923
|
-
|
|
11924
|
-
|
|
12355
|
+
isSessionIdle(laneKey) {
|
|
12356
|
+
return !this.runningLanes.has(laneKey);
|
|
12357
|
+
}
|
|
12358
|
+
getRunningLaneKeys() {
|
|
12359
|
+
return Array.from(this.runningLanes);
|
|
12360
|
+
}
|
|
12361
|
+
scheduleLane(laneKey) {
|
|
12362
|
+
if (this.isStopped)
|
|
12363
|
+
return;
|
|
12364
|
+
if (this.runningLanes.has(laneKey))
|
|
12365
|
+
return;
|
|
12366
|
+
this.runningLanes.add(laneKey);
|
|
12367
|
+
const tail = this.runLane(laneKey).catch((err) => {
|
|
12368
|
+
console.error(`[EventHandler] lane ${laneKey} 处理异常:`, err);
|
|
12369
|
+
}).finally(() => {
|
|
12370
|
+
this.runningLanes.delete(laneKey);
|
|
12371
|
+
this.laneTails.delete(laneKey);
|
|
12372
|
+
const rest = this.lanes.get(laneKey);
|
|
12373
|
+
if (rest && rest.length > 0) {
|
|
12374
|
+
this.scheduleLane(laneKey);
|
|
11925
12375
|
}
|
|
11926
|
-
|
|
11927
|
-
|
|
12376
|
+
});
|
|
12377
|
+
this.laneTails.set(laneKey, tail);
|
|
12378
|
+
}
|
|
12379
|
+
async runLane(laneKey) {
|
|
12380
|
+
while (!this.isStopped) {
|
|
12381
|
+
const lane = this.lanes.get(laneKey);
|
|
12382
|
+
if (!lane || lane.length === 0) {
|
|
11928
12383
|
break;
|
|
11929
12384
|
}
|
|
11930
|
-
|
|
11931
|
-
|
|
11932
|
-
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
|
|
11936
|
-
|
|
11937
|
-
|
|
11938
|
-
|
|
12385
|
+
const head = lane[0];
|
|
12386
|
+
const sessionId = await runWithEnvContextAsync(head.envContext, () => this.resolveSessionForItem(laneKey, head));
|
|
12387
|
+
if (this.isStopped)
|
|
12388
|
+
break;
|
|
12389
|
+
const canonicalKey = sessionId ?? laneKey;
|
|
12390
|
+
if (canonicalKey !== laneKey) {
|
|
12391
|
+
const mine = this.lanes.get(laneKey) ?? [];
|
|
12392
|
+
if (mine.length > 0) {
|
|
12393
|
+
const target = this.lanes.get(canonicalKey);
|
|
12394
|
+
if (target) {
|
|
12395
|
+
target.push(...mine);
|
|
12396
|
+
} else {
|
|
12397
|
+
this.lanes.set(canonicalKey, mine);
|
|
11939
12398
|
}
|
|
12399
|
+
this.lanes.delete(laneKey);
|
|
12400
|
+
this.scheduleLane(canonicalKey);
|
|
11940
12401
|
}
|
|
11941
|
-
|
|
11942
|
-
console.warn(`[EventHandler] Failed to ensure chat-keyed session (chatId=${item.chatId}), ` + `falling back to trigger_session_id path`);
|
|
11943
|
-
sessionId = await this.ensureActiveSession(item.triggerSessionId);
|
|
11944
|
-
}
|
|
11945
|
-
} else {
|
|
11946
|
-
sessionId = await this.ensureActiveSession(item.triggerSessionId);
|
|
12402
|
+
return;
|
|
11947
12403
|
}
|
|
12404
|
+
await this.waitForIdle(sessionId ?? laneKey);
|
|
12405
|
+
if (this.isStopped)
|
|
12406
|
+
break;
|
|
12407
|
+
const claimKey = sessionId ?? laneKey;
|
|
12408
|
+
const releaseClaim = await this.claimSession(claimKey);
|
|
11948
12409
|
if (this.isStopped) {
|
|
12410
|
+
releaseClaim();
|
|
11949
12411
|
break;
|
|
11950
12412
|
}
|
|
11951
|
-
|
|
11952
|
-
|
|
12413
|
+
await this.acquireSlot();
|
|
12414
|
+
if (this.isStopped) {
|
|
12415
|
+
releaseClaim();
|
|
12416
|
+
this.releaseSlot();
|
|
12417
|
+
break;
|
|
11953
12418
|
}
|
|
11954
12419
|
try {
|
|
12420
|
+
const item = lane.shift();
|
|
12421
|
+
if (!item)
|
|
12422
|
+
break;
|
|
12423
|
+
this.inFlightCount++;
|
|
12424
|
+
try {
|
|
12425
|
+
await this.processItem(item, sessionId);
|
|
12426
|
+
} finally {
|
|
12427
|
+
this.inFlightCount--;
|
|
12428
|
+
}
|
|
12429
|
+
} finally {
|
|
12430
|
+
releaseClaim();
|
|
12431
|
+
this.releaseSlot();
|
|
12432
|
+
}
|
|
12433
|
+
}
|
|
12434
|
+
const rest = this.lanes.get(laneKey);
|
|
12435
|
+
if (!rest || rest.length === 0) {
|
|
12436
|
+
this.lanes.delete(laneKey);
|
|
12437
|
+
}
|
|
12438
|
+
}
|
|
12439
|
+
async resolveSessionForItem(laneKey, item) {
|
|
12440
|
+
if (item.sessionResolved) {
|
|
12441
|
+
return item.resolvedSessionId ?? null;
|
|
12442
|
+
}
|
|
12443
|
+
const resolved = await this.resolveSessionForItemUncached(laneKey, item);
|
|
12444
|
+
item.sessionResolved = true;
|
|
12445
|
+
item.resolvedSessionId = resolved;
|
|
12446
|
+
return resolved;
|
|
12447
|
+
}
|
|
12448
|
+
async resolveSessionForItemUncached(laneKey, item) {
|
|
12449
|
+
if (item.chatId && this.sessionComponent) {
|
|
12450
|
+
const cached = this.chatSessionCache.get(item.chatId);
|
|
12451
|
+
if (cached)
|
|
12452
|
+
return cached;
|
|
12453
|
+
let resolved = await this.ensureChatSession(item.chatId);
|
|
12454
|
+
if (!resolved) {
|
|
12455
|
+
console.warn(`[EventHandler] Failed to ensure chat-keyed session (chatId=${item.chatId}), ` + `falling back to trigger_session_id path`);
|
|
12456
|
+
resolved = await this.ensureActiveSession(item.triggerSessionId);
|
|
12457
|
+
} else {
|
|
12458
|
+
this.chatSessionCache.set(item.chatId, resolved);
|
|
12459
|
+
}
|
|
12460
|
+
return resolved;
|
|
12461
|
+
}
|
|
12462
|
+
return this.ensureActiveSession(item.triggerSessionId);
|
|
12463
|
+
}
|
|
12464
|
+
async processItem(item, sessionId) {
|
|
12465
|
+
if (sessionId) {
|
|
12466
|
+
item.eventSessionId = sessionId;
|
|
12467
|
+
}
|
|
12468
|
+
try {
|
|
12469
|
+
await runWithEnvContextAsync(item.envContext, async () => {
|
|
11955
12470
|
if (this.onEventWithEnv) {
|
|
11956
12471
|
await this.onEventWithEnv(item);
|
|
11957
|
-
|
|
12472
|
+
return;
|
|
11958
12473
|
}
|
|
11959
12474
|
await this.onEvent(item.message);
|
|
11960
|
-
}
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
}
|
|
11969
|
-
|
|
11970
|
-
|
|
12475
|
+
});
|
|
12476
|
+
} catch (error) {
|
|
12477
|
+
console.error(`[EventHandler] 处理事件失败:`, error);
|
|
12478
|
+
try {
|
|
12479
|
+
await notifyReactFailure2({
|
|
12480
|
+
error,
|
|
12481
|
+
envEvent: item.envEvent,
|
|
12482
|
+
sessionId: sessionId ?? undefined
|
|
12483
|
+
});
|
|
12484
|
+
} catch (notifyErr) {
|
|
12485
|
+
console.error(`[EventHandler] 失败说明回飞书异常:`, notifyErr);
|
|
11971
12486
|
}
|
|
11972
12487
|
}
|
|
11973
|
-
|
|
12488
|
+
}
|
|
12489
|
+
async processQueue() {
|
|
12490
|
+
if (this.isStopped)
|
|
12491
|
+
return;
|
|
12492
|
+
if (this.legacyQueue.length > 0) {
|
|
12493
|
+
const pending = this.legacyQueue.splice(0, this.legacyQueue.length);
|
|
12494
|
+
for (const item of pending) {
|
|
12495
|
+
if (!item.envContext) {
|
|
12496
|
+
item.envContext = getEnvContext();
|
|
12497
|
+
}
|
|
12498
|
+
const laneKey = this.laneKeyFor(item);
|
|
12499
|
+
const lane = this.lanes.get(laneKey);
|
|
12500
|
+
if (lane)
|
|
12501
|
+
lane.push(item);
|
|
12502
|
+
else
|
|
12503
|
+
this.lanes.set(laneKey, [item]);
|
|
12504
|
+
}
|
|
12505
|
+
}
|
|
12506
|
+
for (const [laneKey, lane] of Array.from(this.lanes.entries())) {
|
|
12507
|
+
if (lane.length > 0)
|
|
12508
|
+
this.scheduleLane(laneKey);
|
|
12509
|
+
}
|
|
12510
|
+
const tails = Array.from(this.laneTails.values());
|
|
12511
|
+
if (tails.length > 0) {
|
|
12512
|
+
await Promise.all(tails.map((t) => t.catch(() => {
|
|
12513
|
+
return;
|
|
12514
|
+
})));
|
|
12515
|
+
}
|
|
12516
|
+
}
|
|
12517
|
+
async acquireSlot() {
|
|
12518
|
+
if (this.activeSlots < this.maxConcurrentSessions) {
|
|
12519
|
+
this.activeSlots++;
|
|
12520
|
+
return;
|
|
12521
|
+
}
|
|
12522
|
+
await new Promise((resolve) => {
|
|
12523
|
+
this.slotWaiters.push(resolve);
|
|
12524
|
+
});
|
|
12525
|
+
this.activeSlots++;
|
|
12526
|
+
}
|
|
12527
|
+
releaseSlot() {
|
|
12528
|
+
if (this.activeSlots > 0)
|
|
12529
|
+
this.activeSlots--;
|
|
12530
|
+
const next = this.slotWaiters.shift();
|
|
12531
|
+
if (next)
|
|
12532
|
+
next();
|
|
11974
12533
|
}
|
|
11975
12534
|
async ensureActiveSession(triggerSessionIdOverride) {
|
|
11976
12535
|
if (!this.sessionComponent)
|
|
@@ -12080,6 +12639,7 @@ class EventHandler {
|
|
|
12080
12639
|
clearActiveSession() {
|
|
12081
12640
|
this.activeEventSessionId = null;
|
|
12082
12641
|
this.resolvedSessionByTrigger.clear();
|
|
12642
|
+
this.chatSessionCache.clear();
|
|
12083
12643
|
}
|
|
12084
12644
|
cacheResolvedSession(triggerSessionIdOverride, sessionId) {
|
|
12085
12645
|
if (triggerSessionIdOverride && sessionId) {
|
|
@@ -12089,11 +12649,38 @@ class EventHandler {
|
|
|
12089
12649
|
getActiveEventSessionId() {
|
|
12090
12650
|
return this.activeEventSessionId;
|
|
12091
12651
|
}
|
|
12092
|
-
async waitForIdle() {
|
|
12093
|
-
while (!this.isStopped && !this.
|
|
12652
|
+
async waitForIdle(sessionId) {
|
|
12653
|
+
while (!this.isStopped && !this.isLaneIdle(sessionId)) {
|
|
12094
12654
|
await this.sleep(this.idleCheckInterval);
|
|
12095
12655
|
}
|
|
12096
12656
|
}
|
|
12657
|
+
isLaneIdle(sessionId) {
|
|
12658
|
+
if (this.isSessionIdleFn && sessionId) {
|
|
12659
|
+
return this.isSessionIdleFn(sessionId);
|
|
12660
|
+
}
|
|
12661
|
+
return this.isIdle();
|
|
12662
|
+
}
|
|
12663
|
+
claimSession(sessionKey) {
|
|
12664
|
+
const prev = this.sessionChains.get(sessionKey) ?? Promise.resolve();
|
|
12665
|
+
let releaseFn;
|
|
12666
|
+
const current = new Promise((resolve) => {
|
|
12667
|
+
releaseFn = resolve;
|
|
12668
|
+
});
|
|
12669
|
+
const tail = prev.then(() => current);
|
|
12670
|
+
this.sessionChains.set(sessionKey, tail);
|
|
12671
|
+
return prev.then(() => {
|
|
12672
|
+
let released = false;
|
|
12673
|
+
return () => {
|
|
12674
|
+
if (released)
|
|
12675
|
+
return;
|
|
12676
|
+
released = true;
|
|
12677
|
+
releaseFn();
|
|
12678
|
+
if (this.sessionChains.get(sessionKey) === tail) {
|
|
12679
|
+
this.sessionChains.delete(sessionKey);
|
|
12680
|
+
}
|
|
12681
|
+
};
|
|
12682
|
+
});
|
|
12683
|
+
}
|
|
12097
12684
|
sleep(ms) {
|
|
12098
12685
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
12099
12686
|
}
|
|
@@ -12106,6 +12693,14 @@ __legacyDecorateClassTS([
|
|
|
12106
12693
|
]),
|
|
12107
12694
|
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12108
12695
|
], EventHandler.prototype, "handleEvent", null);
|
|
12696
|
+
__legacyDecorateClassTS([
|
|
12697
|
+
TracedAs5("event_handler.telemetry_notice", { recordParams: true, recordResult: false }),
|
|
12698
|
+
__legacyMetadataTS("design:type", Function),
|
|
12699
|
+
__legacyMetadataTS("design:paramtypes", [
|
|
12700
|
+
typeof EnvEvent === "undefined" ? Object : EnvEvent
|
|
12701
|
+
]),
|
|
12702
|
+
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12703
|
+
], EventHandler.prototype, "sendTelemetryNotice", null);
|
|
12109
12704
|
__legacyDecorateClassTS([
|
|
12110
12705
|
TracedAs5("cli.event-handler.formatEvent", { recordParams: true, recordResult: false }),
|
|
12111
12706
|
__legacyMetadataTS("design:type", Function),
|
|
@@ -12188,12 +12783,24 @@ function createEventHandlerForInteractive(options) {
|
|
|
12188
12783
|
const dbPath = options.dbPath ?? createDefaultEventStoreDbPath();
|
|
12189
12784
|
const eventStore = getOrCreateEventStore(dbPath);
|
|
12190
12785
|
const getHandlerForEsType = getHandlerForEsTypeForEnv(options.env);
|
|
12786
|
+
const maxConcurrentSessions = options.maxConcurrentSessions ?? readMaxConcurrentSessionsFromEnv();
|
|
12191
12787
|
return new EventHandler({
|
|
12192
12788
|
...options,
|
|
12789
|
+
...maxConcurrentSessions !== undefined ? { maxConcurrentSessions } : {},
|
|
12193
12790
|
eventStore,
|
|
12194
12791
|
getHandlerForEsType
|
|
12195
12792
|
});
|
|
12196
12793
|
}
|
|
12794
|
+
var MAX_CONCURRENT_SESSIONS_ENV = "ROY_AGENT_MAX_CONCURRENT_SESSIONS";
|
|
12795
|
+
function readMaxConcurrentSessionsFromEnv(env = process.env) {
|
|
12796
|
+
const raw = env[MAX_CONCURRENT_SESSIONS_ENV];
|
|
12797
|
+
if (raw === undefined || raw.trim() === "")
|
|
12798
|
+
return;
|
|
12799
|
+
const parsed = Number(raw);
|
|
12800
|
+
if (!Number.isFinite(parsed) || parsed < 1)
|
|
12801
|
+
return;
|
|
12802
|
+
return Math.max(1, Math.floor(parsed));
|
|
12803
|
+
}
|
|
12197
12804
|
|
|
12198
12805
|
// src/commands/shared/esc-active-descendant.ts
|
|
12199
12806
|
function hasActiveDescendants(env, parentSessionId) {
|
|
@@ -12866,7 +13473,8 @@ var USER_PROMPT = COLORS.accent("❯ ");
|
|
|
12866
13473
|
class REPL {
|
|
12867
13474
|
rl;
|
|
12868
13475
|
options;
|
|
12869
|
-
|
|
13476
|
+
executingSessions = new Map;
|
|
13477
|
+
static LEGACY_SESSION_KEY = "__legacy_isExecuting__";
|
|
12870
13478
|
replMode = "ready";
|
|
12871
13479
|
statusBarCtx = {};
|
|
12872
13480
|
isAltEnterPending = false;
|
|
@@ -13059,7 +13667,10 @@ ${COLORS.system("[正在中断...]")}`);
|
|
|
13059
13667
|
}
|
|
13060
13668
|
}
|
|
13061
13669
|
abortStream();
|
|
13062
|
-
this.
|
|
13670
|
+
for (const sid of this.getExecutingSessionIds()) {
|
|
13671
|
+
abortStream(sid);
|
|
13672
|
+
}
|
|
13673
|
+
this.abortAllExecutingSessions();
|
|
13063
13674
|
console.log(`${COLORS.system("[已中断,可以继续输入]")}`);
|
|
13064
13675
|
} else {
|
|
13065
13676
|
console.log(`
|
|
@@ -13144,7 +13755,8 @@ ${COLORS.system("[没有正在执行的请求]")}`);
|
|
|
13144
13755
|
this.inkApi = api;
|
|
13145
13756
|
},
|
|
13146
13757
|
onSubmit: async (input) => {
|
|
13147
|
-
|
|
13758
|
+
const inkSessionId = this.options.sessionId;
|
|
13759
|
+
this.markSessionExecuting(inkSessionId, true);
|
|
13148
13760
|
inkApiRef?.setMode("executing");
|
|
13149
13761
|
try {
|
|
13150
13762
|
await this.options.onExecute(input);
|
|
@@ -13162,7 +13774,7 @@ ${COLORS.system("[没有正在执行的请求]")}`);
|
|
|
13162
13774
|
inkApiRef?.appendMessage({ role: "system", content: `[Error] ${msg}` });
|
|
13163
13775
|
throw err;
|
|
13164
13776
|
} finally {
|
|
13165
|
-
this.
|
|
13777
|
+
this.markSessionExecuting(inkSessionId, false);
|
|
13166
13778
|
}
|
|
13167
13779
|
},
|
|
13168
13780
|
onCommand: async (cmd) => {
|
|
@@ -13315,7 +13927,8 @@ ${COLORS.error("❓ 未知命令: /" + cmd + "}")}`);
|
|
|
13315
13927
|
}
|
|
13316
13928
|
}
|
|
13317
13929
|
async handleMessage(message) {
|
|
13318
|
-
|
|
13930
|
+
const inputSessionId = this.options.sessionId;
|
|
13931
|
+
if (!this.isSessionIdle(inputSessionId)) {
|
|
13319
13932
|
console.log(`
|
|
13320
13933
|
${COLORS.system("⏳ 上一次请求尚未完成,请稍候...")}
|
|
13321
13934
|
`);
|
|
@@ -13382,7 +13995,7 @@ ${COLORS.system("[通知2] ❯ " + message.replace(/\n/g, `
|
|
|
13382
13995
|
};
|
|
13383
13996
|
}
|
|
13384
13997
|
}
|
|
13385
|
-
|
|
13998
|
+
const agentContext = {
|
|
13386
13999
|
agentType: agentFromEvent,
|
|
13387
14000
|
pluginEnabled,
|
|
13388
14001
|
envEvent: envEventForAgent,
|
|
@@ -13390,9 +14003,12 @@ ${COLORS.system("[通知2] ❯ " + message.replace(/\n/g, `
|
|
|
13390
14003
|
eventSessionId,
|
|
13391
14004
|
systemPrompt: envEvent.metadata?.systemPrompt
|
|
13392
14005
|
};
|
|
13393
|
-
await this.executeInternal(message, {
|
|
14006
|
+
await this.executeInternal(message, {
|
|
14007
|
+
showPrompt: true,
|
|
14008
|
+
sessionId: eventSessionId,
|
|
14009
|
+
agentContext
|
|
14010
|
+
});
|
|
13394
14011
|
}
|
|
13395
|
-
currentAgentContext = undefined;
|
|
13396
14012
|
lastReplyChannel = undefined;
|
|
13397
14013
|
lastReplyChannelByChat = new Map;
|
|
13398
14014
|
static REPLY_CHANNEL_CHAT_CACHE_MAX = 100;
|
|
@@ -13411,7 +14027,56 @@ ${COLORS.system("[通知2] ❯ " + message.replace(/\n/g, `
|
|
|
13411
14027
|
}
|
|
13412
14028
|
}
|
|
13413
14029
|
isIdle() {
|
|
13414
|
-
return !this.
|
|
14030
|
+
return !this.anySessionBusy();
|
|
14031
|
+
}
|
|
14032
|
+
anySessionBusy() {
|
|
14033
|
+
for (const count of this.execSet().values()) {
|
|
14034
|
+
if (count > 0)
|
|
14035
|
+
return true;
|
|
14036
|
+
}
|
|
14037
|
+
return false;
|
|
14038
|
+
}
|
|
14039
|
+
execSet() {
|
|
14040
|
+
if (!this.executingSessions)
|
|
14041
|
+
this.executingSessions = new Map;
|
|
14042
|
+
return this.executingSessions;
|
|
14043
|
+
}
|
|
14044
|
+
get isExecuting() {
|
|
14045
|
+
return this.anySessionBusy();
|
|
14046
|
+
}
|
|
14047
|
+
set isExecuting(executing) {
|
|
14048
|
+
if (!executing) {
|
|
14049
|
+
this.execSet().clear();
|
|
14050
|
+
return;
|
|
14051
|
+
}
|
|
14052
|
+
this.execSet().set(REPL.LEGACY_SESSION_KEY, 1);
|
|
14053
|
+
}
|
|
14054
|
+
markSessionExecuting(sessionId, executing) {
|
|
14055
|
+
const counts = this.execSet();
|
|
14056
|
+
if (executing) {
|
|
14057
|
+
counts.set(sessionId, (counts.get(sessionId) ?? 0) + 1);
|
|
14058
|
+
return;
|
|
14059
|
+
}
|
|
14060
|
+
const current = counts.get(sessionId) ?? 0;
|
|
14061
|
+
if (current <= 1) {
|
|
14062
|
+
counts.delete(sessionId);
|
|
14063
|
+
} else {
|
|
14064
|
+
counts.set(sessionId, current - 1);
|
|
14065
|
+
}
|
|
14066
|
+
}
|
|
14067
|
+
isSessionIdle(sessionId) {
|
|
14068
|
+
return (this.execSet().get(sessionId) ?? 0) <= 0;
|
|
14069
|
+
}
|
|
14070
|
+
getExecutingSessionIds() {
|
|
14071
|
+
const ids = [];
|
|
14072
|
+
for (const [sid, count] of this.execSet().entries()) {
|
|
14073
|
+
if (count > 0)
|
|
14074
|
+
ids.push(sid);
|
|
14075
|
+
}
|
|
14076
|
+
return ids;
|
|
14077
|
+
}
|
|
14078
|
+
abortAllExecutingSessions() {
|
|
14079
|
+
this.execSet().clear();
|
|
13415
14080
|
}
|
|
13416
14081
|
setMode(mode, detail) {
|
|
13417
14082
|
this.replMode = mode;
|
|
@@ -13434,30 +14099,37 @@ ${COLORS.system("[通知2] ❯ " + message.replace(/\n/g, `
|
|
|
13434
14099
|
}));
|
|
13435
14100
|
}
|
|
13436
14101
|
async executeInternal(message, options) {
|
|
13437
|
-
|
|
13438
|
-
|
|
13439
|
-
|
|
13440
|
-
|
|
14102
|
+
const execSessionId = options.sessionId ?? this.options.sessionId;
|
|
14103
|
+
const agentContext = {
|
|
14104
|
+
...options.agentContext,
|
|
14105
|
+
eventSessionId: options.agentContext?.eventSessionId ?? execSessionId
|
|
14106
|
+
};
|
|
13441
14107
|
resetStreamAbort();
|
|
13442
14108
|
const provider = getTracerProvider3();
|
|
13443
14109
|
const tracer = provider.getTracer("roy-tracer");
|
|
13444
|
-
const
|
|
14110
|
+
const payloadTraceContext = agentContext?.envEvent?.metadata?.traceContext;
|
|
14111
|
+
const existingContext = payloadTraceContext ?? provider.getGlobalContext();
|
|
13445
14112
|
let rootSpan;
|
|
13446
14113
|
let traceId;
|
|
13447
|
-
|
|
13448
|
-
rootSpan = tracer.startSpan("interactive.execute", {
|
|
13449
|
-
parent: existingContext
|
|
13450
|
-
});
|
|
13451
|
-
traceId = existingContext.traceId;
|
|
13452
|
-
} else {
|
|
13453
|
-
rootSpan = tracer.startSpan("interactive.execute");
|
|
13454
|
-
traceId = rootSpan.spanContext.traceId;
|
|
13455
|
-
provider.setGlobalContext(rootSpan.spanContext);
|
|
13456
|
-
}
|
|
13457
|
-
console.log(`${COLORS.system("[Trace] " + traceId)}`);
|
|
13458
|
-
console.log(`${COLORS.progress("[thinking...]")}`);
|
|
14114
|
+
let runTraceContext = existingContext;
|
|
13459
14115
|
try {
|
|
13460
|
-
|
|
14116
|
+
this.markSessionExecuting(execSessionId, true);
|
|
14117
|
+
this.setMode("executing");
|
|
14118
|
+
await provider.withGlobalContext(runTraceContext, async () => {
|
|
14119
|
+
if (existingContext) {
|
|
14120
|
+
rootSpan = tracer.startSpan("interactive.execute", {
|
|
14121
|
+
parent: existingContext
|
|
14122
|
+
});
|
|
14123
|
+
traceId = existingContext.traceId;
|
|
14124
|
+
} else {
|
|
14125
|
+
rootSpan = tracer.startSpan("interactive.execute");
|
|
14126
|
+
traceId = rootSpan.spanContext.traceId;
|
|
14127
|
+
runTraceContext = rootSpan.spanContext;
|
|
14128
|
+
}
|
|
14129
|
+
console.log(`${COLORS.system("[Trace] " + traceId)}`);
|
|
14130
|
+
console.log(`${COLORS.progress("[thinking...]")}`);
|
|
14131
|
+
await this.options.onExecute(message, traceId, agentContext);
|
|
14132
|
+
});
|
|
13461
14133
|
if (this.options.onAfterExecute) {
|
|
13462
14134
|
try {
|
|
13463
14135
|
await this.options.onAfterExecute(this.lastUsage);
|
|
@@ -13473,7 +14145,7 @@ ${COLORS.system("[通知2] ❯ " + message.replace(/\n/g, `
|
|
|
13473
14145
|
console.error(`
|
|
13474
14146
|
${COLORS.error("❌ 执行失败: " + error)}
|
|
13475
14147
|
`);
|
|
13476
|
-
rootSpan
|
|
14148
|
+
rootSpan?.setAttribute("error", String(error));
|
|
13477
14149
|
try {
|
|
13478
14150
|
await notifyReactFailure3({
|
|
13479
14151
|
error,
|
|
@@ -13485,14 +14157,15 @@ ${COLORS.error("❌ 执行失败: " + error)}
|
|
|
13485
14157
|
}
|
|
13486
14158
|
}
|
|
13487
14159
|
} finally {
|
|
13488
|
-
|
|
13489
|
-
|
|
13490
|
-
|
|
13491
|
-
|
|
13492
|
-
|
|
14160
|
+
try {
|
|
14161
|
+
provider.runInGlobalContextSync(runTraceContext, () => rootSpan?.end());
|
|
14162
|
+
} catch (endErr) {
|
|
14163
|
+
logger6.warn("root span end failed (ignored)", { error: String(endErr) });
|
|
14164
|
+
}
|
|
14165
|
+
this.markSessionExecuting(execSessionId, false);
|
|
14166
|
+
if (this.isIdle()) {
|
|
14167
|
+
this.setMode("ready");
|
|
13493
14168
|
}
|
|
13494
|
-
this.isExecuting = false;
|
|
13495
|
-
this.setMode("ready");
|
|
13496
14169
|
if (options.showPrompt) {
|
|
13497
14170
|
this.redrawInputArea();
|
|
13498
14171
|
if (this.rl) {
|
|
@@ -13662,6 +14335,9 @@ ${COLORS.error("❌ 执行失败: " + error)}
|
|
|
13662
14335
|
}
|
|
13663
14336
|
}
|
|
13664
14337
|
abortStream();
|
|
14338
|
+
for (const sid of this.getExecutingSessionIds()) {
|
|
14339
|
+
abortStream(sid);
|
|
14340
|
+
}
|
|
13665
14341
|
if (this.inkApi && typeof this.inkApi.finishAssistantStream === "function") {
|
|
13666
14342
|
try {
|
|
13667
14343
|
this.inkApi.finishAssistantStream();
|
|
@@ -13688,7 +14364,7 @@ ${COLORS.error("❌ 执行失败: " + error)}
|
|
|
13688
14364
|
}
|
|
13689
14365
|
}
|
|
13690
14366
|
if (isExecuting) {
|
|
13691
|
-
this.
|
|
14367
|
+
this.abortAllExecutingSessions();
|
|
13692
14368
|
}
|
|
13693
14369
|
this.setMode("ready");
|
|
13694
14370
|
if (this.rl) {
|
|
@@ -13891,6 +14567,57 @@ function createInteractiveCommand(externalEnvService) {
|
|
|
13891
14567
|
quiet: true,
|
|
13892
14568
|
silent: useInk
|
|
13893
14569
|
});
|
|
14570
|
+
const MAX_SESSION_EXECUTORS = 16;
|
|
14571
|
+
const sessionExecutors = new Map;
|
|
14572
|
+
const runningTurns = new Set;
|
|
14573
|
+
let sessionChunkCallback = undefined;
|
|
14574
|
+
const getSessionExecutor = (sid) => {
|
|
14575
|
+
if (!sid)
|
|
14576
|
+
return queryExecutor;
|
|
14577
|
+
const cached = sessionExecutors.get(sid);
|
|
14578
|
+
if (cached) {
|
|
14579
|
+
sessionExecutors.delete(sid);
|
|
14580
|
+
sessionExecutors.set(sid, cached);
|
|
14581
|
+
return cached;
|
|
14582
|
+
}
|
|
14583
|
+
const created = new QueryExecutor({
|
|
14584
|
+
env,
|
|
14585
|
+
sessionComponent,
|
|
14586
|
+
output,
|
|
14587
|
+
quiet: true,
|
|
14588
|
+
silent: true
|
|
14589
|
+
});
|
|
14590
|
+
created.initStreamOutput(llmComponent);
|
|
14591
|
+
created.setSessionScope(sid);
|
|
14592
|
+
created.setAcceptsSidelessEvents(false);
|
|
14593
|
+
created.setChunkCallback(sessionChunkCallback);
|
|
14594
|
+
sessionExecutors.set(sid, created);
|
|
14595
|
+
while (sessionExecutors.size > MAX_SESSION_EXECUTORS) {
|
|
14596
|
+
let victim;
|
|
14597
|
+
for (const key of sessionExecutors.keys()) {
|
|
14598
|
+
if (!runningTurns.has(key)) {
|
|
14599
|
+
victim = key;
|
|
14600
|
+
break;
|
|
14601
|
+
}
|
|
14602
|
+
}
|
|
14603
|
+
if (victim === undefined)
|
|
14604
|
+
break;
|
|
14605
|
+
const evicted = sessionExecutors.get(victim);
|
|
14606
|
+
sessionExecutors.delete(victim);
|
|
14607
|
+
try {
|
|
14608
|
+
evicted?.dispose();
|
|
14609
|
+
} catch {}
|
|
14610
|
+
}
|
|
14611
|
+
return created;
|
|
14612
|
+
};
|
|
14613
|
+
const disposeSessionExecutors = () => {
|
|
14614
|
+
for (const ex of sessionExecutors.values()) {
|
|
14615
|
+
try {
|
|
14616
|
+
ex.dispose();
|
|
14617
|
+
} catch {}
|
|
14618
|
+
}
|
|
14619
|
+
sessionExecutors.clear();
|
|
14620
|
+
};
|
|
13894
14621
|
let currentAgentContext = undefined;
|
|
13895
14622
|
if (args.agent && args.agent !== "default") {
|
|
13896
14623
|
const agentComponent = env.getComponent("agent");
|
|
@@ -13993,7 +14720,8 @@ function createInteractiveCommand(externalEnvService) {
|
|
|
13993
14720
|
version: getPackageVersion(),
|
|
13994
14721
|
useInk,
|
|
13995
14722
|
onExecute: async (message, traceId, agentContext) => {
|
|
13996
|
-
|
|
14723
|
+
const runSessionId = agentContext?.eventSessionId;
|
|
14724
|
+
resetStreamAbort(runSessionId);
|
|
13997
14725
|
const preloadedPrompts = eventSourceSystemPrompts;
|
|
13998
14726
|
const eventSourceSystemPrompt = preloadedPrompts.length > 0 ? joinResolvedSystemPrompts(preloadedPrompts) : undefined;
|
|
13999
14727
|
const mergedContext = {
|
|
@@ -14008,16 +14736,27 @@ function createInteractiveCommand(externalEnvService) {
|
|
|
14008
14736
|
}
|
|
14009
14737
|
};
|
|
14010
14738
|
const effectiveSessionId = mergedContext.eventSessionId ?? sessionId;
|
|
14011
|
-
const
|
|
14012
|
-
|
|
14013
|
-
|
|
14014
|
-
|
|
14015
|
-
|
|
14016
|
-
|
|
14017
|
-
|
|
14739
|
+
const isEventDrivenTurn = !!runSessionId && runSessionId !== sessionId;
|
|
14740
|
+
const exec2 = isEventDrivenTurn ? getSessionExecutor(effectiveSessionId) : queryExecutor;
|
|
14741
|
+
runningTurns.add(effectiveSessionId);
|
|
14742
|
+
try {
|
|
14743
|
+
const result = await exec2.execute(message, effectiveSessionId, {
|
|
14744
|
+
showReasoning: args.reasoning,
|
|
14745
|
+
showToolCalls: args.toolCalls,
|
|
14746
|
+
showToolResults: args.toolResults
|
|
14747
|
+
}, traceId, mergedContext);
|
|
14748
|
+
if (result && !exec2.getFullText()) {
|
|
14749
|
+
console.log(result);
|
|
14750
|
+
} else if (isEventDrivenTurn) {
|
|
14751
|
+
const full = exec2.getFullText();
|
|
14752
|
+
if (full)
|
|
14753
|
+
console.log(full);
|
|
14754
|
+
}
|
|
14755
|
+
repl.setLastUsage(exec2.getLastUsage());
|
|
14756
|
+
return result;
|
|
14757
|
+
} finally {
|
|
14758
|
+
runningTurns.delete(effectiveSessionId);
|
|
14018
14759
|
}
|
|
14019
|
-
repl.setLastUsage(queryExecutor.getLastUsage());
|
|
14020
|
-
return result;
|
|
14021
14760
|
},
|
|
14022
14761
|
onAfterExecute: (usage) => {
|
|
14023
14762
|
if (useInk)
|
|
@@ -14042,6 +14781,7 @@ function createInteractiveCommand(externalEnvService) {
|
|
|
14042
14781
|
onSwitchSession: async (newSessionId) => {
|
|
14043
14782
|
const result = await sessionManager.switchSession(newSessionId);
|
|
14044
14783
|
queryExecutor.dispose();
|
|
14784
|
+
disposeSessionExecutors();
|
|
14045
14785
|
return result;
|
|
14046
14786
|
},
|
|
14047
14787
|
onCompact: async () => {
|
|
@@ -14091,6 +14831,7 @@ function createInteractiveCommand(externalEnvService) {
|
|
|
14091
14831
|
eventTypes: ["task.background.*", "event-source.event.*"],
|
|
14092
14832
|
formatter: new EventMessageFormatter({ prefix: "[通知]" }),
|
|
14093
14833
|
isIdle: () => repl.isIdle(),
|
|
14834
|
+
isSessionIdle: (sid) => repl.isSessionIdle(sid),
|
|
14094
14835
|
sessionComponent,
|
|
14095
14836
|
onEvent: async (_message) => {},
|
|
14096
14837
|
onEventWithEnv: async (formattedEvent) => {
|
|
@@ -14124,6 +14865,20 @@ function createInteractiveCommand(externalEnvService) {
|
|
|
14124
14865
|
repl.sessionManager = sessionManager;
|
|
14125
14866
|
repl.sessionComponent = sessionComponent;
|
|
14126
14867
|
repl._queryExecutor = queryExecutor;
|
|
14868
|
+
{
|
|
14869
|
+
let inkCb = null;
|
|
14870
|
+
Object.defineProperty(repl, "inkChunkCallback", {
|
|
14871
|
+
configurable: true,
|
|
14872
|
+
get: () => inkCb,
|
|
14873
|
+
set: (cb) => {
|
|
14874
|
+
inkCb = cb;
|
|
14875
|
+
sessionChunkCallback = cb ?? undefined;
|
|
14876
|
+
for (const ex of sessionExecutors.values()) {
|
|
14877
|
+
ex.setChunkCallback(sessionChunkCallback);
|
|
14878
|
+
}
|
|
14879
|
+
}
|
|
14880
|
+
});
|
|
14881
|
+
}
|
|
14127
14882
|
await repl.start();
|
|
14128
14883
|
process.removeListener("SIGINT", sigintHandler);
|
|
14129
14884
|
console.log("正在清理资源...");
|
|
@@ -14135,6 +14890,7 @@ function createInteractiveCommand(externalEnvService) {
|
|
|
14135
14890
|
}
|
|
14136
14891
|
eventHandler.stop();
|
|
14137
14892
|
queryExecutor.dispose();
|
|
14893
|
+
disposeSessionExecutors();
|
|
14138
14894
|
const esComponent = env.getComponent("event-source");
|
|
14139
14895
|
if (esComponent) {
|
|
14140
14896
|
const sources = esComponent.list?.();
|
|
@@ -15502,6 +16258,7 @@ async function loadHandleQuery(options, hooks) {
|
|
|
15502
16258
|
const llmComponent = envService.getLLM();
|
|
15503
16259
|
queryExecutor.initStreamOutput(llmComponent ?? null);
|
|
15504
16260
|
const handleQuery = async (message, envEvent) => {
|
|
16261
|
+
const eventTraceContext = envEvent?.metadata?.traceContext;
|
|
15505
16262
|
const mergedContext = {
|
|
15506
16263
|
...agentContext,
|
|
15507
16264
|
model: options.model,
|
|
@@ -15516,7 +16273,7 @@ async function loadHandleQuery(options, hooks) {
|
|
|
15516
16273
|
showReasoning: options.reasoning ?? true,
|
|
15517
16274
|
showToolCalls: options.toolCalls ?? true,
|
|
15518
16275
|
showToolResults: options.toolResults ?? false
|
|
15519
|
-
},
|
|
16276
|
+
}, eventTraceContext?.traceId, mergedContext);
|
|
15520
16277
|
return String(result ?? "");
|
|
15521
16278
|
};
|
|
15522
16279
|
const taskEventHandler = new TaskEventHandler({
|
|
@@ -15593,6 +16350,7 @@ function createStreamHandleQuery(queryExecutor, sessionId, options, agentContext
|
|
|
15593
16350
|
break;
|
|
15594
16351
|
}
|
|
15595
16352
|
});
|
|
16353
|
+
const eventTraceContext = envEvent?.metadata?.traceContext;
|
|
15596
16354
|
const mergedContext = {
|
|
15597
16355
|
...agentContext,
|
|
15598
16356
|
model: options.model,
|
|
@@ -15607,7 +16365,7 @@ function createStreamHandleQuery(queryExecutor, sessionId, options, agentContext
|
|
|
15607
16365
|
showReasoning: options.reasoning ?? true,
|
|
15608
16366
|
showToolCalls: true,
|
|
15609
16367
|
showToolResults: true
|
|
15610
|
-
},
|
|
16368
|
+
}, eventTraceContext?.traceId, mergedContext);
|
|
15611
16369
|
return String(result ?? "");
|
|
15612
16370
|
};
|
|
15613
16371
|
}
|