@modelprofile.com/flexharness 10.2.1 → 10.4.0
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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +1 -1
- package/dist_ts/index.js +2 -2
- package/dist_ts/utils.slashcommands.js +3 -3
- package/dist_ts_browser/classes.flexprovider.d.ts +9 -0
- package/dist_ts_browser/classes.flexprovider.js +135 -0
- package/dist_ts_browser/index.d.ts +3 -0
- package/dist_ts_browser/index.js +3 -0
- package/dist_ts_browser/interfaces.d.ts +8 -0
- package/dist_ts_browser/interfaces.js +2 -0
- package/dist_ts_browser/plugins.d.ts +7 -0
- package/dist_ts_browser/plugins.js +5 -0
- package/dist_ts_browser/tool.context.d.ts +2 -0
- package/dist_ts_browser/tool.context.js +61 -0
- package/package.json +13 -3
- package/readme.md +9 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +2 -0
- package/ts/readme.md +9 -2
- package/ts/utils.slashcommands.ts +2 -2
- package/ts_browser/classes.flexprovider.ts +140 -0
- package/ts_browser/index.ts +6 -0
- package/ts_browser/interfaces.ts +12 -0
- package/ts_browser/plugins.ts +23 -0
- package/ts_browser/readme.md +32 -0
- package/ts_browser/tool.context.ts +67 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@modelprofile.com/flexharness',
|
|
6
|
-
version: '10.
|
|
6
|
+
version: '10.4.0',
|
|
7
7
|
description: 'The FlexHarness toolbox source repository and shared dependency catalog.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSwrQkFBK0I7SUFDckMsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLDBFQUEwRTtDQUN4RixDQUFBIn0=
|
package/dist_ts/index.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export * from './errors.js';
|
|
|
4
4
|
export * from './interfaces.js';
|
|
5
5
|
export { normalizeJsonValue } from './utils.json.js';
|
|
6
6
|
export { assertFlexProjectManagementSnapshot, assertFlexProjectManagementRecord, assertFlexProjectManagementTombstone, createEmptyFlexProjectManagementSnapshot, } from './utils.projectmanagement.js';
|
|
7
|
-
export { FLEX_SLASH_COMMAND_MAX_INPUT_BYTES, parseSlashCommand, } from './utils.slashcommands.js';
|
|
7
|
+
export { FLEX_SLASH_COMMAND_MAX_INPUT_BYTES, isReservedSlashCommandName, isValidSlashCommandName, parseSlashCommand, } from './utils.slashcommands.js';
|
package/dist_ts/index.js
CHANGED
|
@@ -4,5 +4,5 @@ export * from './errors.js';
|
|
|
4
4
|
export * from './interfaces.js';
|
|
5
5
|
export { normalizeJsonValue } from './utils.json.js';
|
|
6
6
|
export { assertFlexProjectManagementSnapshot, assertFlexProjectManagementRecord, assertFlexProjectManagementTombstone, createEmptyFlexProjectManagementSnapshot, } from './utils.projectmanagement.js';
|
|
7
|
-
export { FLEX_SLASH_COMMAND_MAX_INPUT_BYTES, parseSlashCommand, } from './utils.slashcommands.js';
|
|
8
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
7
|
+
export { FLEX_SLASH_COMMAND_MAX_INPUT_BYTES, isReservedSlashCommandName, isValidSlashCommandName, parseSlashCommand, } from './utils.slashcommands.js';
|
|
8
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDBCQUEwQixDQUFDO0FBQ3pDLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxpQkFBaUIsQ0FBQztBQUNoQyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUNyRCxPQUFPLEVBQ0wsbUNBQW1DLEVBQ25DLGlDQUFpQyxFQUNqQyxvQ0FBb0MsRUFDcEMsd0NBQXdDLEdBQ3pDLE1BQU0sOEJBQThCLENBQUM7QUFDdEMsT0FBTyxFQUNMLGtDQUFrQyxFQUNsQywwQkFBMEIsRUFDMUIsdUJBQXVCLEVBQ3ZCLGlCQUFpQixHQUNsQixNQUFNLDBCQUEwQixDQUFDIn0=
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FlexHarnessValidationError } from './errors.js';
|
|
2
2
|
export const FLEX_SLASH_COMMAND_MAX_INPUT_BYTES = 768 * 1024;
|
|
3
|
-
const slashCommandNamePattern = /^[a-z][a-z0-
|
|
4
|
-
const slashCommandPattern = /^\/([a-z][a-z0-
|
|
3
|
+
const slashCommandNamePattern = /^[a-z][a-z0-9_-]{0,63}$/u;
|
|
4
|
+
const slashCommandPattern = /^\/([a-z][a-z0-9_-]{0,63})(?:$|(\s+)([\s\S]*))$/u;
|
|
5
5
|
const slashCommandArgumentsPattern = /(?:\[Image\s+\d+\]|"[^"]*"|'[^']*'|[^\s"']+)/gi;
|
|
6
6
|
const slashCommandQuoteTrimPattern = /^["']|["']$/g;
|
|
7
7
|
const slashCommandPlaceholderPattern = /\$(\d+)/g;
|
|
@@ -165,4 +165,4 @@ export function expandSlashCommandTemplate(template, rawArguments, arguments_) {
|
|
|
165
165
|
}
|
|
166
166
|
return expanded;
|
|
167
167
|
}
|
|
168
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
168
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuc2xhc2hjb21tYW5kcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3V0aWxzLnNsYXNoY29tbWFuZHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLDBCQUEwQixFQUFFLE1BQU0sYUFBYSxDQUFDO0FBTXpELE1BQU0sQ0FBQyxNQUFNLGtDQUFrQyxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUM7QUFFN0QsTUFBTSx1QkFBdUIsR0FBRywwQkFBMEIsQ0FBQztBQUMzRCxNQUFNLG1CQUFtQixHQUFHLGtEQUFrRCxDQUFDO0FBQy9FLE1BQU0sNEJBQTRCLEdBQUcsZ0RBQWdELENBQUM7QUFDdEYsTUFBTSw0QkFBNEIsR0FBRyxjQUFjLENBQUM7QUFDcEQsTUFBTSw4QkFBOEIsR0FBRyxVQUFVLENBQUM7QUFDbEQsTUFBTSxpQ0FBaUMsR0FBRyxzQkFBc0IsQ0FBQztBQUNqRSxNQUFNLHlCQUF5QixHQUFHLElBQUksR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztBQUUvRSxNQUFNLENBQUMsTUFBTSxzQ0FBc0MsR0FBRzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0NBa0VyRCxDQUFDO0FBRUYsTUFBTSxVQUFVLHVCQUF1QixDQUFDLElBQVk7SUFDbEQsT0FBTyx1QkFBdUIsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7QUFDNUMsQ0FBQztBQUVELE1BQU0sVUFBVSwwQkFBMEIsQ0FBQyxJQUFZO0lBQ3JELE9BQU8seUJBQXlCLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQzdDLENBQUM7QUFFRCxTQUFTLDZCQUE2QixDQUFDLFlBQW9CO0lBQ3pELE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxLQUFLLENBQUMsNEJBQTRCLENBQUMsSUFBSSxF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
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { IBrowserRuntimeFlexToolProviderOptions } from './interfaces.js';
|
|
3
|
+
export declare class BrowserRuntimeFlexToolProvider<TScope> implements plugins.IFlexToolProvider<TScope> {
|
|
4
|
+
private readonly client;
|
|
5
|
+
private readonly resolveCapability;
|
|
6
|
+
private state;
|
|
7
|
+
constructor(options: IBrowserRuntimeFlexToolProviderOptions<TScope>);
|
|
8
|
+
provideTools(context: plugins.IFlexToolProviderContext<TScope>): Promise<plugins.IFlexToolHandle>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { createFramedToolBrowserContext } from './tool.context.js';
|
|
3
|
+
const browserActions = [
|
|
4
|
+
'navigate',
|
|
5
|
+
'snapshot',
|
|
6
|
+
'screenshot',
|
|
7
|
+
'click',
|
|
8
|
+
'fill',
|
|
9
|
+
'press',
|
|
10
|
+
];
|
|
11
|
+
const boundedIdentifier = (minimum, maximum) => plugins.z.string().trim().min(minimum).max(maximum);
|
|
12
|
+
const runBindingSchema = plugins.z.object({
|
|
13
|
+
scopeId: boundedIdentifier(1, 128),
|
|
14
|
+
sessionId: boundedIdentifier(1, 128),
|
|
15
|
+
runId: boundedIdentifier(1, 256),
|
|
16
|
+
});
|
|
17
|
+
const capabilitySchema = plugins.z.strictObject({
|
|
18
|
+
capabilityToken: boundedIdentifier(16, 512),
|
|
19
|
+
});
|
|
20
|
+
export class BrowserRuntimeFlexToolProvider {
|
|
21
|
+
client;
|
|
22
|
+
resolveCapability;
|
|
23
|
+
state = 'unused';
|
|
24
|
+
constructor(options) {
|
|
25
|
+
if (!options || !options.client || typeof options.resolveCapability !== 'function') {
|
|
26
|
+
throw new plugins.BrowserRuntimeError('INVALID_INPUT');
|
|
27
|
+
}
|
|
28
|
+
this.client = options.client;
|
|
29
|
+
this.resolveCapability = options.resolveCapability;
|
|
30
|
+
}
|
|
31
|
+
async provideTools(context) {
|
|
32
|
+
if (this.state !== 'unused')
|
|
33
|
+
throw new plugins.BrowserRuntimeError('BUSY');
|
|
34
|
+
this.state = 'provisioning';
|
|
35
|
+
try {
|
|
36
|
+
const binding = runBindingSchema.safeParse(context);
|
|
37
|
+
if (!binding.success)
|
|
38
|
+
throw new plugins.BrowserRuntimeError('INVALID_INPUT');
|
|
39
|
+
if (this.client.scopeId !== binding.data.scopeId
|
|
40
|
+
|| this.client.sessionId.harnessId !== 'flex'
|
|
41
|
+
|| this.client.sessionId.nativeId !== binding.data.sessionId
|
|
42
|
+
|| this.client.runId !== binding.data.runId)
|
|
43
|
+
throw new plugins.BrowserRuntimeError('CAPABILITY_INVALID');
|
|
44
|
+
context.signal.throwIfAborted();
|
|
45
|
+
const capability = capabilitySchema.safeParse(await this.resolveCapability(context));
|
|
46
|
+
if (!capability.success)
|
|
47
|
+
throw new plugins.BrowserRuntimeError('INVALID_INPUT');
|
|
48
|
+
context.signal.throwIfAborted();
|
|
49
|
+
await this.client.acquire(capability.data.capabilityToken, context.signal);
|
|
50
|
+
context.signal.throwIfAborted();
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
this.state = 'closed';
|
|
54
|
+
const cleanupErrors = [];
|
|
55
|
+
await this.client.release().catch((cleanupError) => cleanupErrors.push(cleanupError));
|
|
56
|
+
await this.client.close().catch((cleanupError) => cleanupErrors.push(cleanupError));
|
|
57
|
+
if (cleanupErrors.length > 0) {
|
|
58
|
+
throw new AggregateError([error, ...cleanupErrors], 'Browser tool provisioning cleanup failed.');
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
this.state = 'active';
|
|
63
|
+
const tools = plugins.createBrowserTools({
|
|
64
|
+
abortSignal: context.signal,
|
|
65
|
+
browser: createFramedToolBrowserContext(this.client, context.signal),
|
|
66
|
+
requestPermission: async (request) => {
|
|
67
|
+
const action = plugins.z.enum(browserActions).safeParse(request.metadata?.action);
|
|
68
|
+
if (!action.success)
|
|
69
|
+
throw new plugins.BrowserRuntimeError('INVALID_INPUT');
|
|
70
|
+
const toolCallId = request.toolCallId === undefined
|
|
71
|
+
? undefined
|
|
72
|
+
: boundedIdentifier(1, 128).safeParse(request.toolCallId);
|
|
73
|
+
if (toolCallId !== undefined && !toolCallId.success) {
|
|
74
|
+
throw new plugins.BrowserRuntimeError('INVALID_INPUT');
|
|
75
|
+
}
|
|
76
|
+
const signal = request.abortSignal
|
|
77
|
+
? AbortSignal.any([context.signal, request.abortSignal])
|
|
78
|
+
: context.signal;
|
|
79
|
+
signal.throwIfAborted();
|
|
80
|
+
const permission = context.requestPermission({
|
|
81
|
+
kind: `browser.${action.data}`,
|
|
82
|
+
description: `Allow the browser to perform the ${action.data} action.`,
|
|
83
|
+
...(toolCallId === undefined ? {} : { toolCallId: toolCallId.data }),
|
|
84
|
+
metadata: { action: action.data },
|
|
85
|
+
});
|
|
86
|
+
let onAbort;
|
|
87
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
88
|
+
onAbort = () => reject(new plugins.BrowserRuntimeError('ABORTED'));
|
|
89
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
90
|
+
if (signal.aborted)
|
|
91
|
+
onAbort();
|
|
92
|
+
});
|
|
93
|
+
try {
|
|
94
|
+
await Promise.race([permission, aborted]);
|
|
95
|
+
signal.throwIfAborted();
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
signal.removeEventListener('abort', onAbort);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
}, {
|
|
102
|
+
allowedActions: browserActions,
|
|
103
|
+
maxLines: 2000,
|
|
104
|
+
maxBytes: 128 * 1024,
|
|
105
|
+
});
|
|
106
|
+
let closePromise;
|
|
107
|
+
const close = () => {
|
|
108
|
+
if (closePromise)
|
|
109
|
+
return closePromise;
|
|
110
|
+
closePromise = (async () => {
|
|
111
|
+
context.signal.removeEventListener('abort', onRunAbort);
|
|
112
|
+
const cleanupErrors = [];
|
|
113
|
+
await this.client.release().catch((error) => cleanupErrors.push(error));
|
|
114
|
+
await this.client.close().catch((error) => cleanupErrors.push(error));
|
|
115
|
+
this.state = 'closed';
|
|
116
|
+
if (cleanupErrors.length === 1)
|
|
117
|
+
throw cleanupErrors[0];
|
|
118
|
+
if (cleanupErrors.length > 1) {
|
|
119
|
+
throw new AggregateError(cleanupErrors, 'Browser tool cleanup failed.');
|
|
120
|
+
}
|
|
121
|
+
})();
|
|
122
|
+
return closePromise;
|
|
123
|
+
};
|
|
124
|
+
const onRunAbort = () => {
|
|
125
|
+
void close().catch(() => undefined);
|
|
126
|
+
};
|
|
127
|
+
context.signal.addEventListener('abort', onRunAbort, { once: true });
|
|
128
|
+
if (context.signal.aborted) {
|
|
129
|
+
await close();
|
|
130
|
+
throw new plugins.BrowserRuntimeError('ABORTED');
|
|
131
|
+
}
|
|
132
|
+
return { tools, close };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5mbGV4cHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19icm93c2VyL2NsYXNzZXMuZmxleHByb3ZpZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBRXhDLE9BQU8sRUFBRSw4QkFBOEIsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRW5FLE1BQU0sY0FBYyxHQUFHO0lBQ3JCLFVBQVU7SUFDVixVQUFVO0lBQ1YsWUFBWTtJQUNaLE9BQU87SUFDUCxNQUFNO0lBQ04sT0FBTztDQUNpRCxDQUFDO0FBRTNELE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxPQUFlLEVBQUUsT0FBZSxFQUFFLEVBQUUsQ0FDN0QsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBRXRELE1BQU0sZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFDeEMsT0FBTyxFQUFFLGlCQUFpQixDQUFDLENBQUMsRUFBRSxHQUFHLENBQUM7SUFDbEMsU0FBUyxFQUFFLGlCQUFpQixDQUFDLENBQUMsRUFBRSxHQUFHLENBQUM7SUFDcEMsS0FBSyxFQUFFLGlCQUFpQixDQUFDLENBQUMsRUFBRSxHQUFHLENBQUM7Q0FDakMsQ0FBQyxDQUFDO0FBRUgsTUFBTSxnQkFBZ0IsR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLFlBQVksQ0FBQztJQUM5QyxlQUFlLEVBQUUsaUJBQWlCLENBQUMsRUFBRSxFQUFFLEdBQUcsQ0FBQztDQUM1QyxDQUFDLENBQUM7QUFFSCxNQUFNLE9BQU8sOEJBQThCO0lBRXhCLE1BQU0sQ0FBcUM7SUFDM0MsaUJBQWlCLENBQXNFO0lBQ2hHLEtBQUssR0FBb0QsUUFBUSxDQUFDO0lBRTFFLFlBQVksT0FBdUQ7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
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { BrowserRuntimeFlexToolProvider } from './classes.flexprovider.js';
|
|
2
|
+
export { createFramedToolBrowserContext } from './tool.context.js';
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19icm93c2VyL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSw4QkFBOEIsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzNFLE9BQU8sRUFBRSw4QkFBOEIsRUFBRSxNQUFNLG1CQUFtQixDQUFDIn0=
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type * as plugins from './plugins.js';
|
|
2
|
+
export interface IResolvedBrowserFlexCapability {
|
|
3
|
+
capabilityToken: string;
|
|
4
|
+
}
|
|
5
|
+
export interface IBrowserRuntimeFlexToolProviderOptions<TScope> {
|
|
6
|
+
client: plugins.BrowserRuntimeFramedClient;
|
|
7
|
+
resolveCapability(context: plugins.IFlexToolProviderContext<TScope>): Promise<IResolvedBrowserFlexCapability> | IResolvedBrowserFlexCapability;
|
|
8
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BrowserRuntimeError, validateAgentAction } from '@push.rocks/smartbrowser/runtime';
|
|
3
|
+
import { createBrowserTools } from '@modelprofile.com/flexharness/tools';
|
|
4
|
+
export { z, BrowserRuntimeError, validateAgentAction, createBrowserTools };
|
|
5
|
+
export type { BrowserRuntimeFramedClient, TBrowserAgentAction, } from '@push.rocks/smartbrowser/runtime';
|
|
6
|
+
export type { IFlexToolHandle, IFlexToolProvider, IFlexToolProviderContext, } from '@modelprofile.com/flexharness';
|
|
7
|
+
export type { IBrowserToolInput, IToolBrowserContext, IToolBrowserRunOptions, TBrowserToolAction, } from '@modelprofile.com/flexharness/tools';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BrowserRuntimeError, validateAgentAction, } from '@push.rocks/smartbrowser/runtime';
|
|
3
|
+
import { createBrowserTools } from '@modelprofile.com/flexharness/tools';
|
|
4
|
+
export { z, BrowserRuntimeError, validateAgentAction, createBrowserTools };
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX2Jyb3dzZXIvcGx1Z2lucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBQ3hCLE9BQU8sRUFDTCxtQkFBbUIsRUFDbkIsbUJBQW1CLEdBQ3BCLE1BQU0sa0NBQWtDLENBQUM7QUFDMUMsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFFekUsT0FBTyxFQUFFLENBQUMsRUFBRSxtQkFBbUIsRUFBRSxtQkFBbUIsRUFBRSxrQkFBa0IsRUFBRSxDQUFDIn0=
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
const browserToolInputSchema = plugins.z.strictObject({
|
|
3
|
+
action: plugins.z.preprocess((value) => typeof value === 'string' ? value.trim() : value ?? 'snapshot', plugins.z.enum(['navigate', 'snapshot', 'screenshot', 'click', 'fill', 'press'])),
|
|
4
|
+
url: plugins.z.string().optional(),
|
|
5
|
+
selector: plugins.z.string().optional(),
|
|
6
|
+
text: plugins.z.string().optional(),
|
|
7
|
+
timeoutMs: plugins.z.number().int().min(100).max(120_000).optional(),
|
|
8
|
+
});
|
|
9
|
+
export function createFramedToolBrowserContext(client, runSignal) {
|
|
10
|
+
return {
|
|
11
|
+
execute: async (input, options) => {
|
|
12
|
+
const parsed = browserToolInputSchema.safeParse(input);
|
|
13
|
+
if (!parsed.success)
|
|
14
|
+
throw new plugins.BrowserRuntimeError('INVALID_INPUT');
|
|
15
|
+
const { action, url, selector, text, timeoutMs } = parsed.data;
|
|
16
|
+
let runtimeAction;
|
|
17
|
+
switch (action) {
|
|
18
|
+
case 'navigate':
|
|
19
|
+
runtimeAction = plugins.validateAgentAction({
|
|
20
|
+
action,
|
|
21
|
+
url: url ?? '',
|
|
22
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
23
|
+
});
|
|
24
|
+
break;
|
|
25
|
+
case 'snapshot':
|
|
26
|
+
case 'screenshot':
|
|
27
|
+
runtimeAction = { action };
|
|
28
|
+
break;
|
|
29
|
+
case 'click':
|
|
30
|
+
runtimeAction = plugins.validateAgentAction({
|
|
31
|
+
action,
|
|
32
|
+
selector: selector ?? '',
|
|
33
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
34
|
+
});
|
|
35
|
+
break;
|
|
36
|
+
case 'fill':
|
|
37
|
+
runtimeAction = plugins.validateAgentAction({
|
|
38
|
+
action,
|
|
39
|
+
selector: selector ?? '',
|
|
40
|
+
text: text ?? '',
|
|
41
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
42
|
+
});
|
|
43
|
+
break;
|
|
44
|
+
case 'press':
|
|
45
|
+
runtimeAction = plugins.validateAgentAction({
|
|
46
|
+
action,
|
|
47
|
+
selector: selector ?? '',
|
|
48
|
+
key: text ?? '',
|
|
49
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
50
|
+
});
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
const signal = options?.abortSignal
|
|
54
|
+
? AbortSignal.any([runSignal, options.abortSignal])
|
|
55
|
+
: runSignal;
|
|
56
|
+
signal.throwIfAborted();
|
|
57
|
+
return client.executeAgentAction(runtimeAction, { signal });
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbC5jb250ZXh0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfYnJvd3Nlci90b29sLmNvbnRleHQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFFeEMsTUFBTSxzQkFBc0IsR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLFlBQVksQ0FBQztJQUNwRCxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQzFCLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxPQUFPLEtBQUssS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxJQUFJLFVBQVUsRUFDekUsT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxVQUFVLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQ2pGO0lBQ0QsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQ2xDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUN2QyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDbkMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxRQUFRLEVBQUU7Q0FDckUsQ0FBQyxDQUFDO0FBRUgsTUFBTSxVQUFVLDhCQUE4QixDQUM1QyxNQUEwQyxFQUMxQyxTQUFzQjtJQUV0QixPQUFPO1FBQ0wsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFnQyxFQUFFLE9BQXdD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
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"author": "Task Venture Capital GmbH",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "@modelprofile.com/flexharness",
|
|
5
|
-
"version": "10.
|
|
5
|
+
"version": "10.4.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"description": "Managed agent sessions with tools, MCP, chat, OCR, documents and browser control through focused subpath imports.",
|
|
8
8
|
"main": "./dist_ts/index.js",
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
"types": "./dist_ts_tools_node/index.d.ts",
|
|
25
25
|
"import": "./dist_ts_tools_node/index.js"
|
|
26
26
|
},
|
|
27
|
+
"./browser": {
|
|
28
|
+
"types": "./dist_ts_browser/index.d.ts",
|
|
29
|
+
"import": "./dist_ts_browser/index.js"
|
|
30
|
+
},
|
|
27
31
|
"./compaction": {
|
|
28
32
|
"types": "./dist_ts_compaction/index.d.ts",
|
|
29
33
|
"import": "./dist_ts_compaction/index.js"
|
|
@@ -70,14 +74,15 @@
|
|
|
70
74
|
}
|
|
71
75
|
},
|
|
72
76
|
"dependencies": {
|
|
73
|
-
"@modelprofile.com/flexharness-agent": "10.
|
|
74
|
-
"@modelprofile.com/flexharness-models": "10.
|
|
77
|
+
"@modelprofile.com/flexharness-agent": "10.4.0",
|
|
78
|
+
"@modelprofile.com/flexharness-models": "10.4.0",
|
|
75
79
|
"@push.rocks/smartrequest": "^5.5.1",
|
|
76
80
|
"ai": "^7.0.111",
|
|
77
81
|
"zod": "^4.6.5"
|
|
78
82
|
},
|
|
79
83
|
"peerDependencies": {
|
|
80
84
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
85
|
+
"@push.rocks/smartbrowser": "^5.0.0",
|
|
81
86
|
"@push.rocks/smartpdf": "^6.0.0",
|
|
82
87
|
"sharp": "^0.35.3",
|
|
83
88
|
"puppeteer": "^24.42.0",
|
|
@@ -93,6 +98,9 @@
|
|
|
93
98
|
"@modelcontextprotocol/sdk": {
|
|
94
99
|
"optional": true
|
|
95
100
|
},
|
|
101
|
+
"@push.rocks/smartbrowser": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
96
104
|
"@push.rocks/smartpdf": {
|
|
97
105
|
"optional": true
|
|
98
106
|
},
|
|
@@ -139,6 +147,8 @@
|
|
|
139
147
|
"dist_ts_tools",
|
|
140
148
|
"ts_tools_node",
|
|
141
149
|
"dist_ts_tools_node",
|
|
150
|
+
"ts_browser",
|
|
151
|
+
"dist_ts_browser",
|
|
142
152
|
"ts_compaction",
|
|
143
153
|
"dist_ts_compaction",
|
|
144
154
|
"ts_mcp",
|
package/readme.md
CHANGED
|
@@ -55,6 +55,7 @@ or provider SDKs.
|
|
|
55
55
|
| --- | --- | --- |
|
|
56
56
|
| `/tools` | Host-supplied execution contexts, HTTP and JSON tools | None |
|
|
57
57
|
| `/tools/node` | Local filesystem and process tools, file-backed job adapters | None |
|
|
58
|
+
| `/browser` | Run-scoped SmartBrowser framed-client adapter for Flex tool providers | `@push.rocks/smartbrowser` |
|
|
58
59
|
| `/compaction` | Model-driven conversation compaction | None |
|
|
59
60
|
| `/media` | Vision recipes using an injected model | None |
|
|
60
61
|
| `/ocr` | `ImageOcr`, OCR contracts and Mistral transport | None |
|
|
@@ -75,6 +76,12 @@ only for the entrypoints your project imports, for example:
|
|
|
75
76
|
pnpm add @modelprofile.com/flexharness lit
|
|
76
77
|
```
|
|
77
78
|
|
|
79
|
+
For a managed SmartBrowser resource, install its optional browser peer:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
pnpm add @modelprofile.com/flexharness @push.rocks/smartbrowser
|
|
83
|
+
```
|
|
84
|
+
|
|
78
85
|
```typescript
|
|
79
86
|
import { ChatSession } from '@modelprofile.com/flexharness/chat';
|
|
80
87
|
import '@modelprofile.com/flexharness/chat/web';
|
|
@@ -642,7 +649,7 @@ The run takes a steer in at its next step boundary: after the tool results of it
|
|
|
642
649
|
|
|
643
650
|
## Slash Commands
|
|
644
651
|
|
|
645
|
-
`parseSlashCommand()` is the public strict pure parser. It accepts at most 768 KiB, returns `not-command` for input not starting with `/`, `malformed` for invalid slash syntax, and `parsed` with the exact input, lowercase command name, separator-stripped raw argument text, and OpenCode-compatible quoted tokenization. Command names match `[a-z][a-z0-
|
|
652
|
+
`parseSlashCommand()` is the public strict pure parser. It accepts at most 768 KiB, returns `not-command` for input not starting with `/`, `malformed` for invalid slash syntax, and `parsed` with the exact input, lowercase command name, separator-stripped raw argument text, and OpenCode-compatible quoted tokenization. Command names match `[a-z][a-z0-9_-]{0,63}`. The public `isValidSlashCommandName(name)` and `isReservedSlashCommandName(name)` helpers let applications validate registration names against the same rules. Single and double quotes group tokens and are stripped; escapes are not interpreted.
|
|
646
653
|
|
|
647
654
|
Applications register immutable custom commands at construction:
|
|
648
655
|
|
|
@@ -688,7 +695,7 @@ const harness = new FlexHarness({
|
|
|
688
695
|
|
|
689
696
|
`compact`, `init`, `undo`, and `redo` are reserved. `listSlashCommands()` verifies the scope and session and returns immutable data-only descriptors with `kind`, placeholder `hints`, current immediate availability, and `workspaceReversion`. `compact`, `undo`, `redo`, and custom handlers require an otherwise idle command session. Prompt templates and `init` use the normal bounded FIFO and may wait behind an active prompt. Their descriptors report availability from the same queue-admission conditions used by execution: lifecycle, slash ownership, pending reversion, root-session eligibility, outstanding count, and estimated prompt bytes. A dynamic capacity race may still produce `FlexHarnessQueueFullError` during admission. Only one slash-command execution may own a session at a time.
|
|
690
697
|
|
|
691
|
-
At most 128 custom commands may be registered. Every registration must be a plain object with exactly one of `template` or `handler`; names must match `[a-z][a-z0-
|
|
698
|
+
At most 128 custom commands may be registered. Every registration must be a plain object with exactly one of `template` or `handler`; names must match `[a-z][a-z0-9_-]{0,63}`, be unique, and not use a reserved name. Optional descriptions must be non-empty and at most 2048 UTF-8 bytes. Templates must be non-empty and at most 768 KiB, and the expanded prompt must also fit 768 KiB. Registrations are copied and frozen during construction.
|
|
692
699
|
|
|
693
700
|
`/undo` and `/redo`, plus `undoSession()` and `redoSession()`, move a durable history cursor. The direct methods return `{ revertedRunId }` and `{ restoredRunId }`; the slash forms return `{ type: 'operation', name: 'undo' | 'redo' }`. Each committed cursor move emits one `session.history.changed` event with `direction`, `runId`, and the selected session identity. A committed branch emits the same event with `direction: 'branch'` and no `runId`, so controllers should refresh the complete selected session. Capture finalization, cleanup, and metadata-only changes do not emit this event.
|
|
694
701
|
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/index.ts
CHANGED
package/ts/readme.md
CHANGED
|
@@ -55,6 +55,7 @@ or provider SDKs.
|
|
|
55
55
|
| --- | --- | --- |
|
|
56
56
|
| `/tools` | Host-supplied execution contexts, HTTP and JSON tools | None |
|
|
57
57
|
| `/tools/node` | Local filesystem and process tools, file-backed job adapters | None |
|
|
58
|
+
| `/browser` | Run-scoped SmartBrowser framed-client adapter for Flex tool providers | `@push.rocks/smartbrowser` |
|
|
58
59
|
| `/compaction` | Model-driven conversation compaction | None |
|
|
59
60
|
| `/media` | Vision recipes using an injected model | None |
|
|
60
61
|
| `/ocr` | `ImageOcr`, OCR contracts and Mistral transport | None |
|
|
@@ -75,6 +76,12 @@ only for the entrypoints your project imports, for example:
|
|
|
75
76
|
pnpm add @modelprofile.com/flexharness lit
|
|
76
77
|
```
|
|
77
78
|
|
|
79
|
+
For a managed SmartBrowser resource, install its optional browser peer:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
pnpm add @modelprofile.com/flexharness @push.rocks/smartbrowser
|
|
83
|
+
```
|
|
84
|
+
|
|
78
85
|
```typescript
|
|
79
86
|
import { ChatSession } from '@modelprofile.com/flexharness/chat';
|
|
80
87
|
import '@modelprofile.com/flexharness/chat/web';
|
|
@@ -642,7 +649,7 @@ The run takes a steer in at its next step boundary: after the tool results of it
|
|
|
642
649
|
|
|
643
650
|
## Slash Commands
|
|
644
651
|
|
|
645
|
-
`parseSlashCommand()` is the public strict pure parser. It accepts at most 768 KiB, returns `not-command` for input not starting with `/`, `malformed` for invalid slash syntax, and `parsed` with the exact input, lowercase command name, separator-stripped raw argument text, and OpenCode-compatible quoted tokenization. Command names match `[a-z][a-z0-
|
|
652
|
+
`parseSlashCommand()` is the public strict pure parser. It accepts at most 768 KiB, returns `not-command` for input not starting with `/`, `malformed` for invalid slash syntax, and `parsed` with the exact input, lowercase command name, separator-stripped raw argument text, and OpenCode-compatible quoted tokenization. Command names match `[a-z][a-z0-9_-]{0,63}`. The public `isValidSlashCommandName(name)` and `isReservedSlashCommandName(name)` helpers let applications validate registration names against the same rules. Single and double quotes group tokens and are stripped; escapes are not interpreted.
|
|
646
653
|
|
|
647
654
|
Applications register immutable custom commands at construction:
|
|
648
655
|
|
|
@@ -688,7 +695,7 @@ const harness = new FlexHarness({
|
|
|
688
695
|
|
|
689
696
|
`compact`, `init`, `undo`, and `redo` are reserved. `listSlashCommands()` verifies the scope and session and returns immutable data-only descriptors with `kind`, placeholder `hints`, current immediate availability, and `workspaceReversion`. `compact`, `undo`, `redo`, and custom handlers require an otherwise idle command session. Prompt templates and `init` use the normal bounded FIFO and may wait behind an active prompt. Their descriptors report availability from the same queue-admission conditions used by execution: lifecycle, slash ownership, pending reversion, root-session eligibility, outstanding count, and estimated prompt bytes. A dynamic capacity race may still produce `FlexHarnessQueueFullError` during admission. Only one slash-command execution may own a session at a time.
|
|
690
697
|
|
|
691
|
-
At most 128 custom commands may be registered. Every registration must be a plain object with exactly one of `template` or `handler`; names must match `[a-z][a-z0-
|
|
698
|
+
At most 128 custom commands may be registered. Every registration must be a plain object with exactly one of `template` or `handler`; names must match `[a-z][a-z0-9_-]{0,63}`, be unique, and not use a reserved name. Optional descriptions must be non-empty and at most 2048 UTF-8 bytes. Templates must be non-empty and at most 768 KiB, and the expanded prompt must also fit 768 KiB. Registrations are copied and frozen during construction.
|
|
692
699
|
|
|
693
700
|
`/undo` and `/redo`, plus `undoSession()` and `redoSession()`, move a durable history cursor. The direct methods return `{ revertedRunId }` and `{ restoredRunId }`; the slash forms return `{ type: 'operation', name: 'undo' | 'redo' }`. Each committed cursor move emits one `session.history.changed` event with `direction`, `runId`, and the selected session identity. A committed branch emits the same event with `direction: 'branch'` and no `runId`, so controllers should refresh the complete selected session. Capture finalization, cleanup, and metadata-only changes do not emit this event.
|
|
694
701
|
|
|
@@ -6,8 +6,8 @@ import type {
|
|
|
6
6
|
|
|
7
7
|
export const FLEX_SLASH_COMMAND_MAX_INPUT_BYTES = 768 * 1024;
|
|
8
8
|
|
|
9
|
-
const slashCommandNamePattern = /^[a-z][a-z0-
|
|
10
|
-
const slashCommandPattern = /^\/([a-z][a-z0-
|
|
9
|
+
const slashCommandNamePattern = /^[a-z][a-z0-9_-]{0,63}$/u;
|
|
10
|
+
const slashCommandPattern = /^\/([a-z][a-z0-9_-]{0,63})(?:$|(\s+)([\s\S]*))$/u;
|
|
11
11
|
const slashCommandArgumentsPattern = /(?:\[Image\s+\d+\]|"[^"]*"|'[^']*'|[^\s"']+)/gi;
|
|
12
12
|
const slashCommandQuoteTrimPattern = /^["']|["']$/g;
|
|
13
13
|
const slashCommandPlaceholderPattern = /\$(\d+)/g;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { IBrowserRuntimeFlexToolProviderOptions } from './interfaces.js';
|
|
3
|
+
import { createFramedToolBrowserContext } from './tool.context.js';
|
|
4
|
+
|
|
5
|
+
const browserActions = [
|
|
6
|
+
'navigate',
|
|
7
|
+
'snapshot',
|
|
8
|
+
'screenshot',
|
|
9
|
+
'click',
|
|
10
|
+
'fill',
|
|
11
|
+
'press',
|
|
12
|
+
] as const satisfies readonly plugins.TBrowserToolAction[];
|
|
13
|
+
|
|
14
|
+
const boundedIdentifier = (minimum: number, maximum: number) =>
|
|
15
|
+
plugins.z.string().trim().min(minimum).max(maximum);
|
|
16
|
+
|
|
17
|
+
const runBindingSchema = plugins.z.object({
|
|
18
|
+
scopeId: boundedIdentifier(1, 128),
|
|
19
|
+
sessionId: boundedIdentifier(1, 128),
|
|
20
|
+
runId: boundedIdentifier(1, 256),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const capabilitySchema = plugins.z.strictObject({
|
|
24
|
+
capabilityToken: boundedIdentifier(16, 512),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export class BrowserRuntimeFlexToolProvider<TScope>
|
|
28
|
+
implements plugins.IFlexToolProvider<TScope> {
|
|
29
|
+
private readonly client: plugins.BrowserRuntimeFramedClient;
|
|
30
|
+
private readonly resolveCapability: IBrowserRuntimeFlexToolProviderOptions<TScope>['resolveCapability'];
|
|
31
|
+
private state: 'unused' | 'provisioning' | 'active' | 'closed' = 'unused';
|
|
32
|
+
|
|
33
|
+
constructor(options: IBrowserRuntimeFlexToolProviderOptions<TScope>) {
|
|
34
|
+
if (!options || !options.client || typeof options.resolveCapability !== 'function') {
|
|
35
|
+
throw new plugins.BrowserRuntimeError('INVALID_INPUT');
|
|
36
|
+
}
|
|
37
|
+
this.client = options.client;
|
|
38
|
+
this.resolveCapability = options.resolveCapability;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public async provideTools(
|
|
42
|
+
context: plugins.IFlexToolProviderContext<TScope>,
|
|
43
|
+
): Promise<plugins.IFlexToolHandle> {
|
|
44
|
+
if (this.state !== 'unused') throw new plugins.BrowserRuntimeError('BUSY');
|
|
45
|
+
this.state = 'provisioning';
|
|
46
|
+
try {
|
|
47
|
+
const binding = runBindingSchema.safeParse(context);
|
|
48
|
+
if (!binding.success) throw new plugins.BrowserRuntimeError('INVALID_INPUT');
|
|
49
|
+
if (
|
|
50
|
+
this.client.scopeId !== binding.data.scopeId
|
|
51
|
+
|| this.client.sessionId.harnessId !== 'flex'
|
|
52
|
+
|| this.client.sessionId.nativeId !== binding.data.sessionId
|
|
53
|
+
|| this.client.runId !== binding.data.runId
|
|
54
|
+
) throw new plugins.BrowserRuntimeError('CAPABILITY_INVALID');
|
|
55
|
+
context.signal.throwIfAborted();
|
|
56
|
+
const capability = capabilitySchema.safeParse(await this.resolveCapability(context));
|
|
57
|
+
if (!capability.success) throw new plugins.BrowserRuntimeError('INVALID_INPUT');
|
|
58
|
+
context.signal.throwIfAborted();
|
|
59
|
+
await this.client.acquire(capability.data.capabilityToken, context.signal);
|
|
60
|
+
context.signal.throwIfAborted();
|
|
61
|
+
} catch (error) {
|
|
62
|
+
this.state = 'closed';
|
|
63
|
+
const cleanupErrors: unknown[] = [];
|
|
64
|
+
await this.client.release().catch((cleanupError: unknown) => cleanupErrors.push(cleanupError));
|
|
65
|
+
await this.client.close().catch((cleanupError: unknown) => cleanupErrors.push(cleanupError));
|
|
66
|
+
if (cleanupErrors.length > 0) {
|
|
67
|
+
throw new AggregateError([error, ...cleanupErrors], 'Browser tool provisioning cleanup failed.');
|
|
68
|
+
}
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
this.state = 'active';
|
|
72
|
+
|
|
73
|
+
const tools = plugins.createBrowserTools({
|
|
74
|
+
abortSignal: context.signal,
|
|
75
|
+
browser: createFramedToolBrowserContext(this.client, context.signal),
|
|
76
|
+
requestPermission: async (request) => {
|
|
77
|
+
const action = plugins.z.enum(browserActions).safeParse(request.metadata?.action);
|
|
78
|
+
if (!action.success) throw new plugins.BrowserRuntimeError('INVALID_INPUT');
|
|
79
|
+
const toolCallId = request.toolCallId === undefined
|
|
80
|
+
? undefined
|
|
81
|
+
: boundedIdentifier(1, 128).safeParse(request.toolCallId);
|
|
82
|
+
if (toolCallId !== undefined && !toolCallId.success) {
|
|
83
|
+
throw new plugins.BrowserRuntimeError('INVALID_INPUT');
|
|
84
|
+
}
|
|
85
|
+
const signal = request.abortSignal
|
|
86
|
+
? AbortSignal.any([context.signal, request.abortSignal])
|
|
87
|
+
: context.signal;
|
|
88
|
+
signal.throwIfAborted();
|
|
89
|
+
const permission = context.requestPermission({
|
|
90
|
+
kind: `browser.${action.data}`,
|
|
91
|
+
description: `Allow the browser to perform the ${action.data} action.`,
|
|
92
|
+
...(toolCallId === undefined ? {} : { toolCallId: toolCallId.data }),
|
|
93
|
+
metadata: { action: action.data },
|
|
94
|
+
});
|
|
95
|
+
let onAbort!: () => void;
|
|
96
|
+
const aborted = new Promise<never>((_resolve, reject) => {
|
|
97
|
+
onAbort = () => reject(new plugins.BrowserRuntimeError('ABORTED'));
|
|
98
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
99
|
+
if (signal.aborted) onAbort();
|
|
100
|
+
});
|
|
101
|
+
try {
|
|
102
|
+
await Promise.race([permission, aborted]);
|
|
103
|
+
signal.throwIfAborted();
|
|
104
|
+
} finally {
|
|
105
|
+
signal.removeEventListener('abort', onAbort);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
}, {
|
|
109
|
+
allowedActions: browserActions,
|
|
110
|
+
maxLines: 2000,
|
|
111
|
+
maxBytes: 128 * 1024,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
let closePromise: Promise<void> | undefined;
|
|
115
|
+
const close = (): Promise<void> => {
|
|
116
|
+
if (closePromise) return closePromise;
|
|
117
|
+
closePromise = (async () => {
|
|
118
|
+
context.signal.removeEventListener('abort', onRunAbort);
|
|
119
|
+
const cleanupErrors: unknown[] = [];
|
|
120
|
+
await this.client.release().catch((error: unknown) => cleanupErrors.push(error));
|
|
121
|
+
await this.client.close().catch((error: unknown) => cleanupErrors.push(error));
|
|
122
|
+
this.state = 'closed';
|
|
123
|
+
if (cleanupErrors.length === 1) throw cleanupErrors[0];
|
|
124
|
+
if (cleanupErrors.length > 1) {
|
|
125
|
+
throw new AggregateError(cleanupErrors, 'Browser tool cleanup failed.');
|
|
126
|
+
}
|
|
127
|
+
})();
|
|
128
|
+
return closePromise;
|
|
129
|
+
};
|
|
130
|
+
const onRunAbort = (): void => {
|
|
131
|
+
void close().catch(() => undefined);
|
|
132
|
+
};
|
|
133
|
+
context.signal.addEventListener('abort', onRunAbort, { once: true });
|
|
134
|
+
if (context.signal.aborted) {
|
|
135
|
+
await close();
|
|
136
|
+
throw new plugins.BrowserRuntimeError('ABORTED');
|
|
137
|
+
}
|
|
138
|
+
return { tools, close };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type * as plugins from './plugins.js';
|
|
2
|
+
|
|
3
|
+
export interface IResolvedBrowserFlexCapability {
|
|
4
|
+
capabilityToken: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface IBrowserRuntimeFlexToolProviderOptions<TScope> {
|
|
8
|
+
client: plugins.BrowserRuntimeFramedClient;
|
|
9
|
+
resolveCapability(
|
|
10
|
+
context: plugins.IFlexToolProviderContext<TScope>,
|
|
11
|
+
): Promise<IResolvedBrowserFlexCapability> | IResolvedBrowserFlexCapability;
|
|
12
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import {
|
|
3
|
+
BrowserRuntimeError,
|
|
4
|
+
validateAgentAction,
|
|
5
|
+
} from '@push.rocks/smartbrowser/runtime';
|
|
6
|
+
import { createBrowserTools } from '@modelprofile.com/flexharness/tools';
|
|
7
|
+
|
|
8
|
+
export { z, BrowserRuntimeError, validateAgentAction, createBrowserTools };
|
|
9
|
+
export type {
|
|
10
|
+
BrowserRuntimeFramedClient,
|
|
11
|
+
TBrowserAgentAction,
|
|
12
|
+
} from '@push.rocks/smartbrowser/runtime';
|
|
13
|
+
export type {
|
|
14
|
+
IFlexToolHandle,
|
|
15
|
+
IFlexToolProvider,
|
|
16
|
+
IFlexToolProviderContext,
|
|
17
|
+
} from '@modelprofile.com/flexharness';
|
|
18
|
+
export type {
|
|
19
|
+
IBrowserToolInput,
|
|
20
|
+
IToolBrowserContext,
|
|
21
|
+
IToolBrowserRunOptions,
|
|
22
|
+
TBrowserToolAction,
|
|
23
|
+
} from '@modelprofile.com/flexharness/tools';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# FlexHarness browser adapter
|
|
2
|
+
|
|
3
|
+
Install both packages before importing this optional entrypoint:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
pnpm add @modelprofile.com/flexharness @push.rocks/smartbrowser
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Import `BrowserRuntimeFlexToolProvider` from `@modelprofile.com/flexharness/browser` and `BrowserRuntimeFramedClient` from `@push.rocks/smartbrowser/runtime`. Create a fresh client and provider for each Flex run. The host supplies the trusted framed streams and the complete SmartBrowser binding when constructing the client; the provider resolves only the capability token.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { BrowserRuntimeFlexToolProvider } from '@modelprofile.com/flexharness/browser';
|
|
13
|
+
import { BrowserRuntimeFramedClient } from '@push.rocks/smartbrowser/runtime';
|
|
14
|
+
|
|
15
|
+
const client = new BrowserRuntimeFramedClient({
|
|
16
|
+
...trustedBinding,
|
|
17
|
+
readable,
|
|
18
|
+
writable,
|
|
19
|
+
});
|
|
20
|
+
const provider = new BrowserRuntimeFlexToolProvider({
|
|
21
|
+
client,
|
|
22
|
+
resolveCapability: () => ({ capabilityToken }),
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`trustedBinding`, `readable`, `writable`, and `capabilityToken` are host-provided values. The binding contains the exact project, browser resource, attachment authority and revision, actor, peer, Flex scope, session, run, and channel identities. The provider checks the Flex run's scope, session, and run against the client before resolving or acquiring a capability. SmartBrowser's framed server validates the token against the complete trusted binding.
|
|
27
|
+
|
|
28
|
+
The provider exposes `navigate`, `snapshot`, `screenshot`, `click`, `fill`, and `press` through the existing `/tools` browser tool. Navigation and input actions request Flex permission as `browser.<action>` with the tool call ID. Snapshot and screenshot are observations and do not request permission. Tool and run cancellation reach the framed client. The returned Flex tool handle releases the capability and closes the client; FlexHarness closes this handle after the run. A screenshot returns bounded artifact metadata, which the host can use for a separate authorized artifact read.
|
|
29
|
+
|
|
30
|
+
This entrypoint replaces `BrowserRuntimeFlexToolProvider` from `@modelprofile.com/browser-runtime`. Browser resource ownership, capabilities, framed transport, and artifact storage now come from `@push.rocks/smartbrowser/runtime`.
|
|
31
|
+
|
|
32
|
+
For hosts that used the old client's `client.asToolBrowserContext()`, import `createFramedToolBrowserContext` from this entrypoint and call `createFramedToolBrowserContext(client, runSignal)`. It returns the same `IToolBrowserContext` shape, maps the six supported actions to `executeAgentAction()`, and combines the run signal with each tool invocation's abort signal.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
|
|
3
|
+
const browserToolInputSchema = plugins.z.strictObject({
|
|
4
|
+
action: plugins.z.preprocess(
|
|
5
|
+
(value) => typeof value === 'string' ? value.trim() : value ?? 'snapshot',
|
|
6
|
+
plugins.z.enum(['navigate', 'snapshot', 'screenshot', 'click', 'fill', 'press']),
|
|
7
|
+
),
|
|
8
|
+
url: plugins.z.string().optional(),
|
|
9
|
+
selector: plugins.z.string().optional(),
|
|
10
|
+
text: plugins.z.string().optional(),
|
|
11
|
+
timeoutMs: plugins.z.number().int().min(100).max(120_000).optional(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export function createFramedToolBrowserContext(
|
|
15
|
+
client: plugins.BrowserRuntimeFramedClient,
|
|
16
|
+
runSignal: AbortSignal,
|
|
17
|
+
): plugins.IToolBrowserContext {
|
|
18
|
+
return {
|
|
19
|
+
execute: async (input: plugins.IBrowserToolInput, options?: plugins.IToolBrowserRunOptions) => {
|
|
20
|
+
const parsed = browserToolInputSchema.safeParse(input);
|
|
21
|
+
if (!parsed.success) throw new plugins.BrowserRuntimeError('INVALID_INPUT');
|
|
22
|
+
const { action, url, selector, text, timeoutMs } = parsed.data;
|
|
23
|
+
let runtimeAction: plugins.TBrowserAgentAction;
|
|
24
|
+
switch (action) {
|
|
25
|
+
case 'navigate':
|
|
26
|
+
runtimeAction = plugins.validateAgentAction({
|
|
27
|
+
action,
|
|
28
|
+
url: url ?? '',
|
|
29
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
30
|
+
});
|
|
31
|
+
break;
|
|
32
|
+
case 'snapshot':
|
|
33
|
+
case 'screenshot':
|
|
34
|
+
runtimeAction = { action };
|
|
35
|
+
break;
|
|
36
|
+
case 'click':
|
|
37
|
+
runtimeAction = plugins.validateAgentAction({
|
|
38
|
+
action,
|
|
39
|
+
selector: selector ?? '',
|
|
40
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
41
|
+
});
|
|
42
|
+
break;
|
|
43
|
+
case 'fill':
|
|
44
|
+
runtimeAction = plugins.validateAgentAction({
|
|
45
|
+
action,
|
|
46
|
+
selector: selector ?? '',
|
|
47
|
+
text: text ?? '',
|
|
48
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
49
|
+
});
|
|
50
|
+
break;
|
|
51
|
+
case 'press':
|
|
52
|
+
runtimeAction = plugins.validateAgentAction({
|
|
53
|
+
action,
|
|
54
|
+
selector: selector ?? '',
|
|
55
|
+
key: text ?? '',
|
|
56
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
57
|
+
});
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
const signal = options?.abortSignal
|
|
61
|
+
? AbortSignal.any([runSignal, options.abortSignal])
|
|
62
|
+
: runSignal;
|
|
63
|
+
signal.throwIfAborted();
|
|
64
|
+
return client.executeAgentAction(runtimeAction, { signal });
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|