@agentscope-ai/agentscope 0.0.2 → 0.0.4

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.
Files changed (90) hide show
  1. package/LICENSE +202 -0
  2. package/dist/agent/index.d.mts +10 -10
  3. package/dist/agent/index.d.ts +10 -10
  4. package/dist/agent/index.js +104 -93
  5. package/dist/agent/index.js.map +1 -1
  6. package/dist/agent/index.mjs +104 -93
  7. package/dist/agent/index.mjs.map +1 -1
  8. package/dist/{base-BOx3UzOl.d.mts → base-1YVBgB4n.d.mts} +2 -2
  9. package/dist/{base-DYlBMCy_.d.mts → base-B_MQMHWr.d.mts} +3 -3
  10. package/dist/{base-Cwi4bjze.d.ts → base-BherSLRs.d.ts} +3 -3
  11. package/dist/{base-NX-knWOv.d.ts → base-CY4DMBH1.d.ts} +1 -1
  12. package/dist/{base-BoIps2RL.d.ts → base-ChWjyzPL.d.ts} +2 -2
  13. package/dist/{base-C7jwyH4Z.d.mts → base-ClilytRZ.d.mts} +1 -1
  14. package/dist/{block-VsnHrllL.d.mts → block-B72uPF1H.d.mts} +7 -5
  15. package/dist/{block-VsnHrllL.d.ts → block-B72uPF1H.d.ts} +7 -5
  16. package/dist/event/index.d.mts +105 -89
  17. package/dist/event/index.d.ts +105 -89
  18. package/dist/event/index.js +8 -8
  19. package/dist/event/index.js.map +1 -1
  20. package/dist/event/index.mjs +8 -8
  21. package/dist/event/index.mjs.map +1 -1
  22. package/dist/formatter/index.d.mts +4 -3
  23. package/dist/formatter/index.d.ts +4 -3
  24. package/dist/formatter/index.js +17 -17
  25. package/dist/formatter/index.js.map +1 -1
  26. package/dist/formatter/index.mjs +17 -17
  27. package/dist/formatter/index.mjs.map +1 -1
  28. package/dist/{index-BcatlwXQ.d.ts → index-BNfyKbQN.d.ts} +1 -1
  29. package/dist/{index-BTJDlKvQ.d.mts → index-UQCwdfet.d.mts} +1 -1
  30. package/dist/mcp/index.d.mts +2 -2
  31. package/dist/mcp/index.d.ts +2 -2
  32. package/dist/mcp/index.js +1 -1
  33. package/dist/mcp/index.js.map +1 -1
  34. package/dist/mcp/index.mjs +1 -1
  35. package/dist/mcp/index.mjs.map +1 -1
  36. package/dist/message/index.d.mts +3 -2
  37. package/dist/message/index.d.ts +3 -2
  38. package/dist/message/index.js +204 -5
  39. package/dist/message/index.js.map +1 -1
  40. package/dist/message/index.mjs +200 -5
  41. package/dist/message/index.mjs.map +1 -1
  42. package/dist/message-CPZd0NIc.d.ts +133 -0
  43. package/dist/message-DgpfAaHK.d.mts +133 -0
  44. package/dist/model/index.d.mts +6 -5
  45. package/dist/model/index.d.ts +6 -5
  46. package/dist/model/index.js +39 -28
  47. package/dist/model/index.js.map +1 -1
  48. package/dist/model/index.mjs +39 -28
  49. package/dist/model/index.mjs.map +1 -1
  50. package/dist/storage/index.d.mts +4 -3
  51. package/dist/storage/index.d.ts +4 -3
  52. package/dist/storage/index.js +4 -4
  53. package/dist/storage/index.js.map +1 -1
  54. package/dist/storage/index.mjs +4 -4
  55. package/dist/storage/index.mjs.map +1 -1
  56. package/dist/tool/index.d.mts +4 -4
  57. package/dist/tool/index.d.ts +4 -4
  58. package/dist/{toolkit-CEpulFi0.d.ts → toolkit-DeOlul5Y.d.ts} +2 -2
  59. package/dist/{toolkit-CGEZSZPa.d.mts → toolkit-jwe7NmVJ.d.mts} +2 -2
  60. package/package.json +87 -87
  61. package/src/agent/agent.test.ts +104 -71
  62. package/src/agent/agent.ts +112 -104
  63. package/src/agent/test-compression.ts +1 -1
  64. package/src/event/index.ts +96 -98
  65. package/src/formatter/base.ts +3 -3
  66. package/src/formatter/dashscope-chat-formatter.test.ts +11 -8
  67. package/src/formatter/dashscope-chat-formatter.ts +3 -3
  68. package/src/formatter/openai-chat-formatter.test.ts +13 -5
  69. package/src/formatter/openai-chat-formatter.ts +6 -6
  70. package/src/mcp/base.ts +1 -1
  71. package/src/mcp/http.test.ts +2 -0
  72. package/src/mcp/stdio.test.ts +1 -0
  73. package/src/message/append-event.test.ts +783 -0
  74. package/src/message/block.ts +8 -4
  75. package/src/message/index.ts +12 -1
  76. package/src/message/message.test.ts +3 -1
  77. package/src/message/message.ts +310 -47
  78. package/src/model/dashscope-model.test.ts +4 -0
  79. package/src/model/dashscope-model.ts +3 -0
  80. package/src/model/deepseek-model.test.ts +2 -0
  81. package/src/model/deepseek-model.ts +3 -0
  82. package/src/model/ollama-model.test.ts +1 -0
  83. package/src/model/ollama-model.ts +2 -0
  84. package/src/model/openai-model.ts +3 -0
  85. package/src/permission/index.ts +13 -0
  86. package/src/storage/file-system.test.ts +4 -3
  87. package/src/storage/file-system.ts +4 -4
  88. package/src/tool/toolkit.test.ts +12 -0
  89. package/dist/message-CkN21KaY.d.mts +0 -99
  90. package/dist/message-CzLeTlua.d.ts +0 -99
