@mastra/code-sdk 1.0.3-alpha.0 → 1.1.0-alpha.2

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 (258) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/acp/agent.js +155 -167
  3. package/dist/acp/agent.js.map +1 -1
  4. package/dist/acp/event-mapper.js +193 -168
  5. package/dist/acp/event-mapper.js.map +1 -1
  6. package/dist/acp/index.js +60 -49
  7. package/dist/acp/index.js.map +1 -1
  8. package/dist/acp/server.js +31 -30
  9. package/dist/acp/server.js.map +1 -1
  10. package/dist/acp.js +1 -4
  11. package/dist/agents/credential-resolver.js +40 -29
  12. package/dist/agents/credential-resolver.js.map +1 -1
  13. package/dist/agents/custom-provider-source.js +16 -9
  14. package/dist/agents/custom-provider-source.js.map +1 -1
  15. package/dist/agents/instructions.js +27 -36
  16. package/dist/agents/instructions.js.map +1 -1
  17. package/dist/agents/mastracode-gateway.js +355 -428
  18. package/dist/agents/mastracode-gateway.js.map +1 -1
  19. package/dist/agents/memory.js +87 -78
  20. package/dist/agents/memory.js.map +1 -1
  21. package/dist/agents/model.js +114 -119
  22. package/dist/agents/model.js.map +1 -1
  23. package/dist/agents/modes/build.js +12 -13
  24. package/dist/agents/modes/build.js.map +1 -1
  25. package/dist/agents/modes/explore.js +14 -13
  26. package/dist/agents/modes/explore.js.map +1 -1
  27. package/dist/agents/modes/plan.js +14 -15
  28. package/dist/agents/modes/plan.js.map +1 -1
  29. package/dist/agents/prompts/agent-instructions.js +78 -69
  30. package/dist/agents/prompts/agent-instructions.js.map +1 -1
  31. package/dist/agents/prompts/build.js +19 -16
  32. package/dist/agents/prompts/build.js.map +1 -1
  33. package/dist/agents/prompts/fast.js +8 -4
  34. package/dist/agents/prompts/fast.js.map +1 -1
  35. package/dist/agents/prompts/index.js +48 -47
  36. package/dist/agents/prompts/index.js.map +1 -1
  37. package/dist/agents/prompts/model.js +6 -5
  38. package/dist/agents/prompts/model.js.map +1 -1
  39. package/dist/agents/prompts/plan.js +20 -16
  40. package/dist/agents/prompts/plan.js.map +1 -1
  41. package/dist/agents/prompts/tool-guidance.js +77 -109
  42. package/dist/agents/prompts/tool-guidance.js.map +1 -1
  43. package/dist/agents/sandbox-filesystem.js +245 -259
  44. package/dist/agents/sandbox-filesystem.js.map +1 -1
  45. package/dist/agents/sandbox-reattach.js +9 -11
  46. package/dist/agents/sandbox-reattach.js.map +1 -1
  47. package/dist/agents/thread-caveman-state.js +92 -81
  48. package/dist/agents/thread-caveman-state.js.map +1 -1
  49. package/dist/agents/tool-availability.js +62 -80
  50. package/dist/agents/tool-availability.js.map +1 -1
  51. package/dist/agents/tools.js +99 -139
  52. package/dist/agents/tools.js.map +1 -1
  53. package/dist/agents/workspace.js +232 -225
  54. package/dist/agents/workspace.js.map +1 -1
  55. package/dist/analytics.js +139 -128
  56. package/dist/analytics.js.map +1 -1
  57. package/dist/auth/authorization-input.js +39 -25
  58. package/dist/auth/authorization-input.js.map +1 -1
  59. package/dist/auth/device-code.js +112 -80
  60. package/dist/auth/device-code.js.map +1 -1
  61. package/dist/auth/index.js +2 -9
  62. package/dist/auth/pkce.js +27 -16
  63. package/dist/auth/pkce.js.map +1 -1
  64. package/dist/auth/providers/anthropic.js +107 -93
  65. package/dist/auth/providers/anthropic.js.map +1 -1
  66. package/dist/auth/providers/github-copilot.js +304 -293
  67. package/dist/auth/providers/github-copilot.js.map +1 -1
  68. package/dist/auth/providers/openai-codex.js +442 -460
  69. package/dist/auth/providers/openai-codex.js.map +1 -1
  70. package/dist/auth/providers/xai.js +179 -145
  71. package/dist/auth/providers/xai.js.map +1 -1
  72. package/dist/auth/storage.js +182 -175
  73. package/dist/auth/storage.js.map +1 -1
  74. package/dist/auth/types.js +1 -1
  75. package/dist/clipboard/index.js +206 -172
  76. package/dist/clipboard/index.js.map +1 -1
  77. package/dist/constants.js +11 -16
  78. package/dist/constants.js.map +1 -1
  79. package/dist/error-classification.js +19 -11
  80. package/dist/error-classification.js.map +1 -1
  81. package/dist/evals/context-builder.js +145 -104
  82. package/dist/evals/context-builder.js.map +1 -1
  83. package/dist/evals/index.js +4 -7
  84. package/dist/evals/scorers/classify-command.js +62 -41
  85. package/dist/evals/scorers/classify-command.js.map +1 -1
  86. package/dist/evals/scorers/efficiency.js +239 -181
  87. package/dist/evals/scorers/efficiency.js.map +1 -1
  88. package/dist/evals/scorers/extract-tools.js +51 -47
  89. package/dist/evals/scorers/extract-tools.js.map +1 -1
  90. package/dist/evals/scorers/index.js +1 -5
  91. package/dist/evals/scorers/outcome.js +321 -212
  92. package/dist/evals/scorers/outcome.js.map +1 -1
  93. package/dist/goal-manager.js +266 -261
  94. package/dist/goal-manager.js.map +1 -1
  95. package/dist/goal-signal.js +14 -13
  96. package/dist/goal-signal.js.map +1 -1
  97. package/dist/headless/cli.js +153 -154
  98. package/dist/headless/cli.js.map +1 -1
  99. package/dist/headless/flags.js +180 -178
  100. package/dist/headless/flags.js.map +1 -1
  101. package/dist/headless/format.js +72 -71
  102. package/dist/headless/format.js.map +1 -1
  103. package/dist/headless/index.js +5 -31
  104. package/dist/headless/policy.js +35 -31
  105. package/dist/headless/policy.js.map +1 -1
  106. package/dist/headless/run-mc.js +332 -334
  107. package/dist/headless/run-mc.js.map +1 -1
  108. package/dist/headless/types.js +16 -7
  109. package/dist/headless/types.js.map +1 -1
  110. package/dist/hooks/config.js +56 -61
  111. package/dist/hooks/config.js.map +1 -1
  112. package/dist/hooks/executor.js +118 -116
  113. package/dist/hooks/executor.js.map +1 -1
  114. package/dist/hooks/index.js +4 -15
  115. package/dist/hooks/manager.js +221 -280
  116. package/dist/hooks/manager.js.map +1 -1
  117. package/dist/hooks/types.js +5 -4
  118. package/dist/hooks/types.js.map +1 -1
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +632 -712
  121. package/dist/index.js.map +1 -1
  122. package/dist/ipc/ipc-reporter.js +19 -22
  123. package/dist/ipc/ipc-reporter.js.map +1 -1
  124. package/dist/lsp/client.js +248 -306
  125. package/dist/lsp/client.js.map +1 -1
  126. package/dist/lsp/index.js +1 -6
  127. package/dist/lsp/language.js +39 -43
  128. package/dist/lsp/language.js.map +1 -1
  129. package/dist/lsp/manager.js +78 -93
  130. package/dist/lsp/manager.js.map +1 -1
  131. package/dist/lsp/server.js +151 -120
  132. package/dist/lsp/server.js.map +1 -1
  133. package/dist/lsp/workspace.js +50 -47
  134. package/dist/lsp/workspace.js.map +1 -1
  135. package/dist/mcp/config.js +210 -199
  136. package/dist/mcp/config.js.map +1 -1
  137. package/dist/mcp/index.js +2 -9
  138. package/dist/mcp/manager.js +482 -475
  139. package/dist/mcp/manager.js.map +1 -1
  140. package/dist/mcp/types.js +1 -1
  141. package/dist/onboarding/custom-packs.js +14 -17
  142. package/dist/onboarding/custom-packs.js.map +1 -1
  143. package/dist/onboarding/custom-providers.js +22 -20
  144. package/dist/onboarding/custom-providers.js.map +1 -1
  145. package/dist/onboarding/index.d.ts +1 -1
  146. package/dist/onboarding/index.d.ts.map +1 -1
  147. package/dist/onboarding/index.js +3 -21
  148. package/dist/onboarding/om-settings.d.ts +5 -0
  149. package/dist/onboarding/om-settings.d.ts.map +1 -1
  150. package/dist/onboarding/om-settings.js +41 -20
  151. package/dist/onboarding/om-settings.js.map +1 -1
  152. package/dist/onboarding/packs.d.ts +4 -6
  153. package/dist/onboarding/packs.d.ts.map +1 -1
  154. package/dist/onboarding/packs.js +154 -105
  155. package/dist/onboarding/packs.js.map +1 -1
  156. package/dist/onboarding/settings.js +575 -517
  157. package/dist/onboarding/settings.js.map +1 -1
  158. package/dist/permissions.js +112 -99
  159. package/dist/permissions.js.map +1 -1
  160. package/dist/plugin.js +21 -23
  161. package/dist/plugin.js.map +1 -1
  162. package/dist/plugins/dependencies.js +64 -87
  163. package/dist/plugins/dependencies.js.map +1 -1
  164. package/dist/plugins/install.js +159 -169
  165. package/dist/plugins/install.js.map +1 -1
  166. package/dist/plugins/loader.js +170 -185
  167. package/dist/plugins/loader.js.map +1 -1
  168. package/dist/plugins/manager.js +374 -380
  169. package/dist/plugins/manager.js.map +1 -1
  170. package/dist/plugins/manifest.js +38 -63
  171. package/dist/plugins/manifest.js.map +1 -1
  172. package/dist/plugins/package-link.js +48 -64
  173. package/dist/plugins/package-link.js.map +1 -1
  174. package/dist/plugins/paths.js +18 -19
  175. package/dist/plugins/paths.js.map +1 -1
  176. package/dist/plugins/registry.js +97 -81
  177. package/dist/plugins/registry.js.map +1 -1
  178. package/dist/plugins/scaffold.js +62 -98
  179. package/dist/plugins/scaffold.js.map +1 -1
  180. package/dist/plugins/types.js +1 -1
  181. package/dist/processors/plan-rejection-abort.js +45 -44
  182. package/dist/processors/plan-rejection-abort.js.map +1 -1
  183. package/dist/providers/amazon-bedrock-gateway.js +134 -125
  184. package/dist/providers/amazon-bedrock-gateway.js.map +1 -1
  185. package/dist/providers/amazon-bedrock.js +68 -47
  186. package/dist/providers/amazon-bedrock.js.map +1 -1
  187. package/dist/providers/claude-max.js +153 -133
  188. package/dist/providers/claude-max.js.map +1 -1
  189. package/dist/providers/github-copilot.js +232 -230
  190. package/dist/providers/github-copilot.js.map +1 -1
  191. package/dist/providers/openai-codex.js +271 -260
  192. package/dist/providers/openai-codex.js.map +1 -1
  193. package/dist/providers/xai.js +57 -50
  194. package/dist/providers/xai.js.map +1 -1
  195. package/dist/schema.js +91 -89
  196. package/dist/schema.js.map +1 -1
  197. package/dist/theme-palette.js +18 -14
  198. package/dist/theme-palette.js.map +1 -1
  199. package/dist/tool-names.js +46 -37
  200. package/dist/tool-names.js.map +1 -1
  201. package/dist/tools/index.js +2 -8
  202. package/dist/tools/request-sandbox-access.js +71 -80
  203. package/dist/tools/request-sandbox-access.js.map +1 -1
  204. package/dist/tools/utils.js +26 -16
  205. package/dist/tools/utils.js.map +1 -1
  206. package/dist/tools/web-search.js +51 -55
  207. package/dist/tools/web-search.js.map +1 -1
  208. package/dist/utils/binaries.js +65 -61
  209. package/dist/utils/binaries.js.map +1 -1
  210. package/dist/utils/debug-log.js +51 -47
  211. package/dist/utils/debug-log.js.map +1 -1
  212. package/dist/utils/errors.js +157 -192
  213. package/dist/utils/errors.js.map +1 -1
  214. package/dist/utils/gateway-sync.js +49 -37
  215. package/dist/utils/gateway-sync.js.map +1 -1
  216. package/dist/utils/path-security.js +7 -6
  217. package/dist/utils/path-security.js.map +1 -1
  218. package/dist/utils/plan-diff.js +58 -39
  219. package/dist/utils/plan-diff.js.map +1 -1
  220. package/dist/utils/plans.js +92 -74
  221. package/dist/utils/plans.js.map +1 -1
  222. package/dist/utils/project.js +318 -256
  223. package/dist/utils/project.js.map +1 -1
  224. package/dist/utils/signals-pubsub.js +117 -99
  225. package/dist/utils/signals-pubsub.js.map +1 -1
  226. package/dist/utils/slash-command-loader.js +118 -154
  227. package/dist/utils/slash-command-loader.js.map +1 -1
  228. package/dist/utils/slash-command-processor.js +92 -83
  229. package/dist/utils/slash-command-processor.js.map +1 -1
  230. package/dist/utils/stdin-pipe.js +87 -63
  231. package/dist/utils/stdin-pipe.js.map +1 -1
  232. package/dist/utils/storage-factory.js +104 -84
  233. package/dist/utils/storage-factory.js.map +1 -1
  234. package/dist/utils/storage-maintenance.js +249 -211
  235. package/dist/utils/storage-maintenance.js.map +1 -1
  236. package/dist/utils/thread-lock.js +96 -92
  237. package/dist/utils/thread-lock.js.map +1 -1
  238. package/dist/utils/token-estimator.js +12 -12
  239. package/dist/utils/token-estimator.js.map +1 -1
  240. package/dist/utils/update-check.d.ts +1 -1
  241. package/dist/utils/update-check.js +341 -259
  242. package/dist/utils/update-check.js.map +1 -1
  243. package/dist/voice/stt-registry.js +131 -113
  244. package/dist/voice/stt-registry.js.map +1 -1
  245. package/package.json +11 -11
  246. package/dist/acp.js.map +0 -1
  247. package/dist/auth/index.js.map +0 -1
  248. package/dist/auth/types.js.map +0 -1
  249. package/dist/evals/index.js.map +0 -1
  250. package/dist/evals/scorers/index.js.map +0 -1
  251. package/dist/headless/index.js.map +0 -1
  252. package/dist/hooks/index.js.map +0 -1
  253. package/dist/lsp/index.js.map +0 -1
  254. package/dist/mcp/index.js.map +0 -1
  255. package/dist/mcp/types.js.map +0 -1
  256. package/dist/onboarding/index.js.map +0 -1
  257. package/dist/plugins/types.js.map +0 -1
  258. package/dist/tools/index.js.map +0 -1
