@trusty-squire/mcp 1.1.13-rc.2 → 1.1.13-rc.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +137 -52
  2. package/assets/login/vnc-input.js +9 -0
  3. package/assets/login/vnc.html +214 -202
  4. package/dist/api-client.d.ts +49 -4
  5. package/dist/api-client.d.ts.map +1 -1
  6. package/dist/api-client.js +25 -32
  7. package/dist/api-client.js.map +1 -1
  8. package/dist/bin.js +2 -3
  9. package/dist/bin.js.map +1 -1
  10. package/dist/bot/browser.d.ts +198 -33
  11. package/dist/bot/browser.d.ts.map +1 -1
  12. package/dist/bot/browser.js +3270 -1029
  13. package/dist/bot/browser.js.map +1 -1
  14. package/dist/bot/compact-observation-v2.d.ts +160 -0
  15. package/dist/bot/compact-observation-v2.d.ts.map +1 -0
  16. package/dist/bot/compact-observation-v2.js +950 -0
  17. package/dist/bot/compact-observation-v2.js.map +1 -0
  18. package/dist/bot/credential-shape.d.ts +2 -0
  19. package/dist/bot/credential-shape.d.ts.map +1 -1
  20. package/dist/bot/credential-shape.js +9 -3
  21. package/dist/bot/credential-shape.js.map +1 -1
  22. package/dist/bot/element-fingerprint.d.ts +9 -0
  23. package/dist/bot/element-fingerprint.d.ts.map +1 -0
  24. package/dist/bot/element-fingerprint.js +147 -0
  25. package/dist/bot/element-fingerprint.js.map +1 -0
  26. package/dist/bot/google-login.d.ts +44 -42
  27. package/dist/bot/google-login.d.ts.map +1 -1
  28. package/dist/bot/google-login.js +255 -973
  29. package/dist/bot/google-login.js.map +1 -1
  30. package/dist/bot/install-completion.d.ts +1 -2
  31. package/dist/bot/install-completion.d.ts.map +1 -1
  32. package/dist/bot/install-completion.js +1 -2
  33. package/dist/bot/install-completion.js.map +1 -1
  34. package/dist/bot/login-state.d.ts +13 -7
  35. package/dist/bot/login-state.d.ts.map +1 -1
  36. package/dist/bot/login-state.js +49 -113
  37. package/dist/bot/login-state.js.map +1 -1
  38. package/dist/bot/oauth-providers.d.ts +1 -1
  39. package/dist/bot/oauth-providers.d.ts.map +1 -1
  40. package/dist/bot/oauth-providers.js +3 -3
  41. package/dist/bot/oauth-providers.js.map +1 -1
  42. package/dist/bot/oauth-scope.d.ts +4 -0
  43. package/dist/bot/oauth-scope.d.ts.map +1 -0
  44. package/dist/bot/oauth-scope.js +58 -0
  45. package/dist/bot/oauth-scope.js.map +1 -0
  46. package/dist/bot/operator-browser-watchdog.d.ts +127 -0
  47. package/dist/bot/operator-browser-watchdog.d.ts.map +1 -0
  48. package/dist/bot/operator-browser-watchdog.js +438 -0
  49. package/dist/bot/operator-browser-watchdog.js.map +1 -0
  50. package/dist/bot/owner-process-reaper-worker.d.ts +2 -0
  51. package/dist/bot/owner-process-reaper-worker.d.ts.map +1 -0
  52. package/dist/bot/owner-process-reaper-worker.js +17 -0
  53. package/dist/bot/owner-process-reaper-worker.js.map +1 -0
  54. package/dist/bot/owner-process-reaper.d.ts +103 -0
  55. package/dist/bot/owner-process-reaper.d.ts.map +1 -0
  56. package/dist/bot/owner-process-reaper.js +1058 -0
  57. package/dist/bot/owner-process-reaper.js.map +1 -0
  58. package/dist/bot/pay-operator.d.ts +28 -3
  59. package/dist/bot/pay-operator.d.ts.map +1 -1
  60. package/dist/bot/pay-operator.js +364 -105
  61. package/dist/bot/pay-operator.js.map +1 -1
  62. package/dist/bot/profile.d.ts +10 -1
  63. package/dist/bot/profile.d.ts.map +1 -1
  64. package/dist/bot/profile.js +136 -10
  65. package/dist/bot/profile.js.map +1 -1
  66. package/dist/bot/promote-to-skill.js +1 -1
  67. package/dist/bot/promote-to-skill.js.map +1 -1
  68. package/dist/bot/provision-session.d.ts +64 -123
  69. package/dist/bot/provision-session.d.ts.map +1 -1
  70. package/dist/bot/provision-session.js +2622 -909
  71. package/dist/bot/provision-session.js.map +1 -1
  72. package/dist/bot/remote-login-display.d.ts +40 -0
  73. package/dist/bot/remote-login-display.d.ts.map +1 -0
  74. package/dist/bot/remote-login-display.js +619 -0
  75. package/dist/bot/remote-login-display.js.map +1 -0
  76. package/dist/bot/session/model.d.ts +176 -0
  77. package/dist/bot/session/model.d.ts.map +1 -0
  78. package/dist/bot/session/model.js +78 -0
  79. package/dist/bot/session/model.js.map +1 -0
  80. package/dist/install/agents.js +11 -11
  81. package/dist/install/agents.js.map +1 -1
  82. package/dist/install/cli.d.ts +1 -7
  83. package/dist/install/cli.d.ts.map +1 -1
  84. package/dist/install/cli.js +38 -174
  85. package/dist/install/cli.js.map +1 -1
  86. package/dist/install/interactive.d.ts +0 -3
  87. package/dist/install/interactive.d.ts.map +1 -1
  88. package/dist/install/interactive.js +5 -44
  89. package/dist/install/interactive.js.map +1 -1
  90. package/dist/install/ui.d.ts.map +1 -1
  91. package/dist/install/ui.js +3 -15
  92. package/dist/install/ui.js.map +1 -1
  93. package/dist/operator-env.d.ts.map +1 -1
  94. package/dist/operator-env.js +4 -9
  95. package/dist/operator-env.js.map +1 -1
  96. package/dist/server.d.ts +13 -1
  97. package/dist/server.d.ts.map +1 -1
  98. package/dist/server.js +138 -40
  99. package/dist/server.js.map +1 -1
  100. package/dist/session.d.ts +0 -1
  101. package/dist/session.d.ts.map +1 -1
  102. package/dist/session.js +35 -5
  103. package/dist/session.js.map +1 -1
  104. package/dist/tools/credential-label.d.ts +3 -0
  105. package/dist/tools/credential-label.d.ts.map +1 -0
  106. package/dist/tools/credential-label.js +3 -0
  107. package/dist/tools/credential-label.js.map +1 -0
  108. package/dist/tools/credential-mutations.d.ts +167 -0
  109. package/dist/tools/credential-mutations.d.ts.map +1 -0
  110. package/dist/tools/credential-mutations.js +217 -0
  111. package/dist/tools/credential-mutations.js.map +1 -0
  112. package/dist/tools/index.d.ts +3 -1
  113. package/dist/tools/index.d.ts.map +1 -1
  114. package/dist/tools/index.js +6 -3
  115. package/dist/tools/index.js.map +1 -1
  116. package/dist/tools/operate-pay.d.ts +4 -4
  117. package/dist/tools/operate-pay.d.ts.map +1 -1
  118. package/dist/tools/operate-pay.js +280 -75
  119. package/dist/tools/operate-pay.js.map +1 -1
  120. package/dist/tools/provision-drive.d.ts +62 -34
  121. package/dist/tools/provision-drive.d.ts.map +1 -1
  122. package/dist/tools/provision-drive.js +419 -109
  123. package/dist/tools/provision-drive.js.map +1 -1
  124. package/dist/tools/store-credential.d.ts.map +1 -1
  125. package/dist/tools/store-credential.js +8 -4
  126. package/dist/tools/store-credential.js.map +1 -1
  127. package/dist/tools/use-credential.d.ts +5 -0
  128. package/dist/tools/use-credential.d.ts.map +1 -1
  129. package/dist/tools/use-credential.js +11 -5
  130. package/dist/tools/use-credential.js.map +1 -1
  131. package/package.json +6 -3
  132. package/dist/bot/operator-direct-identity.d.ts +0 -26
  133. package/dist/bot/operator-direct-identity.d.ts.map +0 -1
  134. package/dist/bot/operator-direct-identity.js +0 -120
  135. package/dist/bot/operator-direct-identity.js.map +0 -1
  136. package/dist/bot/operator-profile-pool.d.ts +0 -97
  137. package/dist/bot/operator-profile-pool.d.ts.map +0 -1
  138. package/dist/bot/operator-profile-pool.js +0 -855
  139. package/dist/bot/operator-profile-pool.js.map +0 -1
  140. package/dist/bot/xvfb.d.ts +0 -13
  141. package/dist/bot/xvfb.d.ts.map +0 -1
  142. package/dist/bot/xvfb.js +0 -146
  143. package/dist/bot/xvfb.js.map +0 -1
  144. package/dist/install/proxy-url.d.ts +0 -2
  145. package/dist/install/proxy-url.d.ts.map +0 -1
  146. package/dist/install/proxy-url.js +0 -20
  147. package/dist/install/proxy-url.js.map +0 -1
