@polka-codes/core 0.9.102 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@ Core AI services and agent implementations for Polka Codes framework.
8
8
 
9
9
  ## Features
10
10
 
11
- - Multiple AI provider support (Anthropic, DeepSeek, GoogleVertex, Ollama, OpenAI, and OpenRouter)
11
+ - Multiple AI provider support (Anthropic, DeepSeek, GoogleVertex, OpenAI, and OpenRouter)
12
12
  - Extensible agent architecture
13
13
  - Tool integration system with safety enhancements
14
14
  - Type-safe API
@@ -1,15 +1,17 @@
1
1
  import type { FilePart } from 'ai';
2
2
  import type { ImagePart } from 'ai';
3
3
  import type { JSONValue } from '@ai-sdk/provider';
4
- import type { LanguageModelV2 } from '@ai-sdk/provider';
5
- import type { LanguageModelV2ToolResultOutput } from '@ai-sdk/provider';
6
- import type { LanguageModelV2Usage } from '@ai-sdk/provider';
4
+ import type { LanguageModelUsage } from 'ai';
5
+ import type { LanguageModelV3 } from '@ai-sdk/provider';
6
+ import type { LanguageModelV3ToolResultOutput } from '@ai-sdk/provider';
7
+ import type { LanguageModelV3Usage } from '@ai-sdk/provider';
7
8
  import type { ModelMessage } from '@ai-sdk/provider-utils';
8
9
  import type { ProviderOptions } from '@ai-sdk/provider-utils';
9
10
  import type { ReasoningPart } from '@ai-sdk/provider-utils';
10
11
  import type { SystemModelMessage } from '@ai-sdk/provider-utils';
11
12
  import type { TextPart } from 'ai';
12
13
  import type { TextPart as TextPart_2 } from '@ai-sdk/provider-utils';
14
+ import type { ToolApprovalRequest } from '@ai-sdk/provider-utils';
13
15
  import type { ToolModelMessage } from '@ai-sdk/provider-utils';
14
16
  import type { ToolResultPart } from '@ai-sdk/provider-utils';
15
17
  import { ToolSet } from 'ai';
@@ -1143,7 +1145,7 @@ export declare function join(...parts: string[]): string;
1143
1145
  Content of an assistant message.
1144
1146
  It can be a string or an array of text, image, reasoning, redacted reasoning, and tool call parts.
1145
1147
  */
1146
- declare type JsonAssistantContent = string | Array<TextPart_2 | JsonFilePart | ReasoningPart | JsonToolCallPart | ToolResultPart>;
1148
+ declare type JsonAssistantContent = string | Array<TextPart_2 | JsonFilePart | ReasoningPart | JsonToolCallPart | ToolResultPart | ToolApprovalRequest>;
1147
1149
 
1148
1150
  /**
1149
1151
  An assistant message. It can contain text, tool calls, or a combination of text and tool calls.
@@ -1777,7 +1779,7 @@ export { providerConfigSchema as providerConfigSchema_alias_2 }
1777
1779
  declare type ProviderMetadataEntry = {
1778
1780
  provider: string;
1779
1781
  model: string;
1780
- metadata: any;
1782
+ metadata: Record<string, unknown>;
1781
1783
  timestamp: number;
1782
1784
  };
1783
1785
 
@@ -1904,7 +1906,7 @@ declare const responsePrompts: {
1904
1906
  readonly errorInvokeTool: (tool: string, error: unknown) => string;
1905
1907
  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";
1906
1908
  readonly requireUseToolNative: "Error: No tool use detected. You MUST use a tool before proceeding.\n";
1907
- readonly toolResults: (tool: string, result: LanguageModelV2ToolResultOutput) => Array<TextPart | ImagePart | FilePart>;
1909
+ readonly toolResults: (tool: string, result: LanguageModelV3ToolResultOutput) => Array<TextPart | ImagePart | FilePart>;
1908
1910
  readonly commandResult: (command: string, exitCode: number, stdout: string, stderr: string) => string;
1909
1911
  };
1910
1912
  export { responsePrompts }
@@ -2689,12 +2691,15 @@ declare class UsageMeter {
2689
2691
  maxCost?: number;
2690
2692
  pricingService?: PricingService;
2691
2693
  });
2692
- addUsage(llm: LanguageModelV2, resp: {
2693
- usage: LanguageModelV2Usage;
2694
- providerMetadata?: any;
2694
+ addUsage(llm: LanguageModelV3, resp: {
2695
+ usage: LanguageModelV3Usage;
2696
+ providerMetadata?: Record<string, unknown>;
2695
2697
  } | {
2696
- totalUsage: LanguageModelV2Usage;
2697
- providerMetadata?: any;
2698
+ totalUsage: LanguageModelV3Usage;
2699
+ providerMetadata?: Record<string, unknown>;
2700
+ } | {
2701
+ totalUsage: LanguageModelUsage;
2702
+ providerMetadata?: Record<string, unknown>;
2698
2703
  }, options?: {
2699
2704
  modelInfo?: ModelInfo;
2700
2705
  }): Promise<void>;
@@ -2741,9 +2746,12 @@ declare class UsageMeter {
2741
2746
  /** Wait for all pending usage updates to complete. */
