@polka-codes/core 0.8.23 → 0.8.25

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.
@@ -8,19 +8,20 @@ declare abstract class AgentBase {
8
8
  protected readonly config: Readonly<AgentBaseConfig>;
9
9
  protected readonly handlers: Record<string, FullToolInfo>;
10
10
  constructor(name: string, ai: AiServiceBase, config: AgentBaseConfig);
11
+ abort(): void;
11
12
  get parameters(): Readonly<any>;
12
13
  get messages(): Readonly<MessageParam[]>;
13
14
  setMessages(messages: Readonly<MessageParam[]>): void;
14
- start(prompt: string): Promise<ExitReason>;
15
- step(prompt: string): Promise<AssistantMessageContent[]>;
15
+ start(prompt: UserContent): Promise<ExitReason>;
16
+ step(prompt: UserContent): Promise<AssistantMessageContent[]>;
16
17
  handleStepResponse(response: AssistantMessageContent[]): Promise<{
17
18
  type: "reply";
18
- message: string;
19
+ message: UserContent;
19
20
  } | {
20
21
  type: "exit";
21
22
  reason: ExitReason;
22
23
  }>;
23
- continueTask(userMessage: string): Promise<ExitReason>;
24
+ continueTask(userMessage: UserContent): Promise<ExitReason>;
24
25
  protected abstract onBeforeInvokeTool(name: string, args: Record<string, string>): Promise<ToolResponse | undefined>;
25
26
  get model(): {
26
27
  provider: string;
@@ -93,6 +94,7 @@ export { agentsPrompt as agentsPrompt_alias_1 }
93
94
  export { agentsPrompt as agentsPrompt_alias_2 }
94
95
 
95
96
  declare abstract class AiServiceBase {
97
+ #private;
96
98
  readonly usageMeter: UsageMeter;
97
99
  readonly options: AiServiceOptions;
98
100
  constructor(options: AiServiceOptions);
@@ -101,7 +103,8 @@ declare abstract class AiServiceBase {
101
103
  id: string;
102
104
  info: ModelInfo;
103
105
  };
104
- abstract sendImpl(systemPrompt: string, messages: MessageParam[]): ApiStream;
106
+ abstract sendImpl(systemPrompt: string, messages: MessageParam[], signal: AbortSignal): ApiStream;
107
+ abort(): void;
105
108
  send(systemPrompt: string, messages: MessageParam[]): ApiStream;
106
109
  request(systemPrompt: string, messages: MessageParam[]): Promise<{
107
110
  response: string;
@@ -300,7 +303,7 @@ export declare class AnthropicService extends AiServiceBase {
300
303
  info: ModelInfo;
301
304
  };
302
305
  constructor(options: AiServiceOptions);
303
- sendImpl(systemPrompt: string, messages: MessageParam[]): ApiStream;
306
+ sendImpl(systemPrompt: string, messages: MessageParam[], signal: AbortSignal): ApiStream;
304
307
  }
305
308
 
306
309
  declare type ApiStream = AsyncGenerator<ApiStreamChunk>;
@@ -795,7 +798,7 @@ export declare class DeepSeekService extends AiServiceBase {
795
798
  info: ModelInfo;
796
799
  };
797
800
  constructor(options: AiServiceOptions);
798
- sendImpl(systemPrompt: string, messages: MessageParam[]): ApiStream;
801
+ sendImpl(systemPrompt: string, messages: MessageParam[], signal: AbortSignal): ApiStream;
799
802
  }
800
803
 
801
804
  declare const _default: {
@@ -1061,7 +1064,7 @@ export { _default_10 as updateKnowledge_alias_2 }
1061
1064
  declare const _default_11: {
1062
1065
  handler: ToolHandler<{
1063
1066
  readonly name: "write_to_file";
1064
- readonly description: "Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Ensure that the output content does not include incorrect escaped character patterns such as `&lt;` and `&gt;`.";
1067
+ readonly description: "Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Ensure that the output content does not include incorrect escaped character patterns such as `&lt;`, `&gt;`, or `&amp;`. Also ensure there is no unwanted CDATA tags in the content.";
1065
1068
  readonly parameters: [{
1066
1069
  readonly name: "path";
1067
1070
  readonly description: "The path of the file to write to";
@@ -1087,7 +1090,7 @@ declare const _default_11: {
1087
1090
  }, FilesystemProvider>;
1088
1091
  isAvailable: (provider: FilesystemProvider) => boolean;
1089
1092
  name: "write_to_file";
1090
- description: "Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Ensure that the output content does not include incorrect escaped character patterns such as `&lt;` and `&gt;`.";
1093
+ description: "Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Ensure that the output content does not include incorrect escaped character patterns such as `&lt;`, `&gt;`, or `&amp;`. Also ensure there is no unwanted CDATA tags in the content.";
1091
1094
  parameters: [{
1092
1095
  readonly name: "path";
1093
1096
  readonly description: "The path of the file to write to";
@@ -1945,6 +1948,8 @@ declare type ExitReason = {
1945
1948
  type: 'UsageExceeded';
1946
1949
  } | {
1947
1950
  type: 'WaitForUserInput';
1951
+ } | {
1952
+ type: 'Aborted';
1948
1953
  } | ToolResponseExit | ToolResponseInterrupted | ToolResponseHandOver | ToolResponseDelegate | {
1949
1954
  type: 'Pause';
1950
1955
  responses: ToolResponseOrToolPause[];
@@ -2111,6 +2116,8 @@ export declare const handler_alias_8: ToolHandler<typeof toolInfo_alias_8, Files
2111
2116
 
2112
2117
  export declare const handler_alias_9: ToolHandler<typeof toolInfo_alias_9, FilesystemProvider>;
2113
2118
 
2119
+ export declare type ImageBlockParam = Anthropic.Messages.ImageBlockParam;
2120
+
2114
2121
  declare type Input = {
2115
2122
  commitMessages: string;
2116
2123
  commitDiff: string;
@@ -2470,6 +2477,7 @@ declare class MultiAgent {
2470
2477
  }): Promise<ExitReason>;
2471
2478
  continueTask(userMessage: string): Promise<ExitReason>;
2472
2479
  get hasActiveAgent(): boolean;
2480
+ abort(): void;
2473
2481
  }
2474
2482
  export { MultiAgent }
2475
2483
  export { MultiAgent as MultiAgent_alias_1 }
@@ -2493,7 +2501,7 @@ export declare class OllamaService extends AiServiceBase {
2493
2501
  info: ModelInfo;
2494
2502
  };
2495
2503
  constructor(options: AiServiceOptions);
2496
- sendImpl(systemPrompt: string, messages: MessageParam[]): ApiStream;
2504
+ sendImpl(systemPrompt: string, messages: MessageParam[], signal: AbortSignal): ApiStream;
2497
2505
  }
2498
2506
 
2499
2507
  declare const openAiModelInfoSaneDefaults: {
@@ -2515,7 +2523,7 @@ export declare class OpenRouterService extends AiServiceBase {
2515
2523
  info: ModelInfo;
2516
2524
  };
2517
2525
  constructor(options: AiServiceOptions);
2518
- sendImpl(systemPrompt: string, messages: MessageParam[]): ApiStream;
2526
+ sendImpl(systemPrompt: string, messages: MessageParam[], signal: AbortSignal): ApiStream;
2519
2527
  }
2520
2528
 
2521
2529
  declare type Output = {
@@ -2605,15 +2613,24 @@ declare enum Policies {
2605
2613
  export { Policies }
2606
2614
  export { Policies as Policies_alias_1 }
2607
2615
 
2608
- declare const replaceInFile_2: (fileContent: string, diff: string) => Promise<string>;
2616
+ declare const replaceInFile_2: (fileContent: string, diff: string) => ReplaceResult;
2609
2617
  export { replaceInFile_2 as replaceInFileHelper }
2610
2618
  export { replaceInFile_2 as replaceInFile_alias_3 }
2611
2619
  export { replaceInFile_2 as replaceInFile_alias_4 }
2612
2620
 
2621
+ declare type ReplaceResult = {
2622
+ content: string;
2623
+ status: 'no_diff_applied' | 'some_diff_applied' | 'all_diff_applied';
2624
+ appliedCount: number;
2625
+ totalCount: number;
2626
+ };
2627
+ export { ReplaceResult }
2628
+ export { ReplaceResult as ReplaceResult_alias_1 }
2629
+
2613
2630
  declare const responsePrompts: {
2614
2631
  readonly errorInvokeTool: (tool: string, error: unknown) => string;
2615
2632
  readonly requireUseTool: "Error: No tool use detected. You MUST use a tool before proceeding.\ne.g. <tool_tool_name>tool_name</tool_tool_name>\n\nEnsure the opening and closing tags are correctly nested and closed, and that you are using the correct tool name.\nAvoid unnecessary text or symbols before or after the tool use.\nAvoid unnecessary escape characters or special characters.\n";
2616
- readonly toolResults: (tool: string, result: string) => string;
2633
+ readonly toolResults: (tool: string, result: UserContent) => (TextBlockParam | ImageBlockParam)[];
2617
2634
  readonly commandResult: (command: string, exitCode: number, stdout: string, stderr: string) => string;
2618
2635
  };
2619
2636
  export { responsePrompts }
@@ -2726,7 +2743,7 @@ export { TaskEventKind as TaskEventKind_alias_2 }
2726
2743
  */
2727
2744
  declare interface TaskEventStartRequest extends TaskEventBase {
2728
2745
  kind: TaskEventKind.StartRequest;
2729
- userMessage: string;
2746
+ userMessage: UserContent;
2730
2747
  }
2731
2748
  export { TaskEventStartRequest }
2732
2749
  export { TaskEventStartRequest as TaskEventStartRequest_alias_1 }
@@ -2809,6 +2826,8 @@ export { TaskEventUsageExceeded }
2809
2826
  export { TaskEventUsageExceeded as TaskEventUsageExceeded_alias_1 }
2810
2827
  export { TaskEventUsageExceeded as TaskEventUsageExceeded_alias_2 }
2811
2828
 
2829
+ export declare type TextBlockParam = Anthropic.Messages.TextBlockParam;
2830
+
2812
2831
  declare interface TextContent {
2813
2832
  type: 'text';
2814
2833
  content: string;
@@ -3077,7 +3096,7 @@ export declare const toolInfo_alias_12: {
3077
3096
 
3078
3097
  export declare const toolInfo_alias_13: {
3079
3098
  readonly name: "write_to_file";
3080
- readonly description: "Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Ensure that the output content does not include incorrect escaped character patterns such as `&lt;` and `&gt;`.";
3099
+ readonly description: "Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Ensure that the output content does not include incorrect escaped character patterns such as `&lt;`, `&gt;`, or `&amp;`. Also ensure there is no unwanted CDATA tags in the content.";
3081
3100
  readonly parameters: [{
3082
3101
  readonly name: "path";
3083
3102
  readonly description: "The path of the file to write to";
@@ -3427,7 +3446,7 @@ export { ToolResponseInvalid as ToolResponseInvalid_alias_1 }
3427
3446
  declare type ToolResponseOrToolPause = {
3428
3447
  type: 'response';
3429
3448
  tool: string;
3430
- response: string;
3449
+ response: UserContent;
3431
3450
  } | {
3432
3451
  type: 'pause';
3433
3452
  tool: string;
@@ -3446,7 +3465,7 @@ export { ToolResponsePause as ToolResponsePause_alias_1 }
3446
3465
 
3447
3466
  declare type ToolResponseReply = {
3448
3467
  type: ToolResponseType.Reply;
3449
- message: string;
3468
+ message: UserContent;
3450
3469
  };
3451
3470
  export { ToolResponseReply }
3452
3471
  export { ToolResponseReply as ToolResponseReply_alias_1 }
@@ -3530,6 +3549,11 @@ export { UsageMeter }
3530
3549
  export { UsageMeter as UsageMeter_alias_1 }
3531
3550
  export { UsageMeter as UsageMeter_alias_2 }
3532
3551
 
3552
+ declare type UserContent = string | (TextBlockParam | ImageBlockParam)[];
3553
+ export { UserContent }
3554
+ export { UserContent as UserContent_alias_1 }
3555
+ export { UserContent as UserContent_alias_2 }
3556
+
3533
3557
  declare type WebProvider = {
3534
3558
  fetchUrl?: (url: string) => Promise<string>;
3535
3559
  };
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export { defaultModels } from './_tsup-dts-rollup.js';
4
4
  export { createService } from './_tsup-dts-rollup.js';
5
5
  export { UsageMeter } from './_tsup-dts-rollup.js';
6
6
  export { AiServiceBase } from './_tsup-dts-rollup.js';
7
+ export { UserContent } from './_tsup-dts-rollup.js';
7
8
  export { MessageParam } from './_tsup-dts-rollup.js';
8
9
  export { AiServiceOptions } from './_tsup-dts-rollup.js';
9
10
  export { ModelInfo } from './_tsup-dts-rollup.js';
package/dist/index.js CHANGED
@@ -8,14 +8,19 @@ var __export = (target, all) => {
8
8
  var AiServiceBase = class {
9
9
  usageMeter;
10
10
  options;
11
+ #abortController;
11
12
  constructor(options) {
12
13
  this.options = options;
13
14
  this.usageMeter = options.usageMeter;
14
15
  }
16
+ abort() {
17
+ this.#abortController?.abort();
18
+ }
15
19
  async *send(systemPrompt, messages) {
16
20
  this.usageMeter.checkLimit();
17
21
  this.usageMeter.incrementMessageCount();
18
- const stream = this.sendImpl(systemPrompt, messages);
22
+ this.#abortController = new AbortController();
23
+ const stream = this.sendImpl(systemPrompt, messages, this.#abortController.signal);
19
24
  for await (const chunk of stream) {
20
25
  switch (chunk.type) {
21
26
  case "usage":
@@ -28,7 +33,8 @@ var AiServiceBase = class {
28
33
  async request(systemPrompt, messages) {
29
34
  this.usageMeter.checkLimit();
30
35
  this.usageMeter.incrementMessageCount();
31
- const stream = this.sendImpl(systemPrompt, messages);
36
+ this.#abortController = new AbortController();
37
+ const stream = this.sendImpl(systemPrompt, messages, this.#abortController.signal);
32
38
  const usage = {
33
39
  inputTokens: 0,
34
40
  outputTokens: 0,
@@ -208,7 +214,7 @@ var AnthropicService = class extends AiServiceBase {
208
214
  info: anthropicModels[id] ?? anthropicModels[anthropicDefaultModelId]
209
215
  };
210
216
  }
211
- async *sendImpl(systemPrompt, messages) {
217
+ async *sendImpl(systemPrompt, messages, signal) {
212
218
  let stream;
213
219
  const modelId = this.model.id;
214
220
  const cacheControl = this.#options.enableCache ? { type: "ephemeral" } : void 0;
@@ -236,52 +242,58 @@ var AnthropicService = class extends AiServiceBase {
236
242
  }, []);
237
243
  const lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1;
238
244
  const secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1;
239
- stream = await this.#client.messages.create({
240
- model: modelId,
241
- max_tokens: this.model.info.maxTokens || 8192,
242
- thinking: thinkingBudgetTokens ? { type: "enabled", budget_tokens: thinkingBudgetTokens } : void 0,
243
- temperature,
244
- system: [
245
- {
246
- text: systemPrompt,
247
- type: "text",
248
- cache_control: cacheControl
249
- }
250
- ],
251
- // setting cache breakpoint for system prompt so new tasks can reuse it
252
- messages: messages.map((message, index) => {
253
- if (index === lastUserMsgIndex || index === secondLastMsgUserIndex) {
254
- return {
255
- ...message,
256
- content: typeof message.content === "string" ? [
257
- {
258
- type: "text",
259
- text: message.content,
260
- cache_control: cacheControl
261
- }
262
- ] : message.content.map(
263
- (content, contentIndex) => contentIndex === message.content.length - 1 ? {
264
- ...content,
265
- cache_control: cacheControl
266
- } : content
267
- )
268
- };
269
- }
270
- return message;
271
- }),
272
- stream: true
273
- });
245
+ stream = await this.#client.messages.create(
246
+ {
247
+ model: modelId,
248
+ max_tokens: this.model.info.maxTokens || 8192,
249
+ thinking: thinkingBudgetTokens ? { type: "enabled", budget_tokens: thinkingBudgetTokens } : void 0,
250
+ temperature,
251
+ system: [
252
+ {
253
+ text: systemPrompt,
254
+ type: "text",
255
+ cache_control: cacheControl
256
+ }
257
+ ],
258
+ // setting cache breakpoint for system prompt so new tasks can reuse it
259
+ messages: messages.map((message, index) => {
260
+ if (index === lastUserMsgIndex || index === secondLastMsgUserIndex) {
261
+ return {
262
+ ...message,
263
+ content: typeof message.content === "string" ? [
264
+ {
265
+ type: "text",
266
+ text: message.content,
267
+ cache_control: cacheControl
268
+ }
269
+ ] : message.content.map(
270
+ (content, contentIndex) => contentIndex === message.content.length - 1 ? {
271
+ ...content,
272
+ cache_control: cacheControl
273
+ } : content
274
+ )
275
+ };
276
+ }
277
+ return message;
278
+ }),
279
+ stream: true
280
+ },
281
+ { signal }
282
+ );
274
283
  break;
275
284
  }
276
285
  default: {
277
- stream = await this.#client.messages.create({
278
- model: modelId,
279
- max_tokens: this.model.info.maxTokens || 8192,
280
- temperature: 0,
281
- system: [{ text: systemPrompt, type: "text" }],
282
- messages,
283
- stream: true
284
- });
286
+ stream = await this.#client.messages.create(
287
+ {
288
+ model: modelId,
289
+ max_tokens: this.model.info.maxTokens || 8192,
290
+ temperature: 0,
291
+ system: [{ text: systemPrompt, type: "text" }],
292
+ messages,
293
+ stream: true
294
+ },
295
+ { signal }
296
+ );
285
297
  break;
286
298
  }
287
299
  }
@@ -487,19 +499,22 @@ var DeepSeekService = class extends AiServiceBase {
487
499
  info: deepSeekModels[id] ?? deepSeekModels[deepSeekDefaultModelId]
488
500
  };
489
501
  }
490
- async *sendImpl(systemPrompt, messages) {
502
+ async *sendImpl(systemPrompt, messages, signal) {
491
503
  const openAiMessages = [
492
504
  { role: "system", content: systemPrompt },
493
505
  ...convertToOpenAiMessages(messages)
494
506
  ];
495
- const stream = await this.#client.chat.completions.create({
496
- model: this.model.id,
497
- max_completion_tokens: this.model.info.maxTokens,
498
- messages: openAiMessages,
499
- temperature: 0,
500
- stream: true,
501
- stream_options: { include_usage: true }
502
- });
507
+ const stream = await this.#client.chat.completions.create(
508
+ {
509
+ model: this.model.id,
510
+ max_completion_tokens: this.model.info.maxTokens,
511
+ messages: openAiMessages,
512
+ temperature: 0,
513
+ stream: true,
514
+ stream_options: { include_usage: true }
515
+ },
516
+ { signal }
517
+ );
503
518
  for await (const chunk of stream) {
504
519
  const delta = chunk.choices[0]?.delta;
505
520
  if (delta?.reasoning_content) {
@@ -549,17 +564,20 @@ var OllamaService = class extends AiServiceBase {
549
564
  info: openAiModelInfoSaneDefaults
550
565
  };
551
566
  }
552
- async *sendImpl(systemPrompt, messages) {
567
+ async *sendImpl(systemPrompt, messages, signal) {
553
568
  const openAiMessages = [
554
569
  { role: "system", content: systemPrompt },
555
570
  ...convertToOpenAiMessages(messages)
556
571
  ];
557
- const stream = await this.#client.chat.completions.create({
558
- model: this.model.id,
559
- messages: openAiMessages,
560
- temperature: 0,
561
- stream: true
562
- });
572
+ const stream = await this.#client.chat.completions.create(
573
+ {
574
+ model: this.model.id,
575
+ messages: openAiMessages,
576
+ temperature: 0,
577
+ stream: true
578
+ },
579
+ { signal }
580
+ );
563
581
  for await (const chunk of stream) {
564
582
  const delta = chunk.choices[0]?.delta;
565
583
  if (delta?.content) {
@@ -609,7 +627,7 @@ var OpenRouterService = class extends AiServiceBase {
609
627
  this.#modelProviderInfo = data.data;
610
628
  });
611
629
  }
612
- async *sendImpl(systemPrompt, messages) {
630
+ async *sendImpl(systemPrompt, messages, signal) {
613
631
  const openAiMessages = [
614
632
  { role: "system", content: systemPrompt },
615
633
  ...convertToOpenAiMessages(messages)
@@ -662,15 +680,18 @@ var OpenRouterService = class extends AiServiceBase {
662
680
  if (this.model.id === "deepseek/deepseek-chat") {
663
681
  shouldApplyMiddleOutTransform = true;
664
682
  }
665
- const stream = await this.#client.chat.completions.create({
666
- model: this.model.id,
667
- messages: openAiMessages,
668
- temperature: 0,
669
- stream: true,
670
- transforms: shouldApplyMiddleOutTransform ? ["middle-out"] : void 0,
671
- include_reasoning: true,
672
- ...reasoning
673
- });
683
+ const stream = await this.#client.chat.completions.create(
684
+ {
685
+ model: this.model.id,
686
+ messages: openAiMessages,
687
+ temperature: 0,
688
+ stream: true,
689
+ transforms: shouldApplyMiddleOutTransform ? ["middle-out"] : void 0,
690
+ include_reasoning: true,
691
+ ...reasoning
692
+ },
693
+ { signal }
694
+ );
674
695
  let genId;
675
696
  for await (const chunk of stream) {
676
697
  if ("error" in chunk) {
@@ -1008,7 +1029,7 @@ var getArray = (args, name, defaultValue) => {
1008
1029
  };
1009
1030
 
1010
1031
  // src/tools/utils/replaceInFile.ts
1011
- var replaceInFile = async (fileContent, diff) => {
1032
+ var replaceInFile = (fileContent, diff) => {
1012
1033
  const blockPattern = /<<<<<+ SEARCH>?\s*\r?\n([\s\S]*?)\r?\n=======[ \t]*\r?\n([\s\S]*?)\r?\n?>>>>>+ REPLACE/g;
1013
1034
  const blocks = [];
1014
1035
  for (let match = blockPattern.exec(diff); match !== null; match = blockPattern.exec(diff)) {
@@ -1053,14 +1074,32 @@ var replaceInFile = async (fileContent, diff) => {
1053
1074
  const startPos = endPos - strippedSearch.length;
1054
1075
  return content.slice(0, startPos) + replace + content.slice(endPos);
1055
1076
  }
1056
- throw new Error(`Could not find the following text in file:
1057
- ${search}`);
1077
+ return null;
1058
1078
  };
1059
1079
  let updatedFile = fileContent;
1080
+ let appliedCount = 0;
1081
+ const totalCount = blocks.length;
1060
1082
  for (const { search, replace } of blocks) {
1061
- updatedFile = findAndReplace(updatedFile, search, replace);
1083
+ const result = findAndReplace(updatedFile, search, replace);
1084
+ if (result !== null) {
1085
+ updatedFile = result;
1086
+ appliedCount++;
1087
+ }
1062
1088
  }
1063
- return updatedFile;
1089
+ let status;
1090
+ if (appliedCount === 0) {
1091
+ status = "no_diff_applied";
1092
+ } else if (appliedCount < totalCount) {
1093
+ status = "some_diff_applied";
1094
+ } else {
1095
+ status = "all_diff_applied";
1096
+ }
1097
+ return {
1098
+ content: updatedFile,
1099
+ status,
1100
+ appliedCount,
1101
+ totalCount
1102
+ };
1064
1103
  };
1065
1104
 
1066
1105
  // src/tools/askFollowupQuestion.ts
@@ -1759,14 +1798,26 @@ var handler8 = async (provider, args) => {
1759
1798
  if (fileContent == null) {
1760
1799
  return {
1761
1800
  type: "Error" /* Error */,
1762
- message: `<error><replace_in_file_path>${path}</replace_in_file_path><error_message>File not found</error_message></error>`
1801
+ message: `<replace_in_file_result path="${path}" status="failed" message="File not found" />`
1802
+ };
1803
+ }
1804
+ const result = replaceInFile(fileContent, diff);
1805
+ if (result.status === "no_diff_applied") {
1806
+ return {
1807
+ type: "Error" /* Error */,
1808
+ message: `<replace_in_file_result path="${path}" status="failed" message="Unable to apply changes" />`
1809
+ };
1810
+ }
1811
+ await provider.writeFile(path, result.content);
1812
+ if (result.status === "some_diff_applied") {
1813
+ return {
1814
+ type: "Reply" /* Reply */,
1815
+ message: `<replace_in_file_result path="${path}" status="some_diff_applied" applied_count="${result.appliedCount}" total_count="${result.totalCount}" />`
1763
1816
  };
1764
1817
  }
1765
- const result = await replaceInFile(fileContent, diff);
1766
- await provider.writeFile(path, result);
1767
1818
  return {
1768
1819
  type: "Reply" /* Reply */,
1769
- message: `<replace_in_file_path>${path}</replace_in_file_path>`
1820
+ message: `<replace_in_file_result path="${path}" status="all_diff_applied" />`
1770
1821
  };
1771
1822
  };
1772
1823
  var isAvailable8 = (provider) => {
@@ -2166,7 +2217,7 @@ var updateKnowledge_default = {
2166
2217
  // src/tools/writeToFile.ts
2167
2218
  var toolInfo11 = {
2168
2219
  name: "write_to_file",
2169
- description: "Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Ensure that the output content does not include incorrect escaped character patterns such as `&lt;` and `&gt;`.",
2220
+ description: "Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Ensure that the output content does not include incorrect escaped character patterns such as `&lt;`, `&gt;`, or `&amp;`. Also ensure there is no unwanted CDATA tags in the content.",
2170
2221
  parameters: [
2171
2222
  {
2172
2223
  name: "path",
@@ -2217,7 +2268,9 @@ var handler11 = async (provider, args) => {
2217
2268
  };
2218
2269
  }
2219
2270
  const path = getString(args, "path");
2220
- const content = getString(args, "content");
2271
+ let content = getString(args, "content");
2272
+ const trimmedContent = content.trim();
2273
+ if (trimmedContent.startsWith("<![CDATA[") && content.endsWith("]]>")) content = trimmedContent.slice(9, -3);
2221
2274
  await provider.writeFile(path, content);
2222
2275
  return {
2223
2276
  type: "Reply" /* Reply */,
@@ -2773,12 +2826,27 @@ Ensure the opening and closing tags are correctly nested and closed, and that yo
2773
2826
  Avoid unnecessary text or symbols before or after the tool use.
2774
2827
  Avoid unnecessary escape characters or special characters.
2775
2828
  `,
2776
- toolResults: (tool, result) => `<tool_response>
2777
- <tool_name>${tool}</tool_name>
2778
- <tool_result>
2779
- ${result}
2780
- </tool_result>
2781
- </tool_response>`,
2829
+ toolResults: (tool, result) => {
2830
+ if (typeof result === "string") {
2831
+ return [
2832
+ {
2833
+ type: "text",
2834
+ text: `<tool_response name=${tool}>${result}</tool_response>`
2835
+ }
2836
+ ];
2837
+ }
2838
+ return [
2839
+ {
2840
+ type: "text",
2841
+ text: `<tool_response name=${tool}>`
2842
+ },
2843
+ ...result,
2844
+ {
2845
+ type: "text",
2846
+ text: "</tool_response>"
2847
+ }
2848
+ ];
2849
+ },
2782
2850
  commandResult: (command, exitCode, stdout, stderr) => `<command>${command}</command>
2783
2851
  <command_exit_code>${exitCode}</command_exit_code>
2784
2852
  <command_stdout>
@@ -2815,6 +2883,7 @@ var AgentBase = class {
2815
2883
  handlers;
2816
2884
  #messages = [];
2817
2885
  #policies;
2886
+ #aborted = false;
2818
2887
  constructor(name, ai, config) {
2819
2888
  this.ai = ai;
2820
2889
  if (config.agents && config.agents.length > 0) {
@@ -2846,6 +2915,10 @@ ${instance.prompt}`;
2846
2915
  this.config = config;
2847
2916
  this.#policies = policies;
2848
2917
  }
2918
+ abort() {
2919
+ this.#aborted = true;
2920
+ this.ai.abort();
2921
+ }
2849
2922
  get parameters() {
2850
2923
  return this.ai.options.parameters;
2851
2924
  }
@@ -2874,11 +2947,17 @@ ${instance.prompt}`;
2874
2947
  async #processLoop(userMessage) {
2875
2948
  let nextRequest = userMessage;
2876
2949
  while (true) {
2950
+ if (this.#aborted) {
2951
+ return { type: "Aborted" };
2952
+ }
2877
2953
  if (this.ai.usageMeter.isLimitExceeded().result) {
2878
2954
  this.#callback({ kind: "UsageExceeded" /* UsageExceeded */, agent: this });
2879
2955
  return { type: "UsageExceeded" };
2880
2956
  }
2881
2957
  const response = await this.#request(nextRequest);
2958
+ if (this.#aborted) {
2959
+ return { type: "Aborted" };
2960
+ }
2882
2961
  const resp = await this.#handleResponse(response);
2883
2962
  if (resp.type === "exit") {
2884
2963
  this.#callback({ kind: "EndTask" /* EndTask */, agent: this, exitReason: resp.reason });
@@ -2925,14 +3004,23 @@ ${instance.prompt}`;
2925
3004
  }
2926
3005
  }
2927
3006
  } catch (error) {
3007
+ if (error instanceof Error && error.name === "AbortError") {
3008
+ break;
3009
+ }
2928
3010
  console.error("Error in stream:", error);
2929
3011
  }
2930
3012
  if (currentAssistantMessage) {
2931
3013
  break;
2932
3014
  }
3015
+ if (this.#aborted) {
3016
+ break;
3017
+ }
2933
3018
  console.debug(`Retrying request ${i + 1} of ${retryCount}`);
2934
3019
  }
2935
3020
  if (!currentAssistantMessage) {
3021
+ if (this.#aborted) {
3022
+ return [];
3023
+ }
2936
3024
  throw new Error("No assistant message received");
2937
3025
  }
2938
3026
  this.#messages.push({
@@ -2960,23 +3048,26 @@ ${instance.prompt}`;
2960
3048
  await this.#callback({ kind: "ToolUse" /* ToolUse */, agent: this, tool: content.name });
2961
3049
  const toolResp = await this.#invokeTool(content.name, content.params);
2962
3050
  switch (toolResp.type) {
2963
- case "Reply" /* Reply */:
3051
+ case "Reply" /* Reply */: {
2964
3052
  await this.#callback({ kind: "ToolReply" /* ToolReply */, agent: this, tool: content.name });
2965
3053
  toolResponses.push({ type: "response", tool: content.name, response: toolResp.message });
2966
3054
  break;
3055
+ }
2967
3056
  case "Exit" /* Exit */:
2968
3057
  if (toolResponses.length > 0) {
2969
3058
  break outer;
2970
3059
  }
2971
3060
  return { type: "exit", reason: toolResp };
2972
- case "Invalid" /* Invalid */:
3061
+ case "Invalid" /* Invalid */: {
2973
3062
  await this.#callback({ kind: "ToolInvalid" /* ToolInvalid */, agent: this, tool: content.name });
2974
3063
  toolResponses.push({ type: "response", tool: content.name, response: toolResp.message });
2975
3064
  break outer;
2976
- case "Error" /* Error */:
3065
+ }
3066
+ case "Error" /* Error */: {
2977
3067
  await this.#callback({ kind: "ToolError" /* ToolError */, agent: this, tool: content.name });
2978
3068
  toolResponses.push({ type: "response", tool: content.name, response: toolResp.message });
2979
3069
  break outer;
3070
+ }
2980
3071
  case "Interrupted" /* Interrupted */:
2981
3072
  await this.#callback({ kind: "ToolInterrupted" /* ToolInterrupted */, agent: this, tool: content.name });
2982
3073
  return { type: "exit", reason: toolResp };
@@ -3026,7 +3117,7 @@ ${instance.prompt}`;
3026
3117
  if (toolResponses.length === 0) {
3027
3118
  return { type: "reply", message: responsePrompts.requireUseTool };
3028
3119
  }
3029
- const finalResp = toolResponses.filter((resp) => resp.type === "response").map(({ tool, response: response2 }) => responsePrompts.toolResults(tool, response2)).join("\n\n");
3120
+ const finalResp = toolResponses.filter((resp) => resp.type === "response").flatMap(({ tool, response: response2 }) => responsePrompts.toolResults(tool, response2));
3030
3121
  return { type: "reply", message: finalResp };
3031
3122
  }
3032
3123
  async #invokeTool(name, args) {
@@ -3643,6 +3734,7 @@ var MultiAgent = class {
3643
3734
  case "Delegate" /* Delegate */:
3644
3735
  console.warn("Unexpected exit reason", delegateResult);
3645
3736
  break;
3737
+ case "Aborted":
3646
3738
  case "Interrupted" /* Interrupted */:
3647
3739
  return delegateResult;
3648
3740
  case "Exit" /* Exit */:
@@ -3650,6 +3742,7 @@ var MultiAgent = class {
3650
3742
  }
3651
3743
  return delegateResult;
3652
3744
  }
3745
+ case "Aborted":
3653
3746
  case "Interrupted" /* Interrupted */:
3654
3747
  case "Exit" /* Exit */:
3655
3748
  this.#agents.pop();
@@ -3682,6 +3775,11 @@ var MultiAgent = class {
3682
3775
  get hasActiveAgent() {
3683
3776
  return this.#agents.length > 0;
3684
3777
  }
3778
+ abort() {
3779
+ if (this.hasActiveAgent) {
3780
+ this.#agents[this.#agents.length - 1].abort();
3781
+ }
3782
+ }
3685
3783
  };
3686
3784
 
3687
3785
  // src/config.ts
@@ -3758,27 +3856,26 @@ var prompt = `
3758
3856
  You are equipped with **Knowledge Management** capabilities:
3759
3857
 
3760
3858
  1. **What to capture**
3761
- \u2022 Public API of each file (public classes, functions, methods, parameters, return types).
3762
- \u2022 High-level description of each file's purpose.
3763
- \u2022 Invariants and assumptions that must always hold.
3764
- \u2022 Project or directory-specific coding patterns, styles, and architectural conventions.
3765
- \u2022 Rules (commenting, testing, documentation, security, etc.).
3766
- \u2022 Any other insight that a future contributor would find crucial.
3859
+ - Public API of each file (public classes, functions, methods, parameters, return types).
3860
+ - High-level description of each file's purpose.
3861
+ - Invariants and assumptions that must always hold.
3862
+ - Project or directory-specific coding patterns, styles, and architectural conventions.
3863
+ - Any other insight that a future contributor would find crucial.
3767
3864
 
3768
3865
  2. **Where to store it**
3769
- \u2022 Save knowledge in a YAML file named \`knowledge.ai.yml\`.
3770
- \u2022 **Create the file in the repository root if it does not yet exist.**
3771
- \u2022 One file per directory.
3866
+ - Save knowledge in a YAML file named \`knowledge.ai.yml\`.
3867
+ - **Create the file in the repository root if it does not yet exist.**
3868
+ - One file per directory.
3772
3869
  - The repository root file records knowledge that applies project-wide (e.g., service responsibilities, global patterns).
3773
3870
  - Each sub-directory keeps only the knowledge relevant to that directory or package.
3774
- \u2022 Use clear top-level keys such as \`description\`, \`files\`, \`rules\`.
3871
+ - Use clear top-level keys such as \`description\`, \`files\`, \`rules\`.
3775
3872
 
3776
3873
  3. **When to update**
3777
- \u2022 **Default behaviour:** only create / update knowledge for the files you actively read, create, or modify during the current task.
3874
+ - **Default behaviour:** only create / update knowledge for the files you actively read, create, or modify during the current task.
3778
3875
  - Operate on other files **only if the user explicitly requests it**.
3779
- \u2022 **While working**: after reading, analysing, creating, or modifying code, immediately record any new or changed knowledge.
3780
- \u2022 **On refactor / deletion**: locate and delete or amend obsolete entries so that knowledge never drifts from the codebase.
3781
- \u2022 **Granularity**: update only the affected directory's \`knowledge.ai.yml\`, except when the change has global impact.
3876
+ - **While working**: after reading, analysing, creating, or modifying code, immediately record any new or changed knowledge.
3877
+ - **On refactor / deletion**: locate and delete or amend obsolete entries so that knowledge never drifts from the codebase.
3878
+ - **Granularity**: update only the affected directory's \`knowledge.ai.yml\`, except when the change has global impact.
3782
3879
 
3783
3880
  4. **How to format (illustrative)**
3784
3881
  \`\`\`yaml
@@ -3788,19 +3885,14 @@ files:
3788
3885
  description: "Numeric helpers for currency calculations"
3789
3886
  api:
3790
3887
  functions:
3791
- 1:
3792
- name: "add"
3793
- params:
3794
- 1: { name: "a", type: "number" }
3795
- 2: { name: "b", type: "number" }
3796
- returns: "number"
3888
+ 1: add(a: number, b: number): number
3797
3889
  rules:
3798
3890
  1: "rules that apply to all files in this directory"
3799
3891
  \`\`\`
3800
3892
 
3801
3893
  5. **Source of truth**
3802
- \u2022 **Never invent knowledge.** Everything you record must be *directly derived* from existing code, comments, commit messages, or explicit user instructions.
3803
- \u2022 If a section has no confirmed content, omit it rather than guessing.
3894
+ - **Never invent knowledge.** Everything you record must be *directly derived* from existing code, comments, commit messages, or explicit user instructions.
3895
+ - If a section has no confirmed content, omit it rather than guessing.
3804
3896
 
3805
3897
  6. **Automatic context**
3806
3898
  When you are asked to read or modify a file, the orchestration layer will supply any existing knowledge for that path automatically. Use it, refine it, and keep it accurate.
@@ -3809,11 +3901,11 @@ rules:
3809
3901
  You can use the \`updateKnowledge\` tool to efficiently update knowledge files with smart merging capabilities.
3810
3902
 
3811
3903
  8. **Dictionary-First Format**
3812
- \u2022 **Always prefer dictionaries** for structured data.
3813
- \u2022 The **\`files\` section must be a dictionary keyed by file path** (e.g., \`"math.ts": {...}\`).
3814
- \u2022 For other lists (rules, functions, etc.), use numbered dictionaries.
3815
- \u2022 Arrays are allowed only when strict ordering is essential and dictionaries cannot express it.
3816
- \u2022 When removing items, refer to them by their numeric key or index; gaps are fine.
3904
+ - **Always prefer dictionaries** for structured data.
3905
+ - The **\`files\` section must be a dictionary keyed by file path** (e.g., \`"math.ts": {...}\`).
3906
+ - For other lists (rules, functions, etc.), use numbered dictionaries.
3907
+ - Arrays are allowed only when strict ordering is essential and dictionaries cannot express it.
3908
+ - When removing items, refer to them by their numeric key or index; gaps are fine.
3817
3909
 
3818
3910
  Your workflow **must**:
3819
3911
  1. Detect knowledge deltas.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polka-codes/core",
3
- "version": "0.8.23",
3
+ "version": "0.8.25",
4
4
  "license": "AGPL-3.0",
5
5
  "author": "github@polka.codes",
6
6
  "type": "module",