package/README.md CHANGED
@@ -72,31 +72,58 @@ precision (for example, whole yen for JPY and two decimals for USD). The anonymo
72
72
  approval page shows the merchant, checkout origin, amount and currency, item, and
73
73
  reason directly from the short-lived server record before one passkey ceremony
74
74
  authorizes those canonical payment values. You also see the requesting MCP client
75
- (for example, Hermes) and that a saved card will be used before clicking **Approve
76
- payment** to relay the operator-sealed final authorization. When the pre-submission
75
+ (for example, Hermes) and the bound card's label plus last four digits (or its label
76
+ alone for a legacy card) before clicking **Approve payment** to relay the
77
+ operator-sealed final authorization. Before submitting that authorization, you
78
+ can instead choose **Deny payment**; a denial closes that approval attempt and
79
+ prevents any later operator confirmation. When the pre-submission
77
80
  checkout can be machine-read, the payment is refused if its merchant, origin, amount,
78
81
  or currency has changed since approval. If that resume read cannot recover a total,
79
82
  Trusty Squire reuses the original mandate-bound checkout values. Card entry requires the PAN,
80
83
  expiry, and CVV fields; cardholder name and other explicitly labeled billing fields
81
84
  are filled best-effort, so a missing name field does not abort the payment. Sealing