2742
2747
  waitForPending(): Promise<void>;
2743
2748
  getUsageText(): string;
2744
- onFinishHandler(llm: LanguageModelV2): (evt: {
2745
- totalUsage: LanguageModelV2Usage;
2746
- providerMetadata: any;
2749
+ onFinishHandler(llm: LanguageModelV3): (evt: {
2750
+ totalUsage: LanguageModelUsage;
2751
+ providerMetadata?: Record<string, unknown>;
2752
+ } | {
2753
+ totalUsage: LanguageModelV3Usage;
2754
+ providerMetadata?: Record<string, unknown>;
2747
2755
  }) => Promise<void>;
2748
2756
  }
2749
2757
  export { UsageMeter }
package/dist/index.js CHANGED
@@ -29,6 +29,7 @@ var parseJsonFromMarkdown = (markdown) => {
29
29
  };
30
30
 
31
31
  // src/Agent/prompts.ts
32
+ var NO_REASON_PROVIDED = "No reason provided";
32
33
  var responsePrompts = {
33
34
  errorInvokeTool: (tool, error) => `An error occurred while invoking the tool "${tool}": ${error}`,
34
35
  requireUseTool: `Error: No tool use detected. You MUST use a tool before proceeding.
@@ -80,17 +81,23 @@ Avoid unnecessary escape characters or special characters.
80
81
  if (part.type === "text") {
81
82
  return part;
82
83
  }
83
- if (part.mediaType.startsWith("image/")) {
84
+ if ("mediaType" in part && "data" in part && part.mediaType.startsWith("image/")) {
84
85
  return {
85
86
  type: "image",
86
87
  mediaType: part.mediaType,
87
88
  image: part.data
88
89
  };
89
90
  }
91
+ if ("mediaType" in part && "data" in part) {
92
+ return {
93
+ type: "file",
94
+ mediaType: part.mediaType,
95
+ data: part.data
96
+ };
97
+ }
90
98
  return {
91
- type: "file",
92
- mediaType: part.mediaType,
93
- data: part.data
99
+ type: "text",
100
+ text: JSON.stringify(part)
94
101
  };
95
102
  }),
96
103
  {
@@ -98,6 +105,21 @@ Avoid unnecessary escape characters or special characters.
98
105
  text: "</tool_response>"
99
106
  }
100
107
  ];
108
+ case "execution-denied":
109
+ return [
110
+ {
111
+ type: "text",
112
+ text: `<tool_response_error name=${tool}>Execution denied: ${result.reason ?? NO_REASON_PROVIDED}</tool_response_error>`
113
+ }
114
+ ];
115
+ default: {
116
+ return [
117
+ {
118
+ type: "text",
119
+ text: `<tool_response_error name=${tool}>Unknown result type: ${JSON.stringify(result)}</tool_response_error>`
120
+ }
121
+ ];
122
+ }
101
123
  }
102
124
  },
103
125
  commandResult: (command, exitCode, stdout, stderr) => `<command>${command}</command>
@@ -2441,6 +2463,14 @@ var writeToFile_default = {
2441
2463
  };
2442
2464
 
2443
2465
  // src/UsageMeter.ts
2466
+ var ZERO_PRICING = {
2467
+ inputPrice: 0,
2468
+ outputPrice: 0,
2469
+ cacheWritesPrice: 0,
2470
+ cacheReadsPrice: 0
2471
+ };
2472
+ var TOKENS_PER_UNIT = 1e6;
2473
+ var MAX_METADATA_ENTRIES = 1e3;
2444
2474
  var UsageMeter = class {
2445
2475
  #totals = { input: 0, output: 0, cachedRead: 0, cost: 0, messageCount: 0 };
2446
2476
  #providerMetadataEntries = [];
@@ -2466,50 +2496,72 @@ var UsageMeter = class {
2466
2496
  this.#maxCost = opts.maxCost ?? 100;
2467
2497
  this.#pricingService = opts.pricingService;
2468
2498
  }
2469
- #calculateUsage(usage, providerMetadata, modelInfo) {
2499
+ #calculateUsageV3(usage, providerMetadata, modelInfo) {
2500
+ if (!usage?.inputTokens || !usage?.outputTokens) {
2501
+ return { input: 0, output: 0, cachedRead: 0, cost: 0 };
2502
+ }
2503
+ const providerMetadataKey = Object.keys(providerMetadata ?? {})[0];
2504
+ const metadata = providerMetadata?.[providerMetadataKey] ?? {};
2505
+ const inputTokens = usage.inputTokens.noCache ?? usage.inputTokens.total ?? 0;
2506
+ const cachedReadTokens = usage.inputTokens.cacheRead ?? 0;
2507
+ const cachedWriteTokens = usage.inputTokens.cacheWrite ?? 0;
2508
+ const outputTokens = usage.outputTokens.total ?? usage.outputTokens.text ?? 0;
2509
+ return this.#calculateCost(inputTokens, outputTokens, cachedReadTokens, cachedWriteTokens, providerMetadataKey, metadata, modelInfo);
2510
+ }
2511
+ #calculateUsageAiPackage(usage, providerMetadata, modelInfo) {
2512
+ if (!usage?.inputTokens || !usage?.outputTokens) {
2513
+ return { input: 0, output: 0, cachedRead: 0, cost: 0 };
2514
+ }
2470
2515
  const providerMetadataKey = Object.keys(providerMetadata ?? {})[0];
2471
2516
  const metadata = providerMetadata?.[providerMetadataKey] ?? {};
2517
+ const details = usage.inputTokenDetails;
2518
+ const outputDetails = usage.outputTokenDetails;
2519
+ const inputTokens = details?.noCacheTokens ?? usage.inputTokens ?? 0;
2520
+ const cachedReadTokens = details?.cacheReadTokens ?? 0;
2521
+ const cachedWriteTokens = details?.cacheWriteTokens ?? 0;
2522
+ const outputTokens = outputDetails?.textTokens ?? usage.outputTokens ?? 0;
2523
+ return this.#calculateCost(inputTokens, outputTokens, cachedReadTokens, cachedWriteTokens, providerMetadataKey, metadata, modelInfo);
2524
+ }
2525
+ #calculateCost(inputTokens, outputTokens, cachedReadTokens, cachedWriteTokens, providerMetadataKey, metadata, modelInfo) {
2526
+ const getNumber = (obj, key) => {
2527
+ const value = obj[key];
2528
+ return typeof value === "number" ? value : void 0;
2529
+ };
2472
2530
  switch (providerMetadataKey) {
2473
- case "openrouter":
2531
+ case "openrouter": {
2532
+ const usage = metadata.usage;
2533
+ const cost = typeof usage === "object" && usage !== null && "cost" in usage && typeof usage.cost === "number" ? usage.cost : 0;
2474
2534
  return {
2475
- input: usage.inputTokens ?? 0,
2476
- output: usage.outputTokens ?? 0,
2477
- cachedRead: usage.cachedInputTokens ?? 0,
2478
- cost: metadata.usage?.cost ?? 0
2535
+ input: inputTokens,
2536
+ output: outputTokens,
2537
+ cachedRead: cachedReadTokens,
2538
+ cost
2479
2539
  };
2540
+ }
2480
2541
  case "anthropic": {
2481
- const cachedRead = usage.cachedInputTokens ?? 0;
2482
- const cacheWrite = metadata?.promptCacheMissTokens ?? 0;
2483
- const input = usage.inputTokens ?? 0;
2484
- const output = usage.outputTokens ?? 0;
2542
+ const cacheWrite = getNumber(metadata, "promptCacheMissTokens") ?? cachedWriteTokens;
2485
2543
  return {
2486
- input: input + cacheWrite + cachedRead,
2487
- output,
2488
- cachedRead,
2489
- cost: (input * modelInfo.inputPrice + output * modelInfo.outputPrice + cacheWrite * modelInfo.cacheWritesPrice + cachedRead * modelInfo.cacheReadsPrice) / 1e6
2544
+ input: inputTokens + cacheWrite + cachedReadTokens,
2545
+ output: outputTokens,
2546
+ cachedRead: cachedReadTokens,
2547
+ cost: (inputTokens * modelInfo.inputPrice + outputTokens * modelInfo.outputPrice + cacheWrite * modelInfo.cacheWritesPrice + cachedReadTokens * modelInfo.cacheReadsPrice) / TOKENS_PER_UNIT
2490
2548
  };
2491
2549
  }
2492
2550
  case "deepseek": {
2493
- const cachedRead = usage.cachedInputTokens ?? 0;
2494
- const cacheWrite = metadata.promptCacheMissTokens ?? 0;
2495
- const input = usage.inputTokens ?? 0;
2496
- const output = usage.outputTokens ?? 0;
2551
+ const cacheWrite = getNumber(metadata, "promptCacheMissTokens") ?? cachedWriteTokens;
2497
2552
  return {
2498
- input,
2499
- output,
2500
- cachedRead,
2501
- cost: (output * modelInfo.outputPrice + cacheWrite * modelInfo.inputPrice + cachedRead * modelInfo.cacheReadsPrice) / 1e6
2553
+ input: inputTokens,
2554
+ output: outputTokens,
2555
+ cachedRead: cachedReadTokens,
2556
+ cost: (outputTokens * modelInfo.outputPrice + cacheWrite * modelInfo.inputPrice + cachedReadTokens * modelInfo.cacheReadsPrice) / TOKENS_PER_UNIT
2502
2557
  };
2503
2558
  }
2504
2559
  default: {
2505
- const cachedRead = usage.cachedInputTokens ?? 0;
2506
- const input = usage.inputTokens ?? 0;
2507
- const output = usage.outputTokens ?? 0;
2508
2560
  return {
2509
- input,
2510
- output,
2511
- cachedRead,
2512
- cost: (input * modelInfo.inputPrice + output * modelInfo.outputPrice) / 1e6
2561
+ input: inputTokens,
2562
+ output: outputTokens,
2563
+ cachedRead: cachedReadTokens,
2564
+ cost: (inputTokens * modelInfo.inputPrice + outputTokens * modelInfo.outputPrice) / TOKENS_PER_UNIT
2513
2565
  };
2514
2566
  }
2515
2567
  }
@@ -2526,34 +2578,35 @@ var UsageMeter = class {
2526
2578
  this.#modelInfos[key] = modelInfo;
2527
2579
  }
2528
2580
  } catch {
2529
- modelInfo = {
2530
- inputPrice: 0,
2531
- outputPrice: 0,
2532
- cacheWritesPrice: 0,
2533
- cacheReadsPrice: 0
2534
- };
2581
+ modelInfo = ZERO_PRICING;
2535
2582
  }
