@midscene/core 1.10.2 → 1.10.3

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 (116) hide show
  1. package/dist/es/agent/agent.mjs +40 -1
  2. package/dist/es/agent/agent.mjs.map +1 -1
  3. package/dist/es/agent/metrics.mjs +81 -0
  4. package/dist/es/agent/metrics.mjs.map +1 -0
  5. package/dist/es/agent/task-builder.mjs +1 -1
  6. package/dist/es/agent/task-builder.mjs.map +1 -1
  7. package/dist/es/agent/utils.mjs +1 -1
  8. package/dist/es/ai-model/connectivity/run-connectivity-test.mjs +9 -3
  9. package/dist/es/ai-model/connectivity/run-connectivity-test.mjs.map +1 -1
  10. package/dist/es/ai-model/inspect.mjs +3 -3
  11. package/dist/es/ai-model/inspect.mjs.map +1 -1
  12. package/dist/es/ai-model/model-adapter/resolve.mjs +2 -2
  13. package/dist/es/ai-model/model-adapter/resolve.mjs.map +1 -1
  14. package/dist/es/ai-model/models/doubao.mjs +38 -60
  15. package/dist/es/ai-model/models/doubao.mjs.map +1 -1
  16. package/dist/es/ai-model/models/glm.mjs +4 -0
  17. package/dist/es/ai-model/models/glm.mjs.map +1 -1
  18. package/dist/es/ai-model/models/gpt.mjs +5 -1
  19. package/dist/es/ai-model/models/gpt.mjs.map +1 -1
  20. package/dist/es/ai-model/models/kimi.mjs +4 -0
  21. package/dist/es/ai-model/models/kimi.mjs.map +1 -1
  22. package/dist/es/ai-model/models/mimo.mjs +3 -2
  23. package/dist/es/ai-model/models/mimo.mjs.map +1 -1
  24. package/dist/es/ai-model/models/qwen.mjs.map +1 -1
  25. package/dist/es/ai-model/models/ui-tars/adapter.mjs +2 -41
  26. package/dist/es/ai-model/models/ui-tars/adapter.mjs.map +1 -1
  27. package/dist/es/ai-model/models/utils/intent.mjs +6 -0
  28. package/dist/es/ai-model/models/utils/intent.mjs.map +1 -0
  29. package/dist/es/ai-model/prompt/extraction.mjs +9 -3
  30. package/dist/es/ai-model/prompt/extraction.mjs.map +1 -1
  31. package/dist/es/ai-model/prompt/recorder-metadata-generator.mjs +2 -1
  32. package/dist/es/ai-model/prompt/recorder-metadata-generator.mjs.map +1 -1
  33. package/dist/es/ai-model/service-caller/index.mjs +40 -19
  34. package/dist/es/ai-model/service-caller/index.mjs.map +1 -1
  35. package/dist/es/ai-model/service-caller/json.mjs +31 -28
  36. package/dist/es/ai-model/service-caller/json.mjs.map +1 -1
  37. package/dist/es/dump/html-utils.mjs +63 -1
  38. package/dist/es/dump/html-utils.mjs.map +1 -1
  39. package/dist/es/dump/screenshot-restoration.mjs +6 -0
  40. package/dist/es/dump/screenshot-restoration.mjs.map +1 -1
  41. package/dist/es/index.mjs.map +1 -1
  42. package/dist/es/report-generator.mjs +28 -1
  43. package/dist/es/report-generator.mjs.map +1 -1
  44. package/dist/es/report-markdown.mjs +300 -35
  45. package/dist/es/report-markdown.mjs.map +1 -1
  46. package/dist/es/report.mjs +29 -1
  47. package/dist/es/report.mjs.map +1 -1
  48. package/dist/es/service/index.mjs +3 -3
  49. package/dist/es/service/index.mjs.map +1 -1
  50. package/dist/es/types.mjs.map +1 -1
  51. package/dist/es/utils.mjs +2 -2
  52. package/dist/lib/agent/agent.js +40 -1
  53. package/dist/lib/agent/agent.js.map +1 -1
  54. package/dist/lib/agent/metrics.js +115 -0
  55. package/dist/lib/agent/metrics.js.map +1 -0
  56. package/dist/lib/agent/task-builder.js +1 -1
  57. package/dist/lib/agent/task-builder.js.map +1 -1
  58. package/dist/lib/agent/utils.js +1 -1
  59. package/dist/lib/ai-model/connectivity/run-connectivity-test.js +9 -3
  60. package/dist/lib/ai-model/connectivity/run-connectivity-test.js.map +1 -1
  61. package/dist/lib/ai-model/inspect.js +3 -3
  62. package/dist/lib/ai-model/inspect.js.map +1 -1
  63. package/dist/lib/ai-model/model-adapter/resolve.js +1 -1
  64. package/dist/lib/ai-model/model-adapter/resolve.js.map +1 -1
  65. package/dist/lib/ai-model/models/doubao.js +38 -69
  66. package/dist/lib/ai-model/models/doubao.js.map +1 -1
  67. package/dist/lib/ai-model/models/glm.js +4 -0
  68. package/dist/lib/ai-model/models/glm.js.map +1 -1
  69. package/dist/lib/ai-model/models/gpt.js +5 -1
  70. package/dist/lib/ai-model/models/gpt.js.map +1 -1
  71. package/dist/lib/ai-model/models/kimi.js +4 -0
  72. package/dist/lib/ai-model/models/kimi.js.map +1 -1
  73. package/dist/lib/ai-model/models/mimo.js +3 -2
  74. package/dist/lib/ai-model/models/mimo.js.map +1 -1
  75. package/dist/lib/ai-model/models/qwen.js.map +1 -1
  76. package/dist/lib/ai-model/models/ui-tars/adapter.js +1 -40
  77. package/dist/lib/ai-model/models/ui-tars/adapter.js.map +1 -1
  78. package/dist/lib/ai-model/models/utils/intent.js +40 -0
  79. package/dist/lib/ai-model/models/utils/intent.js.map +1 -0
  80. package/dist/lib/ai-model/prompt/extraction.js +8 -2
  81. package/dist/lib/ai-model/prompt/extraction.js.map +1 -1
  82. package/dist/lib/ai-model/prompt/recorder-metadata-generator.js +4 -3
  83. package/dist/lib/ai-model/prompt/recorder-metadata-generator.js.map +1 -1
  84. package/dist/lib/ai-model/service-caller/index.js +44 -23
  85. package/dist/lib/ai-model/service-caller/index.js.map +1 -1
  86. package/dist/lib/ai-model/service-caller/json.js +33 -33
  87. package/dist/lib/ai-model/service-caller/json.js.map +1 -1
  88. package/dist/lib/dump/html-utils.js +65 -0
  89. package/dist/lib/dump/html-utils.js.map +1 -1
  90. package/dist/lib/dump/screenshot-restoration.js +6 -0
  91. package/dist/lib/dump/screenshot-restoration.js.map +1 -1
  92. package/dist/lib/index.js.map +1 -1
  93. package/dist/lib/report-generator.js +27 -0
  94. package/dist/lib/report-generator.js.map +1 -1
  95. package/dist/lib/report-markdown.js +300 -35
  96. package/dist/lib/report-markdown.js.map +1 -1
  97. package/dist/lib/report.js +28 -0
  98. package/dist/lib/report.js.map +1 -1
  99. package/dist/lib/service/index.js +3 -3
  100. package/dist/lib/service/index.js.map +1 -1
  101. package/dist/lib/types.js.map +1 -1
  102. package/dist/lib/utils.js +2 -2
  103. package/dist/types/agent/agent.d.ts +15 -0
  104. package/dist/types/agent/index.d.ts +1 -0
  105. package/dist/types/agent/metrics.d.ts +48 -0
  106. package/dist/types/ai-model/model-adapter/types.d.ts +8 -0
  107. package/dist/types/ai-model/models/doubao.d.ts +1 -4
  108. package/dist/types/ai-model/models/utils/intent.d.ts +2 -0
  109. package/dist/types/ai-model/service-caller/index.d.ts +8 -3
  110. package/dist/types/ai-model/service-caller/json.d.ts +19 -2
  111. package/dist/types/dump/html-utils.d.ts +2 -0
  112. package/dist/types/dump/screenshot-restoration.d.ts +1 -1
  113. package/dist/types/index.d.ts +1 -1
  114. package/dist/types/report-generator.d.ts +6 -0
  115. package/dist/types/types.d.ts +11 -2
  116. package/package.json +3 -3