@@ -1,201 +1,259 @@
1
- import { createScorer, filterRun } from "@mastra/core/evals";
2
1
  import { extractToolCalls } from "./extract-tools.js";
2
+ import { createScorer, filterRun } from "@mastra/core/evals";
3
+ //#region src/evals/scorers/efficiency.ts
4
+ /**
5
+ * Dimension weights (must sum to 1.0).
6
+ *
7
+ * - redundancy: Duplicate mutation tool calls with identical args.
8
+ * - turnCount: Total assistant turns to complete the task.
9
+ * - retryEfficiency: How quickly the agent recovers from tool failures.
10
+ * - readBeforeEdit: Whether files are read before being edited.
11
+ */
3
12
  const WEIGHTS = {
4
- redundancy: 0.35,
5
- turnCount: 0.3,
6
- retryEfficiency: 0.2,
7
- readBeforeEdit: 0.15
13
+ redundancy: .35,
14
+ turnCount: .3,
15
+ retryEfficiency: .2,
16
+ readBeforeEdit: .15
8
17
  };
18
+ /**
19
+ * Scoring thresholds and multipliers.
20
+ */
9
21
  const THRESHOLDS = {
10
- /** Redundancy ratio below this is considered acceptable (no penalty). */
11
- redundancyAcceptableRate: 0.05,
12
- /** Penalty multiplier applied to redundancy ratio above the acceptable rate. */
13
- redundancyPenaltyMultiplier: 3,
14
- /** Turns at or below this is considered normal (score 1.0). */
15
- turnCountNormalMax: 8,
16
- /** Penalty per turn beyond the normal maximum (for turns 9–15). */
17
- turnCountPenaltyPerTurn: 0.04,
18
- /** Penalty per turn beyond 15. */
19
- turnCountExtendedPenaltyPerTurn: 0.03,
20
- /** Number of consecutive failures before a retry chain is "excessive". */
21
- retryExcessiveThreshold: 3,
22
- /** Tools considered "edit" tools that should be preceded by a read. */
23
- editTools: ["string_replace_lsp", "ast_smart_edit"],
24
- /** Tools that satisfy read-before-edit (only `view` truly reads a specific file). */
25
- readTools: ["view"],
26
- /** Tools whose repeated identical calls are intentional (idempotent reads). */
27
- redundancyWhitelist: ["view", "search_content", "find_files", "lsp_inspect", "web_search", "web_extract"],
28
- /** Minimum tool calls required to produce a meaningful score. */
29
- minToolCalls: 2
22
+ /** Redundancy ratio below this is considered acceptable (no penalty). */
23
+ redundancyAcceptableRate: .05,
24
+ /** Penalty multiplier applied to redundancy ratio above the acceptable rate. */
25
+ redundancyPenaltyMultiplier: 3,
26
+ /** Turns at or below this is considered normal (score 1.0). */
27
+ turnCountNormalMax: 8,
28
+ /** Penalty per turn beyond the normal maximum (for turns 9–15). */
29
+ turnCountPenaltyPerTurn: .04,
30
+ /** Penalty per turn beyond 15. */
31
+ turnCountExtendedPenaltyPerTurn: .03,
32
+ /** Number of consecutive failures before a retry chain is "excessive". */
33
+ retryExcessiveThreshold: 3,
34
+ /** Tools considered "edit" tools that should be preceded by a read. */
35
+ editTools: ["string_replace_lsp", "ast_smart_edit"],
36
+ /** Tools that satisfy read-before-edit (only `view` truly reads a specific file). */
37
+ readTools: ["view"],
38
+ /** Tools whose repeated identical calls are intentional (idempotent reads). */
39
+ redundancyWhitelist: [
40
+ "view",
41
+ "search_content",
42
+ "find_files",
43
+ "lsp_inspect",
44
+ "web_search",
45
+ "web_extract"
46
+ ],
47
+ /** Minimum tool calls required to produce a meaningful score. */
48
+ minToolCalls: 2
30
49
  };