2536
- modelInfo = modelInfo ?? {
2537
- inputPrice: 0,
2538
- outputPrice: 0,
2539
- cacheWritesPrice: 0,
2540
- cacheReadsPrice: 0
2541
- };
2583
+ modelInfo = modelInfo ?? ZERO_PRICING;
2542
2584
  const usage = "totalUsage" in resp ? resp.totalUsage : resp.usage;
2543
- const result = this.#calculateUsage(usage, resp.providerMetadata, modelInfo);
2544
- this.#totals.input += result.input || 0;
2545
- this.#totals.output += result.output || 0;
2546
- this.#totals.cachedRead += result.cachedRead || 0;
2547
- this.#totals.cost += result.cost || 0;
2585
+ let result;
2586
+ if ("inputTokenDetails" in usage || typeof usage.inputTokens === "number") {
2587
+ result = this.#calculateUsageAiPackage(usage, resp.providerMetadata, modelInfo);
2588
+ } else {
2589
+ result = this.#calculateUsageV3(usage, resp.providerMetadata, modelInfo);
2590
+ }
2591
+ this.#totals.input += result.input;
2592
+ this.#totals.output += result.output;
2593
+ this.#totals.cachedRead += result.cachedRead;
2594
+ this.#totals.cost += result.cost;
2548
2595
  this.#totals.messageCount += 1;