@@ -16,32 +16,36 @@ export interface HintBlock {
16
16
  id: string;
17
17
  }
18
18
 
19
+ export type ToolCallState = 'pending' | 'asking' | 'allowed' | 'submitted' | 'finished';
20
+
19
21
  export interface ToolCallBlock {
20
22
  type: 'tool_call';
21
23
  name: string;
22
24
  id: string;
23
25
  input: string;
24
- awaitUserConfirmation?: boolean;
26
+ state: ToolCallState;
25
27
  }
26
28
 
29
+ export type ToolResultState = 'success' | 'error' | 'interrupted' | 'denied' | 'running';
30
+
27
31
  export interface ToolResultBlock {
28
32
  type: 'tool_result';
29
33
  id: string;
30
34
  name: string;
31
35
  output: string | (TextBlock | DataBlock)[];
32
- state: 'success' | 'error' | 'interrupted' | 'running';
36
+ state: ToolResultState;
33
37
  }
34
38
 
35
39
  export interface Base64Source {
36
40
  type: 'base64';
37
41
  data: string;
38
- mediaType: string;
42
+ media_type: string;
39
43
  }
40
44
 
41
45
  export interface URLSource {
42
46
  type: 'url';
43
47
  url: string;
44
- mediaType: string;
48
+ media_type: string;
45
49
  }
46
50
 