50
+ /**
51
+ * Count assistant turns (messages with role === 'assistant').
52
+ */
31
53
  function countAssistantTurns(messages) {
32
- return messages.filter((m) => m.role === "assistant").length;
54
+ return messages.filter((m) => m.role === "assistant").length;
33
55
  }
56
+ /**
57
+ * Calculate redundant call ratio.
58
+ * Only counts mutation/action tools as redundant — reads are whitelisted.
59
+ */
34
60
  function scoreRedundancy(calls) {
35
- if (calls.length < 2) return { score: 1, ratio: 0, detail: "Too few calls" };
36
- const mutationCalls = calls.filter((c) => !THRESHOLDS.redundancyWhitelist.includes(c.toolName));
37
- if (mutationCalls.length < 2) return { score: 1, ratio: 0, detail: "No mutation tools to check" };
38
- const fingerprints = /* @__PURE__ */ new Map();
39
- for (const call of mutationCalls) {
40
- const fp = `${call.toolName}:${JSON.stringify(call.args)}`;
41
- const existing = fingerprints.get(fp) ?? { count: 0, successCount: 0 };
42
- existing.count++;
43
- if (!call.isError) existing.successCount++;
44
- fingerprints.set(fp, existing);
45
- }
46
- let redundantCount = 0;
47
- for (const { successCount } of fingerprints.values()) {
48
- if (successCount > 1) redundantCount += successCount - 1;
49
- }
50
- const ratio = redundantCount / mutationCalls.length;
51
- const acceptable = THRESHOLDS.redundancyAcceptableRate;
52
- const score = ratio <= acceptable ? 1 : Math.max(0, 1 - (ratio - acceptable) * THRESHOLDS.redundancyPenaltyMultiplier);
53
- return {
54
- score,
55
- ratio,
56
- detail: `${redundantCount}/${mutationCalls.length} redundant mutations (${(ratio * 100).toFixed(0)}%)`
57
- };
61
+ if (calls.length < 2) return {
62
+ score: 1,
63
+ ratio: 0,
64
+ detail: "Too few calls"
65
+ };
66
+ const mutationCalls = calls.filter((c) => !THRESHOLDS.redundancyWhitelist.includes(c.toolName));
67
+ if (mutationCalls.length < 2) return {
68
+ score: 1,
69
+ ratio: 0,
70
+ detail: "No mutation tools to check"
71
+ };
72
+ const fingerprints = /* @__PURE__ */ new Map();
73
+ for (const call of mutationCalls) {
74
+ const fp = `${call.toolName}:${JSON.stringify(call.args)}`;
75
+ const existing = fingerprints.get(fp) ?? {
76
+ count: 0,
77
+ successCount: 0
78
+ };
79
+ existing.count++;
80
+ if (!call.isError) existing.successCount++;
81
+ fingerprints.set(fp, existing);
82
+ }
83
+ let redundantCount = 0;
84
+ for (const { successCount } of fingerprints.values()) if (successCount > 1) redundantCount += successCount - 1;
85
+ const ratio = redundantCount / mutationCalls.length;
86
+ const acceptable = THRESHOLDS.redundancyAcceptableRate;
87
+ return {
88
+ score: ratio <= acceptable ? 1 : Math.max(0, 1 - (ratio - acceptable) * THRESHOLDS.redundancyPenaltyMultiplier),
89
+ ratio,
90
+ detail: `${redundantCount}/${mutationCalls.length} redundant mutations (${(ratio * 100).toFixed(0)}%)`
91
+ };
58
92
  }
93
+ /**
94
+ * Score turn count efficiency.
95
+ * Fewer turns to accomplish the task = more efficient.
96
+ */
59
97
  function scoreTurnCount(turns) {
60
- const normalMax = THRESHOLDS.turnCountNormalMax;
61
- const penaltyPerTurn = THRESHOLDS.turnCountPenaltyPerTurn;
62
- const extendedPenalty = THRESHOLDS.turnCountExtendedPenaltyPerTurn;
63
- if (turns <= 1) return { score: 1, detail: `${turns} turn` };
64
- if (turns <= normalMax) return { score: 1, detail: `${turns} turns (within normal range)` };
65
- if (turns <= 15) {
66
- const score2 = 1 - (turns - normalMax) * penaltyPerTurn;
67
- return { score: score2, detail: `${turns} turns (slightly extended)` };
68
- }
69
- const midPenalty = (15 - normalMax) * penaltyPerTurn;
70
- const score = Math.max(0, 1 - midPenalty - (turns - 15) * extendedPenalty);
71
- return { score, detail: `${turns} turns (extended session)` };
98
+ const normalMax = THRESHOLDS.turnCountNormalMax;
99
+ const penaltyPerTurn = THRESHOLDS.turnCountPenaltyPerTurn;
100
+ const extendedPenalty = THRESHOLDS.turnCountExtendedPenaltyPerTurn;
101
+ if (turns <= 1) return {
102
+ score: 1,
103
+ detail: `${turns} turn`
104
+ };
105
+ if (turns <= normalMax) return {
106
+ score: 1,
107
+ detail: `${turns} turns (within normal range)`
108
+ };
109
+ if (turns <= 15) return {
110
+ score: 1 - (turns - normalMax) * penaltyPerTurn,
111
+ detail: `${turns} turns (slightly extended)`
112
+ };
113
+ const midPenalty = (15 - normalMax) * penaltyPerTurn;
114
+ return {
115
+ score: Math.max(0, 1 - midPenalty - (turns - 15) * extendedPenalty),
116
+ detail: `${turns} turns (extended session)`
117
+ };
72
118
  }