82
85
  and cleanup touch only those selected payment controls; merchant shipping address and
83
- country controls remain untouched. A submit is reported as `payment_submitted` only
86
+ country controls remain untouched. If the checkout has a selected merchant-saved card
87
+ alongside the newly filled card, Trusty Squire selects the sole unambiguous new-card
88
+ radio and verifies both that choice and the filled fields again immediately before
89
+ submission; ambiguous choices, selected saved-card options, and failed verification
90
+ are refused with `payment_card_selection_ambiguous`. A charge is treated as
91
+ dispatched only after the browser observes a concrete charge/order request, a
92
+ terminal merchant outcome, or genuine 3-D Secure evidence; native form validation
93
+ alone does not claim a dispatch. A submit is reported as `payment_submitted` only
84
94
  after the checkout reaches a new merchant order-confirmation URL with a substantive
85
95
  order or receipt identity. The browser completes 3-D Secure natively, including
86
96
  out-of-band bank-app challenges — Trusty Squire never manipulates or intercepts the
87
97
  challenge; it uses read-only checks while polling for that same order-confirmation
88
- signal. A bare click that produces no confirmation and no detected challenge returns
89
- `payment_outcome_unknown` instead of guessing that the charge succeeded. A detected
90
- challenge that remains unresolved on timeout stays
91
- `payment_3ds_required` with `needs_user.wall: "3ds"`, handing control back for user
92
- completion. Neither status is success or permits blind resubmission: manually check
93
- the merchant's order state before any retry.
98
+ signal. At that last observable boundary, a mismatch between the released card and
99
+ issuer, network, or last-four evidence rendered by the 3-D Secure issuer/app is
100
+ returned as a structured `warning` with `kind: "payment_instrument_mismatch"` and
101
+ expected-versus-observed evidence. The warning persists through resumable
102
+ `operate_payment_status` calls; it neither changes the payment status nor cancels,
103
+ approves, or modifies the challenge, so the cardholder retains the decision whether
104
+ to continue. A dispatched attempt with no confirmed merchant outcome and no genuine
105
+ 3-D Secure evidence remains `payment_outcome_unknown`, including across resumable
106
+ status checks; Trusty Squire never relabels that uncertainty as 3-D Secure. A detected
107
+ challenge that remains unresolved on timeout stays `payment_3ds_required` with
108
+ `needs_user.wall: "3ds"`, handing control back for user completion. Neither status is
109
+ success or permits blind resubmission: manually check the merchant's order state
110
+ before any retry.
111
+
112
+ `operate_pay` surfaces the approval link before its bounded server-side wait. It
113
+ may wait up to one minute for approval, denial, or expiry; if it returns
114
+ `approval_pending` first, call `operate_pay` again with the same arguments. That
115
+ call resumes the same approval and one-passkey boundary instead of creating a new
116
+ link. `operate_payment_status` is a non-charging alternative for inspecting the
117
+ pre-charge approval and is the continuation tool for an already-submitted unknown
118
+ or 3-D Secure outcome. Its `wait_seconds` accepts 0-60 (default 0) to bound-wait
119
+ instead of taking an instant peek. Denial or expiry is terminal for that session's
120
+ attempt: repeated calls return the same result and never mint a replacement
121
+ approval. Close the session and start a fresh one before making a genuinely new
122
+ payment attempt.
94
123
 
