@adminforth/agent 2.0.10 → 2.2.0
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/README.md +333 -10
- package/dist/application/ports.d.ts +66 -0
- package/dist/application/runTurnUseCase.d.ts +71 -0
- package/dist/application/runTurnUseCase.js +364 -0
- package/dist/custom/ChatFooter.vue +58 -30
- package/dist/custom/SteerDebugPanel.vue +45 -0
- package/dist/custom/SteerQueue.vue +54 -0
- package/dist/custom/composables/agentStore/constants.ts +1 -0
- package/dist/custom/composables/agentStore/useAgentSessions.ts +35 -16
- package/dist/custom/composables/agentStore/useAgentSteerQueue.ts +114 -0
- package/dist/custom/composables/useAgentStore.ts +46 -1
- package/dist/custom/conversation_area/MessageRenderer.vue +7 -1
- package/dist/custom/speech_recognition_frontend/AudioLines.vue +1 -1
- package/dist/custom/speech_recognition_frontend/MicrophoneButon.vue +3 -3
- package/dist/{agentEvents.d.ts → domain/agentEvents.d.ts} +5 -1
- package/dist/domain/steerBuffer.d.ts +33 -0
- package/dist/domain/steerBuffer.js +51 -0
- package/dist/{agent → domain}/systemPrompt.js +1 -1
- package/dist/{agent/turn → domain}/turnTypes.d.ts +41 -37
- package/dist/{agent/turn/VegaLiteStreamBuffer.d.ts → domain/vegaLiteStreamBuffer.d.ts} +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +49 -35
- package/dist/{agent/runtime/AgentContext.d.ts → llm/agentContext.d.ts} +3 -3
- package/dist/{agent/simpleAgent.d.ts → llm/agentModels.d.ts} +18 -16
- package/dist/llm/agentModels.js +65 -0
- package/dist/{agent/runtime/AgentRuntime.d.ts → llm/agentRuntime.d.ts} +14 -2
- package/dist/{agent/runtime/AgentRuntime.js → llm/agentRuntime.js} +41 -6
- package/dist/llm/langGraphLlm.d.ts +25 -0
- package/dist/llm/langGraphLlm.js +54 -0
- package/dist/{agent/middleware/apiBasedTools.d.ts → llm/middleware/apiToolsMiddleware.d.ts} +1 -1
- package/dist/{agent/middleware/apiBasedTools.js → llm/middleware/apiToolsMiddleware.js} +8 -14
- package/dist/{agent → llm}/middleware/sequenceDebug.d.ts +1 -1
- package/dist/llm/middleware/steerMiddleware.d.ts +21 -0
- package/dist/llm/middleware/steerMiddleware.js +50 -0
- package/dist/{agent/models/AgentModelFactory.d.ts → llm/modelFactory.d.ts} +1 -1
- package/dist/{agent/models/AgentModelFactory.js → llm/modelFactory.js} +1 -1
- package/dist/llm/streamAdapter.d.ts +18 -0
- package/dist/llm/streamAdapter.js +117 -0
- package/dist/{sessionStore.d.ts → persistence/sessionStore.d.ts} +19 -2
- package/dist/{sessionStore.js → persistence/sessionStore.js} +53 -0
- package/dist/{agent/tools/AgentToolProvider.d.ts → tools/agentToolProvider.d.ts} +1 -1
- package/dist/{agent/tools/AgentToolProvider.js → tools/agentToolProvider.js} +1 -1
- package/dist/{apiBasedTools.js → tools/apiBasedTools.js} +0 -3
- package/dist/{agent/tools → tools}/apiTool.d.ts +1 -1
- package/dist/{agent/tools → tools}/fetchSkill.js +1 -1
- package/dist/{agent/tools → tools}/fetchToolSchema.d.ts +1 -1
- package/dist/{agent/tools → tools}/index.d.ts +1 -1
- package/dist/{agent/tools → tools}/navigateUser.d.ts +3 -3
- package/dist/{endpoints → transport/http}/context.d.ts +12 -4
- package/dist/transport/http/context.js +1 -0
- package/dist/{endpoints/core.js → transport/http/coreEndpoints.js} +23 -3
- package/dist/{endpoints/sessions.js → transport/http/sessionEndpoints.js} +36 -1
- package/dist/transport/sse/sseWriter.d.ts +29 -0
- package/dist/{surfaces/web-sse/createSseEventEmitter.js → transport/sse/sseWriter.js} +27 -0
- package/dist/{chatSurfaceService.d.ts → transport/surfaces/chatSurfaceService.d.ts} +3 -3
- package/dist/{chatSurfaceService.js → transport/surfaces/chatSurfaceService.js} +7 -5
- package/dist/{agent/speech/SpeechTurnService.d.ts → transport/surfaces/speechTurnService.d.ts} +1 -1
- package/dist/{agent/speech/SpeechTurnService.js → transport/surfaces/speechTurnService.js} +2 -2
- package/dist/types.d.ts +10 -4
- package/package.json +11 -2
- package/.woodpecker/buildRelease.sh +0 -13
- package/.woodpecker/buildSlackNotify.sh +0 -46
- package/.woodpecker/release.yml +0 -57
- package/agent/checkpointer.ts +0 -329
- package/agent/languageDetect.ts +0 -92
- package/agent/middleware/apiBasedTools.ts +0 -174
- package/agent/middleware/openAiResponsesContinuation.ts +0 -92
- package/agent/middleware/sequenceDebug.ts +0 -429
- package/agent/models/AgentModeResolver.ts +0 -9
- package/agent/models/AgentModelFactory.ts +0 -28
- package/agent/runtime/AgentContext.ts +0 -30
- package/agent/runtime/AgentRuntime.ts +0 -89
- package/agent/simpleAgent.ts +0 -199
- package/agent/skills/registry.ts +0 -162
- package/agent/speech/SpeechTurnService.ts +0 -179
- package/agent/systemPrompt.ts +0 -133
- package/agent/toolCallEvents.ts +0 -135
- package/agent/tools/AgentToolProvider.ts +0 -28
- package/agent/tools/apiTool.ts +0 -66
- package/agent/tools/fetchSkill.ts +0 -68
- package/agent/tools/fetchToolSchema.ts +0 -47
- package/agent/tools/getUserLocation.ts +0 -45
- package/agent/tools/index.ts +0 -31
- package/agent/tools/navigateUser.ts +0 -210
- package/agent/turn/TurnContextBuilder.ts +0 -36
- package/agent/turn/TurnLifecycleService.ts +0 -47
- package/agent/turn/TurnPersistenceService.ts +0 -33
- package/agent/turn/TurnPromptBuilder.ts +0 -51
- package/agent/turn/TurnStreamConsumer.ts +0 -69
- package/agent/turn/VegaLiteStreamBuffer.ts +0 -90
- package/agent/turn/turnTypes.ts +0 -100
- package/agentEvents.ts +0 -75
- package/agentTurnService.ts +0 -314
- package/apiBasedTools.ts +0 -712
- package/build.log +0 -67
- package/chatSurfaceService.ts +0 -319
- package/custom/ChatFooter.vue +0 -173
- package/custom/ChatHeader.vue +0 -78
- package/custom/ChatSurface.vue +0 -160
- package/custom/CustomAutoScrollContainer.vue +0 -164
- package/custom/SessionsHistory.vue +0 -88
- package/custom/chat.ts +0 -82
- package/custom/composables/agentAudio/utils.ts +0 -220
- package/custom/composables/agentStore/constants.ts +0 -19
- package/custom/composables/agentStore/pageContext.ts +0 -8
- package/custom/composables/agentStore/useAgentChat.ts +0 -241
- package/custom/composables/agentStore/useAgentPlaceholder.ts +0 -142
- package/custom/composables/agentStore/useAgentSessions.ts +0 -350
- package/custom/composables/useAgentAudio.ts +0 -272
- package/custom/composables/useAgentStore.ts +0 -510
- package/custom/composables/useAgentTransitions.ts +0 -58
- package/custom/conversation_area/ConversationArea.vue +0 -301
- package/custom/conversation_area/MessageRenderer.vue +0 -49
- package/custom/conversation_area/ProcessingTimeline.vue +0 -286
- package/custom/conversation_area/ReasoningRenderer.vue +0 -79
- package/custom/conversation_area/SystemMessageRenderer.vue +0 -28
- package/custom/conversation_area/TextRenderer.vue +0 -182
- package/custom/conversation_area/ThreeDotsAnimation.vue +0 -35
- package/custom/conversation_area/ToolApprovalRenderer.vue +0 -98
- package/custom/conversation_area/ToolRenderer.vue +0 -178
- package/custom/conversation_area/ToolsGroup.vue +0 -59
- package/custom/env.d.ts +0 -7
- package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +0 -393
- package/custom/incremark_code_renderers/incremarkCodeHighlight.ts +0 -285
- package/custom/incremark_code_renderers/incremarkRenderer.ts +0 -653
- package/custom/incremark_code_renderers/renderIncremarkMarkdown.ts +0 -118
- package/custom/package.json +0 -30
- package/custom/pnpm-lock.yaml +0 -2394
- package/custom/public/agentAudio/agent-processing.mp3 +0 -0
- package/custom/skills/analyze_data/SKILL.md +0 -25
- package/custom/skills/charts/SKILL.md +0 -193
- package/custom/skills/fetch_data/SKILL.md +0 -31
- package/custom/skills/mutate_data/SKILL.md +0 -121
- package/custom/speech_recognition_frontend/AudioLines.vue +0 -43
- package/custom/speech_recognition_frontend/MicrophoneButon.vue +0 -185
- package/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +0 -22
- package/custom/speech_recognition_frontend/voiceActivityDetection.ts +0 -62
- package/custom/tsconfig.json +0 -15
- package/custom/types.ts +0 -108
- package/custom/utils.ts +0 -38
- package/dist/agent/middleware/openAiResponsesContinuation.d.ts +0 -1
- package/dist/agent/middleware/openAiResponsesContinuation.js +0 -66
- package/dist/agent/models/AgentModeResolver.d.ts +0 -9
- package/dist/agent/models/AgentModeResolver.js +0 -9
- package/dist/agent/simpleAgent.js +0 -118
- package/dist/agent/turn/TurnContextBuilder.d.ts +0 -14
- package/dist/agent/turn/TurnContextBuilder.js +0 -31
- package/dist/agent/turn/TurnLifecycleService.d.ts +0 -24
- package/dist/agent/turn/TurnLifecycleService.js +0 -47
- package/dist/agent/turn/TurnPersistenceService.d.ts +0 -13
- package/dist/agent/turn/TurnPersistenceService.js +0 -35
- package/dist/agent/turn/TurnPromptBuilder.d.ts +0 -19
- package/dist/agent/turn/TurnPromptBuilder.js +0 -43
- package/dist/agent/turn/TurnStreamConsumer.d.ts +0 -11
- package/dist/agent/turn/TurnStreamConsumer.js +0 -84
- package/dist/agentTurnService.d.ts +0 -24
- package/dist/agentTurnService.js +0 -269
- package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +0 -14
- package/endpoints/chatSurfaces.ts +0 -66
- package/endpoints/context.ts +0 -52
- package/endpoints/core.ts +0 -181
- package/endpoints/sessions.ts +0 -183
- package/errors.ts +0 -10
- package/index.ts +0 -192
- package/pnpm-workspace.yaml +0 -4
- package/sanitizeSpeechText.ts +0 -32
- package/sessionStore.ts +0 -105
- package/surfaces/web-sse/createSseEventEmitter.ts +0 -311
- package/tsconfig.json +0 -14
- package/types.ts +0 -127
- /package/dist/{agent/turn/turnTypes.js → application/ports.js} +0 -0
- /package/dist/{agentEvents.js → domain/agentEvents.js} +0 -0
- /package/dist/{agent → domain}/languageDetect.d.ts +0 -0
- /package/dist/{agent → domain}/languageDetect.js +0 -0
- /package/dist/{agent → domain}/systemPrompt.d.ts +0 -0
- /package/dist/{agent → domain}/toolCallEvents.d.ts +0 -0
- /package/dist/{agent → domain}/toolCallEvents.js +0 -0
- /package/dist/{endpoints/context.js → domain/turnTypes.js} +0 -0
- /package/dist/{agent/turn/VegaLiteStreamBuffer.js → domain/vegaLiteStreamBuffer.js} +0 -0
- /package/dist/{agent/runtime/AgentContext.js → llm/agentContext.js} +0 -0
- /package/dist/{agent → llm}/middleware/sequenceDebug.js +0 -0
- /package/dist/{agent/checkpointer.d.ts → persistence/checkpointStore.d.ts} +0 -0
- /package/dist/{agent/checkpointer.js → persistence/checkpointStore.js} +0 -0
- /package/dist/{errors.d.ts → shared/errors.d.ts} +0 -0
- /package/dist/{errors.js → shared/errors.js} +0 -0
- /package/dist/{sanitizeSpeechText.d.ts → shared/sanitizeSpeechText.d.ts} +0 -0
- /package/dist/{sanitizeSpeechText.js → shared/sanitizeSpeechText.js} +0 -0
- /package/dist/{apiBasedTools.d.ts → tools/apiBasedTools.d.ts} +0 -0
- /package/dist/{agent/tools → tools}/apiTool.js +0 -0
- /package/dist/{agent/tools → tools}/fetchSkill.d.ts +0 -0
- /package/dist/{agent/tools → tools}/fetchToolSchema.js +0 -0
- /package/dist/{agent/tools → tools}/getUserLocation.d.ts +0 -0
- /package/dist/{agent/tools → tools}/getUserLocation.js +0 -0
- /package/dist/{agent/tools → tools}/index.js +0 -0
- /package/dist/{agent/tools → tools}/navigateUser.js +0 -0
- /package/dist/{agent → tools}/skills/registry.d.ts +0 -0
- /package/dist/{agent → tools}/skills/registry.js +0 -0
- /package/dist/{endpoints/chatSurfaces.d.ts → transport/http/chatSurfaceEndpoints.d.ts} +0 -0
- /package/dist/{endpoints/chatSurfaces.js → transport/http/chatSurfaceEndpoints.js} +0 -0
- /package/dist/{endpoints/core.d.ts → transport/http/coreEndpoints.d.ts} +0 -0
- /package/dist/{endpoints/sessions.d.ts → transport/http/sessionEndpoints.d.ts} +0 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
11
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
12
|
+
var m = o[Symbol.asyncIterator], i;
|
|
13
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
14
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
15
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
16
|
+
};
|
|
17
|
+
import { logger } from "adminforth";
|
|
18
|
+
import { randomUUID } from "crypto";
|
|
19
|
+
import { VegaLiteStreamBuffer } from "../domain/vegaLiteStreamBuffer.js";
|
|
20
|
+
import { buildAgentTurnSystemPrompt } from "../domain/systemPrompt.js";
|
|
21
|
+
import { getErrorMessage, isAbortError } from "../shared/errors.js";
|
|
22
|
+
function getApprovalDecision(input) {
|
|
23
|
+
return "approvalDecision" in input
|
|
24
|
+
&& (input.approvalDecision === "approve" || input.approvalDecision === "reject")
|
|
25
|
+
? input.approvalDecision
|
|
26
|
+
: undefined;
|
|
27
|
+
}
|
|
28
|
+
function buildHitlDecision(decision, prompt) {
|
|
29
|
+
if (decision === "approve") {
|
|
30
|
+
return { type: "approve" };
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
type: "reject",
|
|
34
|
+
message: prompt
|
|
35
|
+
? `User rejected the pending tool execution and sent a new instruction instead: ${prompt}`
|
|
36
|
+
: "User rejected executing this tool",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function buildHitlResumeValue(input) {
|
|
40
|
+
return {
|
|
41
|
+
decisions: Array.from({ length: input.count }, () => (buildHitlDecision(input.decision, input.prompt))),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Build the provider-agnostic resume payload for a human-in-the-loop turn.
|
|
46
|
+
* Exported for unit testing; the LLM port wraps it into a LangGraph Command.
|
|
47
|
+
*/
|
|
48
|
+
export function buildResumeValue(input) {
|
|
49
|
+
var _a;
|
|
50
|
+
const interrupts = (_a = input.interrupts) !== null && _a !== void 0 ? _a : [];
|
|
51
|
+
if (interrupts.length === 0) {
|
|
52
|
+
throw new Error("No pending approval interrupt found for resume.");
|
|
53
|
+
}
|
|
54
|
+
if (interrupts.length === 1) {
|
|
55
|
+
return buildHitlResumeValue({
|
|
56
|
+
decision: input.decision,
|
|
57
|
+
count: interrupts[0].count,
|
|
58
|
+
prompt: input.prompt,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return Object.fromEntries(interrupts.map((interrupt) => [
|
|
62
|
+
interrupt.id,
|
|
63
|
+
buildHitlResumeValue({
|
|
64
|
+
decision: input.decision,
|
|
65
|
+
count: interrupt.count,
|
|
66
|
+
prompt: input.prompt,
|
|
67
|
+
}),
|
|
68
|
+
]));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Single entry point for running an agent turn: prepare (start/resume) → build
|
|
72
|
+
* the prompt → stream from the LLM port → consume the typed stream into SSE
|
|
73
|
+
* events → persist the result. Replaces the former AgentTurnService plus the
|
|
74
|
+
* TurnLifecycle/Context/Prompt/Persistence/StreamConsumer + ModeResolver stack.
|
|
75
|
+
*/
|
|
76
|
+
export class RunTurnUseCase {
|
|
77
|
+
constructor(deps) {
|
|
78
|
+
this.deps = deps;
|
|
79
|
+
this.pendingInterrupts = new Map();
|
|
80
|
+
}
|
|
81
|
+
resolveMode(modeName) {
|
|
82
|
+
var _a;
|
|
83
|
+
return (_a = this.deps.modes.find((mode) => mode.name === modeName)) !== null && _a !== void 0 ? _a : this.deps.modes[0];
|
|
84
|
+
}
|
|
85
|
+
buildContext(input, turnId) {
|
|
86
|
+
var _a, _b;
|
|
87
|
+
return {
|
|
88
|
+
adminUser: input.adminUser,
|
|
89
|
+
sessionId: input.sessionId,
|
|
90
|
+
turnId,
|
|
91
|
+
abortSignal: input.abortSignal,
|
|
92
|
+
currentPage: input.currentPage,
|
|
93
|
+
chatSurface: input.chatSurface,
|
|
94
|
+
userTimeZone: (_a = input.userTimeZone) !== null && _a !== void 0 ? _a : "UTC",
|
|
95
|
+
adminPublicOrigin: (_b = input.adminPublicOrigin) !== null && _b !== void 0 ? _b : this.deps.getAdminforth().config.baseUrlSlashed,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Resolve the pending HITL interrupts for a resume. With a persistent checkpointer the
|
|
100
|
+
* checkpoint is authoritative (correct across instances and restarts); with the in-memory
|
|
101
|
+
* MemorySaver the in-process cache is authoritative (single process, cannot be stale).
|
|
102
|
+
*/
|
|
103
|
+
resolvePendingInterrupts(sessionId, mode) {
|
|
104
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
105
|
+
var _a;
|
|
106
|
+
if (this.deps.hasPersistentCheckpointer) {
|
|
107
|
+
// The in-process cache can be stale after a cross-instance resume, so it is intentionally
|
|
108
|
+
// NOT consulted here. A failure to read the checkpoint is surfaced as a real error — never
|
|
109
|
+
// masked as "no pending approval".
|
|
110
|
+
try {
|
|
111
|
+
return yield this.deps.llm.getPendingInterrupts({
|
|
112
|
+
completionAdapter: mode.completionAdapter,
|
|
113
|
+
sessionId,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
logger.error(`Failed to read pending approval state from checkpoint for session "${sessionId}": ${getErrorMessage(error)}`);
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return (_a = this.pendingInterrupts.get(sessionId)) !== null && _a !== void 0 ? _a : [];
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
prepareTurn(input) {
|
|
125
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
126
|
+
const sequenceDebugSink = this.deps.createDebugSink();
|
|
127
|
+
const mode = this.resolveMode(input.modeName);
|
|
128
|
+
const approvalDecision = getApprovalDecision(input);
|
|
129
|
+
const shouldResume = Boolean(approvalDecision);
|
|
130
|
+
let turnId;
|
|
131
|
+
let previousUserMessages = [];
|
|
132
|
+
let initialResponse;
|
|
133
|
+
let resumeInterrupts;
|
|
134
|
+
if (shouldResume) {
|
|
135
|
+
resumeInterrupts = yield this.resolvePendingInterrupts(input.sessionId, mode);
|
|
136
|
+
if (resumeInterrupts.length === 0) {
|
|
137
|
+
throw new Error(`No pending approval interrupt found for session "${input.sessionId}".`);
|
|
138
|
+
}
|
|
139
|
+
const resumeState = yield this.deps.sessions.getResumeState(input.sessionId);
|
|
140
|
+
turnId = resumeState.turnId;
|
|
141
|
+
initialResponse = resumeState.initialResponse;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
previousUserMessages = yield this.deps.sessions.getPreviousUserMessages(input.sessionId);
|
|
145
|
+
turnId = yield this.deps.sessions.createNewTurn(input.sessionId, input.prompt);
|
|
146
|
+
yield this.deps.sessions.touchSession(input.sessionId);
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
prompt: input.prompt,
|
|
150
|
+
sessionId: input.sessionId,
|
|
151
|
+
turnId,
|
|
152
|
+
previousUserMessages,
|
|
153
|
+
mode,
|
|
154
|
+
context: this.buildContext(input, turnId),
|
|
155
|
+
observability: {
|
|
156
|
+
emit: input.emit,
|
|
157
|
+
sequenceDebugSink,
|
|
158
|
+
},
|
|
159
|
+
resume: shouldResume
|
|
160
|
+
? { decision: approvalDecision, interrupts: resumeInterrupts }
|
|
161
|
+
: undefined,
|
|
162
|
+
initialResponse,
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
buildMessages(prepared) {
|
|
167
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
168
|
+
const userLanguage = yield this.deps.llm
|
|
169
|
+
.detectLanguage({
|
|
170
|
+
completionAdapter: prepared.mode.completionAdapter,
|
|
171
|
+
prompt: prepared.prompt,
|
|
172
|
+
previousUserMessages: prepared.previousUserMessages,
|
|
173
|
+
})
|
|
174
|
+
.catch((error) => {
|
|
175
|
+
var _a;
|
|
176
|
+
if (((_a = prepared.context.abortSignal) === null || _a === void 0 ? void 0 : _a.aborted) || isAbortError(error)) {
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
logger.warn(`Failed to detect user language: ${getErrorMessage(error)}`);
|
|
180
|
+
return null;
|
|
181
|
+
});
|
|
182
|
+
const adminforth = this.deps.getAdminforth();
|
|
183
|
+
const systemPrompt = buildAgentTurnSystemPrompt({
|
|
184
|
+
agentSystemPrompt: yield this.deps.getAgentSystemPrompt(),
|
|
185
|
+
adminUser: prepared.context.adminUser,
|
|
186
|
+
usernameField: adminforth.config.auth.usernameField,
|
|
187
|
+
userLanguage,
|
|
188
|
+
chatSurface: prepared.context.chatSurface,
|
|
189
|
+
});
|
|
190
|
+
return [
|
|
191
|
+
{ role: "system", content: systemPrompt },
|
|
192
|
+
{ role: "user", content: prepared.prompt },
|
|
193
|
+
];
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
handleInterrupt(prepared, interrupt, descriptors) {
|
|
197
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
198
|
+
var _a, _b, _c;
|
|
199
|
+
if (!this.deps.hasPersistentCheckpointer) {
|
|
200
|
+
const existing = (_a = this.pendingInterrupts.get(prepared.sessionId)) !== null && _a !== void 0 ? _a : [];
|
|
201
|
+
const merged = new Map(existing.map((item) => [item.id, item.count]));
|
|
202
|
+
for (const item of descriptors) {
|
|
203
|
+
merged.set(item.id, item.count);
|
|
204
|
+
}
|
|
205
|
+
this.pendingInterrupts.set(prepared.sessionId, [...merged.entries()].map(([id, count]) => ({ id, count })));
|
|
206
|
+
}
|
|
207
|
+
yield ((_c = (_b = prepared.observability).emit) === null || _c === void 0 ? void 0 : _c.call(_b, {
|
|
208
|
+
type: "interrupt",
|
|
209
|
+
sessionId: prepared.sessionId,
|
|
210
|
+
interrupt,
|
|
211
|
+
}));
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
runAgentTurn(prepared) {
|
|
215
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
216
|
+
var _a, e_1, _b, _c;
|
|
217
|
+
const streamInput = prepared.resume
|
|
218
|
+
? {
|
|
219
|
+
resume: buildResumeValue({
|
|
220
|
+
decision: prepared.resume.decision,
|
|
221
|
+
interrupts: prepared.resume.interrupts,
|
|
222
|
+
prompt: prepared.prompt,
|
|
223
|
+
}),
|
|
224
|
+
}
|
|
225
|
+
: { messages: yield this.buildMessages(prepared) };
|
|
226
|
+
const stream = yield this.deps.llm.streamTurn({
|
|
227
|
+
completionAdapter: prepared.mode.completionAdapter,
|
|
228
|
+
input: streamInput,
|
|
229
|
+
context: prepared.context,
|
|
230
|
+
observability: prepared.observability,
|
|
231
|
+
});
|
|
232
|
+
const { emit } = prepared.observability;
|
|
233
|
+
const abortSignal = prepared.context.abortSignal;
|
|
234
|
+
const textBuffer = new VegaLiteStreamBuffer();
|
|
235
|
+
let fullResponse = "";
|
|
236
|
+
let interrupted = false;
|
|
237
|
+
try {
|
|
238
|
+
try {
|
|
239
|
+
for (var _d = true, stream_1 = __asyncValues(stream), stream_1_1; stream_1_1 = yield stream_1.next(), _a = stream_1_1.done, !_a; _d = true) {
|
|
240
|
+
_c = stream_1_1.value;
|
|
241
|
+
_d = false;
|
|
242
|
+
const chunk = _c;
|
|
243
|
+
if (abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted) {
|
|
244
|
+
throw new DOMException("This operation was aborted", "AbortError");
|
|
245
|
+
}
|
|
246
|
+
if (chunk.kind === "interrupt") {
|
|
247
|
+
interrupted = true;
|
|
248
|
+
yield this.handleInterrupt(prepared, chunk.interrupt, chunk.descriptors);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (chunk.kind === "reasoning") {
|
|
252
|
+
if (chunk.delta) {
|
|
253
|
+
yield (emit === null || emit === void 0 ? void 0 : emit({ type: "reasoning-delta", delta: chunk.delta }));
|
|
254
|
+
}
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (chunk.delta) {
|
|
258
|
+
fullResponse += chunk.delta;
|
|
259
|
+
yield textBuffer.push(chunk.delta, emit);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
264
|
+
finally {
|
|
265
|
+
try {
|
|
266
|
+
if (!_d && !_a && (_b = stream_1.return)) yield _b.call(stream_1);
|
|
267
|
+
}
|
|
268
|
+
finally { if (e_1) throw e_1.error; }
|
|
269
|
+
}
|
|
270
|
+
yield textBuffer.flush(emit);
|
|
271
|
+
return { text: fullResponse };
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
// When the turn is interrupted for HITL approval it will resume, so any steers
|
|
275
|
+
// buffered meanwhile must survive to be applied on resume. Otherwise the turn is
|
|
276
|
+
// done and leftover (unconsumed) steers must not leak into the next turn.
|
|
277
|
+
if (!interrupted) {
|
|
278
|
+
this.deps.steerBuffer.clear(prepared.sessionId);
|
|
279
|
+
}
|
|
280
|
+
if (!this.deps.hasPersistentCheckpointer && !interrupted) {
|
|
281
|
+
this.pendingInterrupts.delete(prepared.sessionId);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
runAndPersistAgentResponse(input) {
|
|
287
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
288
|
+
var _a, _b;
|
|
289
|
+
const prepared = yield this.prepareTurn(input);
|
|
290
|
+
let fullResponse = (_a = prepared.initialResponse) !== null && _a !== void 0 ? _a : "";
|
|
291
|
+
let aborted = false;
|
|
292
|
+
let failed = false;
|
|
293
|
+
try {
|
|
294
|
+
const agentResponse = yield this.runAgentTurn(prepared);
|
|
295
|
+
fullResponse += agentResponse.text;
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
if (((_b = input.abortSignal) === null || _b === void 0 ? void 0 : _b.aborted) || isAbortError(error)) {
|
|
299
|
+
aborted = true;
|
|
300
|
+
logger.info(input.abortLogMessage);
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
failed = true;
|
|
304
|
+
fullResponse = getErrorMessage(error);
|
|
305
|
+
logger.error(`${input.failureLogMessage}:\n${fullResponse}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
prepared.observability.sequenceDebugSink.flush();
|
|
309
|
+
yield this.deps.sessions.saveTurnResponse({
|
|
310
|
+
turnId: prepared.turnId,
|
|
311
|
+
responseText: fullResponse,
|
|
312
|
+
debugHistory: prepared.observability.sequenceDebugSink.getHistory(),
|
|
313
|
+
});
|
|
314
|
+
return {
|
|
315
|
+
text: fullResponse,
|
|
316
|
+
turnId: prepared.turnId,
|
|
317
|
+
aborted,
|
|
318
|
+
failed,
|
|
319
|
+
};
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
handleTurn(input) {
|
|
323
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
324
|
+
var _a, _b;
|
|
325
|
+
yield input.emit({
|
|
326
|
+
type: "turn-started",
|
|
327
|
+
messageId: randomUUID(),
|
|
328
|
+
});
|
|
329
|
+
const agentResponse = yield this.runAndPersistAgentResponse({
|
|
330
|
+
prompt: input.prompt,
|
|
331
|
+
sessionId: input.sessionId,
|
|
332
|
+
modeName: input.modeName,
|
|
333
|
+
userTimeZone: input.userTimeZone,
|
|
334
|
+
currentPage: input.currentPage,
|
|
335
|
+
chatSurface: input.chatSurface,
|
|
336
|
+
adminPublicOrigin: input.adminPublicOrigin,
|
|
337
|
+
approvalDecision: input.approvalDecision,
|
|
338
|
+
abortSignal: input.abortSignal,
|
|
339
|
+
adminUser: input.adminUser,
|
|
340
|
+
emit: input.emit,
|
|
341
|
+
failureLogMessage: (_a = input.failureLogMessage) !== null && _a !== void 0 ? _a : "Agent response failed",
|
|
342
|
+
abortLogMessage: (_b = input.abortLogMessage) !== null && _b !== void 0 ? _b : "Agent response aborted",
|
|
343
|
+
});
|
|
344
|
+
if (agentResponse.failed) {
|
|
345
|
+
yield input.emit({
|
|
346
|
+
type: "error",
|
|
347
|
+
error: agentResponse.text,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
else if (!agentResponse.aborted) {
|
|
351
|
+
yield input.emit({
|
|
352
|
+
type: "response",
|
|
353
|
+
text: agentResponse.text,
|
|
354
|
+
sessionId: input.sessionId,
|
|
355
|
+
turnId: agentResponse.turnId,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
yield input.emit({
|
|
359
|
+
type: "finish",
|
|
360
|
+
});
|
|
361
|
+
return agentResponse;
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
@@ -7,27 +7,30 @@
|
|
|
7
7
|
transition: `transform ${agentTransitions.TRANSITION_DURATION}ms ease-in-out`
|
|
8
8
|
}"
|
|
9
9
|
>
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
<SteerQueue />
|
|
11
|
+
<!-- <SteerDebugPanel /> -->
|
|
12
|
+
<div
|
|
13
|
+
class="w-full border rounded-lg flex flex-col"
|
|
14
|
+
:class="agentStore.isAudioChatMode ? 'border-none mt-8' : 'border dark:bg-gray-700'"
|
|
15
|
+
>
|
|
14
16
|
<textarea
|
|
15
17
|
v-if="!agentStore.isAudioChatMode"
|
|
16
18
|
v-model="agentStore.userMessageInput"
|
|
17
19
|
ref="textInput"
|
|
20
|
+
rows="1"
|
|
18
21
|
@input="autoResize"
|
|
19
22
|
:class="[
|
|
20
|
-
'
|
|
23
|
+
'h-8 px-4 py-3 border-b rounded-xl rounded-b-none w-full resize-none overflow-hidden text-lightInputText dark:text-darkInputText rounded-md bg-transparent text-sm bg-gray-50 dark:bg-gray-700 dark:border-gray-600 focus:outline-none',
|
|
21
24
|
{ '!text-base': coreStore.isIos }
|
|
22
25
|
]"
|
|
23
|
-
:placeholder="
|
|
24
|
-
:disabled="agentStore.isMessageInputBlocked"
|
|
26
|
+
:placeholder="placeholderText"
|
|
25
27
|
@keydown.enter.exact.prevent="sendMessage"
|
|
26
28
|
/>
|
|
29
|
+
<div class="flex items-center justify-between px-2 py-1">
|
|
27
30
|
<div
|
|
28
31
|
v-if="agentStore.availableModes.length > 1"
|
|
29
32
|
ref="modeMenu"
|
|
30
|
-
class="
|
|
33
|
+
class="relative"
|
|
31
34
|
>
|
|
32
35
|
<button
|
|
33
36
|
aria-label="Select mode"
|
|
@@ -46,22 +49,30 @@
|
|
|
46
49
|
:class="isModeMenuOpen ? '!rotate-180' : '!rotate-0'"
|
|
47
50
|
/>
|
|
48
51
|
</button>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@click="selectMode(mode.name)"
|
|
52
|
+
<Transition
|
|
53
|
+
enter-active-class="transition ease-out duration-200"
|
|
54
|
+
enter-from-class="opacity-0 scale-95"
|
|
55
|
+
enter-to-class="opacity-100 scale-100"
|
|
56
|
+
leave-active-class="transition ease-in duration-150"
|
|
57
|
+
leave-from-class="opacity-100 scale-100"
|
|
58
|
+
leave-to-class="opacity-0 scale-95"
|
|
59
|
+
>
|
|
60
|
+
<div
|
|
61
|
+
v-if="isModeMenuOpen"
|
|
62
|
+
class="absolute bottom-full left-0 mb-2 min-w-40 overflow-hidden rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-600 dark:bg-gray-800"
|
|
61
63
|
>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
<button
|
|
65
|
+
v-for="mode in agentStore.availableModes"
|
|
66
|
+
:key="mode.name"
|
|
67
|
+
class="block w-full px-3 py-2 text-left text-sm text-lightInputText transition-colors duration-150 hover:bg-gray-100 dark:text-darkInputText dark:hover:bg-gray-700"
|
|
68
|
+
:class="mode.name === agentStore.activeModeName ? 'bg-gray-100 dark:bg-gray-700' : ''"
|
|
69
|
+
type="button"
|
|
70
|
+
@click="selectMode(mode.name)"
|
|
71
|
+
>
|
|
72
|
+
{{ mode.name }}
|
|
73
|
+
</button>
|
|
74
|
+
</div>
|
|
75
|
+
</Transition>
|
|
65
76
|
</div>
|
|
66
77
|
<MicrophoneButton
|
|
67
78
|
v-if="props.meta.hasAudioAdapter"
|
|
@@ -69,32 +80,33 @@
|
|
|
69
80
|
<template v-if="!agentStore.isAudioChatMode">
|
|
70
81
|
<Button
|
|
71
82
|
v-if="!agentStore.isResponseInProgress"
|
|
72
|
-
class="
|
|
83
|
+
class=" !p-0 h-7 w-7 transition-opacity duration-200"
|
|
73
84
|
@click="sendMessage"
|
|
74
|
-
:disabled="!agentStore.trimmedUserMessage
|
|
85
|
+
:disabled="!agentStore.trimmedUserMessage"
|
|
75
86
|
>
|
|
76
87
|
<IconArrowUpOutline
|
|
77
|
-
class="w-
|
|
88
|
+
class="w-6 h-6
|
|
78
89
|
text-white"
|
|
79
90
|
/>
|
|
80
91
|
</Button>
|
|
81
92
|
<Button
|
|
82
93
|
v-else
|
|
83
|
-
class="
|
|
94
|
+
class="right-4 bottom-2 !p-0 h-7 w-7"
|
|
84
95
|
@click="stopCurrentRequest"
|
|
85
96
|
>
|
|
86
97
|
<div
|
|
87
|
-
class="w-
|
|
98
|
+
class="w-2.5 h-2.5 bg-white rounded-sm"
|
|
88
99
|
/>
|
|
89
100
|
</Button>
|
|
90
101
|
</template>
|
|
102
|
+
</div>
|
|
91
103
|
</div>
|
|
92
104
|
</div>
|
|
93
105
|
</template>
|
|
94
106
|
|
|
95
107
|
<script setup lang="ts">
|
|
96
108
|
import { IconArrowUpOutline, IconAngleDownOutline } from '@iconify-prerendered/vue-flowbite';
|
|
97
|
-
import { useTemplateRef, onMounted, ref, onUnmounted } from 'vue';
|
|
109
|
+
import { useTemplateRef, onMounted, ref, onUnmounted, watch, computed } from 'vue';
|
|
98
110
|
import { onClickOutside } from '@vueuse/core'
|
|
99
111
|
import { useAgentStore } from './composables/useAgentStore';
|
|
100
112
|
import { useAgentTransitions } from './composables/useAgentTransitions';
|
|
@@ -102,6 +114,8 @@ import { Button } from '@/afcl';
|
|
|
102
114
|
import { useCoreStore } from '@/stores/core';
|
|
103
115
|
import { remToPx } from './utils';
|
|
104
116
|
import MicrophoneButton from './speech_recognition_frontend/MicrophoneButon.vue';
|
|
117
|
+
import SteerQueue from './SteerQueue.vue';
|
|
118
|
+
import SteerDebugPanel from './SteerDebugPanel.vue';
|
|
105
119
|
|
|
106
120
|
const props = defineProps<{
|
|
107
121
|
meta: {
|
|
@@ -124,6 +138,13 @@ const agentTransitions = useAgentTransitions();
|
|
|
124
138
|
const coreStore = useCoreStore();
|
|
125
139
|
const isModeMenuOpen = ref(false);
|
|
126
140
|
|
|
141
|
+
const placeholderText = computed(() => {
|
|
142
|
+
if (agentStore.isTurnActive) {
|
|
143
|
+
return 'Queue a follow-up — send after, or steer it…';
|
|
144
|
+
}
|
|
145
|
+
return agentStore.userMessagePlaceholder;
|
|
146
|
+
});
|
|
147
|
+
|
|
127
148
|
onClickOutside(modeMenu as any, () => { isModeMenuOpen.value = false; });
|
|
128
149
|
|
|
129
150
|
onMounted(async () => {
|
|
@@ -131,6 +152,11 @@ onMounted(async () => {
|
|
|
131
152
|
agentStore.setCurrentGenerationModeFromLocalStorage();
|
|
132
153
|
agentStore.regisrerTextInput(textInput.value);
|
|
133
154
|
textInput.value?.focus();
|
|
155
|
+
autoResize();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
watch(() => textInput.value?.value, () => {
|
|
159
|
+
autoResize();
|
|
134
160
|
});
|
|
135
161
|
|
|
136
162
|
function autoResize() {
|
|
@@ -161,7 +187,9 @@ function selectMode(modeName: string) {
|
|
|
161
187
|
async function sendMessage() {
|
|
162
188
|
if (agentStore.isAudioChatMode) return;
|
|
163
189
|
isModeMenuOpen.value = false;
|
|
164
|
-
|
|
190
|
+
// Routes to a steer (folded into the running turn) while generating, or a normal
|
|
191
|
+
// new-turn send when idle.
|
|
192
|
+
await agentStore.submitUserMessage();
|
|
165
193
|
autoResize();
|
|
166
194
|
props.conversationAreaRef?.handleSendMessage();
|
|
167
195
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
TEMPORARY debug helper: enqueues a test steer message into the buffer WITHOUT sending
|
|
3
|
+
or steering it, so the SteerQueue enter/leave animation can be exercised without a live
|
|
4
|
+
generating turn. Remove before shipping.
|
|
5
|
+
-->
|
|
6
|
+
<template>
|
|
7
|
+
<div
|
|
8
|
+
class="fixed bottom-[400px] left-4 z-[9999] flex items-center gap-2 rounded-lg border border-dashed border-amber-400 bg-amber-50/95 px-2.5 py-2 shadow-lg backdrop-blur dark:border-amber-500 dark:bg-amber-950/95"
|
|
9
|
+
>
|
|
10
|
+
<span class="text-[10px] font-bold uppercase tracking-wider text-amber-600 dark:text-amber-400">
|
|
11
|
+
Steer debug
|
|
12
|
+
</span>
|
|
13
|
+
<input
|
|
14
|
+
v-model="debugText"
|
|
15
|
+
type="text"
|
|
16
|
+
placeholder="Test steer text…"
|
|
17
|
+
class="w-44 rounded-md border border-amber-300 bg-white px-2 py-1 text-xs text-gray-800 focus:outline-none focus:ring-1 focus:ring-amber-400 dark:border-amber-600 dark:bg-gray-800 dark:text-gray-100"
|
|
18
|
+
@keydown.enter.prevent="addTestSteer"
|
|
19
|
+
/>
|
|
20
|
+
<button
|
|
21
|
+
type="button"
|
|
22
|
+
class="rounded-md bg-amber-500 px-2.5 py-1 text-xs font-semibold text-white transition-colors hover:bg-amber-600 active:bg-amber-700"
|
|
23
|
+
@click="addTestSteer"
|
|
24
|
+
>
|
|
25
|
+
Add steer
|
|
26
|
+
</button>
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<script setup lang="ts">
|
|
31
|
+
import { ref } from 'vue';
|
|
32
|
+
import { useAgentStore } from './composables/useAgentStore';
|
|
33
|
+
|
|
34
|
+
const agentStore = useAgentStore();
|
|
35
|
+
const debugText = ref('');
|
|
36
|
+
let counter = 0;
|
|
37
|
+
|
|
38
|
+
function addTestSteer() {
|
|
39
|
+
counter += 1;
|
|
40
|
+
const text = debugText.value.trim() || `Test steer #${counter}`;
|
|
41
|
+
// enqueue() only pushes onto the client-side buffer — no /agent/steer, no send.
|
|
42
|
+
agentStore.enqueueSteerMessage(text);
|
|
43
|
+
debugText.value = '';
|
|
44
|
+
}
|
|
45
|
+
</script>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="agentStore.steerQueue.length"
|
|
4
|
+
class="absolute bottom-full left-0 w-full mb-2 flex flex-col gap-1.5 px-2 z-20"
|
|
5
|
+
>
|
|
6
|
+
<TransitionGroup
|
|
7
|
+
appear
|
|
8
|
+
enter-active-class="transition ease-out duration-150"
|
|
9
|
+
enter-from-class="opacity-0 translate-y-1"
|
|
10
|
+
enter-to-class="opacity-100 translate-y-0"
|
|
11
|
+
leave-active-class="transition ease-in duration-100"
|
|
12
|
+
leave-from-class="opacity-100 translate-y-0"
|
|
13
|
+
leave-to-class="opacity-0 translate-y-1"
|
|
14
|
+
>
|
|
15
|
+
<div
|
|
16
|
+
v-for="item in agentStore.steerQueue"
|
|
17
|
+
:key="item.id"
|
|
18
|
+
class="flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-3 py-1.5 shadow-sm dark:border-gray-600 dark:bg-gray-800"
|
|
19
|
+
>
|
|
20
|
+
<span
|
|
21
|
+
class="flex-1 min-w-0 truncate text-sm text-lightInputText dark:text-darkInputText"
|
|
22
|
+
:title="item.text"
|
|
23
|
+
>
|
|
24
|
+
{{ item.text }}
|
|
25
|
+
</span>
|
|
26
|
+
<span class="shrink-0 text-xs text-gray-400 dark:text-gray-500">
|
|
27
|
+
{{ $t('queued') }}
|
|
28
|
+
</span>
|
|
29
|
+
<button
|
|
30
|
+
type="button"
|
|
31
|
+
class="shrink-0 rounded-md px-2 py-1 text-xs font-medium text-lightPrimary transition-colors hover:bg-lightPrimary/10 dark:text-darkPrimary dark:hover:bg-darkPrimary/10"
|
|
32
|
+
:title="$t('Steer into the current response')"
|
|
33
|
+
@click="agentStore.steerQueuedMessage(item.id)"
|
|
34
|
+
>
|
|
35
|
+
{{ $t('Steer') }}
|
|
36
|
+
</button>
|
|
37
|
+
<button
|
|
38
|
+
type="button"
|
|
39
|
+
class="shrink-0 flex h-6 w-6 items-center justify-center rounded-md text-lg leading-none text-gray-400 transition-colors hover:bg-gray-200 hover:text-gray-600 dark:hover:bg-gray-700 dark:hover:text-gray-300"
|
|
40
|
+
:title="$t('Remove from queue')"
|
|
41
|
+
@click="agentStore.removeQueuedMessage(item.id)"
|
|
42
|
+
>
|
|
43
|
+
×
|
|
44
|
+
</button>
|
|
45
|
+
</div>
|
|
46
|
+
</TransitionGroup>
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
<script setup lang="ts">
|
|
51
|
+
import { useAgentStore } from './composables/useAgentStore';
|
|
52
|
+
|
|
53
|
+
const agentStore = useAgentStore();
|
|
54
|
+
</script>
|
|
@@ -11,6 +11,7 @@ export const PLACEHOLDER_TYPING_DELAY_MS = 60;
|
|
|
11
11
|
export const PLACEHOLDER_DELETING_DELAY_MS = 35;
|
|
12
12
|
export const PLACEHOLDER_HOLD_DELAY_MS = 3000;
|
|
13
13
|
export const PRE_SESSION_ID = 'pre-session';
|
|
14
|
+
export const STEER_PERSIST_PREFIX = '__adminforth_steer__:';
|
|
14
15
|
|
|
15
16
|
export enum RESERVED_SYSTEM_MESSAGE_CONTENT {
|
|
16
17
|
START_AUDIO_CHAT = 'START_AUDIO_CHAT',
|