119
+ /**
120
+ * Score failed-then-succeeded ratio.
121
+ * Groups retry chains by tool name + target file path for more accurate detection.
122
+ */
73
123
  function scoreRetryEfficiency(calls) {
74
- if (calls.length < 2) return { score: 1, detail: "Too few calls" };
75
- const groupKey = (call) => {
76
- const path = String(call.args.path ?? call.args.command ?? "");
77
- return `${call.toolName}:${path}`;
78
- };
79
- const byGroup = /* @__PURE__ */ new Map();
80
- for (const call of calls) {
81
- const key = groupKey(call);
82
- const arr = byGroup.get(key) ?? [];
83
- arr.push(call);
84
- byGroup.set(key, arr);
85
- }
86
- let totalRetryChains = 0;
87
- let excessiveRetries = 0;
88
- const threshold = THRESHOLDS.retryExcessiveThreshold;
89
- for (const [, toolCalls] of byGroup) {
90
- let consecutiveFailures = 0;
91
- for (const call of toolCalls) {
92
- if (call.isError) {
93
- consecutiveFailures++;
94
- } else {
95
- if (consecutiveFailures > 0) {
96
- totalRetryChains++;
97
- if (consecutiveFailures >= threshold) excessiveRetries++;
98
- }
99
- consecutiveFailures = 0;
100
- }
101
- }
102
- if (consecutiveFailures > 0) {
103
- totalRetryChains++;
104
- if (consecutiveFailures >= threshold) excessiveRetries++;
105
- }
106
- }
107
- if (totalRetryChains === 0) return { score: 1, detail: "No retry chains" };
108
- if (excessiveRetries === 0) return { score: 0.9, detail: `${totalRetryChains} retry chain(s), all resolved quickly` };
109
- const score = Math.max(0, 0.8 - excessiveRetries * 0.2);
110
- return {
111
- score,
112
- detail: `${excessiveRetries} excessive retry chain(s) (${threshold}+ failures before success)`
113
- };
124
+ if (calls.length < 2) return {
125
+ score: 1,
126
+ detail: "Too few calls"
127
+ };
128
+ const groupKey = (call) => {
129
+ const path = String(call.args.path ?? call.args.command ?? "");
130
+ return `${call.toolName}:${path}`;
131
+ };
132
+ const byGroup = /* @__PURE__ */ new Map();
133
+ for (const call of calls) {
134
+ const key = groupKey(call);
135
+ const arr = byGroup.get(key) ?? [];
136
+ arr.push(call);
137
+ byGroup.set(key, arr);
138
+ }
139
+ let totalRetryChains = 0;
140
+ let excessiveRetries = 0;
141
+ const threshold = THRESHOLDS.retryExcessiveThreshold;
142
+ for (const [, toolCalls] of byGroup) {
143
+ let consecutiveFailures = 0;
144
+ for (const call of toolCalls) if (call.isError) consecutiveFailures++;
145
+ else {
146
+ if (consecutiveFailures > 0) {
147
+ totalRetryChains++;
148
+ if (consecutiveFailures >= threshold) excessiveRetries++;
149
+ }
150
+ consecutiveFailures = 0;
151
+ }
152
+ if (consecutiveFailures > 0) {
153
+ totalRetryChains++;
154
+ if (consecutiveFailures >= threshold) excessiveRetries++;
155
+ }
156
+ }
157
+ if (totalRetryChains === 0) return {
158
+ score: 1,
159
+ detail: "No retry chains"
160
+ };
161
+ if (excessiveRetries === 0) return {
162
+ score: .9,
163
+ detail: `${totalRetryChains} retry chain(s), all resolved quickly`
164
+ };
165
+ return {
166
+ score: Math.max(0, .8 - excessiveRetries * .2),
167
+ detail: `${excessiveRetries} excessive retry chain(s) (${threshold}+ failures before success)`
168
+ };
114
169
  }
170
+ /**
171
+ * Score read-before-edit compliance.
172
+ * Only `view` satisfies a read — search/find don't provide specific file content.
173
+ */
115
174
  function scoreReadBeforeEdit(calls) {
116
- const editTools = THRESHOLDS.editTools;
117
- const readTools = THRESHOLDS.readTools;
118
- const edits = calls.filter((c) => editTools.includes(c.toolName));
119
- if (edits.length === 0) return { score: 1, detail: "No edits to check" };
120
- const readPaths = /* @__PURE__ */ new Set();
121
- let compliant = 0;
122
- let total = 0;
123
- for (const call of calls) {
124
- const rawPath = String(call.args.path ?? "");
125
- if (!rawPath) continue;
126
- if (readTools.includes(call.toolName)) {
127
- readPaths.add(rawPath);
128
- } else if (editTools.includes(call.toolName)) {
129
- total++;
130
- if (hasReadPath(readPaths, rawPath)) {
131
- compliant++;
132
- }
133
- }
134
- }
135
- if (total === 0) return { score: 1, detail: "No path-targeted edits" };
136
- const ratio = compliant / total;
137
- const violations = total - compliant;
138
- if (violations === 0) return { score: 1, detail: `All ${total} edits had prior reads` };
139
- return { score: ratio, detail: `${violations}/${total} edits without prior read` };
175
+ const editTools = THRESHOLDS.editTools;
176
+ const readTools = THRESHOLDS.readTools;
177
+ if (calls.filter((c) => editTools.includes(c.toolName)).length === 0) return {
178
+ score: 1,
179
+ detail: "No edits to check"
180
+ };
181
+ const readPaths = /* @__PURE__ */ new Set();
182
+ let compliant = 0;
183
+ let total = 0;
184
+ for (const call of calls) {
185
+ const rawPath = String(call.args.path ?? "");
186
+ if (!rawPath) continue;
187
+ if (readTools.includes(call.toolName)) readPaths.add(rawPath);
188
+ else if (editTools.includes(call.toolName)) {
189
+ total++;
190
+ if (hasReadPath(readPaths, rawPath)) compliant++;
191
+ }
192
+ }
193
+ if (total === 0) return {
194
+ score: 1,
195
+ detail: "No path-targeted edits"
196
+ };
197
+ const ratio = compliant / total;
198
+ const violations = total - compliant;
199
+ if (violations === 0) return {
200
+ score: 1,
201
+ detail: `All ${total} edits had prior reads`
202
+ };
203
+ return {
204
+ score: ratio,
205
+ detail: `${violations}/${total} edits without prior read`
206
+ };
140
207
  }
208
+ /** Check if any read path matches the edit path, using suffix matching for mixed abs/relative paths. */
141
209
  function hasReadPath(readPaths, editPath) {
142
- if (readPaths.has(editPath)) return true;
143
- for (const rp of readPaths) {
144
- if (rp.endsWith(`/${editPath}`) || editPath.endsWith(`/${rp}`)) return true;
145
- }
146
- return false;
210
+ if (readPaths.has(editPath)) return true;
211
+ for (const rp of readPaths) if (rp.endsWith(`/${editPath}`) || editPath.endsWith(`/${rp}`)) return true;
212
+ return false;
147
213
  }
