@adminforth/agent 2.2.5 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/ports.d.ts +18 -0
- package/dist/application/runTurnUseCase.d.ts +17 -1
- package/dist/application/runTurnUseCase.js +145 -4
- package/dist/custom/ChatFooter.vue +61 -23
- package/dist/custom/ChatSurface.vue +1 -0
- package/dist/custom/composables/agentStore/useAgentChat.ts +122 -7
- package/dist/custom/composables/agentStore/useAgentSessions.ts +8 -5
- package/dist/custom/composables/agentStore/useAgentSteerQueue.ts +5 -1
- package/dist/custom/composables/useAgentStore.ts +83 -0
- package/dist/custom/conversation_area/MessageRenderer.vue +114 -29
- package/dist/custom/conversation_area/TextRenderer.vue +16 -6
- package/dist/custom/speech_recognition_frontend/MicrophoneButon.vue +4 -0
- package/dist/custom/types.ts +7 -0
- package/dist/domain/agentEvents.d.ts +4 -0
- package/dist/domain/turnTypes.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/llm/agentModels.d.ts +1 -0
- package/dist/llm/agentRuntime.d.ts +14 -0
- package/dist/llm/agentRuntime.js +42 -3
- package/dist/llm/langGraphLlm.d.ts +8 -0
- package/dist/llm/langGraphLlm.js +12 -0
- package/dist/persistence/sessionStore.d.ts +30 -0
- package/dist/persistence/sessionStore.js +56 -1
- package/dist/transport/http/context.d.ts +4 -2
- package/dist/transport/http/coreEndpoints.js +35 -0
- package/dist/transport/http/sessionEndpoints.js +3 -0
- package/dist/transport/sse/sseWriter.js +12 -0
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
|
@@ -63,13 +63,65 @@ export class AgentSessionStore {
|
|
|
63
63
|
}
|
|
64
64
|
getSessionTurns(sessionId) {
|
|
65
65
|
return __awaiter(this, void 0, void 0, function* () {
|
|
66
|
-
const turns = yield this.getAdminforth().resource(this.options.turnResource.resourceId).list([Filters.EQ(this.options.turnResource.sessionIdField, sessionId)], undefined, undefined, [Sorts.ASC(this.options.turnResource.createdAtField)]);
|
|
66
|
+
const turns = yield this.getAdminforth().resource(this.options.turnResource.resourceId).list([Filters.EQ(this.options.turnResource.sessionIdField, sessionId)], undefined, undefined, [Sorts.ASC(this.options.turnResource.createdAtField), Sorts.ASC(this.options.turnResource.idField)]);
|
|
67
67
|
return turns.map(turn => ({
|
|
68
|
+
id: turn[this.options.turnResource.idField],
|
|
68
69
|
prompt: turn[this.options.turnResource.promptField],
|
|
69
70
|
response: turn[this.options.turnResource.responseField],
|
|
70
71
|
}));
|
|
71
72
|
});
|
|
72
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Ordered non-system (real conversation) turns with their stored checkpoint id.
|
|
76
|
+
* Used by message editing to locate the target turn and its previous turn's
|
|
77
|
+
* fork point. Stable order (`createdAt ASC, id ASC`) so positions never drift.
|
|
78
|
+
*/
|
|
79
|
+
getAgentTurns(sessionId) {
|
|
80
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
const checkpointIdField = this.options.turnResource.checkpointIdField;
|
|
82
|
+
const turns = yield this.getAdminforth().resource(this.options.turnResource.resourceId).list([Filters.EQ(this.options.turnResource.sessionIdField, sessionId)], undefined, undefined, [Sorts.ASC(this.options.turnResource.createdAtField), Sorts.ASC(this.options.turnResource.idField)]);
|
|
83
|
+
return turns
|
|
84
|
+
.filter((turn) => turn[this.options.turnResource.promptField] !== AGENT_SYSTEM_TURN_PROMPT)
|
|
85
|
+
.map((turn) => ({
|
|
86
|
+
id: turn[this.options.turnResource.idField],
|
|
87
|
+
prompt: turn[this.options.turnResource.promptField],
|
|
88
|
+
response: turn[this.options.turnResource.responseField],
|
|
89
|
+
checkpointId: checkpointIdField
|
|
90
|
+
? (turn[checkpointIdField] ? String(turn[checkpointIdField]) : null)
|
|
91
|
+
: null,
|
|
92
|
+
}));
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Apply a message edit and branch: replace the edited turn's prompt and truncate
|
|
97
|
+
* everything after it. Deletes later turns FIRST, then updates the edited turn, so
|
|
98
|
+
* an interruption can only leave a consistent "truncated, edit-not-yet-applied"
|
|
99
|
+
* state (recoverable by retry) — never "edited but stale later turns remain".
|
|
100
|
+
* (AdminForth's resource API has no transactions, hence the deliberate ordering.)
|
|
101
|
+
*/
|
|
102
|
+
editTurnAndTruncateAfter(input) {
|
|
103
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
const r = this.options.turnResource;
|
|
105
|
+
const resource = this.getAdminforth().resource(r.resourceId);
|
|
106
|
+
const turns = yield resource.list([Filters.EQ(r.sessionIdField, input.sessionId)], undefined, undefined, [Sorts.ASC(r.createdAtField), Sorts.ASC(r.idField)]);
|
|
107
|
+
const targetIndex = turns.findIndex((turn) => turn[r.idField] === input.turnId);
|
|
108
|
+
if (targetIndex === -1) {
|
|
109
|
+
throw new Error(`Turn "${input.turnId}" not found in session "${input.sessionId}".`);
|
|
110
|
+
}
|
|
111
|
+
const laterTurns = turns.slice(targetIndex + 1);
|
|
112
|
+
for (const turn of laterTurns) {
|
|
113
|
+
yield resource.delete(turn[r.idField]);
|
|
114
|
+
}
|
|
115
|
+
const updates = {
|
|
116
|
+
[r.promptField]: input.newPrompt,
|
|
117
|
+
[r.responseField]: "not_finished",
|
|
118
|
+
};
|
|
119
|
+
if (r.checkpointIdField) {
|
|
120
|
+
updates[r.checkpointIdField] = null;
|
|
121
|
+
}
|
|
122
|
+
yield resource.update(input.turnId, updates);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
73
125
|
getPreviousUserMessages(sessionId) {
|
|
74
126
|
return __awaiter(this, void 0, void 0, function* () {
|
|
75
127
|
const turns = yield this.getAdminforth().resource(this.options.turnResource.resourceId).list([Filters.EQ(this.options.turnResource.sessionIdField, sessionId)], 2, undefined, [Sorts.DESC(this.options.turnResource.createdAtField)]);
|
|
@@ -121,6 +173,9 @@ export class AgentSessionStore {
|
|
|
121
173
|
if (this.options.turnResource.debugField) {
|
|
122
174
|
turnUpdates[this.options.turnResource.debugField] = input.debugHistory;
|
|
123
175
|
}
|
|
176
|
+
if (this.options.turnResource.checkpointIdField && input.checkpointId !== undefined) {
|
|
177
|
+
turnUpdates[this.options.turnResource.checkpointIdField] = input.checkpointId;
|
|
178
|
+
}
|
|
124
179
|
yield this.getAdminforth()
|
|
125
180
|
.resource(this.options.turnResource.resourceId)
|
|
126
181
|
.update(input.turnId, turnUpdates);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ChatSurfaceAdapter, ChatSurfaceEventSink, ChatSurfaceIncomingMessage, IAdminForth } from "adminforth";
|
|
2
|
-
import type { HandleSpeechTurnInput, HandleTurnInput, RunAndPersistAgentResponseInput, RunAndPersistAgentResponseResult } from "../../domain/turnTypes.js";
|
|
2
|
+
import type { HandleEditTurnInput, HandleSpeechTurnInput, HandleTurnInput, RunAndPersistAgentResponseInput, RunAndPersistAgentResponseResult } from "../../domain/turnTypes.js";
|
|
3
3
|
import type { PluginOptions } from "../../types.js";
|
|
4
4
|
export type SessionTurn = {
|
|
5
|
+
id: string;
|
|
5
6
|
prompt: string;
|
|
6
7
|
response: string;
|
|
7
8
|
};
|
|
@@ -9,6 +10,7 @@ export type AgentEndpointsContext = {
|
|
|
9
10
|
adminforth: IAdminForth;
|
|
10
11
|
options: PluginOptions;
|
|
11
12
|
handleTurn(input: HandleTurnInput): Promise<RunAndPersistAgentResponseResult>;
|
|
13
|
+
handleEditTurn(input: HandleEditTurnInput): Promise<RunAndPersistAgentResponseResult>;
|
|
12
14
|
handleSpeechTurn(input: HandleSpeechTurnInput): Promise<RunAndPersistAgentResponseResult | null>;
|
|
13
15
|
steer(input: {
|
|
14
16
|
sessionId: string;
|
|
@@ -24,6 +26,6 @@ export type AgentEndpointsContext = {
|
|
|
24
26
|
appendSteerToCurrentTurn(sessionId: string, steerText: string): Promise<void>;
|
|
25
27
|
handleChatSurfaceMessage(adapter: ChatSurfaceAdapter, incoming: ChatSurfaceIncomingMessage, sink: ChatSurfaceEventSink): Promise<void>;
|
|
26
28
|
};
|
|
27
|
-
export type CoreEndpointsContext = Pick<AgentEndpointsContext, "options" | "handleTurn" | "handleSpeechTurn" | "steer">;
|
|
29
|
+
export type CoreEndpointsContext = Pick<AgentEndpointsContext, "options" | "handleTurn" | "handleEditTurn" | "handleSpeechTurn" | "steer">;
|
|
28
30
|
export type SessionEndpointsContext = Pick<AgentEndpointsContext, "adminforth" | "options" | "getSessionTurns" | "createNewTurn" | "createSystemTurn" | "appendSteerToCurrentTurn">;
|
|
29
31
|
export type ChatSurfaceEndpointsContext = Pick<AgentEndpointsContext, "adminforth" | "options" | "handleChatSurfaceMessage">;
|
|
@@ -30,6 +30,14 @@ const agentSteerBodySchema = z.object({
|
|
|
30
30
|
sessionId: z.string(),
|
|
31
31
|
message: z.string().min(1),
|
|
32
32
|
}).strict();
|
|
33
|
+
const agentEditBodySchema = z.object({
|
|
34
|
+
sessionId: z.string(),
|
|
35
|
+
turnId: z.string(),
|
|
36
|
+
message: z.string().min(1),
|
|
37
|
+
mode: z.string().nullish(),
|
|
38
|
+
timeZone: z.string().optional(),
|
|
39
|
+
currentPage: currentPageContextSchema.optional(),
|
|
40
|
+
}).strict();
|
|
33
41
|
// Sent as multipart/form-data (via multer), so every field arrives as a plain string on the wire.
|
|
34
42
|
// `currentPage` is JSON.stringify'd by the client into a form field and must be parsed back into
|
|
35
43
|
// an object. Zod -> JSON-Schema conversion cannot represent `.transform()` at all (it throws), so
|
|
@@ -103,6 +111,33 @@ export function setupCoreEndpoints(ctx, server) {
|
|
|
103
111
|
return null;
|
|
104
112
|
})
|
|
105
113
|
});
|
|
114
|
+
server.endpoint({
|
|
115
|
+
method: 'POST',
|
|
116
|
+
path: `/agent/edit`,
|
|
117
|
+
request_schema: agentEditBodySchema,
|
|
118
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, _raw_express_res, abortSignal }) {
|
|
119
|
+
var _b;
|
|
120
|
+
const data = body;
|
|
121
|
+
const emit = createSseEventEmitter(_raw_express_res, {
|
|
122
|
+
vercelAiUiMessageStream: true,
|
|
123
|
+
closeActiveBlockOnToolStart: true,
|
|
124
|
+
});
|
|
125
|
+
yield ctx.handleEditTurn({
|
|
126
|
+
prompt: data.message,
|
|
127
|
+
turnId: data.turnId,
|
|
128
|
+
sessionId: data.sessionId,
|
|
129
|
+
modeName: data.mode,
|
|
130
|
+
userTimeZone: (_b = data.timeZone) !== null && _b !== void 0 ? _b : 'UTC',
|
|
131
|
+
currentPage: data.currentPage,
|
|
132
|
+
abortSignal,
|
|
133
|
+
adminUser: adminUser,
|
|
134
|
+
emit,
|
|
135
|
+
failureLogMessage: "Agent message edit streaming failed",
|
|
136
|
+
abortLogMessage: "Agent message edit streaming aborted by the client",
|
|
137
|
+
});
|
|
138
|
+
return null;
|
|
139
|
+
})
|
|
140
|
+
});
|
|
106
141
|
server.endpoint({
|
|
107
142
|
method: 'POST',
|
|
108
143
|
path: `/agent/approval`,
|
|
@@ -81,6 +81,7 @@ export function setupSessionEndpoints(ctx, server) {
|
|
|
81
81
|
messages.push({
|
|
82
82
|
text: turn.response,
|
|
83
83
|
role: 'system',
|
|
84
|
+
turnId: turn.id,
|
|
84
85
|
});
|
|
85
86
|
return messages;
|
|
86
87
|
}
|
|
@@ -88,12 +89,14 @@ export function setupSessionEndpoints(ctx, server) {
|
|
|
88
89
|
messages.push({
|
|
89
90
|
text: turn.prompt,
|
|
90
91
|
role: 'user',
|
|
92
|
+
turnId: turn.id,
|
|
91
93
|
});
|
|
92
94
|
}
|
|
93
95
|
if (turn.response && turn.response !== "not_finished") {
|
|
94
96
|
messages.push({
|
|
95
97
|
text: turn.response,
|
|
96
98
|
role: 'assistant',
|
|
99
|
+
turnId: turn.id,
|
|
97
100
|
});
|
|
98
101
|
}
|
|
99
102
|
return messages;
|
|
@@ -124,6 +124,15 @@ function createAgentEventStream(res, options = {}) {
|
|
|
124
124
|
},
|
|
125
125
|
});
|
|
126
126
|
},
|
|
127
|
+
turnPersisted(sessionId, turnId) {
|
|
128
|
+
stream.send({
|
|
129
|
+
type: isAiUiMessageStream ? "data-turn-persisted" : "turn-persisted",
|
|
130
|
+
data: {
|
|
131
|
+
sessionId,
|
|
132
|
+
turnId,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
},
|
|
127
136
|
response(text, sessionId, turnId) {
|
|
128
137
|
stream.send({
|
|
129
138
|
type: isAiUiMessageStream ? "data-response" : "response",
|
|
@@ -240,6 +249,9 @@ export function createSseEventEmitter(res, options = {}) {
|
|
|
240
249
|
case "transcript":
|
|
241
250
|
stream.transcript(event.text, event.language);
|
|
242
251
|
break;
|
|
252
|
+
case "turn-persisted":
|
|
253
|
+
stream.turnPersisted(event.sessionId, event.turnId);
|
|
254
|
+
break;
|
|
243
255
|
case "response":
|
|
244
256
|
stream.response(event.text, event.sessionId, event.turnId);
|
|
245
257
|
break;
|
package/dist/types.d.ts
CHANGED