95
124
  Every payment response includes its `session_id`. Pass that same ID to every
96
- follow-up `operate_pay` and canonical `operate_payment_status` call. Pass
97
- `wait_seconds` (0-15, default 0) to bound-wait for a change instead of an instant
98
- peek. Omitting `session_id` remains compatible only while this MCP process has
99
- exactly one session; it never selects a newest or arbitrary checkout.
125
+ follow-up payment call. Omitting `session_id` remains compatible only while this
126
+ MCP process has exactly one session; it never selects a newest or arbitrary checkout.
100
127
 
101
128
  Some split checkouts collect the card before the final order-confirmation step. On the
102
129
  card-entry page, `operate_pay { phase: "fill_card" }` first reads the live total. A
@@ -136,8 +163,7 @@ or Shopify PCI card fields. Trusty Squire does not sign in to PayPal or use vaul
136
163
  PayPal credentials. After any submit that has not yet reached a confirmed order,
137
164
  Trusty Squire waits 180 seconds by default for native completion, including
138
165
  out-of-band bank-app approval. A linked Telegram chat receives a challenge-specific
139
- nudge when 3-D Secure is detected, or cautious bank-app guidance when no on-page
140
- challenge appeared. Standard cross-processor 3-D Secure signals and recognized
166
+ nudge only after 3-D Secure is detected. Standard cross-processor 3-D Secure signals and recognized
141
167
  CardinalCommerce or Stripe challenge frames classify the first case only when the
142
168
  containing frame is visibly rendered. Hidden 3-D Secure Method pre-authentication and
143
169
  captcha-hosted frames never count as 3-D Secure, and an ordinary Shopify PCI card-field
@@ -158,7 +184,7 @@ request.
158
184
  npx @trusty-squire/mcp connect