2549
2596
  if (resp.providerMetadata && Object.keys(resp.providerMetadata).length > 0) {
2550
- const providerKey = Object.keys(resp.providerMetadata)[0];
2551
- this.#providerMetadataEntries.push({
2552
- provider: providerKey || llm.provider,
2553
- model: llm.modelId,
2554
- metadata: resp.providerMetadata[providerKey] || resp.providerMetadata,
2555
- timestamp: Date.now()
2556
- });
2597
+ const providerKey = Object.keys(resp.providerMetadata)[0] || llm.provider;
2598
+ const metadata = resp.providerMetadata[providerKey];
2599
+ if (typeof metadata === "object" && metadata !== null && !Array.isArray(metadata)) {
2600
+ this.#providerMetadataEntries.push({
2601
+ provider: providerKey,
2602
+ model: llm.modelId,
2603
+ metadata,
2604
+ timestamp: Date.now()
2605
+ });
2606
+ }
2607
+ if (this.#providerMetadataEntries.length > MAX_METADATA_ENTRIES) {
2608
+ this.#providerMetadataEntries.splice(0, this.#providerMetadataEntries.length - MAX_METADATA_ENTRIES);
2609
+ }
2557
2610
  }
2558
2611
  })();
2559
2612
  this.#pendingUpdates.add(updatePromise);
