@nuanu-ai/agentbrowse 0.2.7 → 0.2.8
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/README.md +36 -8
- package/dist/agentpay-stagehand-llm.d.ts.map +1 -1
- package/dist/agentpay-stagehand-llm.js +5 -1
- package/dist/commands/act.d.ts +6 -2
- package/dist/commands/act.d.ts.map +1 -1
- package/dist/commands/act.js +840 -55
- package/dist/commands/act.test-harness.d.ts +19 -0
- package/dist/commands/act.test-harness.d.ts.map +1 -0
- package/dist/commands/act.test-harness.js +245 -0
- package/dist/commands/action-acceptance.d.ts +90 -0
- package/dist/commands/action-acceptance.d.ts.map +1 -0
- package/dist/commands/action-acceptance.js +1411 -0
- package/dist/commands/action-artifacts.d.ts +33 -0
- package/dist/commands/action-artifacts.d.ts.map +1 -0
- package/dist/commands/action-artifacts.js +104 -0
- package/dist/commands/action-execution-guards.d.ts +5 -0
- package/dist/commands/action-execution-guards.d.ts.map +1 -0
- package/dist/commands/action-execution-guards.js +3 -0
- package/dist/commands/action-executor-helpers.d.ts +21 -0
- package/dist/commands/action-executor-helpers.d.ts.map +1 -0
- package/dist/commands/action-executor-helpers.js +242 -0
- package/dist/commands/action-executor.d.ts +12 -0
- package/dist/commands/action-executor.d.ts.map +1 -0
- package/dist/commands/action-executor.js +45 -0
- package/dist/commands/action-fallbacks.d.ts +6 -0
- package/dist/commands/action-fallbacks.d.ts.map +1 -0
- package/dist/commands/action-fallbacks.js +43 -0
- package/dist/commands/action-value-projection.d.ts +32 -0
- package/dist/commands/action-value-projection.d.ts.map +1 -0
- package/dist/commands/action-value-projection.js +151 -0
- package/dist/commands/browse-actions.d.ts +4 -0
- package/dist/commands/browse-actions.d.ts.map +1 -0
- package/dist/commands/browse-actions.js +4 -0
- package/dist/commands/captcha-solve.d.ts.map +1 -1
- package/dist/commands/captcha-solve.js +13 -3
- package/dist/commands/click-action-executor.d.ts +10 -0
- package/dist/commands/click-action-executor.d.ts.map +1 -0
- package/dist/commands/click-action-executor.js +68 -0
- package/dist/commands/create-intent.d.ts +6 -0
- package/dist/commands/create-intent.d.ts.map +1 -0
- package/dist/commands/create-intent.js +75 -0
- package/dist/commands/datepicker-action-executor.d.ts +12 -0
- package/dist/commands/datepicker-action-executor.d.ts.map +1 -0
- package/dist/commands/datepicker-action-executor.js +218 -0
- package/dist/commands/descriptor-validation.d.ts +27 -0
- package/dist/commands/descriptor-validation.d.ts.map +1 -0
- package/dist/commands/descriptor-validation.js +333 -0
- package/dist/commands/extract-scope-resolution.d.ts +20 -0
- package/dist/commands/extract-scope-resolution.d.ts.map +1 -0
- package/dist/commands/extract-scope-resolution.js +100 -0
- package/dist/commands/extract-stagehand-executor.d.ts +17 -0
- package/dist/commands/extract-stagehand-executor.d.ts.map +1 -0
- package/dist/commands/extract-stagehand-executor.js +18 -0
- package/dist/commands/extract.d.ts +3 -2
- package/dist/commands/extract.d.ts.map +1 -1
- package/dist/commands/extract.js +256 -39
- package/dist/commands/fill-secret.d.ts +7 -0
- package/dist/commands/fill-secret.d.ts.map +1 -0
- package/dist/commands/fill-secret.js +371 -0
- package/dist/commands/get-secrets-catalog.d.ts +6 -0
- package/dist/commands/get-secrets-catalog.d.ts.map +1 -0
- package/dist/commands/get-secrets-catalog.js +23 -0
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +41 -7
- package/dist/commands/navigate.d.ts +2 -1
- package/dist/commands/navigate.d.ts.map +1 -1
- package/dist/commands/navigate.js +49 -12
- package/dist/commands/observe-inventory.d.ts +109 -0
- package/dist/commands/observe-inventory.d.ts.map +1 -0
- package/dist/commands/observe-inventory.js +2837 -0
- package/dist/commands/observe-persistence.d.ts +14 -0
- package/dist/commands/observe-persistence.d.ts.map +1 -0
- package/dist/commands/observe-persistence.js +170 -0
- package/dist/commands/observe-projection.d.ts +84 -0
- package/dist/commands/observe-projection.d.ts.map +1 -0
- package/dist/commands/observe-projection.js +140 -0
- package/dist/commands/observe-protected.d.ts +5 -0
- package/dist/commands/observe-protected.d.ts.map +1 -0
- package/dist/commands/observe-protected.js +18 -0
- package/dist/commands/observe-semantics.d.ts +10 -0
- package/dist/commands/observe-semantics.d.ts.map +1 -0
- package/dist/commands/observe-semantics.js +338 -0
- package/dist/commands/observe-stagehand.d.ts +48 -0
- package/dist/commands/observe-stagehand.d.ts.map +1 -0
- package/dist/commands/observe-stagehand.js +105 -0
- package/dist/commands/observe-surfaces.d.ts +9 -0
- package/dist/commands/observe-surfaces.d.ts.map +1 -0
- package/dist/commands/observe-surfaces.js +195 -0
- package/dist/commands/observe.d.ts +47 -1
- package/dist/commands/observe.d.ts.map +1 -1
- package/dist/commands/observe.js +173 -20
- package/dist/commands/observe.test-harness.d.ts +67 -0
- package/dist/commands/observe.test-harness.d.ts.map +1 -0
- package/dist/commands/observe.test-harness.js +107 -0
- package/dist/commands/poll-intent.d.ts +6 -0
- package/dist/commands/poll-intent.d.ts.map +1 -0
- package/dist/commands/poll-intent.js +57 -0
- package/dist/commands/screenshot.d.ts +2 -1
- package/dist/commands/screenshot.d.ts.map +1 -1
- package/dist/commands/screenshot.js +44 -12
- package/dist/commands/select-action-executor.d.ts +10 -0
- package/dist/commands/select-action-executor.d.ts.map +1 -0
- package/dist/commands/select-action-executor.js +91 -0
- package/dist/commands/semantic-observe.d.ts +24 -0
- package/dist/commands/semantic-observe.d.ts.map +1 -0
- package/dist/commands/semantic-observe.js +344 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +75 -2
- package/dist/commands/structured-grid-action-executor.d.ts +3 -0
- package/dist/commands/structured-grid-action-executor.d.ts.map +1 -0
- package/dist/commands/structured-grid-action-executor.js +4 -0
- package/dist/commands/target-resolution.d.ts +4 -0
- package/dist/commands/target-resolution.d.ts.map +1 -0
- package/dist/commands/target-resolution.js +33 -0
- package/dist/commands/text-input-action-executor.d.ts +5 -0
- package/dist/commands/text-input-action-executor.d.ts.map +1 -0
- package/dist/commands/text-input-action-executor.js +116 -0
- package/dist/commands/user-actionable.d.ts +4 -0
- package/dist/commands/user-actionable.d.ts.map +1 -0
- package/dist/commands/user-actionable.js +95 -0
- package/dist/control-semantics.d.ts +29 -0
- package/dist/control-semantics.d.ts.map +1 -0
- package/dist/control-semantics.js +299 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -32
- package/dist/output.d.ts +14 -2
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +17 -29
- package/dist/playwright-runtime.d.ts +35 -0
- package/dist/playwright-runtime.d.ts.map +1 -0
- package/dist/playwright-runtime.js +224 -0
- package/dist/runtime-resolution.d.ts +9 -0
- package/dist/runtime-resolution.d.ts.map +1 -0
- package/dist/runtime-resolution.js +19 -0
- package/dist/runtime-state.d.ts +217 -0
- package/dist/runtime-state.d.ts.map +1 -0
- package/dist/runtime-state.js +629 -0
- package/dist/secrets/backend.d.ts +32 -0
- package/dist/secrets/backend.d.ts.map +1 -0
- package/dist/secrets/backend.js +169 -0
- package/dist/secrets/catalog-applicability.d.ts +5 -0
- package/dist/secrets/catalog-applicability.d.ts.map +1 -0
- package/dist/secrets/catalog-applicability.js +59 -0
- package/dist/secrets/catalog-sync.d.ts +14 -0
- package/dist/secrets/catalog-sync.d.ts.map +1 -0
- package/dist/secrets/catalog-sync.js +35 -0
- package/dist/secrets/field-policy.d.ts +3 -0
- package/dist/secrets/field-policy.d.ts.map +1 -0
- package/dist/secrets/field-policy.js +3 -0
- package/dist/secrets/fill-ordering.d.ts +11 -0
- package/dist/secrets/fill-ordering.d.ts.map +1 -0
- package/dist/secrets/fill-ordering.js +44 -0
- package/dist/secrets/form-matcher.d.ts +60 -0
- package/dist/secrets/form-matcher.d.ts.map +1 -0
- package/dist/secrets/form-matcher.js +596 -0
- package/dist/secrets/intent-output.d.ts +11 -0
- package/dist/secrets/intent-output.d.ts.map +1 -0
- package/dist/secrets/intent-output.js +64 -0
- package/dist/secrets/mock-agentpay-backend.d.ts +13 -0
- package/dist/secrets/mock-agentpay-backend.d.ts.map +1 -0
- package/dist/secrets/mock-agentpay-backend.js +87 -0
- package/dist/secrets/mock-agentpay-cabinet.d.ts +43 -0
- package/dist/secrets/mock-agentpay-cabinet.d.ts.map +1 -0
- package/dist/secrets/mock-agentpay-cabinet.js +195 -0
- package/dist/secrets/protected-artifact-guard.d.ts +25 -0
- package/dist/secrets/protected-artifact-guard.d.ts.map +1 -0
- package/dist/secrets/protected-artifact-guard.js +26 -0
- package/dist/secrets/protected-bindings.d.ts +10 -0
- package/dist/secrets/protected-bindings.d.ts.map +1 -0
- package/dist/secrets/protected-bindings.js +17 -0
- package/dist/secrets/protected-field-values.d.ts +13 -0
- package/dist/secrets/protected-field-values.d.ts.map +1 -0
- package/dist/secrets/protected-field-values.js +100 -0
- package/dist/secrets/protected-fill.d.ts +47 -0
- package/dist/secrets/protected-fill.d.ts.map +1 -0
- package/dist/secrets/protected-fill.js +512 -0
- package/dist/secrets/types.d.ts +84 -0
- package/dist/secrets/types.d.ts.map +1 -0
- package/dist/secrets/types.js +27 -0
- package/dist/session.d.ts +22 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +74 -2
- package/dist/solver/browser-launcher.d.ts.map +1 -1
- package/dist/solver/browser-launcher.js +6 -3
- package/dist/stagehand-runtime.d.ts +4 -0
- package/dist/stagehand-runtime.d.ts.map +1 -0
- package/dist/stagehand-runtime.js +10 -0
- package/dist/stagehand.d.ts +0 -5
- package/dist/stagehand.d.ts.map +1 -1
- package/dist/stagehand.js +0 -6
- package/package.json +5 -2
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browse fill-secret <fillRef> <intentId> — Fill protected fields from a cached one-time secret payload
|
|
3
|
+
* using the persisted plan and deterministic browser execution.
|
|
4
|
+
*/
|
|
5
|
+
import { outputFailure, outputJSON } from '../output.js';
|
|
6
|
+
import { getFillableForm, getSecretIntentSnapshot, saveProtectedExposure, saveSecretIntentSnapshot, } from '../runtime-state.js';
|
|
7
|
+
import { deleteCachedTransientSecret, getCachedTransientSecret, saveSession } from '../session.js';
|
|
8
|
+
import { connectPlaywright, disconnectPlaywright, resolvePageByRef, syncSessionPage, } from '../playwright-runtime.js';
|
|
9
|
+
import { tryResolveCatalogHost } from '../secrets/catalog-sync.js';
|
|
10
|
+
import { describeSecretIntentStatus, serializeSecretIntentContext, } from '../secrets/intent-output.js';
|
|
11
|
+
import { executeProtectedFill } from '../secrets/protected-fill.js';
|
|
12
|
+
function persistIntentSnapshot(session, snapshot) {
|
|
13
|
+
const persisted = saveSecretIntentSnapshot(session, snapshot);
|
|
14
|
+
saveSession(session);
|
|
15
|
+
return persisted;
|
|
16
|
+
}
|
|
17
|
+
function completeIntent(session, intentId) {
|
|
18
|
+
const current = getSecretIntentSnapshot(session, intentId);
|
|
19
|
+
if (!current) {
|
|
20
|
+
throw new Error('secret_intent_not_found');
|
|
21
|
+
}
|
|
22
|
+
if (current.status === 'completed') {
|
|
23
|
+
return current;
|
|
24
|
+
}
|
|
25
|
+
return persistIntentSnapshot(session, {
|
|
26
|
+
...current,
|
|
27
|
+
status: 'completed',
|
|
28
|
+
completedAt: new Date().toISOString(),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function activateProtectedExposure(session, fillableForm, intentId, reason) {
|
|
32
|
+
saveProtectedExposure(session, {
|
|
33
|
+
pageRef: fillableForm.pageRef,
|
|
34
|
+
scopeRef: fillableForm.scopeRef,
|
|
35
|
+
fillRef: fillableForm.fillRef,
|
|
36
|
+
intentId,
|
|
37
|
+
activatedAt: new Date().toISOString(),
|
|
38
|
+
reason,
|
|
39
|
+
});
|
|
40
|
+
saveSession(session);
|
|
41
|
+
}
|
|
42
|
+
function restrictFormToCandidateFields(fillableForm, storedSecretCandidate) {
|
|
43
|
+
if (!storedSecretCandidate?.fieldKeys || storedSecretCandidate.fieldKeys.length === 0) {
|
|
44
|
+
return fillableForm;
|
|
45
|
+
}
|
|
46
|
+
const allowedFieldKeys = new Set(storedSecretCandidate.fieldKeys);
|
|
47
|
+
const filteredFields = fillableForm.fields.filter((field) => allowedFieldKeys.has(field.fieldKey));
|
|
48
|
+
if (filteredFields.length === fillableForm.fields.length || filteredFields.length === 0) {
|
|
49
|
+
return filteredFields.length === 0 ? fillableForm : { ...fillableForm, fields: filteredFields };
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
...fillableForm,
|
|
53
|
+
fields: filteredFields,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function nextActionForNonApprovedIntent(status) {
|
|
57
|
+
switch (status) {
|
|
58
|
+
case 'pending':
|
|
59
|
+
case 'checking':
|
|
60
|
+
case 'notify':
|
|
61
|
+
case 'confirmation':
|
|
62
|
+
return 'wait-for-approval';
|
|
63
|
+
case 'denied':
|
|
64
|
+
return 'ask-user';
|
|
65
|
+
case 'timed_out':
|
|
66
|
+
case 'completed':
|
|
67
|
+
return 'ask-user';
|
|
68
|
+
default:
|
|
69
|
+
return 'ask-user';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function messageForNonApprovedIntent(status) {
|
|
73
|
+
switch (status) {
|
|
74
|
+
case 'pending':
|
|
75
|
+
case 'checking':
|
|
76
|
+
case 'notify':
|
|
77
|
+
case 'confirmation':
|
|
78
|
+
return 'Protected fill is waiting for user approval in AgentPay Cabinet.';
|
|
79
|
+
case 'denied':
|
|
80
|
+
return 'Protected fill is blocked because the user denied this intent in AgentPay Cabinet.';
|
|
81
|
+
case 'timed_out':
|
|
82
|
+
case 'completed':
|
|
83
|
+
return 'Protected fill requires a new intent because this intent is no longer usable.';
|
|
84
|
+
default:
|
|
85
|
+
return 'Protected fill cannot continue because the intent is not approved.';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function outcomeTypeForNonApprovedIntent(status) {
|
|
89
|
+
return describeSecretIntentStatus(status).outcomeType;
|
|
90
|
+
}
|
|
91
|
+
function reasonForNonApprovedIntent(status) {
|
|
92
|
+
return describeSecretIntentStatus(status).reason;
|
|
93
|
+
}
|
|
94
|
+
export async function fillSecret(session, fillRef, intentId) {
|
|
95
|
+
const fillableForm = getFillableForm(session, fillRef);
|
|
96
|
+
if (!fillableForm) {
|
|
97
|
+
return outputFailure({
|
|
98
|
+
error: 'unknown_fill_ref',
|
|
99
|
+
outcomeType: 'blocked',
|
|
100
|
+
message: 'Protected fill was not started because the requested fill reference is unknown.',
|
|
101
|
+
reason: 'The provided fillRef does not match any currently observed protected fill target.',
|
|
102
|
+
fillRef,
|
|
103
|
+
intentId,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const intent = getSecretIntentSnapshot(session, intentId);
|
|
107
|
+
if (!intent) {
|
|
108
|
+
return outputFailure({
|
|
109
|
+
error: 'secret_intent_not_found',
|
|
110
|
+
outcomeType: 'blocked',
|
|
111
|
+
message: 'Protected fill was not started because the requested intent does not exist.',
|
|
112
|
+
reason: 'The provided intentId was not found in AgentPay Cabinet.',
|
|
113
|
+
fillRef,
|
|
114
|
+
intentId,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const latestIntent = persistIntentSnapshot(session, intent);
|
|
118
|
+
if (latestIntent.fillRef !== fillRef) {
|
|
119
|
+
return outputFailure({
|
|
120
|
+
error: 'secret_intent_fill_mismatch',
|
|
121
|
+
outcomeType: 'blocked',
|
|
122
|
+
message: 'Protected fill was blocked because the approved intent belongs to a different fill target.',
|
|
123
|
+
reason: `The approved intent belongs to fillRef ${latestIntent.fillRef}, not ${fillRef}.`,
|
|
124
|
+
fillRef,
|
|
125
|
+
intentId,
|
|
126
|
+
...serializeSecretIntentContext(latestIntent),
|
|
127
|
+
intentFillRef: latestIntent.fillRef,
|
|
128
|
+
intentStatus: latestIntent.status,
|
|
129
|
+
nextAction: 'reobserve',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
if (!(latestIntent.status === 'approved' || latestIntent.status === 'completed')) {
|
|
133
|
+
return outputFailure({
|
|
134
|
+
error: 'secret_intent_not_approved',
|
|
135
|
+
...(outcomeTypeForNonApprovedIntent(latestIntent.status)
|
|
136
|
+
? { outcomeType: outcomeTypeForNonApprovedIntent(latestIntent.status) }
|
|
137
|
+
: {}),
|
|
138
|
+
message: messageForNonApprovedIntent(latestIntent.status),
|
|
139
|
+
reason: reasonForNonApprovedIntent(latestIntent.status),
|
|
140
|
+
fillRef,
|
|
141
|
+
intentId,
|
|
142
|
+
...serializeSecretIntentContext(latestIntent),
|
|
143
|
+
intentStatus: latestIntent.status,
|
|
144
|
+
nextAction: nextActionForNonApprovedIntent(latestIntent.status),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
const cachedSecret = getCachedTransientSecret(session, intentId);
|
|
148
|
+
if (!cachedSecret) {
|
|
149
|
+
const missingCacheReason = latestIntent.status === 'completed'
|
|
150
|
+
? 'This one-time intent has already delivered its protected payload, but the current session no longer has it cached.'
|
|
151
|
+
: 'Run poll-intent after approval so AgentBrowse can cache the transient protected values before fill-secret.';
|
|
152
|
+
const missingCacheNextAction = latestIntent.status === 'completed' ? 'ask-user' : 'poll-intent';
|
|
153
|
+
const missingCacheMessage = latestIntent.status === 'completed'
|
|
154
|
+
? 'Protected fill could not continue because the one-time secret payload is no longer cached in the current session.'
|
|
155
|
+
: 'Protected fill could not continue because the approved secret payload is not cached in the current session.';
|
|
156
|
+
return outputFailure({
|
|
157
|
+
error: 'transient_secret_not_cached',
|
|
158
|
+
outcomeType: 'blocked',
|
|
159
|
+
message: missingCacheMessage,
|
|
160
|
+
reason: missingCacheReason,
|
|
161
|
+
fillRef,
|
|
162
|
+
intentId,
|
|
163
|
+
...serializeSecretIntentContext(latestIntent),
|
|
164
|
+
intentStatus: latestIntent.status,
|
|
165
|
+
nextAction: missingCacheNextAction,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (cachedSecret.fillRef !== fillRef ||
|
|
169
|
+
cachedSecret.storedSecretRef !== latestIntent.storedSecretRef) {
|
|
170
|
+
deleteCachedTransientSecret(session, intentId);
|
|
171
|
+
saveSession(session);
|
|
172
|
+
return outputFailure({
|
|
173
|
+
error: 'transient_secret_context_mismatch',
|
|
174
|
+
outcomeType: 'blocked',
|
|
175
|
+
message: 'Protected fill was blocked because the cached transient secret no longer matches this approved intent.',
|
|
176
|
+
reason: 'The cached transient payload belongs to a different fill target or stored secret reference.',
|
|
177
|
+
fillRef,
|
|
178
|
+
intentId,
|
|
179
|
+
...serializeSecretIntentContext(latestIntent),
|
|
180
|
+
intentStatus: latestIntent.status,
|
|
181
|
+
nextAction: 'poll-intent',
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
let browser = null;
|
|
185
|
+
try {
|
|
186
|
+
browser = await connectPlaywright(session.cdpUrl);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
deleteCachedTransientSecret(session, intentId);
|
|
190
|
+
saveSession(session);
|
|
191
|
+
return outputFailure({
|
|
192
|
+
error: 'browser_connection_failed',
|
|
193
|
+
message: 'Protected fill could not connect to the browser.',
|
|
194
|
+
fillRef,
|
|
195
|
+
intentId,
|
|
196
|
+
...serializeSecretIntentContext(latestIntent),
|
|
197
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const connectedBrowser = browser;
|
|
201
|
+
let finalResult = null;
|
|
202
|
+
try {
|
|
203
|
+
const page = await resolvePageByRef(connectedBrowser, session, fillableForm.pageRef);
|
|
204
|
+
const syncedPage = await syncSessionPage(session, fillableForm.pageRef, page);
|
|
205
|
+
const observedHost = tryResolveCatalogHost(syncedPage.url);
|
|
206
|
+
const contextMismatchReasons = [];
|
|
207
|
+
if (latestIntent.host && observedHost && latestIntent.host !== observedHost) {
|
|
208
|
+
contextMismatchReasons.push('host');
|
|
209
|
+
}
|
|
210
|
+
if (latestIntent.scopeRef && latestIntent.scopeRef !== fillableForm.scopeRef) {
|
|
211
|
+
contextMismatchReasons.push('scope');
|
|
212
|
+
}
|
|
213
|
+
if (contextMismatchReasons.length > 0) {
|
|
214
|
+
const completedIntent = completeIntent(session, intentId);
|
|
215
|
+
finalResult = {
|
|
216
|
+
success: false,
|
|
217
|
+
payload: {
|
|
218
|
+
error: 'secret_intent_context_mismatch',
|
|
219
|
+
outcomeType: 'blocked',
|
|
220
|
+
message: 'Protected fill was blocked because the page or protected surface no longer matches the approved intent context.',
|
|
221
|
+
reason: 'The current page host or protected surface no longer matches the context that was approved for this intent.',
|
|
222
|
+
fillRef,
|
|
223
|
+
intentId,
|
|
224
|
+
...serializeSecretIntentContext(completedIntent),
|
|
225
|
+
intentStatus: completedIntent.status,
|
|
226
|
+
nextAction: 'reobserve',
|
|
227
|
+
intentReusable: false,
|
|
228
|
+
mismatchReasons: contextMismatchReasons,
|
|
229
|
+
...(latestIntent.host ? { expectedHost: latestIntent.host } : {}),
|
|
230
|
+
...(observedHost ? { observedHost } : {}),
|
|
231
|
+
...(latestIntent.scopeRef ? { expectedScopeRef: latestIntent.scopeRef } : {}),
|
|
232
|
+
...(fillableForm.scopeRef ? { observedScopeRef: fillableForm.scopeRef } : {}),
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
const protectedValues = { ...cachedSecret.values };
|
|
238
|
+
const storedSecretCandidate = fillableForm.storedSecretCandidates.find((candidate) => candidate.storedSecretRef === latestIntent.storedSecretRef);
|
|
239
|
+
const executableForm = restrictFormToCandidateFields(fillableForm, storedSecretCandidate);
|
|
240
|
+
try {
|
|
241
|
+
const execution = await executeProtectedFill({
|
|
242
|
+
session,
|
|
243
|
+
page,
|
|
244
|
+
fillableForm: executableForm,
|
|
245
|
+
protectedValues,
|
|
246
|
+
fieldPolicies: storedSecretCandidate?.fieldPolicies,
|
|
247
|
+
});
|
|
248
|
+
if (execution.kind === 'success') {
|
|
249
|
+
const completedIntent = completeIntent(session, intentId);
|
|
250
|
+
activateProtectedExposure(session, fillableForm, intentId, 'protected_fill_success');
|
|
251
|
+
finalResult = {
|
|
252
|
+
success: true,
|
|
253
|
+
payload: {
|
|
254
|
+
fillRef,
|
|
255
|
+
intentId,
|
|
256
|
+
...serializeSecretIntentContext(completedIntent),
|
|
257
|
+
filledFields: execution.filledFields,
|
|
258
|
+
intentStatus: completedIntent.status,
|
|
259
|
+
message: 'Protected fill completed successfully.',
|
|
260
|
+
reason: 'AgentBrowse filled the protected fields using the one-time secret payload.',
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
else if (execution.kind === 'binding_stale') {
|
|
265
|
+
const completedIntent = completeIntent(session, intentId);
|
|
266
|
+
activateProtectedExposure(session, fillableForm, intentId, 'protected_fill_binding_stale');
|
|
267
|
+
finalResult = {
|
|
268
|
+
success: false,
|
|
269
|
+
payload: {
|
|
270
|
+
error: 'secret_fill_binding_stale',
|
|
271
|
+
outcomeType: 'binding_stale',
|
|
272
|
+
message: 'Protected fill was blocked because the stored field bindings are no longer valid on the page.',
|
|
273
|
+
fillRef,
|
|
274
|
+
intentId,
|
|
275
|
+
...serializeSecretIntentContext(completedIntent),
|
|
276
|
+
intentStatus: completedIntent.status,
|
|
277
|
+
nextAction: 'reobserve',
|
|
278
|
+
targetRef: execution.targetRef,
|
|
279
|
+
fieldKeys: execution.fieldKeys,
|
|
280
|
+
reason: execution.reason,
|
|
281
|
+
attempts: execution.attempts,
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
else if (execution.kind === 'validation_failed') {
|
|
286
|
+
const completedIntent = completeIntent(session, intentId);
|
|
287
|
+
activateProtectedExposure(session, fillableForm, intentId, 'protected_fill_validation_failed');
|
|
288
|
+
finalResult = {
|
|
289
|
+
success: false,
|
|
290
|
+
payload: {
|
|
291
|
+
error: 'secret_validation_failed',
|
|
292
|
+
outcomeType: 'blocked',
|
|
293
|
+
message: 'Protected fill stopped because the stored data did not pass client-side validation.',
|
|
294
|
+
reason: 'Protected execution reported client-side validation errors for one or more filled fields.',
|
|
295
|
+
fillRef,
|
|
296
|
+
intentId,
|
|
297
|
+
...serializeSecretIntentContext(completedIntent),
|
|
298
|
+
intentStatus: completedIntent.status,
|
|
299
|
+
executionResult: 'validation_failed',
|
|
300
|
+
nextAction: 'ask-user',
|
|
301
|
+
manualOverrideAllowed: true,
|
|
302
|
+
filledFields: execution.filledFields,
|
|
303
|
+
fieldErrors: execution.fieldErrors,
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
const completedIntent = completeIntent(session, intentId);
|
|
309
|
+
activateProtectedExposure(session, fillableForm, intentId, 'protected_fill_unexpected_error');
|
|
310
|
+
finalResult = {
|
|
311
|
+
success: false,
|
|
312
|
+
payload: {
|
|
313
|
+
error: 'protected_fill_execution_failed',
|
|
314
|
+
outcomeType: 'blocked',
|
|
315
|
+
message: 'Protected fill failed during protected execution, so this approved intent must not be reused.',
|
|
316
|
+
fillRef,
|
|
317
|
+
intentId,
|
|
318
|
+
...serializeSecretIntentContext(completedIntent),
|
|
319
|
+
intentStatus: completedIntent.status,
|
|
320
|
+
nextAction: 'ask-user',
|
|
321
|
+
reason: execution.reason,
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
const completedIntent = completeIntent(session, intentId);
|
|
328
|
+
activateProtectedExposure(session, fillableForm, intentId, 'protected_fill_unexpected_error');
|
|
329
|
+
finalResult = {
|
|
330
|
+
success: false,
|
|
331
|
+
payload: {
|
|
332
|
+
error: 'protected_fill_execution_failed',
|
|
333
|
+
outcomeType: 'blocked',
|
|
334
|
+
message: 'Protected fill failed during protected execution, so this approved intent must not be reused.',
|
|
335
|
+
fillRef,
|
|
336
|
+
intentId,
|
|
337
|
+
...serializeSecretIntentContext(completedIntent),
|
|
338
|
+
intentStatus: completedIntent.status,
|
|
339
|
+
nextAction: 'ask-user',
|
|
340
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
finally {
|
|
347
|
+
deleteCachedTransientSecret(session, intentId);
|
|
348
|
+
saveSession(session);
|
|
349
|
+
if (browser) {
|
|
350
|
+
disconnectPlaywright(browser);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (!finalResult) {
|
|
354
|
+
return outputFailure({
|
|
355
|
+
error: 'protected_fill_execution_failed',
|
|
356
|
+
outcomeType: 'blocked',
|
|
357
|
+
message: 'Protected fill did not produce a final result.',
|
|
358
|
+
reason: 'Protected execution finished without returning a terminal success or failure payload.',
|
|
359
|
+
fillRef,
|
|
360
|
+
intentId,
|
|
361
|
+
...serializeSecretIntentContext(latestIntent),
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
if (!finalResult.success) {
|
|
365
|
+
return outputFailure(finalResult.payload);
|
|
366
|
+
}
|
|
367
|
+
return outputJSON({
|
|
368
|
+
success: true,
|
|
369
|
+
...finalResult.payload,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browse get-secrets-catalog [url] — Refresh stored-secret catalog metadata for a host.
|
|
3
|
+
*/
|
|
4
|
+
import type { BrowseSession } from '../session.js';
|
|
5
|
+
export declare function getSecretsCatalog(session: BrowseSession, urlOrHost?: string): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=get-secrets-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-secrets-catalog.d.ts","sourceRoot":"","sources":["../../src/commands/get-secrets-catalog.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAKnD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBjG"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browse get-secrets-catalog [url] — Refresh stored-secret catalog metadata for a host.
|
|
3
|
+
*/
|
|
4
|
+
import { saveSession } from '../session.js';
|
|
5
|
+
import { outputContractFailure, outputJSON } from '../output.js';
|
|
6
|
+
import { getCurrentSessionPageUrl, syncSecretCatalogForUrl } from '../secrets/catalog-sync.js';
|
|
7
|
+
export async function getSecretsCatalog(session, urlOrHost) {
|
|
8
|
+
const syncInput = urlOrHost ?? getCurrentSessionPageUrl(session);
|
|
9
|
+
if (!syncInput) {
|
|
10
|
+
return outputContractFailure({
|
|
11
|
+
error: 'secret_sync_missing_url',
|
|
12
|
+
outcomeType: 'blocked',
|
|
13
|
+
message: 'Stored-secret sync could not start because there is no current URL.',
|
|
14
|
+
reason: 'Provide a URL explicitly or create page context first by navigating or observing.',
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
const snapshot = await syncSecretCatalogForUrl(session, syncInput);
|
|
18
|
+
saveSession(session);
|
|
19
|
+
outputJSON({
|
|
20
|
+
success: true,
|
|
21
|
+
...snapshot,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../src/commands/launch.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../src/commands/launch.ts"],"names":[],"mappings":"AAAA;;GAEG;AAsBH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,wBAAsB,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQjF"}
|
package/dist/commands/launch.js
CHANGED
|
@@ -7,8 +7,10 @@ import { ensureProfile } from '../solver/profile-manager.js';
|
|
|
7
7
|
import { launchSolver } from '../solver/browser-launcher.js';
|
|
8
8
|
import { saveSession } from '../session.js';
|
|
9
9
|
import { findChromePid } from '../stagehand.js';
|
|
10
|
-
import {
|
|
10
|
+
import { outputContractFailure, outputJSON } from '../output.js';
|
|
11
11
|
import { applyAgentpayGatewayEnv, tryResolveAgentpayGatewayConfig } from '../agentpay-gateway.js';
|
|
12
|
+
import { connectPlaywright, disconnectPlaywright, syncLaunchPage } from '../playwright-runtime.js';
|
|
13
|
+
import { summarizeSecretCatalog, syncSecretCatalogForUrl } from '../secrets/catalog-sync.js';
|
|
12
14
|
const CDP_PORT = 9222;
|
|
13
15
|
const DEFAULT_PROFILE = 'default';
|
|
14
16
|
const COMPACT_WINDOW = {
|
|
@@ -50,6 +52,8 @@ async function killChromeOnPort(port) {
|
|
|
50
52
|
}
|
|
51
53
|
async function launchManaged(url, profileName, headless, compact) {
|
|
52
54
|
let session;
|
|
55
|
+
let browser = null;
|
|
56
|
+
let secretCatalogSummary;
|
|
53
57
|
try {
|
|
54
58
|
const profile = ensureProfile(profileName);
|
|
55
59
|
const config = readConfig();
|
|
@@ -72,10 +76,15 @@ async function launchManaged(url, profileName, headless, compact) {
|
|
|
72
76
|
});
|
|
73
77
|
}
|
|
74
78
|
catch (err) {
|
|
75
|
-
|
|
79
|
+
outputContractFailure({
|
|
80
|
+
error: 'browser_launch_failed',
|
|
81
|
+
outcomeType: 'blocked',
|
|
82
|
+
message: 'Browser launch failed.',
|
|
83
|
+
reason: formatUnknownError(err),
|
|
84
|
+
});
|
|
76
85
|
}
|
|
77
86
|
const chromePid = findChromePid(CDP_PORT) ?? process.pid;
|
|
78
|
-
|
|
87
|
+
const persistedSession = {
|
|
79
88
|
cdpUrl: session.cdpUrl,
|
|
80
89
|
pid: chromePid,
|
|
81
90
|
port: CDP_PORT,
|
|
@@ -84,10 +93,34 @@ async function launchManaged(url, profileName, headless, compact) {
|
|
|
84
93
|
capabilities: {
|
|
85
94
|
captchaSolve: true,
|
|
86
95
|
},
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
await session.
|
|
96
|
+
};
|
|
97
|
+
saveSession(persistedSession);
|
|
98
|
+
let currentUrl = session.page.url();
|
|
99
|
+
let title = await session.page.title().catch(() => '');
|
|
100
|
+
try {
|
|
101
|
+
browser = await connectPlaywright(session.cdpUrl);
|
|
102
|
+
const syncedPage = await syncLaunchPage(persistedSession, browser, {
|
|
103
|
+
requestedUrl: url,
|
|
104
|
+
fallbackUrl: currentUrl,
|
|
105
|
+
fallbackTitle: title,
|
|
106
|
+
});
|
|
107
|
+
currentUrl = syncedPage.url;
|
|
108
|
+
title = syncedPage.title;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Preserve the successful launch and fall back to the launcher snapshot.
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
if (browser) {
|
|
115
|
+
disconnectPlaywright(browser);
|
|
116
|
+
}
|
|
117
|
+
await session.disconnect();
|
|
118
|
+
}
|
|
119
|
+
if (url) {
|
|
120
|
+
const snapshot = await syncSecretCatalogForUrl(persistedSession, currentUrl || url);
|
|
121
|
+
secretCatalogSummary = summarizeSecretCatalog(snapshot);
|
|
122
|
+
}
|
|
123
|
+
saveSession(persistedSession);
|
|
91
124
|
outputJSON({
|
|
92
125
|
success: true,
|
|
93
126
|
runtime: 'managed',
|
|
@@ -96,6 +129,7 @@ async function launchManaged(url, profileName, headless, compact) {
|
|
|
96
129
|
cdpUrl: session.cdpUrl,
|
|
97
130
|
url: currentUrl,
|
|
98
131
|
title,
|
|
132
|
+
secretCatalog: secretCatalogSummary,
|
|
99
133
|
});
|
|
100
134
|
}
|
|
101
135
|
function getPidsOnPort(port) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* browse navigate <url> — Navigate the current tab to a URL.
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
import type { BrowseSession } from '../session.js';
|
|
5
|
+
export declare function navigate(session: BrowseSession, targetUrl: string): Promise<void>;
|
|
5
6
|
//# sourceMappingURL=navigate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigate.d.ts","sourceRoot":"","sources":["../../src/commands/navigate.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"navigate.d.ts","sourceRoot":"","sources":["../../src/commands/navigate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAiBnD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDvF"}
|
|
@@ -1,26 +1,63 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* browse navigate <url> — Navigate the current tab to a URL.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import { clearProtectedExposure } from '../runtime-state.js';
|
|
5
|
+
import { saveSession } from '../session.js';
|
|
6
|
+
import { outputContractFailure, outputJSON, info } from '../output.js';
|
|
7
|
+
import { summarizeSecretCatalog, syncSecretCatalogForUrl, tryResolveCatalogHost, } from '../secrets/catalog-sync.js';
|
|
8
|
+
import { connectPlaywright, disconnectPlaywright, listPages, resolvePageByRef, syncSessionPage, } from '../playwright-runtime.js';
|
|
9
|
+
export async function navigate(session, targetUrl) {
|
|
10
|
+
let browser = null;
|
|
11
|
+
let failureMessage = null;
|
|
12
|
+
let secretCatalogSummary;
|
|
8
13
|
try {
|
|
9
|
-
|
|
14
|
+
browser = await connectPlaywright(session.cdpUrl);
|
|
10
15
|
}
|
|
11
16
|
catch (err) {
|
|
12
|
-
|
|
17
|
+
return outputContractFailure({
|
|
18
|
+
error: 'browser_connection_failed',
|
|
19
|
+
outcomeType: 'blocked',
|
|
20
|
+
message: 'Navigation could not start because AgentBrowse failed to connect to the browser.',
|
|
21
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
22
|
+
});
|
|
13
23
|
}
|
|
14
24
|
try {
|
|
15
|
-
const
|
|
25
|
+
const pageRef = session.runtime?.currentPageRef ?? 'p0';
|
|
26
|
+
const previousUrl = session.runtime?.pages?.[pageRef]?.url;
|
|
27
|
+
const previousHost = tryResolveCatalogHost(previousUrl);
|
|
28
|
+
const pages = listPages(browser);
|
|
29
|
+
const page = session.runtime?.pages[pageRef] || pages.length > 1
|
|
30
|
+
? await resolvePageByRef(browser, session, pageRef)
|
|
31
|
+
: pages[0];
|
|
32
|
+
if (!page) {
|
|
33
|
+
throw new Error('no_open_pages');
|
|
34
|
+
}
|
|
16
35
|
await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
|
|
36
|
+
const { url, title } = await syncSessionPage(session, pageRef, page);
|
|
37
|
+
clearProtectedExposure(session, pageRef);
|
|
38
|
+
const nextHost = tryResolveCatalogHost(url);
|
|
39
|
+
if (nextHost && nextHost !== previousHost) {
|
|
40
|
+
const snapshot = await syncSecretCatalogForUrl(session, url);
|
|
41
|
+
secretCatalogSummary = summarizeSecretCatalog(snapshot);
|
|
42
|
+
}
|
|
43
|
+
saveSession(session);
|
|
17
44
|
info(`Navigated to: ${targetUrl}`);
|
|
45
|
+
outputJSON({ success: true, pageRef, url, title, secretCatalog: secretCatalogSummary });
|
|
18
46
|
}
|
|
19
47
|
catch (err) {
|
|
20
|
-
|
|
21
|
-
|
|
48
|
+
failureMessage = `Navigation failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
if (browser) {
|
|
52
|
+
disconnectPlaywright(browser);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (failureMessage) {
|
|
56
|
+
outputContractFailure({
|
|
57
|
+
error: 'navigation_failed',
|
|
58
|
+
outcomeType: 'blocked',
|
|
59
|
+
message: 'Navigation failed.',
|
|
60
|
+
reason: failureMessage.replace(/^Navigation failed:\s*/, ''),
|
|
61
|
+
});
|
|
22
62
|
}
|
|
23
|
-
const { url, title } = await getPageInfo(stagehand);
|
|
24
|
-
await stagehand.close();
|
|
25
|
-
outputJSON({ success: true, url, title });
|
|
26
63
|
}
|