47
51
  export interface DataBlock {
@@ -1,9 +1,20 @@
1
- export { Msg, createMsg, getTextContent, getContentBlocks } from './message';
1
+ export {
2
+ Msg,
3
+ createMsg,
4
+ UserMsg,
5
+ AssistantMsg,
6
+ SystemMsg,
7
+ getTextContent,
8
+ getContentBlocks,
9
+ appendEvent,
10
+ } from './message';
2
11
  export {
3
12
  TextBlock,
4
13
  ThinkingBlock,
5
14
  ToolCallBlock,
15
+ ToolCallState,
6
16
  ToolResultBlock,
17
+ ToolResultState,
7
18
  ContentBlock,
8
19
  Base64Source,
9
20
  URLSource,
@@ -13,7 +13,7 @@ describe('Message', () => {
13
13
  ]);
14
14
  expect(msg.role).toBe('user');
15
15
  expect(msg.metadata).toEqual({});
16
- expect(msg.timestamp).toBeDefined();
16
+ expect(msg.created_at).toBeDefined();
17
17
  expect(msg.id).toBeDefined();
18
18
  expect(getTextContent(msg)).toBe('Hello, world!');
19
19
 
@@ -38,6 +38,7 @@ describe('Message', () => {
38
38
  id: '1',
39
39
  name: 'test',
40
40
  input: "{ query: 'What is AI?' }",
41
+ state: 'pending',
41
42
  },
42
43
  {
43
44
  type: 'tool_result',
@@ -64,6 +65,7 @@ describe('Message', () => {
64
65
  id: '1',
65
66
  name: 'test',
66
67
  input: "{ query: 'What is AI?' }",
68
+ state: 'pending',
67
69
  },
68
70
  ]);
69
71
  expect(getContentBlocks(msg, 'tool_result')).toStrictEqual([
@@ -6,7 +6,10 @@ import {
6
6
  ToolResultBlock,
7
7
  ToolCallBlock,
8
8
  DataBlock,
9
+ Base64Source,
10
+ URLSource,
9
11
  } from './block';
12
+ import { AgentEvent, EventType } from '../event';
10
13
 
11
14
  /** A chat message exchanged between agents or between an agent and a model. */
12
15
  export interface Msg {
@@ -21,7 +24,9 @@ export interface Msg {
21
24
  /** Arbitrary key-value metadata attached to the message. */
22
25
  metadata: Record<string, JSONSerializableObject>;
23
26
  /** ISO-8601 creation timestamp. */
24
- timestamp: string;
27
+ created_at: string;
28
+ /** ISO-8601 finished timestamp. */
29
+ finished_at?: string | null;
25
30
  /** Usage information for the message, such as token counts. */
26
31
  usage?: {
27
32
  inputTokens: number;
@@ -30,17 +35,18 @@ export interface Msg {
30
35
  }
31
36
 
32
37
  /**
33
- * Create a new {@link Msg} object, filling in `id` and `timestamp` when omitted.
34
- *
38
+ * Create a new {@link Msg} object, filling in `id` and `created_at` when omitted.
39
+ * A plain string `content` is automatically wrapped in a single {@link TextBlock}.
35
40
  * @param root0
36
41
  * @param root0.name
37
42
  * @param root0.content
38
43
  * @param root0.role
39
44
  * @param root0.metadata
40
45
  * @param root0.id
41
- * @param root0.timestamp
46
+ * @param root0.created_at
47
+ * @param root0.finished_at
42
48
  * @param root0.usage
43
- * @returns A fully-populated {@link Msg} object.
49
+ * @returns A Msg object.
44
50
  */
45
51
  export function createMsg({
46
52
  name,
@@ -48,11 +54,99 @@ export function createMsg({
48
54
  role,
49
55
  metadata = {},
50
56
  id = crypto.randomUUID(),
51
- timestamp = new Date().toISOString(),
57
+ created_at = new Date().toISOString(),
58
+ finished_at,
59
+ usage,
60
+ }: Omit<Msg, 'id' | 'created_at' | 'metadata' | 'content'> &
61
+ Partial<Pick<Msg, 'id' | 'created_at' | 'metadata'>> & {
62
+ content: string | ContentBlock[];
63
+ }): Msg {
64
+ const contentBlocks: ContentBlock[] =
65
+ typeof content === 'string'
66
+ ? [{ id: crypto.randomUUID(), type: 'text', text: content } as TextBlock]
67
+ : content;
68
+ return { id, name, role, content: contentBlocks, metadata, created_at, finished_at, usage };
69
+ }
70
+
71
+ /**
72
+ * Create a user {@link Msg}.
73
+ * @param root0
74
+ * @param root0.name
75
+ * @param root0.content
76
+ * @param root0.metadata
77
+ * @param root0.id
78
+ * @param root0.created_at
79
+ * @returns A Msg object with role 'user'.
80
+ */
81
+ export function UserMsg({
82
+ name,
83
+ content,
84
+ metadata = {},
85
+ id = crypto.randomUUID(),
86
+ created_at = new Date().toISOString(),
87
+ }: {
88
+ name: string;
89
+ content: string | ContentBlock[];
90
+ metadata?: Record<string, JSONSerializableObject>;
91
+ id?: string;
92
+ created_at?: string;
93
+ }): Msg {
94
+ return createMsg({ name, content, role: 'user', metadata, id, created_at });
95
+ }
96
+
97
+ /**
98
+ * Create an assistant {@link Msg}.
99
+ * @param root0
100
+ * @param root0.name
101
+ * @param root0.content
102
+ * @param root0.metadata
103
+ * @param root0.id
104
+ * @param root0.created_at
105
+ * @param root0.usage
106
+ * @returns A Msg object with role 'assistant'.
107
+ */
108
+ export function AssistantMsg({
109
+ name,
110
+ content,
111
+ metadata = {},
112
+ id = crypto.randomUUID(),
113
+ created_at = new Date().toISOString(),
52
114
  usage,
53
- }: Omit<Msg, 'id' | 'timestamp' | 'metadata'> &
54
- Partial<Pick<Msg, 'id' | 'timestamp' | 'metadata'>>): Msg {
55
- return { id, name, role, content, metadata, timestamp, usage } as Msg;
115
+ }: {
116
+ name: string;
117
+ content: string | ContentBlock[];
118
+ metadata?: Record<string, JSONSerializableObject>;
119
+ id?: string;
120
+ created_at?: string;
121
+ usage?: Msg['usage'];
122
+ }): Msg {
123
+ return createMsg({ name, content, role: 'assistant', metadata, id, created_at, usage });
124
+ }
125
+
126
+ /**
127
+ * Create a system {@link Msg}.
128
+ * @param root0
129
+ * @param root0.name
130
+ * @param root0.content
131
+ * @param root0.metadata
132
+ * @param root0.id
133
+ * @param root0.created_at
134
+ * @returns A Msg object with role 'system'.
135
+ */
136
+ export function SystemMsg({
137
+ name,
138
+ content,
139
+ metadata = {},
140
+ id = crypto.randomUUID(),
141
+ created_at = new Date().toISOString(),
142
+ }: {
143
+ name: string;
144
+ content: string | ContentBlock[];
145
+ metadata?: Record<string, JSONSerializableObject>;
146
+ id?: string;
147
+ created_at?: string;
148
+ }): Msg {
149
+ return createMsg({ name, content, role: 'system', metadata, id, created_at });
56
150
  }
57
151
 
58
152
  /**
@@ -67,9 +161,7 @@ export function createMsg({
67
161
  */
68
162
  export function getTextContent(msg: Msg, separator: string = '\n'): string | null {
69
163
  const textBlocks = msg.content.filter(block => block.type === 'text');
70
- if (textBlocks.length === 0) {
71
- return null;
72
- }
164
+ if (textBlocks.length === 0) return null;
73
165
  return textBlocks.map(block => (block as TextBlock).text).join(separator);
74
166
  }
75
167
 
@@ -82,45 +174,10 @@ export function getTextContent(msg: Msg, separator: string = '\n'): string | nul
82
174
  * @returns An array of all {@link ContentBlock} objects.
83
175
  */
84
176
  export function getContentBlocks(msg: Msg): ContentBlock[];
85
- /**
86
- * Return all {@link TextBlock} objects from a message.
87
- *
88
- * @param msg - The message to read.
89
- * @param blockType - `'text'`
90
- * @returns An array of {@link TextBlock} objects.
91
- */
92
177
  export function getContentBlocks(msg: Msg, blockType: 'text'): TextBlock[];
93
- /**
94
- * Return all {@link ThinkingBlock} objects from a message.
95
- *
96
- * @param msg - The message to read.
97
- * @param blockType - `'thinking'`
98
- * @returns An array of {@link ThinkingBlock} objects.
99
- */
100
178
  export function getContentBlocks(msg: Msg, blockType: 'thinking'): ThinkingBlock[];
101
- /**
102
- * Return all {@link DataBlock} objects from a message.
103
- *
104
- * @param msg - The message to read.
105
- * @param blockType - `'video'`
106
- * @returns An array of {@link DataBlock} objects.
107
- */
108
179
  export function getContentBlocks(msg: Msg, blockType: 'data'): DataBlock[];
109
- /**
110
- * Return all {@link ToolCallBlock} objects from a message.
111
- *
112
- * @param msg - The message to read.
113
- * @param blockType - `'tool_call'`
114
- * @returns An array of {@link ToolCallBlock} objects.
115
- */
116
180
  export function getContentBlocks(msg: Msg, blockType: 'tool_call'): ToolCallBlock[];
117
- /**
118
- * Return all {@link ToolResultBlock} objects from a message.
119
- *
120
- * @param msg - The message to read.
121
- * @param blockType - `'tool_result'`
122
- * @returns An array of {@link ToolResultBlock} objects.
123
- */
124
181
  export function getContentBlocks(msg: Msg, blockType: 'tool_result'): ToolResultBlock[];
125
182
  export function getContentBlocks(
126
183
  msg: Msg,
@@ -129,3 +186,209 @@ export function getContentBlocks(
129
186
  if (!blockType) return msg.content;
130
187
  return msg.content.filter(block => block.type === blockType);
131
188
  }
189
+
190
+ /**
191
+ * Find a content block by type and id within a message.
192
+ * @param msg
193
+ * @param blockType
194
+ * @param blockId
195
+ * @returns The matching {@link ContentBlock}, or `undefined` if not found.
196
+ */
197
+ function findBlock(msg: Msg, blockType: string, blockId: string): ContentBlock | undefined {
198
+ return msg.content.find(block => block.type === blockType && block.id === blockId);
199
+ }
200
+
201
+ /**
202
+ * Apply a streaming {@link AgentEvent} to a {@link Msg}, mutating it in place.
203
+ *
204
+ * Only `content` and `finished_at` are ever modified. Events whose
205
+ * `reply_id` does not match `msg.id` are skipped with a warning.
206
+ * @param msg
207
+ * @param event
208
+ * @returns The mutated {@link Msg} object.
209
+ */
210
+ export function appendEvent(msg: Msg, event: AgentEvent): Msg {
211
+ if (!('reply_id' in event)) return msg;
212
+ if (event.reply_id !== msg.id) {
213
+ console.warn(
214
+ `Event reply_id "${event.reply_id}" does not match message id "${msg.id}", skipping.`
215
+ );
216
+ return msg;
217
+ }
218
+
219
+ switch (event.type) {
220
+ case EventType.REPLY_END:
221
+ msg.finished_at = event.created_at;
222
+ break;
223
+
224
+ case EventType.TEXT_BLOCK_START:
225
+ msg.content.push({ type: 'text', id: event.block_id, text: '' });
226
+ break;
227
+
228
+ case EventType.TEXT_BLOCK_DELTA: {
229
+ const block = findBlock(msg, 'text', event.block_id);
230
+ if (!block) {
231
+ console.warn(`TextBlock "${event.block_id}" not found, skipping.`);
232
+ } else {
233
+ (block as TextBlock).text += event.delta;
234
+ }
235
+ break;
236
+ }
237
+
238
+ case EventType.TEXT_BLOCK_END:
239
+ break;
240
+
241
+ case EventType.THINKING_BLOCK_START:
242
+ msg.content.push({ type: 'thinking', id: event.block_id, thinking: '' });
243
+ break;
244
+
245
+ case EventType.THINKING_BLOCK_DELTA: {
246
+ const block = findBlock(msg, 'thinking', event.block_id);
247
+ if (!block) {
248
+ console.warn(`ThinkingBlock "${event.block_id}" not found, skipping.`);
249
+ } else {
250
+ (block as ThinkingBlock).thinking += event.delta;
251
+ }
252
+ break;
253
+ }
254
+
255
+ case EventType.THINKING_BLOCK_END:
256
+ break;
257
+
258
+ case EventType.DATA_BLOCK_START:
259
+ msg.content.push({
260
+ type: 'data',
261
+ id: event.block_id,
262
+ source: { type: 'base64', data: '', media_type: event.media_type },
263
+ });
264
+ break;
265
+
266
+ case EventType.DATA_BLOCK_DELTA: {
267
+ const block = findBlock(msg, 'data', event.block_id);
268
+ if (!block) {
269
+ console.warn(`DataBlock "${event.block_id}" not found, skipping.`);
270
+ } else {
271
+ ((block as DataBlock).source as Base64Source).data += event.data;
272
+ }
273
+ break;
274
+ }
275
+
276
+ case EventType.DATA_BLOCK_END:
277
+ break;
278
+
279
+ case EventType.TOOL_CALL_START:
280
+ msg.content.push({
281
+ type: 'tool_call',
282
+ id: event.tool_call_id,
283
+ name: event.tool_call_name,
284
+ input: '',
285
+ state: 'pending',
286
+ });
287
+ break;
288
+
289
+ case EventType.TOOL_CALL_DELTA: {
290
+ const block = findBlock(msg, 'tool_call', event.tool_call_id);
291
+ if (!block) {
292
+ console.warn(`ToolCallBlock "${event.tool_call_id}" not found, skipping.`);
293
+ } else {
294
+ (block as ToolCallBlock).input += event.delta;
295
+ }
296
+ break;
297
+ }
298
+
299
+ case EventType.TOOL_CALL_END:
300
+ break;
301
+
302
+ case EventType.TOOL_RESULT_START:
303
+ msg.content.push({
304
+ type: 'tool_result',
305
+ id: event.tool_call_id,
306
+ name: event.tool_call_name,
307
+ output: [],
308
+ state: 'running',
309
+ });
310
+ break;
311
+
312
+ case EventType.TOOL_RESULT_TEXT_DELTA: {
313
+ const block = findBlock(msg, 'tool_result', event.tool_call_id);
314
+ if (!block) {
315
+ console.warn(`ToolResultBlock "${event.tool_call_id}" not found, skipping.`);
316
+ } else {
317
+ const trb = block as ToolResultBlock;
318
+ if (typeof trb.output === 'string') {
319
+ trb.output = [{ type: 'text', id: crypto.randomUUID(), text: trb.output }];
320
+ }
321
+ const last = trb.output[trb.output.length - 1];
322
+ if (!last || last.type !== 'text') {
323
+ trb.output.push({
324
+ type: 'text',
325
+ id: event.block_id ?? crypto.randomUUID(),
326
+ text: event.delta,
327
+ });
328
+ } else {
329
+ (last as TextBlock).text += event.delta;
330
+ }
331
+ }
332
+ break;
333
+ }
334
+
335
+ case EventType.TOOL_RESULT_DATA_DELTA: {
336
+ const block = findBlock(msg, 'tool_result', event.tool_call_id);
337
+ if (!block) {
338
+ console.warn(`ToolResultBlock "${event.tool_call_id}" not found, skipping.`);
339
+ } else {
340
+ const trb = block as ToolResultBlock;
341
+ if (typeof trb.output === 'string') {
342
+ trb.output = [{ type: 'text', id: crypto.randomUUID(), text: trb.output }];
343
+ }
344
+ const source: Base64Source | URLSource =
345
+ event.data != null
346
+ ? { type: 'base64', data: event.data, media_type: event.media_type }
347
+ : { type: 'url', url: event.url!, media_type: event.media_type };
348
+ trb.output.push({ type: 'data', id: event.block_id, source });
349
+ }
350
+ break;
351
+ }
352
+
353
+ case EventType.TOOL_RESULT_END: {
354
+ const block = findBlock(msg, 'tool_result', event.tool_call_id);
355
+ if (!block) {
356
+ console.warn(`ToolResultBlock "${event.tool_call_id}" not found, skipping.`);
357
+ } else {
358
+ (block as ToolResultBlock).state = event.state;
359
+ }
360
+ break;
361
+ }
362
+
363
+ case EventType.REQUIRE_USER_CONFIRM:
364
+ for (const tc of event.tool_calls) {
365
+ const b = findBlock(msg, 'tool_call', tc.id);
366
+ if (b) (b as ToolCallBlock).state = 'asking';
367
+ }
368
+ break;
369
+
370
+ case EventType.USER_CONFIRM_RESULT:
371
+ for (const result of event.confirm_results) {
372
+ const b = findBlock(msg, 'tool_call', result.tool_call.id);
373
+ if (b) {
374
+ (b as ToolCallBlock).state = result.confirmed ? 'allowed' : 'finished';
375
+ }
376
+ }
377
+ break;
378
+
379
+ case EventType.REQUIRE_EXTERNAL_EXECUTION:
380
+ for (const tc of event.tool_calls) {
381
+ const b = findBlock(msg, 'tool_call', tc.id);
382
+ if (b) (b as ToolCallBlock).state = 'submitted';
383
+ }
384
+ break;
385
+
386
+ case EventType.EXTERNAL_EXECUTION_RESULT:
387
+ for (const result of event.execution_results) {
388
+ msg.content.push(result);
389
+ }
390
+ break;
391
+ }
392
+
393
+ return msg;
394
+ }
@@ -122,6 +122,7 @@ describe('DashScopeChatModel', () => {
122
122
  name: 'get_current_weather',
123
123
  id: 'call-123',
124
124
  input: '{"location"',
125
+ state: 'pending',
125
126
  });
126
127
 
127
128
  // Chunk 4: Tool call with second part of arguments (delta)
@@ -131,6 +132,7 @@ describe('DashScopeChatModel', () => {
131
132
  name: 'get_current_weather',
132
133
  id: 'call-123',
133
134
  input: ':"Beijing"}',
135
+ state: 'pending',
134
136
  });
135
137
 
136
138
  // Chunk 5: Empty content with usage info
@@ -158,6 +160,7 @@ describe('DashScopeChatModel', () => {
158
160
  name: 'get_current_weather',
159
161
  id: 'call-123',
160
162
  input: '{"location":"Beijing"}',
163
+ state: 'pending',
161
164
  });
162
165
 
163
166
  // Verify usage
@@ -266,6 +269,7 @@ describe('DashScopeChatModel', () => {
266
269
  name: 'get_current_weather',
267
270
  id: 'call-123',
268
271
  input: '{"location":"Beijing"}',
272
+ state: 'pending',
269
273
  });
270
274
 
271
275
  // Verify usage
@@ -231,6 +231,7 @@ export class DashScopeChatModel extends ChatModelBase {
231
231
  id: String(toolCall.id),
232
232
  name: String(toolCall.function.name),
233
233
  input: inputString,
234
+ state: 'pending',
234
235
  });
235
236
  }
236
237
  });
@@ -354,6 +355,7 @@ export class DashScopeChatModel extends ChatModelBase {
354
355
  id: meta.id,
355
356
  name: meta.name,
356
357
  input: deltaArgs,
358
+ state: 'pending',
357
359
  });
358
360
  }
359
361
  });
@@ -387,6 +389,7 @@ export class DashScopeChatModel extends ChatModelBase {
387
389
  id: meta.id,
388
390
  name: meta.name,
389
391
  input: accToolInputs.get(index) || '{}',
392
+ state: 'pending',
390
393
  });
391
394
  });