148
214
  function createEfficiencyScorer() {
149
- return createScorer({
150
- id: "mastracode-efficiency",
151
- name: "MastraCode Efficiency",
152
- description: "Measures coding session efficiency: redundancy, turn count, retry patterns, read-before-edit. 30% sampled.",
153
- type: "agent",
154
- prepareRun: filterRun({ partTypes: ["tool-invocation", "text"] })
155
- }).preprocess(async ({ run }) => {
156
- const messages = run.output ?? [];
157
- const calls = extractToolCalls(messages);
158
- const turns = countAssistantTurns(messages);
159
- if (calls.length < THRESHOLDS.minToolCalls) {
160
- return { empty: true, totalCalls: calls.length, totalTurns: turns };
161
- }
162
- const redundancy = scoreRedundancy(calls);
163
- const turnCount = scoreTurnCount(turns);
164
- const retryEfficiency = scoreRetryEfficiency(calls);
165
- const readBeforeEdit = scoreReadBeforeEdit(calls);
166
- return {
167
- empty: false,
168
- redundancy,
169
- turnCount,
170
- retryEfficiency,
171
- readBeforeEdit,
172
- totalCalls: calls.length,
173
- totalTurns: turns
174
- };
175
- }).generateScore(({ results }) => {
176
- const p = results.preprocessStepResult;
177
- if (p.empty) return 0;
178
- const score = p.redundancy.score * WEIGHTS.redundancy + p.turnCount.score * WEIGHTS.turnCount + p.retryEfficiency.score * WEIGHTS.retryEfficiency + p.readBeforeEdit.score * WEIGHTS.readBeforeEdit;
179
- return Math.round(score * 100) / 100;
180
- }).generateReason(({ results, score }) => {
181
- const p = results.preprocessStepResult;
182
- if (p.empty) {
183
- return `Score: 0 (${p.totalCalls} tool calls \u2014 below minimum threshold for scoring)`;
184
- }
185
- const pct = (w) => `${(w * 100).toFixed(0)}%`;
186
- const parts = [`Score: ${score} (${p.totalCalls} calls, ${p.totalTurns} turns)`];
187
- parts.push(`Redundancy (${pct(WEIGHTS.redundancy)}): ${p.redundancy.detail} [${p.redundancy.score}]`);
188
- parts.push(`Turn count (${pct(WEIGHTS.turnCount)}): ${p.turnCount.detail} [${p.turnCount.score}]`);
189
- parts.push(
190
- `Retry efficiency (${pct(WEIGHTS.retryEfficiency)}): ${p.retryEfficiency.detail} [${p.retryEfficiency.score}]`
191
- );
192
- parts.push(
193
- `Read-before-edit (${pct(WEIGHTS.readBeforeEdit)}): ${p.readBeforeEdit.detail} [${p.readBeforeEdit.score}]`
194
- );
195
- return parts.join("\n");
196
- });
215
+ return createScorer({
216
+ id: "mastracode-efficiency",
217
+ name: "MastraCode Efficiency",
218
+ description: "Measures coding session efficiency: redundancy, turn count, retry patterns, read-before-edit. 30% sampled.",
219
+ type: "agent",
220
+ prepareRun: filterRun({ partTypes: ["tool-invocation", "text"] })
221
+ }).preprocess(async ({ run }) => {
222
+ const messages = run.output ?? [];
223
+ const calls = extractToolCalls(messages);
224
+ const turns = countAssistantTurns(messages);
225
+ if (calls.length < THRESHOLDS.minToolCalls) return {
226
+ empty: true,
227
+ totalCalls: calls.length,
228
+ totalTurns: turns
229
+ };
230
+ return {
231
+ empty: false,
232
+ redundancy: scoreRedundancy(calls),
233
+ turnCount: scoreTurnCount(turns),
234
+ retryEfficiency: scoreRetryEfficiency(calls),
235
+ readBeforeEdit: scoreReadBeforeEdit(calls),
236
+ totalCalls: calls.length,
237
+ totalTurns: turns
238
+ };
239
+ }).generateScore(({ results }) => {
240
+ const p = results.preprocessStepResult;
241
+ if (p.empty) return 0;
242
+ const score = p.redundancy.score * WEIGHTS.redundancy + p.turnCount.score * WEIGHTS.turnCount + p.retryEfficiency.score * WEIGHTS.retryEfficiency + p.readBeforeEdit.score * WEIGHTS.readBeforeEdit;
243
+ return Math.round(score * 100) / 100;
244
+ }).generateReason(({ results, score }) => {
245
+ const p = results.preprocessStepResult;
246
+ if (p.empty) return `Score: 0 (${p.totalCalls} tool calls below minimum threshold for scoring)`;
247
+ const pct = (w) => `${(w * 100).toFixed(0)}%`;
248
+ const parts = [`Score: ${score} (${p.totalCalls} calls, ${p.totalTurns} turns)`];
249
+ parts.push(`Redundancy (${pct(WEIGHTS.redundancy)}): ${p.redundancy.detail} [${p.redundancy.score}]`);
250
+ parts.push(`Turn count (${pct(WEIGHTS.turnCount)}): ${p.turnCount.detail} [${p.turnCount.score}]`);
251
+ parts.push(`Retry efficiency (${pct(WEIGHTS.retryEfficiency)}): ${p.retryEfficiency.detail} [${p.retryEfficiency.score}]`);
252
+ parts.push(`Read-before-edit (${pct(WEIGHTS.readBeforeEdit)}): ${p.readBeforeEdit.detail} [${p.readBeforeEdit.score}]`);
253
+ return parts.join("\n");
254
+ });
197
255
  }