@@ -2619,10 +2672,11 @@ var UsageMeter = class {
2619
2672
  let requestsWithCache = 0;
2620
2673
  for (const entry of entries) {
2621
2674
  const metadata = entry.metadata;
2622
- if (typeof metadata !== "object" || metadata === null) {
2623
- continue;
2624
- }
2625
- const cachedTokens = metadata.cachedPromptTokens ?? metadata.cacheReadTokens ?? metadata.prompt_cache_hit_tokens ?? 0;
2675
+ const getValueAsNumber = (key) => {
2676
+ const value = metadata[key];
2677
+ return typeof value === "number" ? value : void 0;
2678
+ };
2679
+ const cachedTokens = getValueAsNumber("cachedPromptTokens") ?? getValueAsNumber("cacheReadTokens") ?? getValueAsNumber("prompt_cache_hit_tokens") ?? 0;
2626
2680
  if (cachedTokens > 0) {
2627
2681
  totalCachedTokens += cachedTokens;
2628
2682
  requestsWithCache++;
@@ -2700,6 +2754,9 @@ var TaskEventKind = /* @__PURE__ */ ((TaskEventKind2) => {
2700
2754
  })(TaskEventKind || {});
2701
2755
 
2702
2756
  // src/workflow/agent.workflow.ts
2757
+ function isValidToolResultOutput(value) {
2758
+ return typeof value === "object" && value !== null && "type" in value && (value.type === "text" || value.type === "content" || value.type === "execution-denied");
2759
+ }
2703
2760
  var agentWorkflow = async (input, { step, tools, logger }) => {
2704
2761
  const event = (name, event2) => step(name, () => tools.taskEvent(event2));
2705
2762
  const { tools: toolInfo13, maxToolRoundTrips = 200 } = input;
@@ -2813,6 +2870,9 @@ var agentWorkflow = async (input, { step, tools, logger }) => {
2813
2870
  tool: toolCall.toolName,
2814
2871
  content: toolResponse.message
2815
2872
  });
2873
+ if (!isValidToolResultOutput(toolResponse.message)) {
2874
+ throw new Error(`Invalid tool result format from ${toolCall.toolName}`);
2875
+ }
2816
2876
  toolResults.push({
2817
2877
  toolCallId: toolCall.toolCallId,
2818
2878
  toolName: toolCall.toolName,
@@ -2824,6 +2884,9 @@ var agentWorkflow = async (input, { step, tools, logger }) => {
2824
2884
  tool: toolCall.toolName,
2825
2885
  error: toolResponse.message ?? "Unknown error"
2826
2886
  });
2887
+ if (!isValidToolResultOutput(toolResponse.message)) {
2888
+ throw new Error(`Invalid tool result format from ${toolCall.toolName}`);
2889
+ }
2827
2890
  toolResults.push({
2828
2891
  toolCallId: toolCall.toolCallId,
2829
2892
  toolName: toolCall.toolName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polka-codes/core",
3
- "version": "0.9.102",
3
+ "version": "0.10.1",
4
4
  "license": "AGPL-3.0",
5
5
  "author": "github@polka.codes",
6
6
  "type": "module",
@@ -17,9 +17,9 @@
17
17
  "build": "tsup src/index.ts --experimental-dts --format esm --clean"
18
18
  },
19
19
  "dependencies": {
20
- "@ai-sdk/provider": "^2.0.1",
21
- "@ai-sdk/provider-utils": "^3.0.20",
22
- "ai": "^5.0.117",
20
+ "@ai-sdk/provider": "^3.0.8",
21
+ "@ai-sdk/provider-utils": "^4.0.19",
22
+ "ai": "^6.0.116",
23
23
  "yaml": "^2.8.2",
24
24
  "zod": "^4.3.5"
25
25
  }