392
395
 
@@ -106,6 +106,7 @@ describe('DeepSeekChatModel', () => {
106
106
  name: 'get_current_weather',
107
107
  id: 'call-123',
108
108
  input: '{"location":"Beijing"}',
109
+ state: 'pending',
109
110
  });
110
111
 
111
112
  // Verify usage
@@ -209,6 +210,7 @@ describe('DeepSeekChatModel', () => {
209
210
  name: 'get_current_weather',
210
211
  id: 'call-123',
211
212
  input: '{"location":"Beijing"}',
213
+ state: 'pending',
212
214
  });
213
215
 
214
216
  // Verify usage
@@ -191,6 +191,7 @@ export class DeepSeekChatModel extends ChatModelBase {
191
191
  id: String(toolCall.id),
192
192
  name: String(toolCall.function.name),
193
193
  input: inputString,
194
+ state: 'pending',
194
195
  });
195
196
  }
196
197
  });
@@ -314,6 +315,7 @@ export class DeepSeekChatModel extends ChatModelBase {
314
315
  id: meta.id,
315
316
  name: meta.name,
316
317
  input: deltaArgs,
318
+ state: 'pending',
317
319
  });
318
320
  }
319
321
  });
@@ -347,6 +349,7 @@ export class DeepSeekChatModel extends ChatModelBase {
347
349
  id: meta.id,
348
350
  name: meta.name,
349
351
  input: accToolInputs.get(index) || '{}',
352
+ state: 'pending',
350
353
  });