198
- export {
199
- createEfficiencyScorer
200
- };
256
+ //#endregion
257
+ export { createEfficiencyScorer };
258
+
201
259
  //# sourceMappingURL=efficiency.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/evals/scorers/efficiency.ts"],"sourcesContent":["/**\n * MastraCode Efficiency Scorer (Code-based, Agent type, 30% sampling)\n *\n * Measures how efficiently the agent completes work through quantitative ratios.\n * Unlike the Outcome Scorer (which checks *what* happened), this checks\n * *how much work* it took.\n *\n * This scorer produces a score from 0-1 but also exposes raw metrics in the\n * reason text for trend analysis. The score represents \"how cleanly did the\n * agent work?\" — not whether it got the right answer.\n *\n * Sampled at 30% because it's slightly more expensive (analyzes message sequences).\n */\n\nimport type { MastraDBMessage } from '@mastra/core/agent';\nimport { createScorer, filterRun } from '@mastra/core/evals';\n\nimport type { ExtractedToolCall } from './extract-tools.js';\nimport { extractToolCalls } from './extract-tools.js';\n\n// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n// CONFIGURATION — Adjust weights and thresholds here\n// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n/**\n * Dimension weights (must sum to 1.0).\n *\n * - redundancy: Duplicate mutation tool calls with identical args.\n * - turnCount: Total assistant turns to complete the task.\n * - retryEfficiency: How quickly the agent recovers from tool failures.\n * - readBeforeEdit: Whether files are read before being edited.\n */\nconst WEIGHTS = {\n redundancy: 0.35,\n turnCount: 0.3,\n retryEfficiency: 0.2,\n readBeforeEdit: 0.15,\n} as const;\n\n/**\n * Scoring thresholds and multipliers.\n */\nconst THRESHOLDS = {\n /** Redundancy ratio below this is considered acceptable (no penalty). */\n redundancyAcceptableRate: 0.05,\n\n /** Penalty multiplier applied to redundancy ratio above the acceptable rate. */\n redundancyPenaltyMultiplier: 3,\n\n /** Turns at or below this is considered normal (score 1.0). */\n turnCountNormalMax: 8,\n\n /** Penalty per turn beyond the normal maximum (for turns 9–15). */\n turnCountPenaltyPerTurn: 0.04,\n\n /** Penalty per turn beyond 15. */\n turnCountExtendedPenaltyPerTurn: 0.03,\n\n /** Number of consecutive failures before a retry chain is \"excessive\". */\n retryExcessiveThreshold: 3,\n\n /** Tools considered \"edit\" tools that should be preceded by a read. */\n editTools: ['string_replace_lsp', 'ast_smart_edit'] as string[],\n\n /** Tools that satisfy read-before-edit (only `view` truly reads a specific file). */\n readTools: ['view'] as string[],\n\n /** Tools whose repeated identical calls are intentional (idempotent reads). */\n redundancyWhitelist: ['view', 'search_content', 'find_files', 'lsp_inspect', 'web_search', 'web_extract'] as string[],\n\n /** Minimum tool calls required to produce a meaningful score. */\n minToolCalls: 2,\n} as const;\n\n/**\n * Count assistant turns (messages with role === 'assistant').\n */\nfunction countAssistantTurns(messages: MastraDBMessage[]): number {\n return messages.filter(m => m.role === 'assistant').length;\n}\n\n/**\n * Calculate redundant call ratio.\n * Only counts mutation/action tools as redundant — reads are whitelisted.\n */\nfunction scoreRedundancy(calls: ExtractedToolCall[]): { score: number; ratio: number; detail: string } {\n if (calls.length < 2) return { score: 1, ratio: 0, detail: 'Too few calls' };\n\n // Only consider non-whitelisted (mutation) tools for redundancy\n const mutationCalls = calls.filter(c => !THRESHOLDS.redundancyWhitelist.includes(c.toolName));\n if (mutationCalls.length < 2) return { score: 1, ratio: 0, detail: 'No mutation tools to check' };\n\n const fingerprints = new Map<string, { count: number; successCount: number }>();\n for (const call of mutationCalls) {\n const fp = `${call.toolName}:${JSON.stringify(call.args)}`;\n const existing = fingerprints.get(fp) ?? { count: 0, successCount: 0 };\n existing.count++;\n if (!call.isError) existing.successCount++;\n fingerprints.set(fp, existing);\n }\n\n // Only count as redundant if the same successful call happened multiple times\n let redundantCount = 0;\n for (const { successCount } of fingerprints.values()) {\n if (successCount > 1) redundantCount += successCount - 1;\n }\n\n const ratio = redundantCount / mutationCalls.length;\n const acceptable = THRESHOLDS.redundancyAcceptableRate;\n const score =\n ratio <= acceptable ? 1 : Math.max(0, 1 - (ratio - acceptable) * THRESHOLDS.redundancyPenaltyMultiplier);\n return {\n score,\n ratio,\n detail: `${redundantCount}/${mutationCalls.length} redundant mutations (${(ratio * 100).toFixed(0)}%)`,\n };\n}\n\n/**\n * Score turn count efficiency.\n * Fewer turns to accomplish the task = more efficient.\n */\nfunction scoreTurnCount(turns: number): { score: number; detail: string } {\n const normalMax = THRESHOLDS.turnCountNormalMax;\n const penaltyPerTurn = THRESHOLDS.turnCountPenaltyPerTurn;\n const extendedPenalty = THRESHOLDS.turnCountExtendedPenaltyPerTurn;\n\n if (turns <= 1) return { score: 1, detail: `${turns} turn` };\n if (turns <= normalMax) return { score: 1, detail: `${turns} turns (within normal range)` };\n if (turns <= 15) {\n const score = 1 - (turns - normalMax) * penaltyPerTurn;\n return { score, detail: `${turns} turns (slightly extended)` };\n }\n\n const midPenalty = (15 - normalMax) * penaltyPerTurn;\n const score = Math.max(0, 1 - midPenalty - (turns - 15) * extendedPenalty);\n return { score, detail: `${turns} turns (extended session)` };\n}\n\n/**\n * Score failed-then-succeeded ratio.\n * Groups retry chains by tool name + target file path for more accurate detection.\n */\nfunction scoreRetryEfficiency(calls: ExtractedToolCall[]): { score: number; detail: string } {\n if (calls.length < 2) return { score: 1, detail: 'Too few calls' };\n\n // Group calls by tool name + target file path (if available)\n const groupKey = (call: ExtractedToolCall): string => {\n const path = String(call.args.path ?? call.args.command ?? '');\n return `${call.toolName}:${path}`;\n };\n\n const byGroup = new Map<string, ExtractedToolCall[]>();\n for (const call of calls) {\n const key = groupKey(call);\n const arr = byGroup.get(key) ?? [];\n arr.push(call);\n byGroup.set(key, arr);\n }\n\n let totalRetryChains = 0;\n let excessiveRetries = 0;\n const threshold = THRESHOLDS.retryExcessiveThreshold;\n\n for (const [, toolCalls] of byGroup) {\n let consecutiveFailures = 0;\n for (const call of toolCalls) {\n if (call.isError) {\n consecutiveFailures++;\n } else {\n if (consecutiveFailures > 0) {\n totalRetryChains++;\n if (consecutiveFailures >= threshold) excessiveRetries++;\n }\n consecutiveFailures = 0;\n }\n }\n // Count unrecovered retry chains (failures at end of group with no success)\n if (consecutiveFailures > 0) {\n totalRetryChains++;\n if (consecutiveFailures >= threshold) excessiveRetries++;\n }\n }\n\n if (totalRetryChains === 0) return { score: 1, detail: 'No retry chains' };\n if (excessiveRetries === 0) return { score: 0.9, detail: `${totalRetryChains} retry chain(s), all resolved quickly` };\n\n const score = Math.max(0, 0.8 - excessiveRetries * 0.2);\n return {\n score,\n detail: `${excessiveRetries} excessive retry chain(s) (${threshold}+ failures before success)`,\n };\n}\n\n/**\n * Score read-before-edit compliance.\n * Only `view` satisfies a read — search/find don't provide specific file content.\n */\nfunction scoreReadBeforeEdit(calls: ExtractedToolCall[]): { score: number; detail: string } {\n const editTools = THRESHOLDS.editTools;\n const readTools = THRESHOLDS.readTools;\n\n const edits = calls.filter(c => editTools.includes(c.toolName));\n if (edits.length === 0) return { score: 1, detail: 'No edits to check' };\n\n // Build set of files that were read before each edit.\n // Use suffix-normalized paths so that \"/abs/path/src/foo.ts\" and \"src/foo.ts\" match.\n const readPaths = new Set<string>();\n let compliant = 0;\n let total = 0;\n\n for (const call of calls) {\n const rawPath = String(call.args.path ?? '');\n if (!rawPath) continue;\n\n if (readTools.includes(call.toolName)) {\n readPaths.add(rawPath);\n } else if (editTools.includes(call.toolName)) {\n total++;\n if (hasReadPath(readPaths, rawPath)) {\n compliant++;\n }\n }\n }\n\n if (total === 0) return { score: 1, detail: 'No path-targeted edits' };\n\n const ratio = compliant / total;\n const violations = total - compliant;\n if (violations === 0) return { score: 1, detail: `All ${total} edits had prior reads` };\n return { score: ratio, detail: `${violations}/${total} edits without prior read` };\n}\n\n/** Check if any read path matches the edit path, using suffix matching for mixed abs/relative paths. */\nfunction hasReadPath(readPaths: Set<string>, editPath: string): boolean {\n if (readPaths.has(editPath)) return true;\n for (const rp of readPaths) {\n if (rp.endsWith(`/${editPath}`) || editPath.endsWith(`/${rp}`)) return true;\n }\n return false;\n}\n\nexport function createEfficiencyScorer() {\n return createScorer({\n id: 'mastracode-efficiency',\n name: 'MastraCode Efficiency',\n description:\n 'Measures coding session efficiency: redundancy, turn count, retry patterns, read-before-edit. 30% sampled.',\n type: 'agent',\n prepareRun: filterRun({ partTypes: ['tool-invocation', 'text'] }),\n })\n .preprocess(async ({ run }) => {\n const messages = (run.output ?? []) as MastraDBMessage[];\n const calls = extractToolCalls(messages);\n const turns = countAssistantTurns(messages);\n\n // H1: Empty sessions → score 0\n if (calls.length < THRESHOLDS.minToolCalls) {\n return { empty: true as const, totalCalls: calls.length, totalTurns: turns };\n }\n\n const redundancy = scoreRedundancy(calls);\n const turnCount = scoreTurnCount(turns);\n const retryEfficiency = scoreRetryEfficiency(calls);\n const readBeforeEdit = scoreReadBeforeEdit(calls);\n\n return {\n empty: false as const,\n redundancy,\n turnCount,\n retryEfficiency,\n readBeforeEdit,\n totalCalls: calls.length,\n totalTurns: turns,\n };\n })\n .generateScore(({ results }) => {\n const p = results.preprocessStepResult;\n if (p.empty) return 0;\n\n const score =\n p.redundancy.score * WEIGHTS.redundancy +\n p.turnCount.score * WEIGHTS.turnCount +\n p.retryEfficiency.score * WEIGHTS.retryEfficiency +\n p.readBeforeEdit.score * WEIGHTS.readBeforeEdit;\n\n return Math.round(score * 100) / 100;\n })\n .generateReason(({ results, score }) => {\n const p = results.preprocessStepResult;\n if (p.empty) {\n return `Score: 0 (${p.totalCalls} tool calls — below minimum threshold for scoring)`;\n }\n\n const pct = (w: number) => `${(w * 100).toFixed(0)}%`;\n const parts: string[] = [`Score: ${score} (${p.totalCalls} calls, ${p.totalTurns} turns)`];\n\n parts.push(`Redundancy (${pct(WEIGHTS.redundancy)}): ${p.redundancy.detail} [${p.redundancy.score}]`);\n parts.push(`Turn count (${pct(WEIGHTS.turnCount)}): ${p.turnCount.detail} [${p.turnCount.score}]`);\n parts.push(\n `Retry efficiency (${pct(WEIGHTS.retryEfficiency)}): ${p.retryEfficiency.detail} [${p.retryEfficiency.score}]`,\n );\n parts.push(\n `Read-before-edit (${pct(WEIGHTS.readBeforeEdit)}): ${p.readBeforeEdit.detail} [${p.readBeforeEdit.score}]`,\n );\n\n return parts.join('\\n');\n });\n}\n"],"mappings":"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","names":["score"]}
