@adminforth/agent 1.5.0 → 1.7.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.
@@ -21,8 +21,11 @@ export type SequenceDebug = {
21
21
  sequenceId: number;
22
22
  startedAt: string;
23
23
  prompt: string;
24
+ promptTokens: number;
24
25
  reasoning: string;
26
+ reasoningTokens: number;
25
27
  text: string;
28
+ textTokens: number;
26
29
  cachedTokens: number;
27
30
  responseId: string | null;
28
31
  toolCalls: SequenceDebugToolCall[];
@@ -37,14 +40,18 @@ type PendingSequenceDebug = Omit<SequenceDebug, "toolCalls" | "endedAt" | "resul
37
40
  };
38
41
 
39
42
  type SequenceDebugModelCall = {
43
+ promptTokens: number;
40
44
  reasoning: string;
45
+ reasoningTokens: number;
41
46
  text: string;
47
+ textTokens: number;
42
48
  cachedTokens: number;
43
49
  responseId: string | null;
44
50
  resultType: SequenceDebugResultType;
45
51
  };
46
52
 
47
53
  type OpenAiUsageMetadata = {
54
+ input_tokens?: number;
48
55
  input_token_details?: {
49
56
  cache_read?: number;
50
57
  };
@@ -70,8 +77,11 @@ function createPendingSequenceDebug(sequenceId: number): PendingSequenceDebug {
70
77
  sequenceId,
71
78
  startedAt: new Date().toISOString(),
72
79
  prompt: "",
80
+ promptTokens: 0,
73
81
  reasoning: "",
82
+ reasoningTokens: 0,
74
83
  text: "",
84
+ textTokens: 0,
75
85
  cachedTokens: 0,
76
86
  responseId: null,
77
87
  toolCalls: [],
@@ -97,8 +107,11 @@ function finalizeSequenceDebug(sequence: PendingSequenceDebug): SequenceDebug {
97
107
  sequenceId: sequence.sequenceId,
98
108
  startedAt: sequence.startedAt,
99
109
  prompt: sequence.prompt,
110
+ promptTokens: sequence.promptTokens,
100
111
  reasoning: sequence.reasoning,
112
+ reasoningTokens: sequence.reasoningTokens,
101
113
  text: sequence.text,
114
+ textTokens: sequence.textTokens,
102
115
  cachedTokens: sequence.cachedTokens,
103
116
  responseId: sequence.responseId,
104
117
  toolCalls: sequence.toolCalls.map(({ completed: _completed, ...toolCall }) => toolCall),
@@ -176,6 +189,29 @@ function hasToolCallSignal(message: {
176
189
  );
177
190
  }
178
191
 
192
+ function hasTokenCounter(model: unknown): model is {
193
+ getNumTokens: (content: string) => Promise<number>;
194
+ } {
195
+ return (
196
+ typeof model === "object" &&
197
+ model !== null &&
198
+ "getNumTokens" in model &&
199
+ typeof model.getNumTokens === "function"
200
+ );
201
+ }
202
+
203
+ async function countTokens(model: unknown, content: string) {
204
+ if (!content) {
205
+ return 0;
206
+ }
207
+
208
+ if (!hasTokenCounter(model)) {
209
+ return 0;
210
+ }
211
+
212
+ return await model.getNumTokens(content);
213
+ }
214
+
179
215
  function extractSequenceResponseDebug(message: AIMessage): SequenceDebugModelCall {
180
216
  const blocks = getMessageBlocks(message);
181
217
  const reasoning = blocks
@@ -188,8 +224,13 @@ function extractSequenceResponseDebug(message: AIMessage): SequenceDebugModelCal
188
224
  .join("");
189
225
 
190
226
  return {
227
+ promptTokens:
228
+ (message.usage_metadata as OpenAiUsageMetadata | undefined)?.input_tokens ??
229
+ 0,
191
230
  reasoning,
231
+ reasoningTokens: 0,
192
232
  text: textFromBlocks || (typeof message.content === "string" ? message.content : ""),
233
+ textTokens: 0,
193
234
  cachedTokens:
194
235
  (message.usage_metadata as OpenAiUsageMetadata | undefined)
195
236
  ?.input_token_details?.cache_read ?? 0,
@@ -238,8 +279,11 @@ export function createSequenceDebugCollector(): SequenceDebugCollector {
238
279
  },
239
280
  handleModelCallComplete(params) {
240
281
  const sequenceDebug = ensureSequenceDebug();
282
+ sequenceDebug.promptTokens = params.promptTokens;
241
283
  sequenceDebug.reasoning = params.reasoning;
284
+ sequenceDebug.reasoningTokens = params.reasoningTokens;
242
285
  sequenceDebug.text = params.text;
286
+ sequenceDebug.textTokens = params.textTokens;
243
287
  sequenceDebug.cachedTokens = params.cachedTokens;
244
288
  sequenceDebug.responseId = params.responseId;
245
289
  sequenceDebug.resultType = params.resultType;
@@ -311,17 +355,31 @@ export function createSequenceDebugMiddleware(
311
355
  return createMiddleware({
312
356
  name: "SequenceDebugMiddleware",
313
357
  async wrapModelCall(request, handler) {
358
+ const prompt = stringifyPromptForDebug({
359
+ systemMessage: request.systemMessage,
360
+ messages: request.messages,
361
+ tools: request.tools,
362
+ modelSettings: request.modelSettings,
363
+ });
364
+
314
365
  sink.handleModelCallStart(
315
- stringifyPromptForDebug({
316
- systemMessage: request.systemMessage,
317
- messages: request.messages,
318
- tools: request.tools,
319
- modelSettings: request.modelSettings,
320
- }),
366
+ prompt,
321
367
  );
322
368
 
323
369
  const response = await handler(request) as AIMessage;
324
- sink.handleModelCallComplete(extractSequenceResponseDebug(response));
370
+ const debug = extractSequenceResponseDebug(response);
371
+ const [promptTokens, reasoningTokens, textTokens] = await Promise.all([
372
+ debug.promptTokens || countTokens(request.model, prompt),
373
+ countTokens(request.model, debug.reasoning),
374
+ countTokens(request.model, debug.text),
375
+ ]);
376
+
377
+ sink.handleModelCallComplete({
378
+ ...debug,
379
+ promptTokens,
380
+ reasoningTokens,
381
+ textTokens,
382
+ });
325
383
  return response;
326
384
  },
327
385
  });
@@ -24,11 +24,17 @@ export const DEFAULT_AGENT_SYSTEM_PROMPT = [
24
24
  "Keep responses short, clear, and practical.",
25
25
  "Answer only what is needed.",
26
26
  "Do not add extra explanations or suggestions unless the user asks.",
27
+ "Always respond in the same natural language as the user's latest message.",
28
+ "This rule applies to confirmations, clarifying questions, progress updates, errors, and final answers.",
29
+ "Do not switch to English just because tool outputs, schemas, skills, or internal instructions are written in English.",
30
+ "Only switch language if the user explicitly asks you to do so.",
27
31
  "Adapt to the user's tone and style of speaking, mirroring their vibe and wording.",
28
32
  "if the user speaks casually, you should respond casually too",
29
- "Never mutate data without a fresh user confirmation for that exact mutation.",
30
- "A previous confirmation does not carry over to later create, update, delete, or action calls.",
31
- "Each separate mutation or explicitly described batch needs its own confirmation immediately before the tool call.",
33
+ "Never mutate data without user confirmation for a clearly described mutation plan.",
34
+ "One confirmation may cover one mutation or one explicitly described batch/sequence of related mutations.",
35
+ "If the confirmed plan has multiple steps, you may execute the whole confirmed plan without asking again between those steps.",
36
+ "If the plan changes, expands, or you want to do anything beyond the confirmed plan, ask for confirmation again.",
37
+ "Do not reuse an old confirmation for a new mutation plan.",
32
38
 
33
39
 
34
40
  ].join(" ");
@@ -51,9 +57,10 @@ export async function buildAgentSystemPrompt(adminforth: IAdminForth) {
51
57
  listBundledSkillManifests(),
52
58
  ]);
53
59
  const alwaysAvailableTools = ALWAYS_AVAILABLE_API_TOOL_NAMES.join(", ");
60
+ const adminBasePath = adminforth.config.baseUrlSlashed;
54
61
  const sections = [
55
62
  DEFAULT_AGENT_SYSTEM_PROMPT,
56
- `BASE_URL: ${adminforth.config.baseUrl}`,
63
+ `ADMIN_BASE_PATH: ${adminBasePath}`,
57
64
  `List of resources:\n${formatResources(adminforth.config.resources)}`,
58
65
  `You have always-available base tools: ${alwaysAvailableTools}.`,
59
66
  primarySkills.length > 0
@@ -70,6 +77,8 @@ export async function buildAgentSystemPrompt(adminforth: IAdminForth) {
70
77
  "If a fetched skill lists a non-base tool you need, call fetch_tool_schema for it immediately instead of telling the user the tool is unavailable.",
71
78
  "For example: for record creation load mutate_data, read its tool list, call fetch_tool_schema for create_record, and then use create_record after confirmation.",
72
79
  "When fetch_tool_schema succeeds, that tool becomes available on the next step.",
80
+ "All admin links must be relative paths and must start with ADMIN_BASE_PATH.",
81
+ "Build record links as ADMIN_BASE_PATH + resource/{resourceId}/show/{primary key}. Do not prepend any extra slash before resource.",
73
82
  "Try to call as many tools as possible in parallel in one step.",
74
83
  ];
75
84
 
package/build.log CHANGED
@@ -29,5 +29,5 @@ custom/skills/fetch_data/SKILL.md
29
29
  custom/skills/mutate_data/
30
30
  custom/skills/mutate_data/SKILL.md
31
31
 
32
- sent 170,096 bytes received 413 bytes 341,018.00 bytes/sec
33
- total size is 168,447 speedup is 0.99
32
+ sent 170,560 bytes received 413 bytes 341,946.00 bytes/sec
33
+ total size is 168,886 speedup is 0.99
@@ -12,4 +12,4 @@ To find specific data record you should use filters. ILIKE filters are preferred
12
12
 
13
13
  For long texts show only several first words and add "..." at the end (only if user did not request this field specifically).
14
14
 
15
- Also when you communicate with user about record, add related link to this record. For example /{BASE_URL}/resource/{resourceId}/show/{primary key}. Use _label from `get_resource_data` as anchor text for link (use markdown link). Links shoudl be always relative path, starting with slash.
15
+ Also when you communicate with user about record, add related link to this record. Build it as `{ADMIN_BASE_PATH}resource/{resourceId}/show/{primary key}`. Use _label from `get_resource_data` as anchor text for link (use markdown link). Links should always be relative paths and must start with `ADMIN_BASE_PATH`. Do not add an extra slash after `ADMIN_BASE_PATH`.
@@ -21,7 +21,7 @@ Use `start_custom_action` and `start_custom_bulk_action` for resource actions.
21
21
  Before performing any state mutation including action calls edit/delete please fetch record which is going to be edited/deleted and show user record in format field → value (show several most important fields which can help user to understand what exactly record he is going to edit or delete).
22
22
 
23
23
  For field values with long texts show only several first words and add "..." at the end.
24
- Also please add related link to record with will be changed. For example /{BASE_URL}/resource/{resourceId}/show/{primary key}. Use _label from `get_resource_data` as anchor text for link (use markdown link). Links shoudl be always relative path, starting with slash.
24
+ Also please add related link to record with will be changed. Build it as `{ADMIN_BASE_PATH}resource/{resourceId}/show/{primary key}`. Use _label from `get_resource_data` as anchor text for link (use markdown link). Links should always be relative paths and must start with `ADMIN_BASE_PATH`. Do not add an extra slash after `ADMIN_BASE_PATH`.
25
25
 
26
26
  And in the same message ask user for final confirmation.
27
27
 
@@ -29,17 +29,22 @@ When creating new record, show user all data which you gona create and in same m
29
29
 
30
30
  Accept any positive confirmation from user like "yes", "sure", "+", anything non-negative call to action, can be considered as confirmation.
31
31
 
32
- A confirmation is valid only for the exact mutation plan from the immediately previous assistant message.
32
+ A confirmation is valid only for the clearly described mutation plan from the immediately previous assistant message.
33
33
 
34
34
  Never reuse an older confirmation for a later mutation.
35
35
 
36
- After one mutation is executed, confirmation is consumed and reset.
36
+ One confirmation may cover:
37
+ - one single mutation
38
+ - one explicitly described batch
39
+ - one short sequence of related mutations that together implement the same user request
37
40
 
38
- If you want to perform another create/update/delete/action after that, ask for confirmation again even if the user previously said "yes".
41
+ If the confirmed plan contains several related mutation steps, execute that whole confirmed plan without asking again between those steps.
39
42
 
40
- If the mutation plan changes in any way (different record, different fields, different values, different number of records, different action), the old confirmation is invalid and you must ask again.
43
+ Ask for confirmation again if the plan changes in any way: different record, different fields, different values, different number of records, different action, or any extra mutation that was not listed in the confirmation message.
41
44
 
42
- If you are creating or deleting multiple records in one batch, you may ask once only for that exact batch, but you must list the whole batch explicitly in the confirmation message. Any extra record outside that described batch requires a new confirmation.
45
+ If you are creating or deleting multiple records in one batch, you may ask once for that exact batch, but list the whole batch explicitly in the confirmation message. Any extra record outside that described batch requires a new confirmation.
46
+
47
+ After the confirmed plan is finished, do not treat that confirmation as still active for later requests.
43
48
 
44
49
  # Calling actions
45
50
 
@@ -57,7 +62,7 @@ If you want to block some user you can confirm that this action by saying:
57
62
  * IP Country: USA
58
63
  * Currently blocked: No // show this field only if it exists in user record
59
64
 
60
- View [John Doe](/resource/users/show/123)
65
+ View [John Doe]({ADMIN_BASE_PATH}resource/users/show/123)
61
66
  Are you sure?
62
67
  ```
63
68
 
@@ -80,7 +85,7 @@ I am going to update user:
80
85
  * IP Country: USA
81
86
  I am going to change email from john_doe@example.com to new_email@example.com
82
87
 
83
- View [John Doe](/admin/resource/users/show/123)
88
+ View [John Doe]({ADMIN_BASE_PATH}resource/users/show/123)
84
89
 
85
90
  Are you sure?
86
91
  ```
@@ -100,7 +105,7 @@ If you gonna delete user record, in confirmation please share full user info (no
100
105
  * Signed up: 2024 Jan 1
101
106
  * IP Country: USA
102
107
 
103
- View [John Doe](/admin/resource/users/show/123)
108
+ View [John Doe]({ADMIN_BASE_PATH}resource/users/show/123)
104
109
 
105
110
  Are you sure?
106
111
  ```
@@ -125,7 +130,7 @@ I am going to create user:
125
130
  * Username: john_doe
126
131
  * Email: john_doe@example.com
127
132
 
128
- View [John Doe](/admin/resource/users/show/421) # 421 is id of new created record
133
+ View [John Doe]({ADMIN_BASE_PATH}resource/users/show/421) # 421 is id of new created record
129
134
 
130
135
  Are you sure?
131
136
  ```
@@ -25,8 +25,11 @@ function createPendingSequenceDebug(sequenceId) {
25
25
  sequenceId,
26
26
  startedAt: new Date().toISOString(),
27
27
  prompt: "",
28
+ promptTokens: 0,
28
29
  reasoning: "",
30
+ reasoningTokens: 0,
29
31
  text: "",
32
+ textTokens: 0,
30
33
  cachedTokens: 0,
31
34
  responseId: null,
32
35
  toolCalls: [],
@@ -47,8 +50,11 @@ function finalizeSequenceDebug(sequence) {
47
50
  sequenceId: sequence.sequenceId,
48
51
  startedAt: sequence.startedAt,
49
52
  prompt: sequence.prompt,
53
+ promptTokens: sequence.promptTokens,
50
54
  reasoning: sequence.reasoning,
55
+ reasoningTokens: sequence.reasoningTokens,
51
56
  text: sequence.text,
57
+ textTokens: sequence.textTokens,
52
58
  cachedTokens: sequence.cachedTokens,
53
59
  responseId: sequence.responseId,
54
60
  toolCalls: sequence.toolCalls.map((_a) => {
@@ -98,8 +104,25 @@ function hasToolCallSignal(message) {
98
104
  (Array.isArray((_a = message.additional_kwargs) === null || _a === void 0 ? void 0 : _a.tool_calls) &&
99
105
  message.additional_kwargs.tool_calls.length > 0));
100
106
  }
107
+ function hasTokenCounter(model) {
108
+ return (typeof model === "object" &&
109
+ model !== null &&
110
+ "getNumTokens" in model &&
111
+ typeof model.getNumTokens === "function");
112
+ }
113
+ function countTokens(model, content) {
114
+ return __awaiter(this, void 0, void 0, function* () {
115
+ if (!content) {
116
+ return 0;
117
+ }
118
+ if (!hasTokenCounter(model)) {
119
+ return 0;
120
+ }
121
+ return yield model.getNumTokens(content);
122
+ });
123
+ }
101
124
  function extractSequenceResponseDebug(message) {
102
- var _a, _b, _c, _d, _e;
125
+ var _a, _b, _c, _d, _e, _f, _g;
103
126
  const blocks = getMessageBlocks(message);
104
127
  const reasoning = blocks
105
128
  .filter((block) => (block === null || block === void 0 ? void 0 : block.type) === "reasoning")
@@ -110,10 +133,13 @@ function extractSequenceResponseDebug(message) {
110
133
  .map((block) => { var _a; return String((_a = block.text) !== null && _a !== void 0 ? _a : ""); })
111
134
  .join("");
112
135
  return {
136
+ promptTokens: (_b = (_a = message.usage_metadata) === null || _a === void 0 ? void 0 : _a.input_tokens) !== null && _b !== void 0 ? _b : 0,
113
137
  reasoning,
138
+ reasoningTokens: 0,
114
139
  text: textFromBlocks || (typeof message.content === "string" ? message.content : ""),
115
- cachedTokens: (_c = (_b = (_a = message.usage_metadata) === null || _a === void 0 ? void 0 : _a.input_token_details) === null || _b === void 0 ? void 0 : _b.cache_read) !== null && _c !== void 0 ? _c : 0,
116
- responseId: (_e = (_d = message.response_metadata) === null || _d === void 0 ? void 0 : _d.id) !== null && _e !== void 0 ? _e : null,
140
+ textTokens: 0,
141
+ cachedTokens: (_e = (_d = (_c = message.usage_metadata) === null || _c === void 0 ? void 0 : _c.input_token_details) === null || _d === void 0 ? void 0 : _d.cache_read) !== null && _e !== void 0 ? _e : 0,
142
+ responseId: (_g = (_f = message.response_metadata) === null || _f === void 0 ? void 0 : _f.id) !== null && _g !== void 0 ? _g : null,
117
143
  resultType: hasToolCallSignal(message) ? "tool_calls" : "final_text",
118
144
  };
119
145
  }
@@ -147,8 +173,11 @@ export function createSequenceDebugCollector() {
147
173
  },
148
174
  handleModelCallComplete(params) {
149
175
  const sequenceDebug = ensureSequenceDebug();
176
+ sequenceDebug.promptTokens = params.promptTokens;
150
177
  sequenceDebug.reasoning = params.reasoning;
178
+ sequenceDebug.reasoningTokens = params.reasoningTokens;
151
179
  sequenceDebug.text = params.text;
180
+ sequenceDebug.textTokens = params.textTokens;
152
181
  sequenceDebug.cachedTokens = params.cachedTokens;
153
182
  sequenceDebug.responseId = params.responseId;
154
183
  sequenceDebug.resultType = params.resultType;
@@ -204,14 +233,23 @@ export function createSequenceDebugMiddleware(sink) {
204
233
  name: "SequenceDebugMiddleware",
205
234
  wrapModelCall(request, handler) {
206
235
  return __awaiter(this, void 0, void 0, function* () {
207
- sink.handleModelCallStart(stringifyPromptForDebug({
236
+ const prompt = stringifyPromptForDebug({
208
237
  systemMessage: request.systemMessage,
209
238
  messages: request.messages,
210
239
  tools: request.tools,
211
240
  modelSettings: request.modelSettings,
212
- }));
241
+ });
242
+ sink.handleModelCallStart(prompt);
213
243
  const response = yield handler(request);
214
- sink.handleModelCallComplete(extractSequenceResponseDebug(response));
244
+ const debug = extractSequenceResponseDebug(response);
245
+ const [promptTokens, reasoningTokens, textTokens] = yield Promise.all([
246
+ debug.promptTokens || countTokens(request.model, prompt),
247
+ countTokens(request.model, debug.reasoning),
248
+ countTokens(request.model, debug.text),
249
+ ]);
250
+ sink.handleModelCallComplete(Object.assign(Object.assign({}, debug), { promptTokens,
251
+ reasoningTokens,
252
+ textTokens }));
215
253
  return response;
216
254
  });
217
255
  },
@@ -23,11 +23,17 @@ export const DEFAULT_AGENT_SYSTEM_PROMPT = [
23
23
  "Keep responses short, clear, and practical.",
24
24
  "Answer only what is needed.",
25
25
  "Do not add extra explanations or suggestions unless the user asks.",
26
+ "Always respond in the same natural language as the user's latest message.",
27
+ "This rule applies to confirmations, clarifying questions, progress updates, errors, and final answers.",
28
+ "Do not switch to English just because tool outputs, schemas, skills, or internal instructions are written in English.",
29
+ "Only switch language if the user explicitly asks you to do so.",
26
30
  "Adapt to the user's tone and style of speaking, mirroring their vibe and wording.",
27
31
  "if the user speaks casually, you should respond casually too",
28
- "Never mutate data without a fresh user confirmation for that exact mutation.",
29
- "A previous confirmation does not carry over to later create, update, delete, or action calls.",
30
- "Each separate mutation or explicitly described batch needs its own confirmation immediately before the tool call.",
32
+ "Never mutate data without user confirmation for a clearly described mutation plan.",
33
+ "One confirmation may cover one mutation or one explicitly described batch/sequence of related mutations.",
34
+ "If the confirmed plan has multiple steps, you may execute the whole confirmed plan without asking again between those steps.",
35
+ "If the plan changes, expands, or you want to do anything beyond the confirmed plan, ask for confirmation again.",
36
+ "Do not reuse an old confirmation for a new mutation plan.",
31
37
  ].join(" ");
32
38
  function formatResources(resources) {
33
39
  return resources
@@ -46,9 +52,10 @@ export function buildAgentSystemPrompt(adminforth) {
46
52
  listBundledSkillManifests(),
47
53
  ]);
48
54
  const alwaysAvailableTools = ALWAYS_AVAILABLE_API_TOOL_NAMES.join(", ");
55
+ const adminBasePath = adminforth.config.baseUrlSlashed;
49
56
  const sections = [
50
57
  DEFAULT_AGENT_SYSTEM_PROMPT,
51
- `BASE_URL: ${adminforth.config.baseUrl}`,
58
+ `ADMIN_BASE_PATH: ${adminBasePath}`,
52
59
  `List of resources:\n${formatResources(adminforth.config.resources)}`,
53
60
  `You have always-available base tools: ${alwaysAvailableTools}.`,
54
61
  primarySkills.length > 0
@@ -65,6 +72,8 @@ export function buildAgentSystemPrompt(adminforth) {
65
72
  "If a fetched skill lists a non-base tool you need, call fetch_tool_schema for it immediately instead of telling the user the tool is unavailable.",
66
73
  "For example: for record creation load mutate_data, read its tool list, call fetch_tool_schema for create_record, and then use create_record after confirmation.",
67
74
  "When fetch_tool_schema succeeds, that tool becomes available on the next step.",
75
+ "All admin links must be relative paths and must start with ADMIN_BASE_PATH.",
76
+ "Build record links as ADMIN_BASE_PATH + resource/{resourceId}/show/{primary key}. Do not prepend any extra slash before resource.",
68
77
  "Try to call as many tools as possible in parallel in one step.",
69
78
  ];
70
79
  return sections.filter(Boolean).join("\n\n");
@@ -12,4 +12,4 @@ To find specific data record you should use filters. ILIKE filters are preferred
12
12
 
13
13
  For long texts show only several first words and add "..." at the end (only if user did not request this field specifically).
14
14
 
15
- Also when you communicate with user about record, add related link to this record. For example /{BASE_URL}/resource/{resourceId}/show/{primary key}. Use _label from `get_resource_data` as anchor text for link (use markdown link). Links shoudl be always relative path, starting with slash.
15
+ Also when you communicate with user about record, add related link to this record. Build it as `{ADMIN_BASE_PATH}resource/{resourceId}/show/{primary key}`. Use _label from `get_resource_data` as anchor text for link (use markdown link). Links should always be relative paths and must start with `ADMIN_BASE_PATH`. Do not add an extra slash after `ADMIN_BASE_PATH`.
@@ -21,7 +21,7 @@ Use `start_custom_action` and `start_custom_bulk_action` for resource actions.
21
21
  Before performing any state mutation including action calls edit/delete please fetch record which is going to be edited/deleted and show user record in format field → value (show several most important fields which can help user to understand what exactly record he is going to edit or delete).
22
22
 
23
23
  For field values with long texts show only several first words and add "..." at the end.
24
- Also please add related link to record with will be changed. For example /{BASE_URL}/resource/{resourceId}/show/{primary key}. Use _label from `get_resource_data` as anchor text for link (use markdown link). Links shoudl be always relative path, starting with slash.
24
+ Also please add related link to record with will be changed. Build it as `{ADMIN_BASE_PATH}resource/{resourceId}/show/{primary key}`. Use _label from `get_resource_data` as anchor text for link (use markdown link). Links should always be relative paths and must start with `ADMIN_BASE_PATH`. Do not add an extra slash after `ADMIN_BASE_PATH`.
25
25
 
26
26
  And in the same message ask user for final confirmation.
27
27
 
@@ -29,17 +29,22 @@ When creating new record, show user all data which you gona create and in same m
29
29
 
30
30
  Accept any positive confirmation from user like "yes", "sure", "+", anything non-negative call to action, can be considered as confirmation.
31
31
 
32
- A confirmation is valid only for the exact mutation plan from the immediately previous assistant message.
32
+ A confirmation is valid only for the clearly described mutation plan from the immediately previous assistant message.
33
33
 
34
34
  Never reuse an older confirmation for a later mutation.
35
35
 
36
- After one mutation is executed, confirmation is consumed and reset.
36
+ One confirmation may cover:
37
+ - one single mutation
38
+ - one explicitly described batch
39
+ - one short sequence of related mutations that together implement the same user request
37
40
 
38
- If you want to perform another create/update/delete/action after that, ask for confirmation again even if the user previously said "yes".
41
+ If the confirmed plan contains several related mutation steps, execute that whole confirmed plan without asking again between those steps.
39
42
 
40
- If the mutation plan changes in any way (different record, different fields, different values, different number of records, different action), the old confirmation is invalid and you must ask again.
43
+ Ask for confirmation again if the plan changes in any way: different record, different fields, different values, different number of records, different action, or any extra mutation that was not listed in the confirmation message.
41
44
 
42
- If you are creating or deleting multiple records in one batch, you may ask once only for that exact batch, but you must list the whole batch explicitly in the confirmation message. Any extra record outside that described batch requires a new confirmation.
45
+ If you are creating or deleting multiple records in one batch, you may ask once for that exact batch, but list the whole batch explicitly in the confirmation message. Any extra record outside that described batch requires a new confirmation.
46
+
47
+ After the confirmed plan is finished, do not treat that confirmation as still active for later requests.
43
48
 
44
49
  # Calling actions
45
50
 
@@ -57,7 +62,7 @@ If you want to block some user you can confirm that this action by saying:
57
62
  * IP Country: USA
58
63
  * Currently blocked: No // show this field only if it exists in user record
59
64
 
60
- View [John Doe](/resource/users/show/123)
65
+ View [John Doe]({ADMIN_BASE_PATH}resource/users/show/123)
61
66
  Are you sure?
62
67
  ```
63
68
 
@@ -80,7 +85,7 @@ I am going to update user:
80
85
  * IP Country: USA
81
86
  I am going to change email from john_doe@example.com to new_email@example.com
82
87
 
83
- View [John Doe](/admin/resource/users/show/123)
88
+ View [John Doe]({ADMIN_BASE_PATH}resource/users/show/123)
84
89
 
85
90
  Are you sure?
86
91
  ```
@@ -100,7 +105,7 @@ If you gonna delete user record, in confirmation please share full user info (no
100
105
  * Signed up: 2024 Jan 1
101
106
  * IP Country: USA
102
107
 
103
- View [John Doe](/admin/resource/users/show/123)
108
+ View [John Doe]({ADMIN_BASE_PATH}resource/users/show/123)
104
109
 
105
110
  Are you sure?
106
111
  ```
@@ -125,7 +130,7 @@ I am going to create user:
125
130
  * Username: john_doe
126
131
  * Email: john_doe@example.com
127
132
 
128
- View [John Doe](/admin/resource/users/show/421) # 421 is id of new created record
133
+ View [John Doe]({ADMIN_BASE_PATH}resource/users/show/421) # 421 is id of new created record
129
134
 
130
135
  Are you sure?
131
136
  ```
package/dist/index.js CHANGED
@@ -159,6 +159,9 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
159
159
  res.write(`data: ${JSON.stringify(obj)}\n\n`);
160
160
  };
161
161
  const emitToolCallEvent = (event) => {
162
+ if (event.phase === "start") {
163
+ endActiveBlock();
164
+ }
162
165
  sequenceDebugCollector.handleToolCallEvent(event);
163
166
  send({
164
167
  type: "data-tool-call",
package/index.ts CHANGED
@@ -177,6 +177,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
177
177
  };
178
178
 
179
179
  const emitToolCallEvent = (event: ToolCallEvent) => {
180
+ if (event.phase === "start") {
181
+ endActiveBlock();
182
+ }
183
+
180
184
  sequenceDebugCollector.handleToolCallEvent(event);
181
185
 
182
186
  send({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/agent",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",