351
354
  });
352
355
 
@@ -106,6 +106,7 @@ describe('OllamaChatModel', () => {
106
106
  type: 'tool_call',
107
107
  name: 'get_current_weather',
108
108
  input: '{"location":"Beijing"}',
109
+ state: 'pending',
109
110
  });
110
111
 
111
112
  // Verify usage
@@ -212,6 +212,7 @@ export class OllamaChatModel extends ChatModelBase {
212
212
  id: toolId,
213
213
  name: func.name,
214
214
  input: JSON.stringify(func.arguments),
215
+ state: 'pending' as const,
215
216
  };
216
217
 
217
218
  toolCalls.set(toolId, toolCallBlock);
@@ -284,6 +285,7 @@ export class OllamaChatModel extends ChatModelBase {
284
285
  id: `${idx}_${toolCall.function.name}`,
285
286
  name: toolCall.function.name,
286
287
  input: JSON.stringify(toolCall.function.arguments),
288
+ state: 'pending',
287
289
  });
288
290
  }
289
291
  }
@@ -117,6 +117,7 @@ export class OpenAIChatModel extends ChatModelBase {
117
117
  id: toolCall.id,
118
118
  name: toolCall.function.name,
119
119
  input: toolCall.function.arguments,
120
+ state: 'pending',
120
121
  });
121
122
  }
122
123
  });
@@ -238,6 +239,7 @@ export class OpenAIChatModel extends ChatModelBase {
238
239
  id: meta.id,
239
240
  name: meta.name,
240
241
  input: deltaArgs,
242
+ state: 'pending',
241
243
  });
242
244
  }
243
245
  });
@@ -276,6 +278,7 @@ export class OpenAIChatModel extends ChatModelBase {
276
278
  id: meta.id,
277
279
  name: meta.name,
278
280
  input: accToolInputs.get(index) || '{}',
281
+ state: 'pending',
279
282
  });
280
283
  });
281
284
 
@@ -0,0 +1,13 @@
1
+ export enum PermissionBehavior {
2
+ ALLOW = 'allow',
3
+ DENY = 'deny',
4
+ ASK = 'ask',
5
+ PASSTHROUGH = 'passthrough',
6
+ }
7
+
8
+ export interface PermissionRule {
9
+ tool_name: string;
10
+ rule_content: string | null;
11
+ behavior: PermissionBehavior;
12
+ source: string;
13
+ }