1
+ {"version":3,"file":"efficiency.js","names":[],"sources":["../../../src/evals/scorers/efficiency.ts"],"sourcesContent":["/**\n * MastraCode Efficiency Scorer (Code-based, Agent type, 30% sampling)\n *\n * Measures how efficiently the agent completes work through quantitative ratios.\n * Unlike the Outcome Scorer (which checks *what* happened), this checks\n * *how much work* it took.\n *\n * This scorer produces a score from 0-1 but also exposes raw metrics in the\n * reason text for trend analysis. The score represents \"how cleanly did the\n * agent work?\" — not whether it got the right answer.\n *\n * Sampled at 30% because it's slightly more expensive (analyzes message sequences).\n */\n\nimport type { MastraDBMessage } from '@mastra/core/agent';\nimport { createScorer, filterRun } from '@mastra/core/evals';\n\nimport type { ExtractedToolCall } from './extract-tools.js';\nimport { extractToolCalls } from './extract-tools.js';\n\n// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n// CONFIGURATION — Adjust weights and thresholds here\n// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n/**\n * Dimension weights (must sum to 1.0).\n *\n * - redundancy: Duplicate mutation tool calls with identical args.\n * - turnCount: Total assistant turns to complete the task.\n * - retryEfficiency: How quickly the agent recovers from tool failures.\n * - readBeforeEdit: Whether files are read before being edited.\n */\nconst WEIGHTS = {\n redundancy: 0.35,\n turnCount: 0.3,\n retryEfficiency: 0.2,\n readBeforeEdit: 0.15,\n} as const;\n\n/**\n * Scoring thresholds and multipliers.\n */\nconst THRESHOLDS = {\n /** Redundancy ratio below this is considered acceptable (no penalty). */\n redundancyAcceptableRate: 0.05,\n\n /** Penalty multiplier applied to redundancy ratio above the acceptable rate. */\n redundancyPenaltyMultiplier: 3,\n\n /** Turns at or below this is considered normal (score 1.0). */\n turnCountNormalMax: 8,\n\n /** Penalty per turn beyond the normal maximum (for turns 9–15). */\n turnCountPenaltyPerTurn: 0.04,\n\n /** Penalty per turn beyond 15. */\n turnCountExtendedPenaltyPerTurn: 0.03,\n\n /** Number of consecutive failures before a retry chain is \"excessive\". */\n retryExcessiveThreshold: 3,\n\n /** Tools considered \"edit\" tools that should be preceded by a read. */\n editTools: ['string_replace_lsp', 'ast_smart_edit'] as string[],\n\n /** Tools that satisfy read-before-edit (only `view` truly reads a specific file). */\n readTools: ['view'] as string[],\n\n /** Tools whose repeated identical calls are intentional (idempotent reads). */\n redundancyWhitelist: ['view', 'search_content', 'find_files', 'lsp_inspect', 'web_search', 'web_extract'] as string[],\n\n /** Minimum tool calls required to produce a meaningful score. */\n minToolCalls: 2,\n} as const;\n\n/**\n * Count assistant turns (messages with role === 'assistant').\n */\nfunction countAssistantTurns(messages: MastraDBMessage[]): number {\n return messages.filter(m => m.role === 'assistant').length;\n}\n\n/**\n * Calculate redundant call ratio.\n * Only counts mutation/action tools as redundant — reads are whitelisted.\n */\nfunction scoreRedundancy(calls: ExtractedToolCall[]): { score: number; ratio: number; detail: string } {\n if (calls.length < 2) return { score: 1, ratio: 0, detail: 'Too few calls' };\n\n // Only consider non-whitelisted (mutation) tools for redundancy\n const mutationCalls = calls.filter(c => !THRESHOLDS.redundancyWhitelist.includes(c.toolName));\n if (mutationCalls.length < 2) return { score: 1, ratio: 0, detail: 'No mutation tools to check' };\n\n const fingerprints = new Map<string, { count: number; successCount: number }>();\n for (const call of mutationCalls) {\n const fp = `${call.toolName}:${JSON.stringify(call.args)}`;\n const existing = fingerprints.get(fp) ?? { count: 0, successCount: 0 };\n existing.count++;\n if (!call.isError) existing.successCount++;\n fingerprints.set(fp, existing);\n }\n\n // Only count as redundant if the same successful call happened multiple times\n let redundantCount = 0;\n for (const { successCount } of fingerprints.values()) {\n if (successCount > 1) redundantCount += successCount - 1;\n }\n\n const ratio = redundantCount / mutationCalls.length;\n const acceptable = THRESHOLDS.redundancyAcceptableRate;\n const score =\n ratio <= acceptable ? 1 : Math.max(0, 1 - (ratio - acceptable) * THRESHOLDS.redundancyPenaltyMultiplier);\n return {\n score,\n ratio,\n detail: `${redundantCount}/${mutationCalls.length} redundant mutations (${(ratio * 100).toFixed(0)}%)`,\n };\n}\n\n/**\n * Score turn count efficiency.\n * Fewer turns to accomplish the task = more efficient.\n */\nfunction scoreTurnCount(turns: number): { score: number; detail: string } {\n const normalMax = THRESHOLDS.turnCountNormalMax;\n const penaltyPerTurn = THRESHOLDS.turnCountPenaltyPerTurn;\n const extendedPenalty = THRESHOLDS.turnCountExtendedPenaltyPerTurn;\n\n if (turns <= 1) return { score: 1, detail: `${turns} turn` };\n if (turns <= normalMax) return { score: 1, detail: `${turns} turns (within normal range)` };\n if (turns <= 15) {\n const score = 1 - (turns - normalMax) * penaltyPerTurn;\n return { score, detail: `${turns} turns (slightly extended)` };\n }\n\n const midPenalty = (15 - normalMax) * penaltyPerTurn;\n const score = Math.max(0, 1 - midPenalty - (turns - 15) * extendedPenalty);\n return { score, detail: `${turns} turns (extended session)` };\n}\n\n/**\n * Score failed-then-succeeded ratio.\n * Groups retry chains by tool name + target file path for more accurate detection.\n */\nfunction scoreRetryEfficiency(calls: ExtractedToolCall[]): { score: number; detail: string } {\n if (calls.length < 2) return { score: 1, detail: 'Too few calls' };\n\n // Group calls by tool name + target file path (if available)\n const groupKey = (call: ExtractedToolCall): string => {\n const path = String(call.args.path ?? call.args.command ?? '');\n return `${call.toolName}:${path}`;\n };\n\n const byGroup = new Map<string, ExtractedToolCall[]>();\n for (const call of calls) {\n const key = groupKey(call);\n const arr = byGroup.get(key) ?? [];\n arr.push(call);\n byGroup.set(key, arr);\n }\n\n let totalRetryChains = 0;\n let excessiveRetries = 0;\n const threshold = THRESHOLDS.retryExcessiveThreshold;\n\n for (const [, toolCalls] of byGroup) {\n let consecutiveFailures = 0;\n for (const call of toolCalls) {\n if (call.isError) {\n consecutiveFailures++;\n } else {\n if (consecutiveFailures > 0) {\n totalRetryChains++;\n if (consecutiveFailures >= threshold) excessiveRetries++;\n }\n consecutiveFailures = 0;\n }\n }\n // Count unrecovered retry chains (failures at end of group with no success)\n if (consecutiveFailures > 0) {\n totalRetryChains++;\n if (consecutiveFailures >= threshold) excessiveRetries++;\n }\n }\n\n if (totalRetryChains === 0) return { score: 1, detail: 'No retry chains' };\n if (excessiveRetries === 0) return { score: 0.9, detail: `${totalRetryChains} retry chain(s), all resolved quickly` };\n\n const score = Math.max(0, 0.8 - excessiveRetries * 0.2);\n return {\n score,\n detail: `${excessiveRetries} excessive retry chain(s) (${threshold}+ failures before success)`,\n };\n}\n\n/**\n * Score read-before-edit compliance.\n * Only `view` satisfies a read — search/find don't provide specific file content.\n */\nfunction scoreReadBeforeEdit(calls: ExtractedToolCall[]): { score: number; detail: string } {\n const editTools = THRESHOLDS.editTools;\n const readTools = THRESHOLDS.readTools;\n\n const edits = calls.filter(c => editTools.includes(c.toolName));\n if (edits.length === 0) return { score: 1, detail: 'No edits to check' };\n\n // Build set of files that were read before each edit.\n // Use suffix-normalized paths so that \"/abs/path/src/foo.ts\" and \"src/foo.ts\" match.\n const readPaths = new Set<string>();\n let compliant = 0;\n let total = 0;\n\n for (const call of calls) {\n const rawPath = String(call.args.path ?? '');\n if (!rawPath) continue;\n\n if (readTools.includes(call.toolName)) {\n readPaths.add(rawPath);\n } else if (editTools.includes(call.toolName)) {\n total++;\n if (hasReadPath(readPaths, rawPath)) {\n compliant++;\n }\n }\n }\n\n if (total === 0) return { score: 1, detail: 'No path-targeted edits' };\n\n const ratio = compliant / total;\n const violations = total - compliant;\n if (violations === 0) return { score: 1, detail: `All ${total} edits had prior reads` };\n return { score: ratio, detail: `${violations}/${total} edits without prior read` };\n}\n\n/** Check if any read path matches the edit path, using suffix matching for mixed abs/relative paths. */\nfunction hasReadPath(readPaths: Set<string>, editPath: string): boolean {\n if (readPaths.has(editPath)) return true;\n for (const rp of readPaths) {\n if (rp.endsWith(`/${editPath}`) || editPath.endsWith(`/${rp}`)) return true;\n }\n return false;\n}\n\nexport function createEfficiencyScorer() {\n return createScorer({\n id: 'mastracode-efficiency',\n name: 'MastraCode Efficiency',\n description:\n 'Measures coding session efficiency: redundancy, turn count, retry patterns, read-before-edit. 30% sampled.',\n type: 'agent',\n prepareRun: filterRun({ partTypes: ['tool-invocation', 'text'] }),\n })\n .preprocess(async ({ run }) => {\n const messages = (run.output ?? []) as MastraDBMessage[];\n const calls = extractToolCalls(messages);\n const turns = countAssistantTurns(messages);\n\n // H1: Empty sessions → score 0\n if (calls.length < THRESHOLDS.minToolCalls) {\n return { empty: true as const, totalCalls: calls.length, totalTurns: turns };\n }\n\n const redundancy = scoreRedundancy(calls);\n const turnCount = scoreTurnCount(turns);\n const retryEfficiency = scoreRetryEfficiency(calls);\n const readBeforeEdit = scoreReadBeforeEdit(calls);\n\n return {\n empty: false as const,\n redundancy,\n turnCount,\n retryEfficiency,\n readBeforeEdit,\n totalCalls: calls.length,\n totalTurns: turns,\n };\n })\n .generateScore(({ results }) => {\n const p = results.preprocessStepResult;\n if (p.empty) return 0;\n\n const score =\n p.redundancy.score * WEIGHTS.redundancy +\n p.turnCount.score * WEIGHTS.turnCount +\n p.retryEfficiency.score * WEIGHTS.retryEfficiency +\n p.readBeforeEdit.score * WEIGHTS.readBeforeEdit;\n\n return Math.round(score * 100) / 100;\n })\n .generateReason(({ results, score }) => {\n const p = results.preprocessStepResult;\n if (p.empty) {\n return `Score: 0 (${p.totalCalls} tool calls — below minimum threshold for scoring)`;\n }\n\n const pct = (w: number) => `${(w * 100).toFixed(0)}%`;\n const parts: string[] = [`Score: ${score} (${p.totalCalls} calls, ${p.totalTurns} turns)`];\n\n parts.push(`Redundancy (${pct(WEIGHTS.redundancy)}): ${p.redundancy.detail} [${p.redundancy.score}]`);\n parts.push(`Turn count (${pct(WEIGHTS.turnCount)}): ${p.turnCount.detail} [${p.turnCount.score}]`);\n parts.push(\n `Retry efficiency (${pct(WEIGHTS.retryEfficiency)}): ${p.retryEfficiency.detail} [${p.retryEfficiency.score}]`,\n );\n parts.push(\n `Read-before-edit (${pct(WEIGHTS.readBeforeEdit)}): ${p.readBeforeEdit.detail} [${p.readBeforeEdit.score}]`,\n );\n\n return parts.join('\\n');\n });\n}\n"],"mappings":"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"}
@@ -1,56 +1,60 @@
1
+ //#region src/evals/scorers/extract-tools.ts
1
2
  function isToolInvocationPart(part) {
2
- return "type" in part && part.type === "tool-invocation" && "toolInvocation" in part;
3
+ return "type" in part && part.type === "tool-invocation" && "toolInvocation" in part;
3
4
  }