@@ -4,6 +4,7 @@ import { type ActionParam, type ActionReturn, type AgentAssertOpt, type AgentOpt
4
4
  import type { AbstractInterface } from '../device';
5
5
  import type { TaskRunner } from '../task-runner';
6
6
  import { ModelConfigManager } from '@midscene/shared/env';
7
+ import { type MidsceneUsageMetrics } from './metrics';
7
8
  import { type RunGherkinScenarioOptions } from './run-gherkin-scenario';
8
9
  import { TaskCache } from './task-cache';
9
10
  import { TaskExecutor } from './tasks';
@@ -29,6 +30,9 @@ export declare class Agent<InterfaceType extends AbstractInterface = AbstractInt
29
30
  dryMode: boolean;
30
31
  onTaskStartTip?: OnTaskStartTip;
31
32
  taskCache?: TaskCache;
33
+ private readonly metricsCollector;
34
+ private usageCallCounter;
35
+ private readonly countedUsageKeys;
32
36
  private dumpUpdateListeners;
33
37
  private readonly progressBus;
34
38
  get onDumpUpdate(): ((dump: string, executionDump?: ExecutionDump) => void) | undefined;
@@ -80,6 +84,17 @@ export declare class Agent<InterfaceType extends AbstractInterface = AbstractInt
80
84
  setAIActContext(prompt: string): Promise<void>;
81
85
  resetDump(): ReportActionDump;
82
86
  appendExecutionDump(execution: ExecutionDump, runner?: TaskRunner): void;
87
+ /**
88
+ * Fold any not-yet-counted task usage from an execution dump into the
89
+ * instance metrics. Snapshots are re-emitted as tasks progress, so each
90
+ * usage value is keyed by `${taskId}:${field}` and counted at most once.
91
+ */
92
+ private collectUsageMetrics;
93
+ private consumeUsage;
94
+ /**
95
+ * Aggregated LLM usage accumulated by this agent since it was created.
96
+ */
97
+ get metrics(): MidsceneUsageMetrics;
83
98
  dumpDataString(opt?: {
84
99
  inlineScreenshots?: boolean;
85
100
  }): string;
@@ -5,6 +5,7 @@ export { extractInsightParam, locateParamStr, paramStr, taskTitleStr, typeStr, }
5
5
  export { type LocateCache, type PlanningCache, TaskCache } from './task-cache';
6
6
  export { cacheFileExt } from './task-cache';
7
7
  export { TaskExecutor } from './tasks';
8
+ export type { MidsceneUsageMetrics, UsageBucket } from './metrics';
8
9
  export type { GherkinStepKeyword, RunGherkinScenarioOptions, } from './run-gherkin-scenario';
9
10
  export type { AgentOpt } from '../types';
10
11
  export type { RecordToReportOptions, RecordToReportScreenshot } from '../types';
@@ -0,0 +1,48 @@
1
+ import type { AIUsageInfo } from '../types';
2
+ /**
3
+ * Aggregated usage for a single grouping key (intent or model).
4
+ */
5
+ export interface UsageBucket {
6
+ promptTokens: number;
7
+ completionTokens: number;
8
+ totalTokens: number;
9
+ calls: number;
10
+ }
11
+ /**
12
+ * Instance-level snapshot of LLM usage accumulated by an Agent.
13
+ *
14
+ * Designed for cost observability: read it back after a logical unit of work
15
+ * and push to tools like Langfuse. `byIntent` / `byModel` provide free
16
+ * breakdowns derived from the usage data Midscene already records per call.
17
+ */
18
+ export interface MidsceneUsageMetrics {
19
+ totalPromptTokens: number;
20
+ totalCompletionTokens: number;
21
+ totalTokens: number;
22
+ totalCachedInput: number;
23
+ totalTimeCostMs: number;
24
+ calls: number;
25
+ byIntent: Record<string, UsageBucket>;
26
+ byModel: Record<string, UsageBucket>;
27
+ }
28
+ /**
29
+ * Pure accumulator for {@link AIUsageInfo}. Deduplication of calls is the
30
+ * caller's responsibility; every `add` counts as one call.
31
+ */
32
+ export declare class MetricsCollector {
33
+ private totalPromptTokens;
34
+ private totalCompletionTokens;
35
+ private totalTokens;
36
+ private totalCachedInput;
37
+ private totalTimeCostMs;
38
+ private calls;
39
+ private byIntent;
40
+ private byModel;
41
+ add(usage: AIUsageInfo): void;
42
+ snapshot(): MidsceneUsageMetrics;
43
+ /**
44
+ * Clear all accumulated state. Not wired to a public Agent API yet; kept for
45
+ * a future per-unit reset (e.g. resetting metrics at the start of a spec).
46
+ */
47
+ reset(): void;
48
+ }
@@ -1,4 +1,5 @@
1
1
  import type { PixelBbox, PlanningAction } from '../../types';
2
+ import type { AIUsageInfo } from '../../types';
2
3
  import type { IModelConfig, TIntent, TModelReasoningEnabled } from '@midscene/shared/env';
3
4
  import type OpenAI from 'openai';
4
5
  import type { JsonParser, JsonParserContext, JsonParserSource } from '../service-caller/json';
@@ -152,6 +153,13 @@ export interface ModelAdapter {
152
153
  export interface ModelRuntime {
153
154
  config: IModelConfig;
154
155
  adapter: ModelAdapter;
156
+ /**
157
+ * Optional callback fired after every underlying model call with the shaped
158
+ * usage info. Provides a single collection point for callers that want to
159
+ * aggregate usage across all model invocations (including auxiliary calls
160
+ * such as order-sensitive judging and deep-locate search-area calls).
161
+ */
162
+ onUsage?: (usage: AIUsageInfo) => void;
155
163
  }
156
164
  export interface ModelAdapterDefinition {
157
165
  jsonParser?: JsonParserPreset | JsonParser;
@@ -1,8 +1,5 @@
1
- import type { JsonParserContext, JsonParserSource, ModelAdapterDefinition } from '../model-adapter/types';
1
+ import type { ModelAdapterDefinition } from '../model-adapter/types';
2
2
  import { type LocateResultValue } from '../shared/model-locate-result';
3
- export declare function normalizeDoubaoJsonObject(obj: any, context?: Pick<JsonParserContext, 'preserveStringValueKeys'>): any;
4
- export declare function shouldRepairDoubaoLocateJson(source: JsonParserSource): source is "planning-action-param" | "locate" | "section-locator";
5
- export declare function preprocessDoubaoLocateJson(input: string): string;
6
3
  export declare function parseDoubaoRawLocateValue(input: unknown): LocateResultValue;
7
4
  export declare const doubaoAdapters: {
8
5
  'doubao-vision': ModelAdapterDefinition;
@@ -0,0 +1,2 @@
1
+ import type { TIntent } from '@midscene/shared/env';
2
+ export declare function isLocateIntent(intent?: TIntent): boolean;
@@ -13,12 +13,17 @@ export declare class AIResponseParseError extends Error {
13
13
  import { type IModelConfig, type TModelFamily } from '@midscene/shared/env';
14
14
  import OpenAI from 'openai';
15
15
  import type { ChatCompletionMessageParam } from 'openai/resources/index';
16
- import { type ModelRuntime } from '../models';
16
+ import type { ModelRuntime } from '../models';
17
17
  import type { AIArgs } from '../types';
18
18
  import type { JsonParserSource } from './json';
19
19
  import { type OpenAIErrorResponseContext } from './openai-error';
20
- export { extractJSONFromCodeBlock, normalJsonParser, safeParseJson, } from './json';
20
+ export { extractJSONFromCodeBlock, parseModelResponseJson, } from './json';
21
21
  export type { JsonParser } from './json';
22
+ /**
23
+ * Internal field name stamped onto every AIUsageInfo shaped by callAI().
24
+ * Used for cross-path dedup when the provider does not return a request_id.
25
+ */
26
+ export declare const INTERNAL_CALL_ID_FIELD = "_midscene_call_id";
22
27
  export declare function createChatClient({ modelConfig, }: {
23
28
  modelConfig: IModelConfig;
24
29
  }): Promise<{
@@ -41,7 +46,7 @@ export declare function callAI(messages: ChatCompletionMessageParam[], modelRunt
41
46
  usage?: AIUsageInfo;
42
47
  isStreamed: boolean;
43
48
  }>;
44
- export declare function callAIWithObjectResponse<T>(messages: ChatCompletionMessageParam[], model: IModelConfig | ModelRuntime, options?: {
49
+ export declare function callAIWithObjectResponse<T>(messages: ChatCompletionMessageParam[], modelRuntime: ModelRuntime, options?: {
45
50
  abortSignal?: AbortSignal;
46
51
  jsonParserSource?: JsonParserSource;
47
52
  }): Promise<{
@@ -1,9 +1,26 @@
1
+ /**
2
+ * Extract the JSON portion from a model response.
3
+ *
4
+ * This mainly handles fenced JSON like ```json { ... } ``` by removing the
5
+ * fence. If that does not produce a JSON object, it falls back to a greedy regex
6
+ * that extracts from the first "{" to the last "}". If that still fails, the
7
+ * original response is returned.
8
+ *
9
+ * Expected model responses are JSON objects, possibly wrapped in markdown
10
+ * fences. Natural language mixed with JSON, or arrays like
11
+ * [{"type":"Tap"}, {"type":"Hover"}], are outside the supported contract and
12
+ * are not reliably recoverable.
13
+ *
14
+ * This legacy extractor is also used by extraction responses that can be any
15
+ * JSON value. In those cases, arrays/strings/numbers have no object braces and
16
+ * pass through unchanged.
17
+ */
1
18
  export declare function extractJSONFromCodeBlock(response: string): string;
2
19
  export type JsonParserSource = 'generic-object' | 'planning-action-param' | 'locate' | 'section-locator';
3
20
  export interface JsonParserContext {
4
21
  source: JsonParserSource;
5
22
  preserveStringValueKeys?: string[];
23
+ requireObject?: boolean;
6
24
  }
7
25
  export type JsonParser = (raw: string, context?: JsonParserContext) => unknown;
8
- export declare function safeParseJson(raw: string, context?: JsonParserContext): any;
9
- export declare const normalJsonParser: JsonParser;
26
+ export declare function parseModelResponseJson(raw: string, context?: JsonParserContext): any;
@@ -1,5 +1,7 @@
1
+ import type { IReportActionDump } from '../types';
1
2
  export declare const escapeContent: (html: string) => string;
2
3
  export declare const unescapeContent: (html: string) => string;
4
+ export declare function generateAgentReportComment(report: IReportActionDump): string;
3
5
  /** Chunk size for streaming file operations (64KB) */
4
6
  export declare const STREAMING_CHUNK_SIZE: number;
5
7
  /**
@@ -2,7 +2,7 @@ import { type ScreenshotRef } from './screenshot-store';
2
2
  /**
3
3
  * Recursively restore image references in parsed data.
4
4
  * Replaces ScreenshotRef with lazy
5
- * { get base64() {...}, capturedAt } objects.
5
+ * { get base64() {...}, capturedAt, sourceRef } objects.
6
6
  * The resolver is only called when .base64 is first accessed.
7
7
  */
8
8
  export declare function restoreImageReferences<T>(data: T, resolveImage: (ref: ScreenshotRef) => string): T;
@@ -10,7 +10,7 @@ export { z };
10
10
  export default Service;
11
11
  export { TaskRunner, Service, getVersion };
12
12
  export type { MidsceneYamlScript, MidsceneYamlTask, MidsceneYamlFlowItem, MidsceneYamlConfigResult, MidsceneYamlConfig, MidsceneYamlScriptWebEnv, MidsceneYamlScriptAndroidEnv, MidsceneYamlScriptIOSEnv, MidsceneYamlScriptEnv, LocateOption, DetailedLocateParam, } from './yaml';
13
- export { Agent, type AgentOpt, type AiActOptions, type GherkinStepKeyword, type RunGherkinScenarioOptions, createAgent, } from './agent';
13
+ export { Agent, type AgentOpt, type AiActOptions, type GherkinStepKeyword, type MidsceneUsageMetrics, type RunGherkinScenarioOptions, type UsageBucket, createAgent, } from './agent';
14
14
  export { describeElementAtPoint, verifyElementDescriptionAtPoint, verifyLocator, type DescribeElementAtPointOptions, type DescribeElementCoordinateSpace, type ElementDescriberRuntime, type VerifyElementDescriptionAtPointOptions, } from './element-describer';
15
15
  export { restoreImageReferences, escapeContent, unescapeContent, parseImageScripts, parseDumpScript, parseDumpScriptAttributes, generateImageScriptTag, generateDumpScriptTag, deriveTaskStatus, deriveCaseStatus, } from './dump';
16
16
  export type { TaskStatusFields, DerivedTaskStatus } from './dump';
@@ -41,7 +41,11 @@ export declare class ReportGenerator implements IReportGenerator {
41
41
  private initialized;
42
42
  private lastExecution?;
43
43
  private lastReportMeta?;
44
+ private executionsByKey;
45
+ private executionCommentKeyByObject;
46
+ private executionCommentKeyIndex;
44
47
  private reportAttributes;
48
+ private agentCommentWritten;
45
49
  private writeQueue;
46
50
  private destroyed;
47
51
  constructor(options: {
@@ -83,6 +87,8 @@ export declare class ReportGenerator implements IReportGenerator {
83
87
  */
84
88
  private writeInlineExecution;
85
89
  private writeDirectoryExecution;
90
+ private appendAgentReportComment;
86
91
  private getExecutionLogKey;
92
+ private getExecutionCommentKey;
87
93
  private persistExecutionDumpToFile;
88
94
  }
@@ -131,12 +131,12 @@ export type ThinkingLevel = 'off' | 'medium' | 'high';
131
131
  export type DeepThinkOption = 'unset' | true | false;
132
132
  export interface ServiceTaskInfo {
133
133
  durationMs: number;
134
- formatResponse?: string;
134
+ formatResponse?: unknown;
135
135
  /**
136
136
  * Adapter-extracted content used by Midscene for parsing. This is not the
137
137
  * full provider response or choices[0].message.
138
138
  */
139
- rawResponse?: string;
139
+ rawResponse?: unknown;
140
140
  rawChoiceMessage?: unknown;
141
141
  usage?: AIUsageInfo;
142
142
  searchArea?: Rect;
@@ -704,6 +704,15 @@ export interface AgentOpt {
704
704
  * ```
705
705
  */
706
706
  createOpenAIClient?: CreateOpenAIClientFn;
707
+ /**
708
+ * Called once per LLM call as soon as its usage is available, with the raw
709
+ * {@link AIUsageInfo} (token counts, model name, intent, request id, etc.).
710
+ *
711
+ * Use this for real-time, per-spec cost observability — e.g. push each call
712
+ * to Langfuse without waiting for the run to finish. For aggregated totals,
713
+ * read `agent.metrics` instead.
714
+ */
715
+ onLLMUsage?: (usage: AIUsageInfo) => void;
707
716
  }
708
717
  export type TestStatus = 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';
709
718
  export interface ReportFileAttributes {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@midscene/core",
3
3
  "description": "Automate browser actions, extract data, and perform assertions using AI. It offers JavaScript SDK, Chrome extension, and support for scripting in YAML. See https://midscenejs.com/ for details.",
4
- "version": "1.10.2",
4
+ "version": "1.10.3",
5
5
  "repository": "https://github.com/web-infra-dev/midscene",
6
6
  "homepage": "https://midscenejs.com/",
7
7
  "main": "./dist/lib/index.js",
@@ -100,14 +100,14 @@
100
100
  "dotenv": "^16.4.5",
101
101
  "fetch-socks": "^1.3.0",
102
102
  "js-yaml": "4.1.0",
103
- "jsonrepair": "3.12.0",
103
+ "jsonrepair": "3.14.0",
104
104
  "mdast-util-from-markdown": "^2.0.2",
105
105
  "mdast-util-to-markdown": "^2.1.2",
106
106
  "openai": "6.3.0",
107
107
  "semver": "7.5.2",
108
108
  "undici": "^6.0.0",
109
109
  "zod": "^3.25.1",
110
- "@midscene/shared": "1.10.2"
110
+ "@midscene/shared": "1.10.3"
111
111
  },
112
112
  "devDependencies": {
113
113
  "@rslib/core": "^0.18.3",