@aiscene/shared 8.0.3 → 8.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/agent-tools/agent-behavior-init-args.mjs +44 -0
- package/dist/es/agent-tools/base-tools.mjs +163 -0
- package/dist/es/agent-tools/chrome-path.mjs +50 -0
- package/dist/es/agent-tools/cli-report-session.mjs +78 -0
- package/dist/es/agent-tools/error-formatter.mjs +106 -0
- package/dist/es/agent-tools/index.mjs +9 -0
- package/dist/es/agent-tools/init-arg-utils.mjs +38 -0
- package/dist/es/agent-tools/observation-artifact.mjs +5 -0
- package/dist/es/agent-tools/observation-record.mjs +248 -0
- package/dist/es/agent-tools/tool-defaults.mjs +54 -0
- package/dist/es/agent-tools/tool-generator.mjs +529 -0
- package/dist/es/agent-tools/types.mjs +3 -0
- package/dist/es/agent-tools/user-prompt.mjs +66 -0
- package/dist/es/cli/interrupt.mjs +116 -0
- package/dist/es/cli/record-command.mjs +130 -0
- package/dist/es/cli/screenshot-file.mjs +24 -0
- package/dist/es/cli/verbose-ai-act.mjs +230 -0
- package/dist/es/cli/verbose-screenshot.mjs +134 -0
- package/dist/es/cli/verbose.mjs +404 -0
- package/dist/es/env/parse-model-config.mjs +1 -1
- package/dist/es/env/types.mjs +3 -1
- package/dist/es/img/info.mjs +45 -1
- package/dist/es/img/photon-loader.mjs +4 -0
- package/dist/es/recorder.mjs +249 -0
- package/dist/lib/agent-tools/agent-behavior-init-args.js +87 -0
- package/dist/lib/agent-tools/base-tools.js +197 -0
- package/dist/lib/agent-tools/chrome-path.js +87 -0
- package/dist/lib/agent-tools/cli-report-session.js +121 -0
- package/dist/lib/agent-tools/error-formatter.js +149 -0
- package/dist/lib/agent-tools/index.js +114 -0
- package/dist/lib/agent-tools/init-arg-utils.js +78 -0
- package/dist/lib/agent-tools/observation-artifact.js +42 -0
- package/dist/lib/agent-tools/observation-record.js +297 -0
- package/dist/lib/agent-tools/tool-defaults.js +97 -0
- package/dist/lib/agent-tools/tool-generator.js +569 -0
- package/dist/lib/agent-tools/types.js +40 -0
- package/dist/lib/agent-tools/user-prompt.js +103 -0
- package/dist/lib/cli/interrupt.js +156 -0
- package/dist/lib/cli/record-command.js +164 -0
- package/dist/lib/cli/screenshot-file.js +58 -0
- package/dist/lib/cli/verbose-ai-act.js +273 -0
- package/dist/lib/cli/verbose-screenshot.js +177 -0
- package/dist/lib/cli/verbose.js +465 -0
- package/dist/lib/env/parse-model-config.js +1 -1
- package/dist/lib/env/types.js +5 -0
- package/dist/lib/img/info.js +48 -1
- package/dist/lib/img/photon-loader.js +38 -0
- package/dist/lib/recorder.js +307 -0
- package/dist/types/agent-tools/agent-behavior-init-args.d.ts +17 -0
- package/dist/types/agent-tools/base-tools.d.ts +158 -0
- package/dist/types/agent-tools/chrome-path.d.ts +2 -0
- package/dist/types/agent-tools/cli-report-session.d.ts +12 -0
- package/dist/types/agent-tools/error-formatter.d.ts +30 -0
- package/dist/types/agent-tools/index.d.ts +9 -0
- package/dist/types/agent-tools/init-arg-utils.d.ts +13 -0
- package/dist/types/agent-tools/observation-artifact.d.ts +10 -0
- package/dist/types/agent-tools/observation-record.d.ts +38 -0
- package/dist/types/agent-tools/tool-defaults.d.ts +63 -0
- package/dist/types/agent-tools/tool-generator.d.ts +13 -0
- package/dist/types/agent-tools/types.d.ts +213 -0
- package/dist/types/agent-tools/user-prompt.d.ts +13 -0
- package/dist/types/cli/interrupt.d.ts +49 -0
- package/dist/types/cli/record-command.d.ts +3 -0
- package/dist/types/cli/screenshot-file.d.ts +10 -0
- package/dist/types/cli/verbose-ai-act.d.ts +44 -0
- package/dist/types/cli/verbose-screenshot.d.ts +10 -0
- package/dist/types/cli/verbose.d.ts +40 -0
- package/dist/types/env/types.d.ts +4 -3
- package/dist/types/img/info.d.ts +2 -0
- package/dist/types/img/photon-loader.d.ts +2 -0
- package/dist/types/mcp/types.d.ts +1 -0
- package/dist/types/recorder.d.ts +113 -0
- package/package.json +1 -1
- package/src/agent-tools/agent-behavior-init-args.ts +109 -0
- package/src/agent-tools/base-tools.ts +399 -0
- package/src/agent-tools/chrome-path.ts +74 -0
- package/src/agent-tools/cli-report-session.ts +130 -0
- package/src/agent-tools/error-formatter.ts +177 -0
- package/src/agent-tools/index.ts +9 -0
- package/src/agent-tools/init-arg-utils.ts +105 -0
- package/src/agent-tools/observation-artifact.ts +29 -0
- package/src/agent-tools/observation-record.ts +331 -0
- package/src/agent-tools/tool-defaults.ts +119 -0
- package/src/agent-tools/tool-generator.ts +866 -0
- package/src/agent-tools/types.ts +250 -0
- package/src/agent-tools/user-prompt.ts +102 -0
- package/src/cli/interrupt.ts +207 -0
- package/src/cli/record-command.ts +177 -0
- package/src/cli/screenshot-file.ts +61 -0
- package/src/cli/verbose-ai-act.ts +387 -0
- package/src/cli/verbose-screenshot.ts +269 -0
- package/src/cli/verbose.ts +753 -0
- package/src/env/types.ts +2 -0
- package/src/img/index.ts +12 -0
- package/src/img/info.ts +61 -0
- package/src/img/photon-loader.ts +5 -0
- package/src/img/transform.ts +261 -2
- package/src/mcp/types.ts +2 -0
- package/src/recorder.ts +625 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { getMidsceneRunSubDir } from "../common.mjs";
|
|
6
|
+
function _define_property(obj, key, value) {
|
|
7
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
8
|
+
value: value,
|
|
9
|
+
enumerable: true,
|
|
10
|
+
configurable: true,
|
|
11
|
+
writable: true
|
|
12
|
+
});
|
|
13
|
+
else obj[key] = value;
|
|
14
|
+
return obj;
|
|
15
|
+
}
|
|
16
|
+
const observationRecordSchema = z.object({
|
|
17
|
+
type: z.literal('midscene_ui_observation'),
|
|
18
|
+
version: z.literal(1),
|
|
19
|
+
startedAt: z.number().finite().nonnegative(),
|
|
20
|
+
endedAt: z.number().finite().nonnegative(),
|
|
21
|
+
frames: z.array(z.object({
|
|
22
|
+
path: z.string().min(1),
|
|
23
|
+
mimeType: z["enum"]([
|
|
24
|
+
'image/png',
|
|
25
|
+
'image/jpeg'
|
|
26
|
+
]),
|
|
27
|
+
capturedAt: z.number().finite().nonnegative()
|
|
28
|
+
}).strict()).min(1),
|
|
29
|
+
shotSize: z.object({
|
|
30
|
+
width: z.number().finite().positive(),
|
|
31
|
+
height: z.number().finite().positive()
|
|
32
|
+
}).strict(),
|
|
33
|
+
shrunkShotToLogicalRatio: z.number().finite().positive()
|
|
34
|
+
}).strict().refine((record)=>record.endedAt >= record.startedAt, {
|
|
35
|
+
path: [
|
|
36
|
+
'endedAt'
|
|
37
|
+
],
|
|
38
|
+
message: 'must be greater than or equal to startedAt'
|
|
39
|
+
});
|
|
40
|
+
function defaultObservationRecordPath() {
|
|
41
|
+
const suffix = Math.random().toString(36).slice(2, 10);
|
|
42
|
+
return resolve(getMidsceneRunSubDir('output'), `observation-${Date.now()}-${suffix}.json`);
|
|
43
|
+
}
|
|
44
|
+
function parseUIObservationRecord(input) {
|
|
45
|
+
const result = observationRecordSchema.safeParse(input);
|
|
46
|
+
if (!result.success) {
|
|
47
|
+
const issue = result.error.issues[0];
|
|
48
|
+
const location = issue.path.length > 0 ? issue.path.join('.') : 'record';
|
|
49
|
+
throw new Error(`Invalid UI observation record at ${location}: ${issue.message}`);
|
|
50
|
+
}
|
|
51
|
+
return result.data;
|
|
52
|
+
}
|
|
53
|
+
function cloneUIObservationRecord(record) {
|
|
54
|
+
return {
|
|
55
|
+
...record,
|
|
56
|
+
frames: record.frames.map((frame)=>({
|
|
57
|
+
...frame
|
|
58
|
+
})),
|
|
59
|
+
shotSize: {
|
|
60
|
+
...record.shotSize
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function readUIObservationRecord(filePath) {
|
|
65
|
+
const resolvedPath = resolve(filePath);
|
|
66
|
+
let parsed;
|
|
67
|
+
try {
|
|
68
|
+
parsed = JSON.parse(readFileSync(resolvedPath, 'utf8'));
|
|
69
|
+
} catch (error) {
|
|
70
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71
|
+
throw new Error(`Failed to read UI observation record ${resolvedPath}: ${message}`);
|
|
72
|
+
}
|
|
73
|
+
const record = parseUIObservationRecord(parsed);
|
|
74
|
+
const manifestDirectory = dirname(resolvedPath);
|
|
75
|
+
return {
|
|
76
|
+
...record,
|
|
77
|
+
frames: record.frames.map((frame, index)=>{
|
|
78
|
+
if (isAbsolute(frame.path)) throw new Error(`Invalid UI observation record at frames.${index}.path: expected a relative image path`);
|
|
79
|
+
const imagePath = resolve(manifestDirectory, frame.path);
|
|
80
|
+
const relativeToManifest = relative(manifestDirectory, imagePath);
|
|
81
|
+
if ('..' === relativeToManifest || relativeToManifest.startsWith(`..${'win32' === process.platform ? '\\' : '/'}`) || isAbsolute(relativeToManifest)) throw new Error(`Invalid UI observation record at frames.${index}.path: image path escapes the manifest directory`);
|
|
82
|
+
try {
|
|
83
|
+
if (!statSync(imagePath).isFile()) throw new Error('path is not a file');
|
|
84
|
+
} catch (error) {
|
|
85
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
86
|
+
throw new Error(`Invalid UI observation record at frames.${index}.path: cannot read image ${imagePath}: ${message}`);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
...frame,
|
|
90
|
+
path: imagePath
|
|
91
|
+
};
|
|
92
|
+
})
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
class UIObservationRecordWriter {
|
|
96
|
+
persistFrame(dataUrl, capturedAt) {
|
|
97
|
+
if (this.disposed) throw new Error('UI observation record writer has been disposed');
|
|
98
|
+
if (this.finalized) throw new Error('UI observation record has already been finalized');
|
|
99
|
+
const match = /^data:image\/(png|jpe?g);base64,([\s\S]+)$/i.exec(dataUrl);
|
|
100
|
+
if (!match) throw new Error('UI observation frame must be a PNG or JPEG data URL');
|
|
101
|
+
const format = 'png' === match[1].toLowerCase() ? 'png' : 'jpeg';
|
|
102
|
+
const mimeType = 'png' === format ? 'image/png' : 'image/jpeg';
|
|
103
|
+
const bytes = Buffer.from(match[2], 'base64');
|
|
104
|
+
if (0 === bytes.length) throw new Error('UI observation frame contains no image data');
|
|
105
|
+
const digest = createHash('sha256').update(bytes).digest('hex');
|
|
106
|
+
const fileName = `${digest}.${format}`;
|
|
107
|
+
mkdirSync(this.temporaryFramesDirectory, {
|
|
108
|
+
recursive: true
|
|
109
|
+
});
|
|
110
|
+
const temporaryPath = join(this.temporaryFramesDirectory, fileName);
|
|
111
|
+
if (!existsSync(temporaryPath)) writeFileSync(temporaryPath, bytes);
|
|
112
|
+
return {
|
|
113
|
+
path: `${basename(this.framesDirectory)}/${fileName}`,
|
|
114
|
+
mimeType,
|
|
115
|
+
capturedAt
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
resolveFramePath(frame) {
|
|
119
|
+
if (this.disposed) throw new Error('UI observation record writer has been disposed');
|
|
120
|
+
const directory = this.finalized ? this.framesDirectory : this.temporaryFramesDirectory;
|
|
121
|
+
return join(directory, basename(frame.path));
|
|
122
|
+
}
|
|
123
|
+
pruneFrames(frames) {
|
|
124
|
+
if (this.disposed) throw new Error('UI observation record writer has been disposed');
|
|
125
|
+
if (this.finalized || !existsSync(this.temporaryFramesDirectory)) return;
|
|
126
|
+
const retainedFiles = new Set(frames.map((frame)=>basename(frame.path)));
|
|
127
|
+
for (const fileName of readdirSync(this.temporaryFramesDirectory))if (!retainedFiles.has(fileName)) rmSync(join(this.temporaryFramesDirectory, fileName), {
|
|
128
|
+
force: true
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
finalize(frames, metadata) {
|
|
132
|
+
if (this.disposed) throw new Error('UI observation record writer has been disposed');
|
|
133
|
+
if (this.finalizedRecord) return this.finalizedRecord;
|
|
134
|
+
const record = parseUIObservationRecord({
|
|
135
|
+
type: 'midscene_ui_observation',
|
|
136
|
+
version: 1,
|
|
137
|
+
frames,
|
|
138
|
+
...metadata
|
|
139
|
+
});
|
|
140
|
+
this.pruneFrames(record.frames);
|
|
141
|
+
mkdirSync(dirname(this.outputPath), {
|
|
142
|
+
recursive: true
|
|
143
|
+
});
|
|
144
|
+
mkdirSync(this.temporaryFramesDirectory, {
|
|
145
|
+
recursive: true
|
|
146
|
+
});
|
|
147
|
+
rmSync(this.framesDirectory, {
|
|
148
|
+
recursive: true,
|
|
149
|
+
force: true
|
|
150
|
+
});
|
|
151
|
+
renameSync(this.temporaryFramesDirectory, this.framesDirectory);
|
|
152
|
+
this.finalized = true;
|
|
153
|
+
this.finalizedRecord = {
|
|
154
|
+
...record,
|
|
155
|
+
frames: record.frames.map((frame)=>({
|
|
156
|
+
...frame,
|
|
157
|
+
path: resolve(dirname(this.outputPath), frame.path)
|
|
158
|
+
}))
|
|
159
|
+
};
|
|
160
|
+
return this.finalizedRecord;
|
|
161
|
+
}
|
|
162
|
+
dispose() {
|
|
163
|
+
if (this.disposed) return;
|
|
164
|
+
rmSync(this.temporaryFramesDirectory, {
|
|
165
|
+
recursive: true,
|
|
166
|
+
force: true
|
|
167
|
+
});
|
|
168
|
+
rmSync(this.framesDirectory, {
|
|
169
|
+
recursive: true,
|
|
170
|
+
force: true
|
|
171
|
+
});
|
|
172
|
+
this.disposed = true;
|
|
173
|
+
this.finalizedRecord = null;
|
|
174
|
+
}
|
|
175
|
+
constructor(filePath){
|
|
176
|
+
_define_property(this, "outputPath", void 0);
|
|
177
|
+
_define_property(this, "framesDirectory", void 0);
|
|
178
|
+
_define_property(this, "temporaryFramesDirectory", void 0);
|
|
179
|
+
_define_property(this, "finalized", false);
|
|
180
|
+
_define_property(this, "disposed", false);
|
|
181
|
+
_define_property(this, "finalizedRecord", null);
|
|
182
|
+
this.outputPath = filePath ? resolve(filePath) : defaultObservationRecordPath();
|
|
183
|
+
const extension = extname(this.outputPath);
|
|
184
|
+
const stem = basename(this.outputPath, extension);
|
|
185
|
+
this.framesDirectory = join(dirname(this.outputPath), `${stem}.frames`);
|
|
186
|
+
this.temporaryFramesDirectory = `${this.framesDirectory}.tmp-${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function writeUIObservationRecord(record, filePath) {
|
|
190
|
+
const validated = parseUIObservationRecord(record);
|
|
191
|
+
const outputPath = filePath ? resolve(filePath) : defaultObservationRecordPath();
|
|
192
|
+
const extension = extname(outputPath);
|
|
193
|
+
const stem = basename(outputPath, extension);
|
|
194
|
+
const framesDirectory = join(dirname(outputPath), `${stem}.frames`);
|
|
195
|
+
const temporaryFramesDirectory = `${framesDirectory}.tmp-${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
|
|
196
|
+
const temporaryManifest = `${outputPath}.tmp-${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
|
|
197
|
+
const copiedPaths = new Map();
|
|
198
|
+
try {
|
|
199
|
+
mkdirSync(temporaryFramesDirectory, {
|
|
200
|
+
recursive: true
|
|
201
|
+
});
|
|
202
|
+
const frames = validated.frames.map((frame, index)=>{
|
|
203
|
+
const sourcePath = resolve(frame.path);
|
|
204
|
+
try {
|
|
205
|
+
if (!statSync(sourcePath).isFile()) throw new Error('path is not a file');
|
|
206
|
+
} catch (error) {
|
|
207
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
208
|
+
throw new Error(`Invalid UI observation record at frames.${index}.path: cannot read image ${sourcePath}: ${message}`);
|
|
209
|
+
}
|
|
210
|
+
let relativePath = copiedPaths.get(sourcePath);
|
|
211
|
+
if (!relativePath) {
|
|
212
|
+
const imageExtension = 'image/png' === frame.mimeType ? 'png' : 'jpeg';
|
|
213
|
+
const fileName = `${String(copiedPaths.size).padStart(4, '0')}.${imageExtension}`;
|
|
214
|
+
relativePath = `${basename(framesDirectory)}/${fileName}`;
|
|
215
|
+
copyFileSync(sourcePath, join(temporaryFramesDirectory, fileName));
|
|
216
|
+
copiedPaths.set(sourcePath, relativePath);
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
...frame,
|
|
220
|
+
path: relativePath
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
const serializedRecord = parseUIObservationRecord({
|
|
224
|
+
...validated,
|
|
225
|
+
frames
|
|
226
|
+
});
|
|
227
|
+
mkdirSync(dirname(outputPath), {
|
|
228
|
+
recursive: true
|
|
229
|
+
});
|
|
230
|
+
writeFileSync(temporaryManifest, JSON.stringify(serializedRecord, null, 2), 'utf8');
|
|
231
|
+
rmSync(framesDirectory, {
|
|
232
|
+
recursive: true,
|
|
233
|
+
force: true
|
|
234
|
+
});
|
|
235
|
+
renameSync(temporaryFramesDirectory, framesDirectory);
|
|
236
|
+
renameSync(temporaryManifest, outputPath);
|
|
237
|
+
return outputPath;
|
|
238
|
+
} finally{
|
|
239
|
+
rmSync(temporaryFramesDirectory, {
|
|
240
|
+
recursive: true,
|
|
241
|
+
force: true
|
|
242
|
+
});
|
|
243
|
+
rmSync(temporaryManifest, {
|
|
244
|
+
force: true
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
export { UIObservationRecordWriter, cloneUIObservationRecord, defaultObservationRecordPath, parseUIObservationRecord, readUIObservationRecord, writeUIObservationRecord };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const TOOL_BEHAVIOR_FLAGS = [
|
|
2
|
+
{
|
|
3
|
+
cli: 'deep-locate',
|
|
4
|
+
description: 'Force deep locate for every locating operation (better precision for small/ambiguous targets, a bit slower).',
|
|
5
|
+
defaults: {
|
|
6
|
+
locate: {
|
|
7
|
+
deepLocate: true
|
|
8
|
+
},
|
|
9
|
+
act: {
|
|
10
|
+
deepLocate: true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
cli: 'deep-think',
|
|
16
|
+
description: 'Plan the act tool with deep thinking (richer context and sub-goal decomposition, a bit slower).',
|
|
17
|
+
defaults: {
|
|
18
|
+
act: {
|
|
19
|
+
deepThink: true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
];
|
|
24
|
+
function mergeToolDefaults(a, b) {
|
|
25
|
+
const locate = {
|
|
26
|
+
...a.locate,
|
|
27
|
+
...b.locate
|
|
28
|
+
};
|
|
29
|
+
const act = {
|
|
30
|
+
...a.act,
|
|
31
|
+
...b.act
|
|
32
|
+
};
|
|
33
|
+
const result = {};
|
|
34
|
+
if (Object.keys(locate).length > 0) result.locate = locate;
|
|
35
|
+
if (Object.keys(act).length > 0) result.act = act;
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
function resolveToolDefaults(isEnabled) {
|
|
39
|
+
return TOOL_BEHAVIOR_FLAGS.reduce((acc, flag)=>isEnabled(flag.cli) ? mergeToolDefaults(acc, flag.defaults) : acc, {});
|
|
40
|
+
}
|
|
41
|
+
function stripBehaviorFlags(argv) {
|
|
42
|
+
const enabled = new Set();
|
|
43
|
+
const rawArgs = [];
|
|
44
|
+
for (const arg of argv){
|
|
45
|
+
const flag = TOOL_BEHAVIOR_FLAGS.find((f)=>arg === `--${f.cli}`);
|
|
46
|
+
if (flag) enabled.add(flag.cli);
|
|
47
|
+
else rawArgs.push(arg);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
rawArgs,
|
|
51
|
+
toolDefaults: resolveToolDefaults((cli)=>enabled.has(cli))
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export { TOOL_BEHAVIOR_FLAGS, mergeToolDefaults, resolveToolDefaults, stripBehaviorFlags };
|