4
5
  function isCompleted(inv) {
5
- return inv.state === "result" || inv.state === "error" || inv.state === "output-error" || inv.state === "output-denied";
6
+ return inv.state === "result" || inv.state === "error" || inv.state === "output-error" || inv.state === "output-denied";
6
7
  }
7
8
  function isErrorState(inv) {
8
- return inv.state === "error" || inv.state === "output-error" || inv.state === "output-denied";
9
+ return inv.state === "error" || inv.state === "output-error" || inv.state === "output-denied";
9
10
  }
11
+ /**
12
+ * Extract completed tool invocations from MastraDBMessages.
13
+ *
14
+ * Handles both:
15
+ * - `content.parts` (primary format, MastraToolInvocationPart)
16
+ * - `content.toolInvocations` (legacy format, AI SDK ToolInvocation[])
17
+ */
10
18
  function extractToolCalls(messages) {
11
- const results = [];
12
- const seenToolCallIds = /* @__PURE__ */ new Set();
13
- let index = 0;
14
- for (const msg of messages) {
15
- if (!msg.content) continue;
16
- for (const part of msg.content.parts ?? []) {
17
- if (isToolInvocationPart(part)) {
18
- const inv = part.toolInvocation;
19
- if (isCompleted(inv)) {
20
- const toolCallId = "toolCallId" in inv ? inv.toolCallId : void 0;
21
- if (toolCallId) seenToolCallIds.add(toolCallId);
22
- results.push({
23
- toolCallId,
24
- toolName: inv.toolName ?? "",
25
- args: inv.args ?? {},
26
- result: inv.result,
27
- isError: isErrorState(inv),
28
- index: index++
29
- });
30
- }
31
- }
32
- }
33
- if (msg.content.toolInvocations) {
34
- for (const inv of msg.content.toolInvocations) {
35
- if (inv.state === "result") {
36
- const toolCallId = "toolCallId" in inv ? inv.toolCallId : void 0;
37
- if (toolCallId && seenToolCallIds.has(toolCallId)) continue;
38
- if (toolCallId) seenToolCallIds.add(toolCallId);
39
- results.push({
40
- toolCallId,
41
- toolName: inv.toolName ?? "",
42
- args: inv.args ?? {},
43
- result: inv.result,
44
- isError: false,
45
- index: index++
46
- });
47
- }
48
- }
49
- }
50
- }
51
- return results;
19
+ const results = [];
20
+ const seenToolCallIds = /* @__PURE__ */ new Set();
21
+ let index = 0;
22
+ for (const msg of messages) {
23
+ if (!msg.content) continue;
24
+ for (const part of msg.content.parts ?? []) if (isToolInvocationPart(part)) {
25
+ const inv = part.toolInvocation;
26
+ if (isCompleted(inv)) {
27
+ const toolCallId = "toolCallId" in inv ? inv.toolCallId : void 0;
28
+ if (toolCallId) seenToolCallIds.add(toolCallId);
29
+ results.push({
30
+ toolCallId,
31
+ toolName: inv.toolName ?? "",
32
+ args: inv.args ?? {},
33
+ result: inv.result,
34
+ isError: isErrorState(inv),
35
+ index: index++
36
+ });
37
+ }
38
+ }
39
+ if (msg.content.toolInvocations) {
40
+ for (const inv of msg.content.toolInvocations) if (inv.state === "result") {
41
+ const toolCallId = "toolCallId" in inv ? inv.toolCallId : void 0;
42
+ if (toolCallId && seenToolCallIds.has(toolCallId)) continue;
43
+ if (toolCallId) seenToolCallIds.add(toolCallId);
44
+ results.push({
45
+ toolCallId,
46
+ toolName: inv.toolName ?? "",
47
+ args: inv.args ?? {},
48
+ result: inv.result,
49
+ isError: false,
50
+ index: index++
51
+ });
52
+ }
53
+ }
54
+ }
55
+ return results;
52
56
  }
53
- export {
54
- extractToolCalls
55
- };
57
+ //#endregion
58
+ export { extractToolCalls };
59
+
56
60
  //# sourceMappingURL=extract-tools.js.map