@dexto/core 1.6.25 → 1.6.26
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/agent/DextoAgent.cjs +27 -14
- package/dist/agent/DextoAgent.d.ts +7 -6
- package/dist/agent/DextoAgent.d.ts.map +1 -1
- package/dist/agent/DextoAgent.js +27 -14
- package/dist/approval/manager.cjs +328 -178
- package/dist/approval/manager.d.ts +39 -31
- package/dist/approval/manager.d.ts.map +1 -1
- package/dist/approval/manager.js +328 -178
- package/dist/approval/session-approval-store.cjs +91 -0
- package/dist/approval/session-approval-store.d.ts +55 -0
- package/dist/approval/session-approval-store.d.ts.map +1 -0
- package/dist/approval/session-approval-store.js +68 -0
- package/dist/llm/executor/turn-executor.cjs +7 -3
- package/dist/llm/executor/turn-executor.d.ts.map +1 -1
- package/dist/llm/executor/turn-executor.js +7 -3
- package/dist/llm/services/factory.cjs +10 -4
- package/dist/llm/services/factory.d.ts +2 -21
- package/dist/llm/services/factory.d.ts.map +1 -1
- package/dist/llm/services/factory.js +11 -7
- package/dist/llm/services/types.d.ts +33 -2
- package/dist/llm/services/types.d.ts.map +1 -1
- package/dist/llm/services/vercel.cjs +4 -5
- package/dist/llm/services/vercel.d.ts +3 -3
- package/dist/llm/services/vercel.d.ts.map +1 -1
- package/dist/llm/services/vercel.js +2 -3
- package/dist/logger/default-logger-factory.d.ts +12 -12
- package/dist/logger/v2/schemas.d.ts +6 -6
- package/dist/mcp/schemas.d.ts +10 -10
- package/dist/session/chat-session.cjs +39 -41
- package/dist/session/chat-session.d.ts +22 -12
- package/dist/session/chat-session.d.ts.map +1 -1
- package/dist/session/chat-session.js +39 -41
- package/dist/session/message-queue-store.cjs +75 -0
- package/dist/session/message-queue-store.d.ts +16 -0
- package/dist/session/message-queue-store.d.ts.map +1 -0
- package/dist/session/message-queue-store.js +52 -0
- package/dist/session/message-queue.cjs +140 -46
- package/dist/session/message-queue.d.ts +18 -6
- package/dist/session/message-queue.d.ts.map +1 -1
- package/dist/session/message-queue.js +140 -46
- package/dist/session/session-manager.cjs +130 -25
- package/dist/session/session-manager.d.ts +18 -1
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manager.js +130 -25
- package/dist/session/title-generator.cjs +9 -2
- package/dist/session/title-generator.d.ts +2 -0
- package/dist/session/title-generator.d.ts.map +1 -1
- package/dist/session/title-generator.js +9 -2
- package/dist/telemetry/errors.cjs +2 -2
- package/dist/telemetry/errors.js +2 -2
- package/dist/telemetry/index.d.ts +1 -1
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +3 -1
- package/dist/telemetry/telemetry.cjs +62 -21
- package/dist/telemetry/telemetry.d.ts +14 -0
- package/dist/telemetry/telemetry.d.ts.map +1 -1
- package/dist/telemetry/telemetry.js +62 -21
- package/dist/test-utils/session-state-stores.cjs +68 -0
- package/dist/test-utils/session-state-stores.js +42 -0
- package/dist/tools/session-tool-preferences-store.cjs +86 -0
- package/dist/tools/session-tool-preferences-store.d.ts +29 -0
- package/dist/tools/session-tool-preferences-store.d.ts.map +1 -0
- package/dist/tools/session-tool-preferences-store.js +63 -0
- package/dist/tools/tool-manager.cjs +131 -32
- package/dist/tools/tool-manager.d.ts +17 -6
- package/dist/tools/tool-manager.d.ts.map +1 -1
- package/dist/tools/tool-manager.js +131 -32
- package/dist/utils/service-initializer.cjs +38 -5
- package/dist/utils/service-initializer.d.ts +11 -1
- package/dist/utils/service-initializer.d.ts.map +1 -1
- package/dist/utils/service-initializer.js +36 -4
- package/package.json +1 -1
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { DextoLogComponent } from "../logger/v2/types.js";
|
|
10
10
|
import { DextoRuntimeError, ErrorScope, ErrorType } from "../errors/index.js";
|
|
11
11
|
import { HookErrorCode } from "../hooks/error-codes.js";
|
|
12
|
+
import { MessageQueueService } from "./message-queue.js";
|
|
12
13
|
import { getUsagePricingMetadata, hasMeaningfulTokenUsage } from "../llm/usage-metadata.js";
|
|
13
14
|
import { parseCodexBaseURL } from "../llm/providers/codex-base-url.js";
|
|
14
15
|
class ChatSession {
|
|
@@ -17,7 +18,7 @@ class ChatSession {
|
|
|
17
18
|
*
|
|
18
19
|
* Each session creates its own isolated services:
|
|
19
20
|
* - ConversationHistoryProvider (with session-specific storage, shared across LLM switches)
|
|
20
|
-
* -
|
|
21
|
+
* - LLM service (creates its own properly-typed ContextManager internally)
|
|
21
22
|
* - SessionEventBus (session-local event handling with forwarding)
|
|
22
23
|
*
|
|
23
24
|
* @param services - The shared services from the agent (state manager, prompt, client managers, etc.)
|
|
@@ -29,6 +30,12 @@ class ChatSession {
|
|
|
29
30
|
this.id = id;
|
|
30
31
|
this.logger = logger.createChild(DextoLogComponent.SESSION);
|
|
31
32
|
this.eventBus = new SessionEventBus();
|
|
33
|
+
this.messageQueue = new MessageQueueService(
|
|
34
|
+
this.eventBus,
|
|
35
|
+
this.logger,
|
|
36
|
+
this.id,
|
|
37
|
+
this.services.messageQueueStore
|
|
38
|
+
);
|
|
32
39
|
this.setupEventForwarding();
|
|
33
40
|
this.logger.debug(`ChatSession ${this.id}: Created, awaiting initialization`);
|
|
34
41
|
}
|
|
@@ -52,10 +59,15 @@ class ChatSession {
|
|
|
52
59
|
/**
|
|
53
60
|
* Handles AI model interactions, tool execution, and response generation for this session.
|
|
54
61
|
*
|
|
55
|
-
* Each session has its own
|
|
62
|
+
* Each session has its own LLM runtime instance that uses the session's
|
|
56
63
|
* ContextManager and event bus.
|
|
57
64
|
*/
|
|
58
65
|
llmService;
|
|
66
|
+
/**
|
|
67
|
+
* Durable queued follow-up messages for this session.
|
|
68
|
+
* Reused across LLM switches so mid-task follow-ups survive service recreation.
|
|
69
|
+
*/
|
|
70
|
+
messageQueue;
|
|
59
71
|
/**
|
|
60
72
|
* Map of event forwarder functions for cleanup.
|
|
61
73
|
* Stores the bound functions so they can be removed from the event bus.
|
|
@@ -147,33 +159,35 @@ class ChatSession {
|
|
|
147
159
|
async initializeServices() {
|
|
148
160
|
const runtimeConfig = this.services.stateManager.getRuntimeConfig(this.id);
|
|
149
161
|
const llmConfig = runtimeConfig.llm;
|
|
150
|
-
|
|
162
|
+
await this.messageQueue.initialize();
|
|
151
163
|
this.historyProvider = createDatabaseHistoryProvider(
|
|
152
164
|
this.services.storageManager.getDatabase(),
|
|
153
165
|
this.id,
|
|
154
166
|
this.logger
|
|
155
167
|
);
|
|
156
|
-
|
|
157
|
-
this.
|
|
168
|
+
this.llmService = await this.createSessionLLMService(llmConfig, runtimeConfig.usageScopeId);
|
|
169
|
+
this.logger.debug(`ChatSession ${this.id}: Services initialized with storage`);
|
|
170
|
+
}
|
|
171
|
+
async createSessionLLMService(llmConfig, usageScopeId) {
|
|
172
|
+
const workspace = await this.services.workspaceManager?.getWorkspace();
|
|
173
|
+
const options = {
|
|
174
|
+
usageScopeId,
|
|
175
|
+
compactionStrategy: this.services.compactionStrategy,
|
|
176
|
+
...workspace?.path !== void 0 && { cwd: workspace.path },
|
|
177
|
+
messageQueue: this.messageQueue
|
|
178
|
+
};
|
|
179
|
+
return createLLMService(
|
|
158
180
|
llmConfig,
|
|
159
181
|
this.services.toolManager,
|
|
160
182
|
this.services.systemPromptManager,
|
|
161
183
|
this.historyProvider,
|
|
162
|
-
// Pass history provider for service to use
|
|
163
184
|
this.eventBus,
|
|
164
|
-
// Use session event bus
|
|
165
185
|
this.id,
|
|
166
186
|
this.services.resourceManager,
|
|
167
|
-
// Pass ResourceManager for blob storage
|
|
168
187
|
this.logger,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
usageScopeId: runtimeConfig.usageScopeId,
|
|
172
|
-
compactionStrategy,
|
|
173
|
-
cwd: workspace?.path
|
|
174
|
-
}
|
|
188
|
+
options,
|
|
189
|
+
this.services.languageModelFactory
|
|
175
190
|
);
|
|
176
|
-
this.logger.debug(`ChatSession ${this.id}: Services initialized with storage`);
|
|
177
191
|
}
|
|
178
192
|
/**
|
|
179
193
|
* Saves a blocked interaction to history when a plugin blocks execution.
|
|
@@ -429,9 +443,9 @@ class ChatSession {
|
|
|
429
443
|
return this.llmService.getContextManager();
|
|
430
444
|
}
|
|
431
445
|
/**
|
|
432
|
-
* Gets the session's
|
|
446
|
+
* Gets the session's LLM service instance.
|
|
433
447
|
*
|
|
434
|
-
* @returns The
|
|
448
|
+
* @returns The session LLM service for this session
|
|
435
449
|
*/
|
|
436
450
|
getLLMService() {
|
|
437
451
|
return this.llmService;
|
|
@@ -458,26 +472,10 @@ class ChatSession {
|
|
|
458
472
|
async switchLLM(newLLMConfig) {
|
|
459
473
|
try {
|
|
460
474
|
const runtimeConfig = this.services.stateManager.getRuntimeConfig(this.id);
|
|
461
|
-
|
|
462
|
-
const compactionStrategy = this.services.compactionStrategy;
|
|
463
|
-
const newLLMService = createLLMService(
|
|
475
|
+
this.llmService = await this.createSessionLLMService(
|
|
464
476
|
newLLMConfig,
|
|
465
|
-
|
|
466
|
-
this.services.systemPromptManager,
|
|
467
|
-
this.historyProvider,
|
|
468
|
-
// Pass the SAME history provider - preserves conversation!
|
|
469
|
-
this.eventBus,
|
|
470
|
-
// Use session event bus
|
|
471
|
-
this.id,
|
|
472
|
-
this.services.resourceManager,
|
|
473
|
-
this.logger,
|
|
474
|
-
{
|
|
475
|
-
usageScopeId: runtimeConfig.usageScopeId,
|
|
476
|
-
compactionStrategy,
|
|
477
|
-
cwd: workspace?.path
|
|
478
|
-
}
|
|
477
|
+
runtimeConfig.usageScopeId
|
|
479
478
|
);
|
|
480
|
-
this.llmService = newLLMService;
|
|
481
479
|
this.logger.info(
|
|
482
480
|
`ChatSession ${this.id}: LLM switched to ${newLLMConfig.provider}/${newLLMConfig.model}`
|
|
483
481
|
);
|
|
@@ -543,8 +541,8 @@ class ChatSession {
|
|
|
543
541
|
* @param message The user message to queue
|
|
544
542
|
* @returns Queue position and message ID
|
|
545
543
|
*/
|
|
546
|
-
queueMessage(message) {
|
|
547
|
-
return this.llmService.getMessageQueue().enqueue(message);
|
|
544
|
+
async queueMessage(message) {
|
|
545
|
+
return await this.llmService.getMessageQueue().enqueue(message);
|
|
548
546
|
}
|
|
549
547
|
/**
|
|
550
548
|
* Get all messages currently in the queue.
|
|
@@ -558,17 +556,17 @@ class ChatSession {
|
|
|
558
556
|
* @param id Message ID to remove
|
|
559
557
|
* @returns true if message was found and removed; false otherwise
|
|
560
558
|
*/
|
|
561
|
-
removeQueuedMessage(id) {
|
|
562
|
-
return this.llmService.getMessageQueue().remove(id);
|
|
559
|
+
async removeQueuedMessage(id) {
|
|
560
|
+
return await this.llmService.getMessageQueue().remove(id);
|
|
563
561
|
}
|
|
564
562
|
/**
|
|
565
563
|
* Clear all queued messages.
|
|
566
564
|
* @returns Number of messages that were cleared
|
|
567
565
|
*/
|
|
568
|
-
clearMessageQueue() {
|
|
566
|
+
async clearMessageQueue() {
|
|
569
567
|
const queue = this.llmService.getMessageQueue();
|
|
570
568
|
const count = queue.pendingCount();
|
|
571
|
-
queue.clear();
|
|
569
|
+
await queue.clear();
|
|
572
570
|
return count;
|
|
573
571
|
}
|
|
574
572
|
/**
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var message_queue_store_exports = {};
|
|
20
|
+
__export(message_queue_store_exports, {
|
|
21
|
+
MessageQueueStore: () => MessageQueueStore
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(message_queue_store_exports);
|
|
24
|
+
class MessageQueueStore {
|
|
25
|
+
constructor(storageManager, logger, options = {}) {
|
|
26
|
+
this.storageManager = storageManager;
|
|
27
|
+
this.logger = logger;
|
|
28
|
+
const cacheTtlMs = options.cacheTtlMs ?? 36e5;
|
|
29
|
+
this.cacheTtlSeconds = Math.max(1, Math.floor(cacheTtlMs / 1e3));
|
|
30
|
+
}
|
|
31
|
+
cacheTtlSeconds;
|
|
32
|
+
buildKey(sessionId) {
|
|
33
|
+
return `session-message-queue:${sessionId}`;
|
|
34
|
+
}
|
|
35
|
+
async load(sessionId) {
|
|
36
|
+
const key = this.buildKey(sessionId);
|
|
37
|
+
const cached = await this.storageManager.getCache().get(key);
|
|
38
|
+
if (Array.isArray(cached)) {
|
|
39
|
+
return structuredClone(cached);
|
|
40
|
+
}
|
|
41
|
+
const stored = await this.storageManager.getDatabase().get(key);
|
|
42
|
+
if (!Array.isArray(stored)) {
|
|
43
|
+
if (stored !== void 0) {
|
|
44
|
+
this.logger.warn("Invalid persisted message queue encountered; ignoring state", {
|
|
45
|
+
key
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
const cloned = structuredClone(stored);
|
|
51
|
+
await this.storageManager.getCache().set(key, cloned, this.cacheTtlSeconds);
|
|
52
|
+
return cloned;
|
|
53
|
+
}
|
|
54
|
+
async save(sessionId, queue) {
|
|
55
|
+
const key = this.buildKey(sessionId);
|
|
56
|
+
if (queue.length === 0) {
|
|
57
|
+
await this.delete(sessionId);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const cloned = structuredClone(queue);
|
|
61
|
+
await this.storageManager.getDatabase().set(key, cloned);
|
|
62
|
+
await this.storageManager.getCache().set(key, cloned, this.cacheTtlSeconds);
|
|
63
|
+
}
|
|
64
|
+
async delete(sessionId) {
|
|
65
|
+
const key = this.buildKey(sessionId);
|
|
66
|
+
await Promise.all([
|
|
67
|
+
this.storageManager.getDatabase().delete(key),
|
|
68
|
+
this.storageManager.getCache().delete(key)
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
73
|
+
0 && (module.exports = {
|
|
74
|
+
MessageQueueStore
|
|
75
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Logger } from '../logger/v2/types.js';
|
|
2
|
+
import type { StorageManager } from '../storage/index.js';
|
|
3
|
+
import type { QueuedMessage } from './types.js';
|
|
4
|
+
export declare class MessageQueueStore {
|
|
5
|
+
private readonly storageManager;
|
|
6
|
+
private readonly logger;
|
|
7
|
+
private readonly cacheTtlSeconds;
|
|
8
|
+
constructor(storageManager: StorageManager, logger: Logger, options?: {
|
|
9
|
+
cacheTtlMs?: number;
|
|
10
|
+
});
|
|
11
|
+
private buildKey;
|
|
12
|
+
load(sessionId: string): Promise<QueuedMessage[]>;
|
|
13
|
+
save(sessionId: string, queue: QueuedMessage[]): Promise<void>;
|
|
14
|
+
delete(sessionId: string): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=message-queue-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-queue-store.d.ts","sourceRoot":"","sources":["../../src/session/message-queue-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,qBAAa,iBAAiB;IAItB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJ3B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAGpB,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,MAAM,EAC/B,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO;IAMzC,OAAO,CAAC,QAAQ;IAIV,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAsBjD,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAY9D,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAOjD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import "../chunk-PTJYTZNU.js";
|
|
2
|
+
class MessageQueueStore {
|
|
3
|
+
constructor(storageManager, logger, options = {}) {
|
|
4
|
+
this.storageManager = storageManager;
|
|
5
|
+
this.logger = logger;
|
|
6
|
+
const cacheTtlMs = options.cacheTtlMs ?? 36e5;
|
|
7
|
+
this.cacheTtlSeconds = Math.max(1, Math.floor(cacheTtlMs / 1e3));
|
|
8
|
+
}
|
|
9
|
+
cacheTtlSeconds;
|
|
10
|
+
buildKey(sessionId) {
|
|
11
|
+
return `session-message-queue:${sessionId}`;
|
|
12
|
+
}
|
|
13
|
+
async load(sessionId) {
|
|
14
|
+
const key = this.buildKey(sessionId);
|
|
15
|
+
const cached = await this.storageManager.getCache().get(key);
|
|
16
|
+
if (Array.isArray(cached)) {
|
|
17
|
+
return structuredClone(cached);
|
|
18
|
+
}
|
|
19
|
+
const stored = await this.storageManager.getDatabase().get(key);
|
|
20
|
+
if (!Array.isArray(stored)) {
|
|
21
|
+
if (stored !== void 0) {
|
|
22
|
+
this.logger.warn("Invalid persisted message queue encountered; ignoring state", {
|
|
23
|
+
key
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
const cloned = structuredClone(stored);
|
|
29
|
+
await this.storageManager.getCache().set(key, cloned, this.cacheTtlSeconds);
|
|
30
|
+
return cloned;
|
|
31
|
+
}
|
|
32
|
+
async save(sessionId, queue) {
|
|
33
|
+
const key = this.buildKey(sessionId);
|
|
34
|
+
if (queue.length === 0) {
|
|
35
|
+
await this.delete(sessionId);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const cloned = structuredClone(queue);
|
|
39
|
+
await this.storageManager.getDatabase().set(key, cloned);
|
|
40
|
+
await this.storageManager.getCache().set(key, cloned, this.cacheTtlSeconds);
|
|
41
|
+
}
|
|
42
|
+
async delete(sessionId) {
|
|
43
|
+
const key = this.buildKey(sessionId);
|
|
44
|
+
await Promise.all([
|
|
45
|
+
this.storageManager.getDatabase().delete(key),
|
|
46
|
+
this.storageManager.getCache().delete(key)
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
MessageQueueStore
|
|
52
|
+
};
|
|
@@ -21,15 +21,71 @@ __export(message_queue_exports, {
|
|
|
21
21
|
MessageQueueService: () => MessageQueueService
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(message_queue_exports);
|
|
24
|
+
class EphemeralMessageQueueStore {
|
|
25
|
+
async load(sessionId) {
|
|
26
|
+
void sessionId;
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
async save(sessionId, queue) {
|
|
30
|
+
void sessionId;
|
|
31
|
+
void queue;
|
|
32
|
+
}
|
|
33
|
+
async delete(sessionId) {
|
|
34
|
+
void sessionId;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
24
37
|
function generateId() {
|
|
25
38
|
return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
26
39
|
}
|
|
27
40
|
class MessageQueueService {
|
|
28
|
-
constructor(eventBus, logger) {
|
|
41
|
+
constructor(eventBus, logger, sessionId, store) {
|
|
29
42
|
this.eventBus = eventBus;
|
|
30
43
|
this.logger = logger;
|
|
44
|
+
this.sessionId = sessionId;
|
|
45
|
+
this.store = store;
|
|
31
46
|
}
|
|
32
47
|
queue = [];
|
|
48
|
+
mutationLock = Promise.resolve();
|
|
49
|
+
initialized = false;
|
|
50
|
+
initializationPromise = null;
|
|
51
|
+
static createEphemeral(eventBus, logger, sessionId) {
|
|
52
|
+
return new MessageQueueService(
|
|
53
|
+
eventBus,
|
|
54
|
+
logger,
|
|
55
|
+
sessionId,
|
|
56
|
+
new EphemeralMessageQueueStore()
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
async initialize() {
|
|
60
|
+
this.initializationPromise ??= this.runWithMutationLock(async () => {
|
|
61
|
+
if (this.initialized) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
this.queue = await this.store.load(this.sessionId);
|
|
65
|
+
if (this.queue.length > 0) {
|
|
66
|
+
this.logger.debug(
|
|
67
|
+
`Restored ${this.queue.length} queued message(s) for session ${this.sessionId}`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
this.initialized = true;
|
|
71
|
+
}).catch((error) => {
|
|
72
|
+
this.initializationPromise = null;
|
|
73
|
+
throw error;
|
|
74
|
+
});
|
|
75
|
+
await this.initializationPromise;
|
|
76
|
+
}
|
|
77
|
+
async persistQueue() {
|
|
78
|
+
await this.store.save(this.sessionId, this.queue);
|
|
79
|
+
}
|
|
80
|
+
runWithMutationLock(fn) {
|
|
81
|
+
const currentResult = this.mutationLock.catch(() => {
|
|
82
|
+
}).then(() => fn());
|
|
83
|
+
this.mutationLock = currentResult.then(
|
|
84
|
+
() => void 0,
|
|
85
|
+
() => void 0
|
|
86
|
+
);
|
|
87
|
+
return currentResult;
|
|
88
|
+
}
|
|
33
89
|
/**
|
|
34
90
|
* Add a message to the queue.
|
|
35
91
|
* Called by API endpoint - returns immediately with queue position.
|
|
@@ -37,25 +93,33 @@ class MessageQueueService {
|
|
|
37
93
|
* @param message The user message to queue
|
|
38
94
|
* @returns Queue position and message ID
|
|
39
95
|
*/
|
|
40
|
-
enqueue(message) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
96
|
+
async enqueue(message) {
|
|
97
|
+
return await this.runWithMutationLock(async () => {
|
|
98
|
+
const queuedMsg = {
|
|
99
|
+
id: generateId(),
|
|
100
|
+
content: message.content,
|
|
101
|
+
queuedAt: Date.now(),
|
|
102
|
+
...message.metadata !== void 0 && { metadata: message.metadata },
|
|
103
|
+
...message.kind !== void 0 && { kind: message.kind }
|
|
104
|
+
};
|
|
105
|
+
this.queue.push(queuedMsg);
|
|
106
|
+
try {
|
|
107
|
+
await this.persistQueue();
|
|
108
|
+
} catch (error) {
|
|
109
|
+
this.queue.pop();
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
this.logger.debug(`Message queued: ${queuedMsg.id}, position: ${this.queue.length}`);
|
|
113
|
+
this.eventBus.emit("message:queued", {
|
|
114
|
+
position: this.queue.length,
|
|
115
|
+
id: queuedMsg.id
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
queued: true,
|
|
119
|
+
position: this.queue.length,
|
|
120
|
+
id: queuedMsg.id
|
|
121
|
+
};
|
|
53
122
|
});
|
|
54
|
-
return {
|
|
55
|
-
queued: true,
|
|
56
|
-
position: this.queue.length,
|
|
57
|
-
id: queuedMsg.id
|
|
58
|
-
};
|
|
59
123
|
}
|
|
60
124
|
/**
|
|
61
125
|
* Dequeue ALL pending messages and coalesce into single injection.
|
|
@@ -76,22 +140,30 @@ class MessageQueueService {
|
|
|
76
140
|
*
|
|
77
141
|
* @returns Coalesced message or null if queue is empty
|
|
78
142
|
*/
|
|
79
|
-
dequeueAll() {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
143
|
+
async dequeueAll() {
|
|
144
|
+
return await this.runWithMutationLock(async () => {
|
|
145
|
+
if (this.queue.length === 0) return null;
|
|
146
|
+
const messages = [...this.queue];
|
|
147
|
+
this.queue = [];
|
|
148
|
+
try {
|
|
149
|
+
await this.persistQueue();
|
|
150
|
+
} catch (error) {
|
|
151
|
+
this.queue = messages;
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
const combined = this.coalesce(messages);
|
|
155
|
+
this.logger.debug(
|
|
156
|
+
`Dequeued ${messages.length} message(s): ${messages.map((m) => m.id).join(", ")}`
|
|
157
|
+
);
|
|
158
|
+
this.eventBus.emit("message:dequeued", {
|
|
159
|
+
count: messages.length,
|
|
160
|
+
ids: messages.map((m) => m.id),
|
|
161
|
+
coalesced: messages.length > 1,
|
|
162
|
+
content: combined.combinedContent,
|
|
163
|
+
messages
|
|
164
|
+
});
|
|
165
|
+
return combined;
|
|
93
166
|
});
|
|
94
|
-
return combined;
|
|
95
167
|
}
|
|
96
168
|
/**
|
|
97
169
|
* Coalesce multiple messages into one (multimodal-aware).
|
|
@@ -187,8 +259,20 @@ class MessageQueueService {
|
|
|
187
259
|
* Clear all pending messages without processing.
|
|
188
260
|
* Used during cleanup/abort.
|
|
189
261
|
*/
|
|
190
|
-
clear() {
|
|
191
|
-
this.
|
|
262
|
+
async clear() {
|
|
263
|
+
await this.runWithMutationLock(async () => {
|
|
264
|
+
if (this.queue.length === 0) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const previousQueue = [...this.queue];
|
|
268
|
+
this.queue = [];
|
|
269
|
+
try {
|
|
270
|
+
await this.persistQueue();
|
|
271
|
+
} catch (error) {
|
|
272
|
+
this.queue = previousQueue;
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
275
|
+
});
|
|
192
276
|
}
|
|
193
277
|
/**
|
|
194
278
|
* Get all queued messages (for UI display).
|
|
@@ -207,16 +291,26 @@ class MessageQueueService {
|
|
|
207
291
|
* Remove a single queued message by ID.
|
|
208
292
|
* @returns true if message was found and removed; false otherwise
|
|
209
293
|
*/
|
|
210
|
-
remove(id) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
294
|
+
async remove(id) {
|
|
295
|
+
return await this.runWithMutationLock(async () => {
|
|
296
|
+
const index = this.queue.findIndex((m) => m.id === id);
|
|
297
|
+
if (index === -1) {
|
|
298
|
+
this.logger.debug(`Remove failed: message ${id} not found in queue`);
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
const [removed] = this.queue.splice(index, 1);
|
|
302
|
+
try {
|
|
303
|
+
await this.persistQueue();
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if (removed) {
|
|
306
|
+
this.queue.splice(index, 0, removed);
|
|
307
|
+
}
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
this.logger.debug(`Message removed: ${id}, remaining: ${this.queue.length}`);
|
|
311
|
+
this.eventBus.emit("message:removed", { id });
|
|
312
|
+
return true;
|
|
313
|
+
});
|
|
220
314
|
}
|
|
221
315
|
}
|
|
222
316
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -2,6 +2,8 @@ import type { SessionEventBus } from '../events/index.js';
|
|
|
2
2
|
import type { QueuedMessage, CoalescedMessage } from './types.js';
|
|
3
3
|
import type { ContentPart } from '../context/types.js';
|
|
4
4
|
import type { Logger } from '../logger/v2/types.js';
|
|
5
|
+
import type { MessageQueueStore } from './message-queue-store.js';
|
|
6
|
+
type MessageQueueBackingStore = Pick<MessageQueueStore, 'load' | 'save' | 'delete'>;
|
|
5
7
|
/**
|
|
6
8
|
* Input for enqueuing a user message to the queue.
|
|
7
9
|
* (Not to be confused with UserMessage from context/types.ts which represents
|
|
@@ -46,8 +48,17 @@ export interface UserMessageInput {
|
|
|
46
48
|
export declare class MessageQueueService {
|
|
47
49
|
private eventBus;
|
|
48
50
|
private logger;
|
|
51
|
+
private sessionId;
|
|
52
|
+
private store;
|
|
49
53
|
private queue;
|
|
50
|
-
|
|
54
|
+
private mutationLock;
|
|
55
|
+
private initialized;
|
|
56
|
+
private initializationPromise;
|
|
57
|
+
static createEphemeral(eventBus: SessionEventBus, logger: Logger, sessionId: string): MessageQueueService;
|
|
58
|
+
constructor(eventBus: SessionEventBus, logger: Logger, sessionId: string, store: MessageQueueBackingStore);
|
|
59
|
+
initialize(): Promise<void>;
|
|
60
|
+
private persistQueue;
|
|
61
|
+
private runWithMutationLock;
|
|
51
62
|
/**
|
|
52
63
|
* Add a message to the queue.
|
|
53
64
|
* Called by API endpoint - returns immediately with queue position.
|
|
@@ -55,11 +66,11 @@ export declare class MessageQueueService {
|
|
|
55
66
|
* @param message The user message to queue
|
|
56
67
|
* @returns Queue position and message ID
|
|
57
68
|
*/
|
|
58
|
-
enqueue(message: UserMessageInput): {
|
|
69
|
+
enqueue(message: UserMessageInput): Promise<{
|
|
59
70
|
queued: true;
|
|
60
71
|
position: number;
|
|
61
72
|
id: string;
|
|
62
|
-
}
|
|
73
|
+
}>;
|
|
63
74
|
/**
|
|
64
75
|
* Dequeue ALL pending messages and coalesce into single injection.
|
|
65
76
|
* Called by executor between steps.
|
|
@@ -79,7 +90,7 @@ export declare class MessageQueueService {
|
|
|
79
90
|
*
|
|
80
91
|
* @returns Coalesced message or null if queue is empty
|
|
81
92
|
*/
|
|
82
|
-
dequeueAll(): CoalescedMessage | null
|
|
93
|
+
dequeueAll(): Promise<CoalescedMessage | null>;
|
|
83
94
|
/**
|
|
84
95
|
* Coalesce multiple messages into one (multimodal-aware).
|
|
85
96
|
* Strategy: Combine with per-kind formatting, preserve all media.
|
|
@@ -97,7 +108,7 @@ export declare class MessageQueueService {
|
|
|
97
108
|
* Clear all pending messages without processing.
|
|
98
109
|
* Used during cleanup/abort.
|
|
99
110
|
*/
|
|
100
|
-
clear(): void
|
|
111
|
+
clear(): Promise<void>;
|
|
101
112
|
/**
|
|
102
113
|
* Get all queued messages (for UI display).
|
|
103
114
|
* Returns a shallow copy to prevent external mutation.
|
|
@@ -111,6 +122,7 @@ export declare class MessageQueueService {
|
|
|
111
122
|
* Remove a single queued message by ID.
|
|
112
123
|
* @returns true if message was found and removed; false otherwise
|
|
113
124
|
*/
|
|
114
|
-
remove(id: string): boolean
|
|
125
|
+
remove(id: string): Promise<boolean>;
|
|
115
126
|
}
|
|
127
|
+
export {};
|
|
116
128
|
//# sourceMappingURL=message-queue.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-queue.d.ts","sourceRoot":"","sources":["../../src/session/message-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"message-queue.d.ts","sourceRoot":"","sources":["../../src/session/message-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,KAAK,wBAAwB,GAAG,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;AAyBpF;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC7B,2DAA2D;IAC3D,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,sCAAsC;IACtC,IAAI,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,mBAAmB;IAoBxB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,KAAK;IAtBjB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,qBAAqB,CAA8B;IAE3D,MAAM,CAAC,eAAe,CAClB,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAClB,mBAAmB;gBAUV,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,wBAAwB;IAGrC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAsBnB,YAAY;IAI1B,OAAO,CAAC,mBAAmB;IAS3B;;;;;;OAMG;IACG,OAAO,CACT,OAAO,EAAE,gBAAgB,GAC1B,OAAO,CAAC;QAAE,MAAM,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAkC1D;;;;;;;;;;;;;;;;;;OAkBG;IACG,UAAU,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAgCpD;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAuGhB;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB5B;;;OAGG;IACH,MAAM,IAAI,aAAa,EAAE;IAIzB;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI1C;;;OAGG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAwB7C"}
|