@crosshands/cli 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/intent/dispatch.d.ts +1 -1
- package/dist/intent/dispatch.d.ts.map +1 -1
- package/dist/intent/dispatch.js +149 -52
- package/dist/intent/dispatch.js.map +1 -1
- package/dist/intent/evaluate.d.ts +11 -1
- package/dist/intent/evaluate.d.ts.map +1 -1
- package/dist/intent/evaluate.js +101 -60
- package/dist/intent/evaluate.js.map +1 -1
- package/dist/intent/http.d.ts +8 -0
- package/dist/intent/http.d.ts.map +1 -0
- package/dist/intent/http.js +4 -0
- package/dist/intent/http.js.map +1 -0
- package/dist/intent/log.d.ts +75 -1
- package/dist/intent/log.d.ts.map +1 -1
- package/dist/intent/log.js +98 -11
- package/dist/intent/log.js.map +1 -1
- package/package.json +6 -6
- package/src/intent/dispatch.ts +161 -64
- package/src/intent/evaluate.ts +120 -64
- package/src/intent/http.ts +10 -0
- package/src/intent/log.ts +182 -10
package/dist/intent/log.d.ts
CHANGED
|
@@ -1,8 +1,82 @@
|
|
|
1
|
+
import type { Suggestion } from '@crosshands/contract';
|
|
2
|
+
import { type JevHttpStats } from './http.js';
|
|
3
|
+
export type JevPhase = 'observe' | 'bind' | 'named';
|
|
4
|
+
type JevShared = {
|
|
5
|
+
callId: string;
|
|
6
|
+
operation: string;
|
|
7
|
+
snapshotId: string;
|
|
8
|
+
phase: JevPhase;
|
|
9
|
+
goal?: string;
|
|
10
|
+
};
|
|
11
|
+
export type JevCallRecord = {
|
|
12
|
+
kind: 'jev.call';
|
|
13
|
+
callId: string;
|
|
14
|
+
operation: string;
|
|
15
|
+
durationMs: number;
|
|
16
|
+
httpMs: number;
|
|
17
|
+
brokerCalls: number;
|
|
18
|
+
ranks: number;
|
|
19
|
+
phase?: JevPhase;
|
|
20
|
+
bound?: boolean;
|
|
21
|
+
error?: string;
|
|
22
|
+
goal?: string;
|
|
23
|
+
};
|
|
24
|
+
export type JevHttpRecord = JevShared & {
|
|
25
|
+
kind: 'jev.http';
|
|
26
|
+
status: number;
|
|
27
|
+
durationMs: number;
|
|
28
|
+
requestBytes: number;
|
|
29
|
+
responseBytes: number;
|
|
30
|
+
candidateCount: number;
|
|
31
|
+
};
|
|
32
|
+
export type JevDecisionRecord = JevShared & {
|
|
33
|
+
kind: 'jev.decision';
|
|
34
|
+
app?: string;
|
|
35
|
+
move: Suggestion['move']['kind'];
|
|
36
|
+
elementIndex?: number;
|
|
37
|
+
reason?: string;
|
|
38
|
+
confidence?: number;
|
|
39
|
+
goalChars: number;
|
|
40
|
+
treeChars: number;
|
|
41
|
+
elementCount: number;
|
|
42
|
+
candidateCount: number;
|
|
43
|
+
parseMs: number;
|
|
44
|
+
evaluateMs?: number;
|
|
45
|
+
};
|
|
46
|
+
export type JevRecord = JevCallRecord | JevHttpRecord | JevDecisionRecord;
|
|
1
47
|
export type JevLogger = {
|
|
2
|
-
emit(record:
|
|
48
|
+
emit(record: JevRecord): void;
|
|
3
49
|
close(): Promise<void>;
|
|
4
50
|
};
|
|
51
|
+
export type JevCall = {
|
|
52
|
+
callId: string;
|
|
53
|
+
operation: string;
|
|
54
|
+
goal?: string;
|
|
55
|
+
ranks: number;
|
|
56
|
+
httpMs: number;
|
|
57
|
+
brokerCalls: number;
|
|
58
|
+
phase?: JevPhase;
|
|
59
|
+
};
|
|
60
|
+
export type JevRankStats = {
|
|
61
|
+
snapshotId: string;
|
|
62
|
+
treeChars: number;
|
|
63
|
+
elementCount: number;
|
|
64
|
+
candidateCount: number;
|
|
65
|
+
parseMs: number;
|
|
66
|
+
goalChars: number;
|
|
67
|
+
evaluateMs?: number;
|
|
68
|
+
app?: string;
|
|
69
|
+
http?: JevHttpStats;
|
|
70
|
+
};
|
|
71
|
+
export type RankRecorder = (phase: JevPhase, stats: JevRankStats, suggestion?: Suggestion) => void;
|
|
5
72
|
export declare function hashGoal(goal: string): string;
|
|
73
|
+
export declare function createJevCall(operation: string, goal?: string): JevCall;
|
|
74
|
+
export declare function recordRank(log: JevLogger | undefined, call: JevCall | undefined, phase: JevPhase, stats: JevRankStats, suggestion?: Suggestion): void;
|
|
75
|
+
export declare function emitCall(log: JevLogger | undefined, call: JevCall | undefined, started: number, extra?: {
|
|
76
|
+
error?: string;
|
|
77
|
+
bound?: boolean;
|
|
78
|
+
}): void;
|
|
6
79
|
export declare function createJevLogger(graphicalSessionId: string, env?: NodeJS.ProcessEnv): JevLogger;
|
|
7
80
|
export declare function createCliJevLogger(env?: NodeJS.ProcessEnv): JevLogger | undefined;
|
|
81
|
+
export {};
|
|
8
82
|
//# sourceMappingURL=log.d.ts.map
|
package/dist/intent/log.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/intent/log.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/intent/log.ts"],"names":[],"mappings":"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"}
|
package/dist/intent/log.js
CHANGED
|
@@ -2,10 +2,89 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { JsonlFileWriter, diagnosticsDirectory } from '@crosshands/runtime';
|
|
3
3
|
import { localClientPaths } from '../local-client.js';
|
|
4
4
|
import { parseJevEnv } from './env.js';
|
|
5
|
+
import { elapsedMs } from './http.js';
|
|
5
6
|
export function hashGoal(goal) {
|
|
6
7
|
return createHash('sha256').update(goal).digest('hex');
|
|
7
8
|
}
|
|
8
9
|
const OMIT = new Set(['goal', 'apiKey', 'treeText', 'text', 'value', 'TYPESAFE_API_KEY']);
|
|
10
|
+
export function createJevCall(operation, goal) {
|
|
11
|
+
return {
|
|
12
|
+
callId: randomUUID(),
|
|
13
|
+
operation,
|
|
14
|
+
...(goal === undefined ? {} : { goal }),
|
|
15
|
+
ranks: 0,
|
|
16
|
+
httpMs: 0,
|
|
17
|
+
brokerCalls: 0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function recordRank(log, call, phase, stats, suggestion) {
|
|
21
|
+
if (call !== undefined) {
|
|
22
|
+
call.phase = phase;
|
|
23
|
+
call.ranks += 1;
|
|
24
|
+
if (stats.http !== undefined)
|
|
25
|
+
call.httpMs += stats.http.durationMs;
|
|
26
|
+
}
|
|
27
|
+
if (log === undefined || call === undefined)
|
|
28
|
+
return;
|
|
29
|
+
const shared = {
|
|
30
|
+
callId: call.callId,
|
|
31
|
+
operation: call.operation,
|
|
32
|
+
snapshotId: stats.snapshotId,
|
|
33
|
+
phase,
|
|
34
|
+
...(call.goal === undefined ? {} : { goal: call.goal })
|
|
35
|
+
};
|
|
36
|
+
if (stats.http !== undefined) {
|
|
37
|
+
log.emit({
|
|
38
|
+
kind: 'jev.http',
|
|
39
|
+
...shared,
|
|
40
|
+
status: stats.http.status,
|
|
41
|
+
durationMs: stats.http.durationMs,
|
|
42
|
+
requestBytes: stats.http.requestBytes,
|
|
43
|
+
responseBytes: stats.http.responseBytes,
|
|
44
|
+
candidateCount: stats.candidateCount
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (suggestion === undefined)
|
|
48
|
+
return;
|
|
49
|
+
const move = suggestion.move;
|
|
50
|
+
log.emit({
|
|
51
|
+
kind: 'jev.decision',
|
|
52
|
+
...shared,
|
|
53
|
+
...(stats.app === undefined ? {} : { app: stats.app }),
|
|
54
|
+
move: move.kind,
|
|
55
|
+
...(move.kind === 'click' ||
|
|
56
|
+
move.kind === 'setValue' ||
|
|
57
|
+
move.kind === 'secondary' ||
|
|
58
|
+
move.kind === 'scroll'
|
|
59
|
+
? { elementIndex: move.elementIndex }
|
|
60
|
+
: {}),
|
|
61
|
+
...(move.kind === 'blocked' ? { reason: move.reason } : {}),
|
|
62
|
+
...(suggestion.confidence === undefined ? {} : { confidence: suggestion.confidence }),
|
|
63
|
+
goalChars: stats.goalChars,
|
|
64
|
+
treeChars: stats.treeChars,
|
|
65
|
+
elementCount: stats.elementCount,
|
|
66
|
+
candidateCount: stats.candidateCount,
|
|
67
|
+
parseMs: stats.parseMs,
|
|
68
|
+
...(stats.evaluateMs === undefined ? {} : { evaluateMs: stats.evaluateMs })
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
export function emitCall(log, call, started, extra = {}) {
|
|
72
|
+
if (log === undefined || call === undefined)
|
|
73
|
+
return;
|
|
74
|
+
log.emit({
|
|
75
|
+
kind: 'jev.call',
|
|
76
|
+
callId: call.callId,
|
|
77
|
+
operation: call.operation,
|
|
78
|
+
durationMs: elapsedMs(started),
|
|
79
|
+
httpMs: call.httpMs,
|
|
80
|
+
brokerCalls: call.brokerCalls,
|
|
81
|
+
ranks: call.ranks,
|
|
82
|
+
...(call.phase === undefined ? {} : { phase: call.phase }),
|
|
83
|
+
...(extra.bound === undefined ? {} : { bound: extra.bound }),
|
|
84
|
+
...(extra.error === undefined ? {} : { error: extra.error }),
|
|
85
|
+
...(call.goal === undefined ? {} : { goal: call.goal })
|
|
86
|
+
});
|
|
87
|
+
}
|
|
9
88
|
export function createJevLogger(graphicalSessionId, env = process.env) {
|
|
10
89
|
const writer = new JsonlFileWriter({
|
|
11
90
|
directory: diagnosticsDirectory(graphicalSessionId, { env }),
|
|
@@ -14,17 +93,25 @@ export function createJevLogger(graphicalSessionId, env = process.env) {
|
|
|
14
93
|
writer.start();
|
|
15
94
|
return {
|
|
16
95
|
emit(record) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
96
|
+
try {
|
|
97
|
+
const raw = record;
|
|
98
|
+
const goal = raw.goal;
|
|
99
|
+
const rest = {
|
|
100
|
+
...raw,
|
|
101
|
+
kind: record.kind,
|
|
102
|
+
v: 1,
|
|
103
|
+
ts: new Date().toISOString()
|
|
104
|
+
};
|
|
105
|
+
for (const key of OMIT)
|
|
106
|
+
delete rest[key];
|
|
107
|
+
writer.emit({
|
|
108
|
+
...rest,
|
|
109
|
+
...(typeof goal === 'string' ? { goalSha256: hashGoal(goal) } : {})
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Jev logs must not fail computer-use.
|
|
114
|
+
}
|
|
28
115
|
},
|
|
29
116
|
close: () => writer.close()
|
|
30
117
|
};
|
package/dist/intent/log.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/intent/log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/intent/log.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crosshands/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Agent-agnostic local computer-use CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@crosshands/
|
|
27
|
-
"@crosshands/
|
|
26
|
+
"@crosshands/runtime": "0.2.1",
|
|
27
|
+
"@crosshands/contract": "0.2.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "24.10.1",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"vitest": "4.1.5"
|
|
33
33
|
},
|
|
34
34
|
"optionalDependencies": {
|
|
35
|
-
"@crosshands/platform-
|
|
36
|
-
"@crosshands/platform-windows": "0.2.
|
|
37
|
-
"@crosshands/platform-
|
|
35
|
+
"@crosshands/platform-darwin": "0.2.1",
|
|
36
|
+
"@crosshands/platform-windows": "0.2.1",
|
|
37
|
+
"@crosshands/platform-linux": "0.2.1"
|
|
38
38
|
},
|
|
39
39
|
"engines": {
|
|
40
40
|
"node": ">=22"
|
package/src/intent/dispatch.ts
CHANGED
|
@@ -18,12 +18,22 @@ import {
|
|
|
18
18
|
import { unwrapBrokerResult } from '../broker-result.js'
|
|
19
19
|
import { parseJevEnv, type JevEnv } from './env.js'
|
|
20
20
|
import {
|
|
21
|
+
JevEvaluateError,
|
|
21
22
|
liveEvaluator,
|
|
22
23
|
namedTargetAllowed,
|
|
23
24
|
suggestionFromAnswers,
|
|
24
25
|
type EvaluateFn
|
|
25
26
|
} from './evaluate.js'
|
|
26
|
-
import
|
|
27
|
+
import { elapsedMs } from './http.js'
|
|
28
|
+
import {
|
|
29
|
+
createJevCall,
|
|
30
|
+
emitCall,
|
|
31
|
+
recordRank,
|
|
32
|
+
type JevCall,
|
|
33
|
+
type JevLogger,
|
|
34
|
+
type JevRankStats,
|
|
35
|
+
type RankRecorder
|
|
36
|
+
} from './log.js'
|
|
27
37
|
import { clickableMoves, parseTreeMoves } from './tree.js'
|
|
28
38
|
|
|
29
39
|
export type IntentBroker = {
|
|
@@ -38,6 +48,10 @@ export type DispatchOptions = {
|
|
|
38
48
|
|
|
39
49
|
const NAMED_GATE = new Set<ComputerOperationName>(['click', 'setValue'])
|
|
40
50
|
|
|
51
|
+
type RankOutcome =
|
|
52
|
+
| { ok: true; suggestion: Suggestion; stats: JevRankStats }
|
|
53
|
+
| { ok: false; stats: JevRankStats }
|
|
54
|
+
|
|
41
55
|
function policyUnavailable() {
|
|
42
56
|
return createComputerError('policy_unavailable', 'Jev could not rank this snapshot').toJSON()
|
|
43
57
|
}
|
|
@@ -90,40 +104,81 @@ function boundIndex(operation: ComputerOperationName, suggestion: Suggestion): n
|
|
|
90
104
|
return undefined
|
|
91
105
|
}
|
|
92
106
|
|
|
107
|
+
function errorFromResult(result: unknown): string | undefined {
|
|
108
|
+
const mutation = PublicMutationResultSchema.safeParse(result)
|
|
109
|
+
if (mutation.success) {
|
|
110
|
+
const outcome = mutation.data.outcome
|
|
111
|
+
if (outcome.state === 'not_attempted' || outcome.state === 'failed') return outcome.error?.code
|
|
112
|
+
return undefined
|
|
113
|
+
}
|
|
114
|
+
const snapshot = PublicSnapshotResultSchema.safeParse(result)
|
|
115
|
+
if (!snapshot.success) return undefined
|
|
116
|
+
return snapshot.data.issues.find((issue) => issue.code === 'policy_unavailable')?.code
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function boundFromCall(call: JevCall | undefined, result: unknown): boolean | undefined {
|
|
120
|
+
if (call?.phase !== 'bind') return undefined
|
|
121
|
+
const mutation = PublicMutationResultSchema.safeParse(result)
|
|
122
|
+
return mutation.success && mutation.data.resolvedTarget !== undefined
|
|
123
|
+
}
|
|
124
|
+
|
|
93
125
|
async function rank(
|
|
94
126
|
look: SnapshotResult,
|
|
95
127
|
goal: string,
|
|
96
128
|
evaluate: EvaluateFn,
|
|
97
|
-
log: JevLogger | undefined,
|
|
98
129
|
app?: string
|
|
99
|
-
): Promise<
|
|
130
|
+
): Promise<RankOutcome> {
|
|
131
|
+
const snapshotId = look.snapshot.id
|
|
132
|
+
const parseStarted = Date.now()
|
|
100
133
|
const moves = clickableMoves(parseTreeMoves(look.snapshot.treeText))
|
|
134
|
+
const stats: JevRankStats = {
|
|
135
|
+
snapshotId,
|
|
136
|
+
treeChars: look.snapshot.treeText.length,
|
|
137
|
+
elementCount: look.snapshot.elementCount,
|
|
138
|
+
candidateCount: moves.length,
|
|
139
|
+
parseMs: elapsedMs(parseStarted),
|
|
140
|
+
goalChars: goal.length,
|
|
141
|
+
app: look.snapshot.app.id
|
|
142
|
+
}
|
|
101
143
|
if (moves.length === 0) {
|
|
102
144
|
return {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
145
|
+
ok: true,
|
|
146
|
+
suggestion: {
|
|
147
|
+
untrusted: true,
|
|
148
|
+
snapshotId,
|
|
149
|
+
move: { kind: 'blocked', reason: 'no_candidate' }
|
|
150
|
+
},
|
|
151
|
+
stats
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const evalStarted = Date.now()
|
|
155
|
+
try {
|
|
156
|
+
const evaluated = await evaluate({
|
|
157
|
+
goal,
|
|
158
|
+
...(app === undefined ? {} : { app }),
|
|
159
|
+
moves
|
|
160
|
+
})
|
|
161
|
+
return {
|
|
162
|
+
ok: true,
|
|
163
|
+
suggestion: suggestionFromAnswers(snapshotId, moves, evaluated.answers),
|
|
164
|
+
stats: {
|
|
165
|
+
...stats,
|
|
166
|
+
evaluateMs: elapsedMs(evalStarted),
|
|
167
|
+
...(evaluated.http === undefined ? {} : { http: evaluated.http })
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} catch (cause) {
|
|
171
|
+
if (cause instanceof ComputerError) throw cause
|
|
172
|
+
const http = cause instanceof JevEvaluateError ? cause.http : undefined
|
|
173
|
+
return {
|
|
174
|
+
ok: false,
|
|
175
|
+
stats: {
|
|
176
|
+
...stats,
|
|
177
|
+
evaluateMs: elapsedMs(evalStarted),
|
|
178
|
+
...(http === undefined ? {} : { http })
|
|
179
|
+
}
|
|
106
180
|
}
|
|
107
181
|
}
|
|
108
|
-
const suggestion = suggestionFromAnswers(
|
|
109
|
-
look.snapshot.id,
|
|
110
|
-
moves,
|
|
111
|
-
await evaluate({ goal, ...(app === undefined ? {} : { app }), moves })
|
|
112
|
-
)
|
|
113
|
-
log?.emit({
|
|
114
|
-
kind: 'jev.decision',
|
|
115
|
-
snapshotId: suggestion.snapshotId,
|
|
116
|
-
move: suggestion.move.kind,
|
|
117
|
-
...(suggestion.move.kind === 'click' ||
|
|
118
|
-
suggestion.move.kind === 'setValue' ||
|
|
119
|
-
suggestion.move.kind === 'secondary' ||
|
|
120
|
-
suggestion.move.kind === 'scroll'
|
|
121
|
-
? { elementIndex: suggestion.move.elementIndex }
|
|
122
|
-
: {}),
|
|
123
|
-
confidence: suggestion.confidence,
|
|
124
|
-
goal
|
|
125
|
-
})
|
|
126
|
-
return suggestion
|
|
127
182
|
}
|
|
128
183
|
|
|
129
184
|
function evaluatorFor(env: JevEnv, override: EvaluateFn | undefined): EvaluateFn | undefined {
|
|
@@ -144,7 +199,7 @@ async function suggestObserve(
|
|
|
144
199
|
goal: string | undefined,
|
|
145
200
|
env: JevEnv,
|
|
146
201
|
evaluate: EvaluateFn | undefined,
|
|
147
|
-
|
|
202
|
+
record: RankRecorder
|
|
148
203
|
): Promise<unknown> {
|
|
149
204
|
const result = await request('getAppState', toBrokerInput('getAppState', input))
|
|
150
205
|
if (goal === undefined || env.kind === 'off') return result
|
|
@@ -152,10 +207,15 @@ async function suggestObserve(
|
|
|
152
207
|
if (env.kind === 'fail_closed' || evaluate === undefined) {
|
|
153
208
|
return withSnapshotIssue(look, policyUnavailable())
|
|
154
209
|
}
|
|
210
|
+
const app = 'app' in input && typeof input.app === 'string' ? input.app : undefined
|
|
155
211
|
try {
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
212
|
+
const ranked = await rank(look, goal, evaluate, app)
|
|
213
|
+
if (!ranked.ok) {
|
|
214
|
+
record('observe', ranked.stats)
|
|
215
|
+
return withSnapshotIssue(look, policyUnavailable())
|
|
216
|
+
}
|
|
217
|
+
record('observe', ranked.stats, ranked.suggestion)
|
|
218
|
+
return parsePublicOutput('getAppState', { ...look, suggestion: ranked.suggestion })
|
|
159
219
|
} catch {
|
|
160
220
|
return withSnapshotIssue(look, policyUnavailable())
|
|
161
221
|
}
|
|
@@ -168,7 +228,7 @@ async function bindIntentTarget(
|
|
|
168
228
|
goal: string | undefined,
|
|
169
229
|
env: JevEnv,
|
|
170
230
|
evaluate: EvaluateFn | undefined,
|
|
171
|
-
|
|
231
|
+
record: RankRecorder
|
|
172
232
|
): Promise<unknown> {
|
|
173
233
|
if (env.kind === 'off') {
|
|
174
234
|
throw createComputerError('invalid_argument', 'intent targeting requires CROSSHANDS_JEV=1')
|
|
@@ -181,16 +241,20 @@ async function bindIntentTarget(
|
|
|
181
241
|
throw createComputerError('invalid_argument', 'intent targeting requires a context token')
|
|
182
242
|
}
|
|
183
243
|
let look: SnapshotResult
|
|
184
|
-
let suggestion: Suggestion
|
|
185
244
|
try {
|
|
186
245
|
look = await lookAgain(request, token)
|
|
187
|
-
suggestion = await rank(look, goal, evaluate, log)
|
|
188
246
|
} catch (cause) {
|
|
189
247
|
if (cause instanceof ComputerError) throw cause
|
|
190
248
|
return notAttempted(policyUnavailable())
|
|
191
249
|
}
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
250
|
+
const ranked = await rank(look, goal, evaluate)
|
|
251
|
+
if (!ranked.ok) {
|
|
252
|
+
record('bind', ranked.stats)
|
|
253
|
+
return notAttempted(policyUnavailable())
|
|
254
|
+
}
|
|
255
|
+
record('bind', ranked.stats, ranked.suggestion)
|
|
256
|
+
const index = boundIndex(operation, ranked.suggestion)
|
|
257
|
+
if (index === undefined) return notAttempted(policyUnavailable(), ranked.suggestion)
|
|
194
258
|
const bound = {
|
|
195
259
|
...splitPublicInput(input).rest,
|
|
196
260
|
contextToken: look.context.token,
|
|
@@ -201,7 +265,7 @@ async function bindIntentTarget(
|
|
|
201
265
|
return parsePublicOutput(operation, {
|
|
202
266
|
...body,
|
|
203
267
|
resolvedTarget: { kind: 'element', elementIndex: index },
|
|
204
|
-
suggestion
|
|
268
|
+
suggestion: ranked.suggestion
|
|
205
269
|
})
|
|
206
270
|
}
|
|
207
271
|
|
|
@@ -210,25 +274,30 @@ async function gateNamedTarget(
|
|
|
210
274
|
input: object,
|
|
211
275
|
goal: string,
|
|
212
276
|
evaluate: EvaluateFn,
|
|
213
|
-
|
|
277
|
+
record: RankRecorder
|
|
214
278
|
): Promise<unknown | undefined> {
|
|
215
279
|
const named = elementIndexOf(input)
|
|
216
280
|
const token = contextTokenOf(input)
|
|
217
281
|
if (token === undefined || named === undefined) return undefined
|
|
218
282
|
try {
|
|
219
283
|
const look = await lookAgain(request, token)
|
|
220
|
-
const
|
|
284
|
+
const ranked = await rank(look, goal, evaluate)
|
|
285
|
+
if (!ranked.ok) {
|
|
286
|
+
record('named', ranked.stats)
|
|
287
|
+
return notAttempted(policyUnavailable())
|
|
288
|
+
}
|
|
289
|
+
record('named', ranked.stats, ranked.suggestion)
|
|
221
290
|
const picked =
|
|
222
|
-
suggestion.move.kind === 'click' || suggestion.move.kind === 'setValue'
|
|
223
|
-
? suggestion.move.elementIndex
|
|
291
|
+
ranked.suggestion.move.kind === 'click' || ranked.suggestion.move.kind === 'setValue'
|
|
292
|
+
? ranked.suggestion.move.elementIndex
|
|
224
293
|
: undefined
|
|
225
|
-
if (picked !== named || !namedTargetAllowed(suggestion.confidence)) {
|
|
294
|
+
if (picked !== named || !namedTargetAllowed(ranked.suggestion.confidence)) {
|
|
226
295
|
return notAttempted(
|
|
227
296
|
createComputerError(
|
|
228
297
|
'goal_mismatch',
|
|
229
298
|
'Named target does not match the per-call goal'
|
|
230
299
|
).toJSON(),
|
|
231
|
-
suggestion
|
|
300
|
+
ranked.suggestion
|
|
232
301
|
)
|
|
233
302
|
}
|
|
234
303
|
} catch {
|
|
@@ -247,30 +316,58 @@ export async function dispatchPublicOperation(
|
|
|
247
316
|
const parsed = parsePublicInput(operation, rawInput)
|
|
248
317
|
const { goal } = splitPublicInput(parsed)
|
|
249
318
|
const evaluate = evaluatorFor(envState, options.evaluate)
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
return suggestObserve(
|
|
255
|
-
request,
|
|
256
|
-
parsed as PublicOperationInput<'getAppState'>,
|
|
257
|
-
goal,
|
|
258
|
-
envState,
|
|
259
|
-
evaluate,
|
|
260
|
-
options.log
|
|
261
|
-
)
|
|
319
|
+
const log = options.log
|
|
320
|
+
const call = log === undefined ? undefined : createJevCall(operation, goal)
|
|
321
|
+
const record: RankRecorder = (phase, stats, suggestion) => {
|
|
322
|
+
recordRank(log, call, phase, stats, suggestion)
|
|
262
323
|
}
|
|
263
|
-
|
|
264
|
-
|
|
324
|
+
const request: IntentBroker['request'] = async (name, input) => {
|
|
325
|
+
if (call !== undefined) call.brokerCalls += 1
|
|
326
|
+
return unwrapBrokerResult(await broker.request(name, input))
|
|
265
327
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
328
|
+
|
|
329
|
+
async function dispatch(): Promise<unknown> {
|
|
330
|
+
if (operation === 'getAppState') {
|
|
331
|
+
return suggestObserve(
|
|
332
|
+
request,
|
|
333
|
+
parsed as PublicOperationInput<'getAppState'>,
|
|
334
|
+
goal,
|
|
335
|
+
envState,
|
|
336
|
+
evaluate,
|
|
337
|
+
record
|
|
338
|
+
)
|
|
339
|
+
}
|
|
340
|
+
if (hasIntentTarget(parsed)) {
|
|
341
|
+
return bindIntentTarget(request, operation, parsed, goal, envState, evaluate, record)
|
|
342
|
+
}
|
|
343
|
+
if (
|
|
344
|
+
goal !== undefined &&
|
|
345
|
+
envState.kind === 'on' &&
|
|
346
|
+
evaluate !== undefined &&
|
|
347
|
+
NAMED_GATE.has(operation)
|
|
348
|
+
) {
|
|
349
|
+
const gated = await gateNamedTarget(request, parsed, goal, evaluate, record)
|
|
350
|
+
if (gated !== undefined) return gated
|
|
351
|
+
}
|
|
352
|
+
return request(operation, toBrokerInput(operation, parsed))
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const started = Date.now()
|
|
356
|
+
try {
|
|
357
|
+
const result = await dispatch()
|
|
358
|
+
if (call !== undefined) {
|
|
359
|
+
const extra: { error?: string; bound?: boolean } = {}
|
|
360
|
+
const error = errorFromResult(result)
|
|
361
|
+
const bound = boundFromCall(call, result)
|
|
362
|
+
if (error !== undefined) extra.error = error
|
|
363
|
+
if (bound !== undefined) extra.bound = bound
|
|
364
|
+
emitCall(log, call, started, extra)
|
|
365
|
+
}
|
|
366
|
+
return result
|
|
367
|
+
} catch (cause) {
|
|
368
|
+
if (call !== undefined) {
|
|
369
|
+
emitCall(log, call, started, cause instanceof ComputerError ? { error: cause.code } : {})
|
|
370
|
+
}
|
|
371
|
+
throw cause
|
|
274
372
|
}
|
|
275
|
-
return request(operation, toBrokerInput(operation, parsed))
|
|
276
373
|
}
|