159
185
  ```
160
186
 
161
- `connect` signs you in with Google or GitHub, detects your coding agent, and merges the `squire` MCP server into its existing configuration. Restart the agent and ask for the finished website outcome. Trusty Squire is free to start.
187
+ `connect` signs you in with Google or GitHub, detects your coding agent, and merges the `squire` MCP server into its existing configuration. On a machine with a user-visible desktop, sign-in opens a local Chrome window. On a headless Linux server, including an SSH/TTY session with an inherited virtual display, interactive login starts a login-scoped Xvfb and noVNC stack and prints a URL you can open on another device. The default quick tunnel and every local helper are torn down when that login completes, times out, fails, or is interrupted. Operators may instead set both `TS_LOGIN_PUBLIC_HOSTNAME` and `TS_LOGIN_LOCAL_PORT` to reuse an externally managed named tunnel; Trusty Squire still tears down its per-login display and local listener, but never creates or stops that external tunnel. Restart the agent and ask for the finished website outcome. Trusty Squire is free to start.
162
188
 
163
189
  To choose a target explicitly:
164
190
 
@@ -175,7 +201,12 @@ remote CDP, macOS, and Windows operator sessions are not supported in this migra
175
201
 
176
202
  1. Your coding agent names the website and the outcome it needs: an account,
177
203
  authenticated setup, app publishing, a purchase, a gift, or a booking.
178
- 2. Trusty Squire works through the service flow one step at a time. Ordinary tasks open an isolated browser profile; tasks that must act as your connected Google identity use the signed-in profile directly and run one at a time.
204
+ 2. Trusty Squire works through the service flow one step at a time. Every task
205
+ opens its own fresh browser profile and restores the snapshot's non-Google
206
+ signed-in state, so independent sessions can run concurrently without opening
207
+ the canonical login profile. Google state is restored inside the serialized
208
+ `oauth_login` or legacy `oauth_click` boundary; sanctioned Gmail verification
209
+ uses a separate temporary identity browser.
179
210
  3. If the flow produces an API key or client secret, Trusty Squire captures it
180
211
  into the vault without returning the raw value through its credential tools.
181
212
  4. The agent can make an authenticated request, create a host-scoped app grant,
@@ -252,58 +283,99 @@ for the system and data flows.
252
283
 
253
284
  ## MCP tools
254
285
 
255
- The default MCP registry exposes 16 tools. The essential operator surface
256
- is `operate_start`, `operate_observe`, `operate_act`, `operate_pay`,
257
- `operate_payment_status`, `operate_finish`, `operate_recipe_run`, and
258
- `operate_recipe_save` — every former standalone workflow/lifecycle/login tool
259
- name was dropped and its behavior folded into `operate_act` as a `kind` (or into
260
- `operate_finish`'s `outcome`); no delegating aliases remain. Poll payment status
261
- via `operate_payment_status(wait_seconds)`.
286
+ The default MCP registry exposes 20 tools. The essential operator surface is
287
+ `operate_start`, `operate_observe`, `operate_observe_query`, `operate_act`,
288
+ `operate_pay`, `operate_payment_status`, `operate_finish`,
289
+ `operate_recipe_run`, and `operate_recipe_save` — every former standalone
290
+ workflow/lifecycle/login tool name was dropped and its behavior folded into
291
+ `operate_act` as a `kind` (or into `operate_finish`'s `outcome`); no delegating
292
+ aliases remain. Continue a pending pre-charge approval by re-calling
293
+ `operate_pay` with the same arguments; use
294
+ `operate_payment_status(wait_seconds)` as a non-charging alternative and for
295
+ post-submit outcome checks. `operate_screenshot(session_id,
296
+ frame_index?, frame_url_contains?, full_page?)` is a read-only debugging capture
297
+ (page or one isolated frame, e.g. a cross-origin 3-D Secure/captcha challenge)
298
+ returned as an actual MCP image. It refuses during an active card fill, when the
299
+ requested capture still contains a sealed or card-shaped value, or when any
300
+ included frame cannot be checked; capture-time pixel redaction remains a second
301
+ safety fence.
262
302
  The maintainer-only `list_extract_failures` → `get_extract_failure`
263
303
  DOM-diagnostics pair is excluded from that surface; set
264
304
  `TRUSTY_SQUIRE_DIAGNOSTICS=1` in the MCP server environment to opt into the
265
- 18-tool diagnostics profile.
305
+ 22-tool diagnostics profile.
306
+
307
+ Operate sessions default to Compact V2 observations: a screened
308
+ `format:"compact-v2"` response with a finite stage, safe title/heading
309
+ semantics, and opaque generation-bound controls in `safe_table`. Raw page text,
310
+ URLs, DOM values, and snapshot files are not part of that format. Use
311
+ `operate_observe_query` with task words or `overflow.next_cursor` to retrieve a
312
+ named or paged control while matching stays inside the live browser. A browser
313
+ action invalidates the current handles; on `reobserve_required`, observe again
314
+ and select a new handle. Exact cursorless `Google` and `GitHub` queries briefly
315
+ refresh controls that hydrate or gain labels after the initial observation, but
316
+ still return only a current sealed handle. `detail:"full"` remains inside the V2
317
+ seal. Maintainers can select the legacy V1 `el_table`/snapshot contract with
318
+ `TRUSTY_SQUIRE_OBSERVE_V2=off`, or exercise V2 without emitting it with
319
+ `shadow`; the detailed wire and migration contract lives in
320
+ [DESIGN-observe-compact.md](docs/DESIGN-observe-compact.md).
266
321
 
267
322
  - Rejected tool calls return a JSON `error` envelope with a stable `code` and
268
323
  message. Malformed and unknown calls fail only that request; they do not stop
269
324
  the shared stdio process or discard its active in-memory operator session.
270
325
  `server_unavailable` includes `retry.max_attempts: 1`: retry once, and never
271
326
  kill or restart the shared operator process.
272
- - `operate_start`, `operate_observe`, and `operate_act` open a website, inspect
273
- the current state, and perform one browser action at a time. Ordinary controls
274
- inside same- and cross-origin frames are included in observations with a
275
- `frame_origin`; known captcha challenge frames stay behind the dedicated
276
- captcha flow. Frame refs and live `text=…`/`css=…` locators preserve that
277
- boundary: same-registrable-domain frames are reachable, cross-domain frames
327
+ - `operate_start`, `operate_observe`, `operate_observe_query`, and `operate_act`
328
+ open a website, inspect the current state, and perform one browser action at a time. Ordinary controls
329
+ inside same- and cross-origin frames are included in observations (as finite
330
+ frame facts in Compact V2 and `frame_origin` in V1); known captcha challenge
331
+ frames stay behind the dedicated captcha flow. Same-registrable-domain frames
332
+ are reachable, cross-domain frames
278
333
  must pass the same domain scope as `goto`/`allow_host`, opaque frames are
279
334
  refused, and `type_secret` never targets any cross-domain frame. Frame refs
280
335
  currently support `click`, `js_click`, `type`, `type_secret`, and `select`;
281
336
  `upload`, `oauth_click`, and `oauth_login` fail closed. If a visible control
282
- has no observed ref, the four locator-capable actions (`click`, `js_click`,
283
- `type`, and `type_secret`) can use a live locator; that one-off fallback is not
284
- replayable.
337
+ has no observed ref, explicitly selected V1 sessions let the four
338
+ locator-capable actions (`click`, `js_click`, `type`, and `type_secret`) use a
339
+ live `text=…`/`css=…` locator; that one-off fallback is not replayable.
340
+ Compact V2 accepts only a handle from its current sealed action map.
285
341
  In a live operator session, in-page XHR/fetch calls to merchant API sibling
286
342
  subdomains are automatically in scope only when they share the registrable
287
343
  domain of a host trusted at session start. Calls outside the session scope fail
288
344
  promptly instead of hanging; page-load resources continue normally, and a
289
- mid-session `allow_host` does not seed sibling-domain widening.
290
- For a task gated by the user's connected Google account, pass
291
- `require_live_identity: true` to `operate_start`. The start fails closed with
292
- a connect handoff if that Google session is unavailable; otherwise it uses the
293
- signed-in profile directly, with one such session active at a time.
294
- When DOM churn invalidates an `@e:` ref, `operate_act` returns `target_stale`
295
- with the last observation generation, `reobserve_required: true`, best-effort
296
- label-keyed `replacement_candidates`, and
297
- `retry_policy: "do_not_retry_old_ref"`; re-observe and choose a current ref.
345
+ mid-session `allow_host` does not seed sibling-domain widening. A small set of
346
+ always-in-scope hosts (recognized payment-provider frames, OAuth/captcha
347
+ providers, and 3-D Secure ACS/directory-server hosts) is exempt from that
348
+ session-start-trust requirement — otherwise a checkout's own out-of-band 3DS
349
+ challenge could never complete its own status poll.
350
+ Every operator task uses the user's Chrome profile directly. Before it starts,
351
+ the operator checks the live Google My Account identity; if the profile is
352
+ signed out, it returns a clear login handoff before navigating to the service.
353
+ To route only that browser session through a proxy, pass `proxy` to
354
+ `operate_start` as an HTTP or HTTPS URL (credentials are optional), or as an
355
+ unauthenticated SOCKS5 URL. The value is launch-only and sensitive: it is not
356
+ returned in session status, action traces, or saved recipes. Omitting it uses
357
+ direct egress.
358
+ Under Compact V2, an expired, forged, wrong-generation, cross-page, or drifted
359
+ `@e:` handle fails opaquely with `reobserve_required`; re-observe and choose a
360
+ current handle. Under V1, DOM churn returns `target_stale` with the last
361
+ observation generation, `reobserve_required: true`, best-effort label-keyed
362
+ `replacement_candidates`, and `retry_policy: "do_not_retry_old_ref"`.
298
363
  Malformed `operate_act` calls return `error.code: "invalid_arguments"` and an
299
364
  `error.guidance` repair object with the allowed kinds, missing fields, a valid
300
365
  example, and a safe alternative instead of only a validation string.
301
366
  For a provider login, pass the observed provider-button ref to the atomic
302
367
  `oauth_login` action. It retains the product tab across provider-owned popup
303
368
  redirects and closes, then returns the post-login product observation even if
304
- `detail` is `none`. `oauth_click` and `oauth_settle` remain for legacy replay
305
- compatibility. If an observation races that legacy transition, the response
306
- reports `oauth.state: "in_progress"` and directs the host to observe again.
369
+ `detail` is `none`. Every `oauth_login` and legacy `oauth_click` is serialized
370
+ from action start through completion and a short release cooldown; other
371
+ session work remains parallel. The whole serialized action has a 30-second
372
+ deadline. If Google does not complete in time, the call returns
373
+ `google_session` re-login guidance and closes that operator session without
374
+ replacing the saved identity; start a fresh session after reconnecting.
375
+ `oauth_click` and `oauth_settle` remain for
376
+ legacy replay compatibility. If an observation races that legacy transition,
377
+ the response reports `oauth.state: "in_progress"` and directs the host to
378
+ observe again.
307
379
  - `operate_act` also owns eight consolidated workflow/lifecycle kinds — the
308
380
  entire operator surface beyond navigation, payment, finish, and recipe
309
381
  replay is reached through `operate_act`'s `kind`:
@@ -344,10 +416,16 @@ DOM-diagnostics pair is excluded from that surface; set
344
416
  - `operate_finish` closes the session and optionally accepts a nested `outcome`.
345
417
  `none` only closes; `credentials` requires `store` and preserves credential
346
418
  extraction, vault storage, and auto-promotion; `result` requires `summary` or
347
- `data` and can run `verify_recipe` before closing. Finish first stops new
348
- calls and drains calls already using that session. Payment state never blocks
349
- teardown: finish clears any remaining payment state and destroys, rather than
350
- pools, a payment-sensitive browser profile.
419
+ `data`. A result is eligible to save portable login state only when
420
+ `verify_recipe` confirms it or `data.confirmed` is `true`; credential outcomes
421
+ qualify only after unblocked extraction and vault storage. `none`, failed or
422
+ unconfirmed outcomes, and payment-sensitive sessions preserve the prior saved
423
+ snapshot. Finish first stops new calls and drains calls already using that
424
+ session within a bounded terminal transition, then closes its browser and
425
+ schedules private-profile removal. Sessions also close automatically after 10 minutes without an
426
+ operation and begin terminal teardown at 30 minutes; only an active payment
427
+ receives the short bounded close grace. Callers should finish promptly instead
428
+ of treating an open browser as durable background state.
351
429
  - `operate_recipe_save` saves a postcondition-verified local recipe under a
352
430
  closed task verb plus the service's registrable domain. It records stable target
353
431
  attributes and exact provenance for Squire-supplied values, not observed refs
@@ -369,12 +447,19 @@ DOM-diagnostics pair is excluded from that surface; set
369
447
  post-submit outcome wait described above before handing back unresolved
370
448
  outcomes. Split checkouts use the `fill_card` then `confirm` flow described
371
449
  above.
372
- `operate_payment_status` follows the [payment guide](#one-prompt) polling
450
+ `operate_payment_status` follows the [payment guide](#one-prompt) bounded-wait
373
451
  contract. It returns the session ID and includes it in every follow-up tool
374
- hint, so an approval is always resumed in its originating browser. Malformed calls return the same
452
+ hint, so an approval or submitted outcome is always observed in its originating
453
+ browser. Malformed calls return the same
375
454
  `error.guidance` repair fields as `operate_act`, including a safe resolution
376
455
  when `card_ref` and `card_label` conflict.
377
456
  - `list_credentials` and `use_credential` find saved credentials and make authenticated API calls without returning raw values.
457
+ - `edit_credential` changes only an existing credential's non-secret name,
458
+ `allowed_hosts`, or `login_hosts`; `delete_credential` soft-deletes one. Each
459
+ first returns a Telegram/passkey approval link bound to the operation, exact
460
+ credential reference, and edit before→after. Resume with only the returned
461
+ `approval_id`. Neither tool can read or alter the secret value; use
462
+ `store_credential` to rotate a secret.
378
463
  - `grant_app_access` and `revoke_app_access` create and remove scoped backend access.
379
464
  - `audit_log` reports credential activity without exposing credential values.
380
465
 
@@ -0,0 +1,9 @@
1
+ export function sendTextAsKeysyms(rfb, value, keysyms) {
2
+ for (const character of value) {
3
+ const codePoint = character.codePointAt(0);
4
+ if (codePoint === undefined) continue;
5
+ const keysym = keysyms.lookup(codePoint);
6
+ rfb.sendKey(keysym, null, true);
7
+ rfb.sendKey(keysym, null, false);
8
+ }
9
+ }