@trusty-squire/mcp 1.1.13-rc.9 → 1.1.13

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 (162) hide show
  1. package/README.md +136 -64
  2. package/assets/login/vnc-input.js +9 -0
  3. package/assets/login/vnc.html +214 -202
  4. package/assets/screencaps/connect-walkthrough.png +0 -0
  5. package/assets/screencaps/connect-walkthrough.svg +1 -1
  6. package/dist/api-client.d.ts +26 -3
  7. package/dist/api-client.d.ts.map +1 -1
  8. package/dist/api-client.js +19 -31
  9. package/dist/api-client.js.map +1 -1
  10. package/dist/bin.js +2 -3
  11. package/dist/bin.js.map +1 -1
  12. package/dist/bot/browser.d.ts +85 -80
  13. package/dist/bot/browser.d.ts.map +1 -1
  14. package/dist/bot/browser.js +1314 -1570
  15. package/dist/bot/browser.js.map +1 -1
  16. package/dist/bot/compact-observation-v2.d.ts +167 -0
  17. package/dist/bot/compact-observation-v2.d.ts.map +1 -0
  18. package/dist/bot/compact-observation-v2.js +1038 -0
  19. package/dist/bot/compact-observation-v2.js.map +1 -0
  20. package/dist/bot/credential-shape.d.ts +2 -0
  21. package/dist/bot/credential-shape.d.ts.map +1 -1
  22. package/dist/bot/credential-shape.js +21 -8
  23. package/dist/bot/credential-shape.js.map +1 -1
  24. package/dist/bot/element-fingerprint.d.ts +9 -0
  25. package/dist/bot/element-fingerprint.d.ts.map +1 -0
  26. package/dist/bot/element-fingerprint.js +147 -0
  27. package/dist/bot/element-fingerprint.js.map +1 -0
  28. package/dist/bot/email-verification.d.ts +7 -1
  29. package/dist/bot/email-verification.d.ts.map +1 -1
  30. package/dist/bot/email-verification.js +48 -14
  31. package/dist/bot/email-verification.js.map +1 -1
  32. package/dist/bot/google-login.d.ts +28 -52
  33. package/dist/bot/google-login.d.ts.map +1 -1
  34. package/dist/bot/google-login.js +246 -1119
  35. package/dist/bot/google-login.js.map +1 -1
  36. package/dist/bot/install-completion.d.ts +1 -2
  37. package/dist/bot/install-completion.d.ts.map +1 -1
  38. package/dist/bot/install-completion.js +1 -2
  39. package/dist/bot/install-completion.js.map +1 -1
  40. package/dist/bot/login-state.d.ts +13 -7
  41. package/dist/bot/login-state.d.ts.map +1 -1
  42. package/dist/bot/login-state.js +50 -114
  43. package/dist/bot/login-state.js.map +1 -1
  44. package/dist/bot/oauth-providers.d.ts +1 -1
  45. package/dist/bot/oauth-providers.d.ts.map +1 -1
  46. package/dist/bot/oauth-providers.js +3 -3
  47. package/dist/bot/oauth-providers.js.map +1 -1
  48. package/dist/bot/oauth-scope.d.ts +4 -0
  49. package/dist/bot/oauth-scope.d.ts.map +1 -0
  50. package/dist/bot/oauth-scope.js +58 -0
  51. package/dist/bot/oauth-scope.js.map +1 -0
  52. package/dist/bot/operator-browser-watchdog.d.ts +18 -6
  53. package/dist/bot/operator-browser-watchdog.d.ts.map +1 -1
  54. package/dist/bot/operator-browser-watchdog.js +91 -37
  55. package/dist/bot/operator-browser-watchdog.js.map +1 -1
  56. package/dist/bot/owner-process-reaper-worker.d.ts +2 -0
  57. package/dist/bot/owner-process-reaper-worker.d.ts.map +1 -0
  58. package/dist/bot/owner-process-reaper-worker.js +17 -0
  59. package/dist/bot/owner-process-reaper-worker.js.map +1 -0
  60. package/dist/bot/owner-process-reaper.d.ts +103 -0
  61. package/dist/bot/owner-process-reaper.d.ts.map +1 -0
  62. package/dist/bot/owner-process-reaper.js +1058 -0
  63. package/dist/bot/owner-process-reaper.js.map +1 -0
  64. package/dist/bot/pay-operator.d.ts +7 -1
  65. package/dist/bot/pay-operator.d.ts.map +1 -1
  66. package/dist/bot/pay-operator.js +269 -103
  67. package/dist/bot/pay-operator.js.map +1 -1
  68. package/dist/bot/profile.d.ts +10 -1
  69. package/dist/bot/profile.d.ts.map +1 -1
  70. package/dist/bot/profile.js +137 -11
  71. package/dist/bot/profile.js.map +1 -1
  72. package/dist/bot/promote-to-skill.js +1 -1
  73. package/dist/bot/promote-to-skill.js.map +1 -1
  74. package/dist/bot/provision-session.d.ts +57 -186
  75. package/dist/bot/provision-session.d.ts.map +1 -1
  76. package/dist/bot/provision-session.js +1976 -1812
  77. package/dist/bot/provision-session.js.map +1 -1
  78. package/dist/bot/remote-login-display.d.ts +61 -0
  79. package/dist/bot/remote-login-display.d.ts.map +1 -0
  80. package/dist/bot/remote-login-display.js +765 -0
  81. package/dist/bot/remote-login-display.js.map +1 -0
  82. package/dist/bot/session/hosts.d.ts +8 -0
  83. package/dist/bot/session/hosts.d.ts.map +1 -0
  84. package/dist/bot/session/hosts.js +36 -0
  85. package/dist/bot/session/hosts.js.map +1 -0
  86. package/dist/bot/session/lifecycle.d.ts +56 -0
  87. package/dist/bot/session/lifecycle.d.ts.map +1 -0
  88. package/dist/bot/session/lifecycle.js +962 -0
  89. package/dist/bot/session/lifecycle.js.map +1 -0
  90. package/dist/bot/session/model.d.ts +175 -0
  91. package/dist/bot/session/model.d.ts.map +1 -0
  92. package/dist/bot/session/model.js +77 -0
  93. package/dist/bot/session/model.js.map +1 -0
  94. package/dist/install/cli.d.ts +29 -9
  95. package/dist/install/cli.d.ts.map +1 -1
  96. package/dist/install/cli.js +243 -322
  97. package/dist/install/cli.js.map +1 -1
  98. package/dist/install/interactive.d.ts +1 -0
  99. package/dist/install/interactive.d.ts.map +1 -1
  100. package/dist/install/interactive.js +19 -5
  101. package/dist/install/interactive.js.map +1 -1
  102. package/dist/install/ui.d.ts.map +1 -1
  103. package/dist/install/ui.js +3 -15
  104. package/dist/install/ui.js.map +1 -1
  105. package/dist/operator-env.js +1 -1
  106. package/dist/operator-env.js.map +1 -1
  107. package/dist/server-instance-registry.d.ts +103 -0
  108. package/dist/server-instance-registry.d.ts.map +1 -0
  109. package/dist/server-instance-registry.js +392 -0
  110. package/dist/server-instance-registry.js.map +1 -0
  111. package/dist/server.d.ts +15 -1
  112. package/dist/server.d.ts.map +1 -1
  113. package/dist/server.js +164 -62
  114. package/dist/server.js.map +1 -1
  115. package/dist/session-guard.d.ts +28 -0
  116. package/dist/session-guard.d.ts.map +1 -0
  117. package/dist/session-guard.js +84 -0
  118. package/dist/session-guard.js.map +1 -0
  119. package/dist/session.d.ts +57 -13
  120. package/dist/session.d.ts.map +1 -1
  121. package/dist/session.js +246 -127
  122. package/dist/session.js.map +1 -1
  123. package/dist/tools/audit-log.d.ts +16 -2
  124. package/dist/tools/audit-log.d.ts.map +1 -1
  125. package/dist/tools/audit-log.js +223 -13
  126. package/dist/tools/audit-log.js.map +1 -1
  127. package/dist/tools/audit-rollup.d.ts +80 -0
  128. package/dist/tools/audit-rollup.d.ts.map +1 -0
  129. package/dist/tools/audit-rollup.js +0 -0
  130. package/dist/tools/audit-rollup.js.map +1 -0
  131. package/dist/tools/fetch-credential.d.ts +37 -0
  132. package/dist/tools/fetch-credential.d.ts.map +1 -0
  133. package/dist/tools/fetch-credential.js +153 -0
  134. package/dist/tools/fetch-credential.js.map +1 -0
  135. package/dist/tools/index.d.ts +3 -1
  136. package/dist/tools/index.d.ts.map +1 -1
  137. package/dist/tools/index.js +7 -3
  138. package/dist/tools/index.js.map +1 -1
  139. package/dist/tools/operate-pay.d.ts +4 -4
  140. package/dist/tools/operate-pay.d.ts.map +1 -1
  141. package/dist/tools/operate-pay.js +151 -96
  142. package/dist/tools/operate-pay.js.map +1 -1
  143. package/dist/tools/provision-drive.d.ts +47 -39
  144. package/dist/tools/provision-drive.d.ts.map +1 -1
  145. package/dist/tools/provision-drive.js +268 -164
  146. package/dist/tools/provision-drive.js.map +1 -1
  147. package/dist/tools/use-credential.d.ts.map +1 -1
  148. package/dist/tools/use-credential.js +3 -2
  149. package/dist/tools/use-credential.js.map +1 -1
  150. package/package.json +4 -6
  151. package/dist/bot/operator-direct-identity.d.ts +0 -26
  152. package/dist/bot/operator-direct-identity.d.ts.map +0 -1
  153. package/dist/bot/operator-direct-identity.js +0 -120
  154. package/dist/bot/operator-direct-identity.js.map +0 -1
  155. package/dist/bot/operator-profile-pool.d.ts +0 -97
  156. package/dist/bot/operator-profile-pool.d.ts.map +0 -1
  157. package/dist/bot/operator-profile-pool.js +0 -855
  158. package/dist/bot/operator-profile-pool.js.map +0 -1
  159. package/dist/bot/xvfb.d.ts +0 -13
  160. package/dist/bot/xvfb.d.ts.map +0 -1
  161. package/dist/bot/xvfb.js +0 -146
  162. package/dist/bot/xvfb.js.map +0 -1
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  Trusty Squire is an **MCP server that lets Claude Code, Codex, Cursor, OpenCode, Goose, and other coding agents sign up, provision, and purchase on your behalf**. It opens a real browser, works through signup, sign-in, setup, and checkout flows one step at a time, clears the bot-detection and email-verification steps that make operator tools stall, and hands the job back to a person only when one is actually required. That covers wiring up OAuth and API keys for the app you're building as much as it covers paying a checkout, sending a gift, or booking something — the same operator primitives drive all of it.
21
21
 
22
- Provider secrets and payment cards are write-only: the agent's credential tools return references and authenticated results, never stored plaintext. The raw secret never needs to enter the agent's context, so it can't be pasted into a commit, leaked in a log, or read back out over chat. Backend access is a host-scoped, rate-limited, independently revocable grant, so a leaked token is killed without rotating the provider key — and you connect Google or GitHub yourself in a real browser, so the agent never types your password. Full [threat model below](#security-and-threat-model).
22
+ Provider secrets and payment cards are write-only: the agent's credential tools return references and authenticated results, never stored plaintext. The raw secret never needs to enter the agent's context, so it can't be pasted into a commit, leaked in a log, or read back out over chat. When a task genuinely needs the plaintext somewhere the agent controls, `fetch_credential` releases it — for one credential, once, and only after you sign that exact request with your passkey. Backend access is a host-scoped, rate-limited, independently revocable grant, so a leaked token is killed without rotating the provider key — and you connect Google or GitHub yourself in a real browser, so the agent never types your password. Full [threat model below](#security-and-threat-model).
23
23
 
24
24
  ## One prompt
25
25
 
@@ -74,7 +74,9 @@ 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
75
  (for example, Hermes) and the bound card's label plus last four digits (or its label
76
76
  alone for a legacy card) before clicking **Approve payment** to relay the
77
- operator-sealed final authorization. When the pre-submission
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
78
80
  checkout can be machine-read, the payment is refused if its merchant, origin, amount,
79
81
  or currency has changed since approval. If that resume read cannot recover a total,
80
82
  Trusty Squire reuses the original mandate-bound checkout values. Card entry requires the PAN,
@@ -85,8 +87,10 @@ country controls remain untouched. If the checkout has a selected merchant-saved
85
87
  alongside the newly filled card, Trusty Squire selects the sole unambiguous new-card
86
88
  radio and verifies both that choice and the filled fields again immediately before
87
89
  submission; ambiguous choices, selected saved-card options, and failed verification
88
- are refused with `payment_card_selection_ambiguous`. A submit is reported as
89
- `payment_submitted` only
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
90
94
  after the checkout reaches a new merchant order-confirmation URL with a substantive
91
95
  order or receipt identity. The browser completes 3-D Secure natively, including
92
96
  out-of-band bank-app challenges — Trusty Squire never manipulates or intercepts the
@@ -97,19 +101,29 @@ returned as a structured `warning` with `kind: "payment_instrument_mismatch"` an
97
101
  expected-versus-observed evidence. The warning persists through resumable
98
102
  `operate_payment_status` calls; it neither changes the payment status nor cancels,
99
103
  approves, or modifies the challenge, so the cardholder retains the decision whether
100
- to continue. A bare click that produces no confirmation and no detected challenge returns
101
- `payment_outcome_unknown` instead of guessing that the charge succeeded, as does a click-
102
- completion failure after input dispatch may already have begun. A detected
103
- challenge that remains unresolved on timeout stays
104
- `payment_3ds_required` with `needs_user.wall: "3ds"`, handing control back for user
105
- completion. Neither status is success or permits blind resubmission: manually check
106
- the merchant's order state before any retry.
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.
107
123
 
108
124
  Every payment response includes its `session_id`. Pass that same ID to every
109
- follow-up `operate_pay` and canonical `operate_payment_status` call. Pass
110
- `wait_seconds` (0-15, default 0) to bound-wait for a change instead of an instant
111
- peek. Omitting `session_id` remains compatible only while this MCP process has
112
- 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.
113
127
 
114
128
  Some split checkouts collect the card before the final order-confirmation step. On the
115
129
  card-entry page, `operate_pay { phase: "fill_card" }` first reads the live total. A
@@ -122,8 +136,9 @@ when the checkout origin still matches. One phone approval
122
136
  binds that amount and releases the card; Trusty Squire fills the card without
123
137
  submitting and its role in the purchase ends there. It fills only the merchant's own
124
138
  HTTPS frames or recognized payment-provider frames. The card stays in the page as
125
- sealed, observation-masked fields while the agent advances to the review step and
126
- places the order. Verify the live final total against the approved
139
+ filled fields while the agent advances to the review step and places the order;
140
+ those fields are ordinary page content in `operate_observe` and
141
+ `operate_screenshot`, not masked. Verify the live final total against the approved
127
142
  `amount_cents`/currency yourself before placing the order; Trusty Squire no longer
128
143
  re-reads the total or submits anything. For `click` and `js_click`, a control whose
129
144
  label looks like pay/place-order may fire only once for that approval. A second
@@ -149,8 +164,7 @@ or Shopify PCI card fields. Trusty Squire does not sign in to PayPal or use vaul
149
164
  PayPal credentials. After any submit that has not yet reached a confirmed order,
150
165
  Trusty Squire waits 180 seconds by default for native completion, including
151
166
  out-of-band bank-app approval. A linked Telegram chat receives a challenge-specific
152
- nudge when 3-D Secure is detected, or cautious bank-app guidance when no on-page
153
- challenge appeared. Standard cross-processor 3-D Secure signals and recognized
167
+ nudge only after 3-D Secure is detected. Standard cross-processor 3-D Secure signals and recognized
154
168
  CardinalCommerce or Stripe challenge frames classify the first case only when the
155
169
  containing frame is visibly rendered. Hidden 3-D Secure Method pre-authentication and
156
170
  captcha-hosted frames never count as 3-D Secure, and an ordinary Shopify PCI card-field
@@ -171,7 +185,16 @@ request.
171
185
  npx @trusty-squire/mcp connect
172
186
  ```
173
187
 
174
- `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.
188
+ `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. If that tunnel's fixed local port is busy, login reports it and uses a one-off quick tunnel for that sign-in instead. Restart the agent and ask for the finished website outcome. Trusty Squire is free to start.
189
+
190
+ `connect` is also the only way to sign in again: `--force-relogin` switches the bound account and `--force-relogin=google` or `--force-relogin=github` refreshes one provider session. It reports success only after re-checking the browser profile and finding that Google session live, so a run that binds the machine but leaves no usable session fails loudly with the command to retry rather than reporting a working install.
191
+
192
+ Local state is kept separately for each connected account, and `connect` pins
193
+ that account in the agent's MCP configuration; connecting another account does
194
+ not replace the first account's state. To remove just one account, run
195
+ `npx @trusty-squire/mcp logout --account=<id>`; without `--account`, `logout`
196
+ removes the most recently connected account. `--account` requires a nonempty
197
+ account ID.
175
198
 
176
199
  To choose a target explicitly:
177
200
 
@@ -188,7 +211,12 @@ remote CDP, macOS, and Windows operator sessions are not supported in this migra
188
211
 
189
212
  1. Your coding agent names the website and the outcome it needs: an account,
190
213
  authenticated setup, app publishing, a purchase, a gift, or a booking.
191
- 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.
214
+ 2. Trusty Squire works through the service flow one step at a time. Every task
215
+ opens its own fresh browser profile and restores the snapshot's non-Google
216
+ signed-in state, so independent sessions can run concurrently without opening
217
+ the canonical login profile. Google state is restored inside the serialized
218
+ `oauth_login` or legacy `oauth_click` boundary; sanctioned Gmail verification
219
+ uses a separate temporary identity browser.
192
220
  3. If the flow produces an API key or client secret, Trusty Squire captures it
193
221
  into the vault without returning the raw value through its credential tools.
194
222
  4. The agent can make an authenticated request, create a host-scoped app grant,
@@ -239,7 +267,7 @@ The result contains a host-scoped egress `base_url` and a `token`, not the Clerk
239
267
 
240
268
  ## Security and threat model
241
269
 
242
- - Provider credentials are encrypted in the vault and are write-only to agent credential tools. Those tools return references or authenticated results, not stored plaintext.
270
+ - Provider credentials are encrypted in the vault and are write-only to agent credential tools. Those tools return references or authenticated results, not stored plaintext. `fetch_credential` is the single exception and it is not the agent's to take: it returns the raw value only after you approve that specific fetch with your passkey, once per approval, with the reveal recorded in the audit ledger.
243
271
  - The raw provider key is injected only into the outbound provider request. It does not need to land in chat, generated code, the consuming app, or the project's `.env` file.
244
272
  - App grants are host-scoped, auditable, rate-limitable, and independently revocable. A leaked grant can be revoked without rotating the provider key.
245
273
  - You connect Google or GitHub in a real browser. Trusty Squire does not ask the coding agent to type those passwords.
@@ -265,42 +293,69 @@ for the system and data flows.
265
293
 
266
294
  ## MCP tools
267
295
 
268
- The default MCP registry exposes 19 tools. The essential operator surface
269
- is `operate_start`, `operate_observe`, `operate_act`, `operate_pay`,
270
- `operate_payment_status`, `operate_finish`, `operate_recipe_run`, and
271
- `operate_recipe_save` — every former standalone workflow/lifecycle/login tool
272
- name was dropped and its behavior folded into `operate_act` as a `kind` (or into
273
- `operate_finish`'s `outcome`); no delegating aliases remain. Poll payment status
274
- via `operate_payment_status(wait_seconds)`. `operate_screenshot(session_id,
296
+ The default MCP registry exposes 20 tools. The essential operator surface is
297
+ `operate_start`, `operate_observe`, `operate_observe_query`, `operate_act`,
298
+ `operate_pay`, `operate_payment_status`, `operate_finish`,
299
+ `operate_recipe_run`, and `operate_recipe_save` — every former standalone
300
+ workflow/lifecycle/login tool name was dropped and its behavior folded into
301
+ `operate_act` as a `kind` (or into `operate_finish`'s `outcome`); no delegating
302
+ aliases remain. Continue a pending pre-charge approval by re-calling
303
+ `operate_pay` with the same arguments; use
304
+ `operate_payment_status(wait_seconds)` as a non-charging alternative and for
305
+ post-submit outcome checks. `operate_screenshot(session_id,
275
306
  frame_index?, frame_url_contains?, full_page?)` is a read-only debugging capture
276
307
  (page or one isolated frame, e.g. a cross-origin 3-D Secure/captcha challenge)
277
- returned as an actual MCP image. It refuses during an active card fill, when the
278
- requested capture still contains a sealed or card-shaped value, or when any
279
- included frame cannot be checked; capture-time pixel redaction remains a second
280
- safety fence.
308
+ returned as an actual MCP image. It returns the page's real pixels: there is no
309
+ redaction pass and no refusal when the page is showing a secret or a card value.
281
310
  The maintainer-only `list_extract_failures` → `get_extract_failure`
282
311
  DOM-diagnostics pair is excluded from that surface; set
283
312
  `TRUSTY_SQUIRE_DIAGNOSTICS=1` in the MCP server environment to opt into the
284
- 21-tool diagnostics profile.
313
+ 22-tool diagnostics profile.
314
+
315
+ Operate sessions default to Compact V2 observations: a compact
316
+ `format:"compact-v2"` response with the live page URL, a finite stage,
317
+ title/heading semantics, and generation-bound controls in `safe_table`. Nothing
318
+ in that response is screened for content — labels and semantics are the page's
319
+ own copy. Page text, DOM values, and snapshot files are omitted as a SIZE budget,
320
+ not as a seal; read a value off the page with `operate_screenshot`, `operate_act
321
+ { kind: "extract" }`, or a V1 session. Use
322
+ `operate_observe_query` with task words or `overflow.next_cursor` to retrieve a
323
+ named or paged control while matching stays inside the live browser. A browser
324
+ action invalidates the current handles; on `reobserve_required`, observe again
325
+ and select a new handle. Exact cursorless `Google` and `GitHub` queries briefly
326
+ refresh controls that hydrate or gain labels after the initial observation, but
327
+ still return only a current handle. `detail:"full"` keeps the V2 format. Maintainers can select the legacy V1 `el_table`/snapshot contract with
328
+ `TRUSTY_SQUIRE_OBSERVE_V2=off`, or exercise V2 without emitting it with
329
+ `shadow`; the detailed wire and migration contract lives in
330
+ [DESIGN-observe-compact.md](docs/DESIGN-observe-compact.md).
285
331
 
286
332
  - Rejected tool calls return a JSON `error` envelope with a stable `code` and
287
333
  message. Malformed and unknown calls fail only that request; they do not stop
288
334
  the shared stdio process or discard its active in-memory operator session.
289
335
  `server_unavailable` includes `retry.max_attempts: 1`: retry once, and never
290
336
  kill or restart the shared operator process.
291
- - `operate_start`, `operate_observe`, and `operate_act` open a website, inspect
292
- the current state, and perform one browser action at a time. Ordinary controls
293
- inside same- and cross-origin frames are included in observations with a
294
- `frame_origin`; known captcha challenge frames stay behind the dedicated
295
- captcha flow. Frame refs and live `text=…`/`css=…` locators preserve that
296
- boundary: same-registrable-domain frames are reachable, cross-domain frames
337
+ - `operate_start`, `operate_observe`, `operate_observe_query`, and `operate_act`
338
+ open a website, inspect the current state, and perform one browser action at a time. Ordinary controls
339
+ inside same- and cross-origin frames are included in observations (as finite
340
+ frame facts in Compact V2 and `frame_origin` in V1); known captcha challenge
341
+ frames stay behind the dedicated captcha flow. Same-registrable-domain frames
342
+ are reachable, cross-domain frames
297
343
  must pass the same domain scope as `goto`/`allow_host`, opaque frames are
298
344
  refused, and `type_secret` never targets any cross-domain frame. Frame refs
299
345
  currently support `click`, `js_click`, `type`, `type_secret`, and `select`;
300
346
  `upload`, `oauth_click`, and `oauth_login` fail closed. If a visible control
301
- has no observed ref, the four locator-capable actions (`click`, `js_click`,
302
- `type`, and `type_secret`) can use a live locator; that one-off fallback is not
303
- replayable.
347
+ has no observed ref, explicitly selected V1 sessions let the four
348
+ locator-capable actions (`click`, `js_click`, `type`, and `type_secret`) use a
349
+ live `text=…`/`css=…` locator; that one-off fallback is not replayable.
350
+ Compact V2 accepts only a handle from its current sealed action map.
351
+ When a `click` or `js_click` opens a new tab or popup (`target=_blank`, a
352
+ `window.open` control), the operator follows it the way a person would: the
353
+ newly opened page becomes the active page, so the next `operate_observe` or
354
+ `operate_act` reads it. This is how an emailed verification or magic link is
355
+ followed. Do not try to `extract` the link's href instead — a single-use login
356
+ token is sealed and is never returned as text; following the tab navigates the
357
+ browser without exposing it. Payment is excluded: during a sealed card fill or
358
+ a live place-order/3-D Secure approval the active page never changes.
304
359
  In a live operator session, in-page XHR/fetch calls to merchant API sibling
305
360
  subdomains are automatically in scope only when they share the registrable
306
361
  domain of a host trusted at session start. Calls outside the session scope fail
@@ -310,28 +365,35 @@ DOM-diagnostics pair is excluded from that surface; set
310
365
  providers, and 3-D Secure ACS/directory-server hosts) is exempt from that
311
366
  session-start-trust requirement — otherwise a checkout's own out-of-band 3DS
312
367
  challenge could never complete its own status poll.
313
- For a task gated by the user's connected Google account, pass
314
- `require_live_identity: true` to `operate_start`. The start fails closed with
315
- a connect handoff if that Google session is unavailable; otherwise it uses the
316
- signed-in profile directly, with one such session active at a time.
368
+ Every operator task uses the user's Chrome profile directly. Before it starts,
369
+ the operator checks the live Google My Account identity; if the profile is
370
+ signed out, it returns a clear login handoff before navigating to the service.
317
371
  To route only that browser session through a proxy, pass `proxy` to
318
372
  `operate_start` as an HTTP or HTTPS URL (credentials are optional), or as an
319
373
  unauthenticated SOCKS5 URL. The value is launch-only and sensitive: it is not
320
374
  returned in session status, action traces, or saved recipes. Omitting it uses
321
375
  direct egress.
322
- When DOM churn invalidates an `@e:` ref, `operate_act` returns `target_stale`
323
- with the last observation generation, `reobserve_required: true`, best-effort
324
- label-keyed `replacement_candidates`, and
325
- `retry_policy: "do_not_retry_old_ref"`; re-observe and choose a current ref.
376
+ Under Compact V2, an expired, forged, wrong-generation, cross-page, or drifted
377
+ `@e:` handle fails opaquely with `reobserve_required`; re-observe and choose a
378
+ current handle. Under V1, DOM churn returns `target_stale` with the last
379
+ observation generation, `reobserve_required: true`, best-effort label-keyed
380
+ `replacement_candidates`, and `retry_policy: "do_not_retry_old_ref"`.
326
381
  Malformed `operate_act` calls return `error.code: "invalid_arguments"` and an
327
382
  `error.guidance` repair object with the allowed kinds, missing fields, a valid
328
383
  example, and a safe alternative instead of only a validation string.
329
384
  For a provider login, pass the observed provider-button ref to the atomic
330
385
  `oauth_login` action. It retains the product tab across provider-owned popup
331
386
  redirects and closes, then returns the post-login product observation even if
332
- `detail` is `none`. `oauth_click` and `oauth_settle` remain for legacy replay
333
- compatibility. If an observation races that legacy transition, the response
334
- reports `oauth.state: "in_progress"` and directs the host to observe again.
387
+ `detail` is `none`. Every `oauth_login` and legacy `oauth_click` is serialized
388
+ from action start through completion and a short release cooldown; other
389
+ session work remains parallel. The whole serialized action has a 30-second
390
+ deadline. If Google does not complete in time, the call returns
391
+ `google_session` re-login guidance and closes that operator session without
392
+ replacing the saved identity; start a fresh session after reconnecting.
393
+ `oauth_click` and `oauth_settle` remain for
394
+ legacy replay compatibility. If an observation races that legacy transition,
395
+ the response reports `oauth.state: "in_progress"` and directs the host to
396
+ observe again.
335
397
  - `operate_act` also owns eight consolidated workflow/lifecycle kinds — the
336
398
  entire operator surface beyond navigation, payment, finish, and recipe
337
399
  replay is reached through `operate_act`'s `kind`:
@@ -356,8 +418,9 @@ DOM-diagnostics pair is excluded from that surface; set
356
418
  - `solve_captcha` drives the in-session captcha gate and returns the
357
419
  fail-fast `needs_user` handoff when it cannot be cleared.
358
420
  - `await_verification` reads the user's own inbox for an email verification
359
- code/link, with sender-scoped search, explicit inbox consent, and
360
- sealed-OTP transfer through `into_slot`.
421
+ code/link by default, with sender-scoped search and sealed-OTP transfer
422
+ through `into_slot`. Advanced configuration or
423
+ `grant_inbox_consent:false` can opt out.
361
424
  - `login_prepare_signup`, `login_store_signup`, and `login_load_saved` own
362
425
  the sealed username/password lifecycle. `login_prepare_signup` seals the
363
426
  user's captured email and a generated password, `login_store_signup`
@@ -372,11 +435,13 @@ DOM-diagnostics pair is excluded from that surface; set
372
435
  - `operate_finish` closes the session and optionally accepts a nested `outcome`.
373
436
  `none` only closes; `credentials` requires `store` and preserves credential
374
437
  extraction, vault storage, and auto-promotion; `result` requires `summary` or
375
- `data` and can run `verify_recipe` before closing. Finish first stops new
376
- calls and drains calls already using that session within a bounded terminal
377
- transition. Payment state never blocks teardown: finish clears any remaining
378
- payment state and destroys, rather than pools, a payment-sensitive browser
379
- profile. Sessions also close automatically after 10 minutes without an
438
+ `data`. A result is eligible to save portable login state only when
439
+ `verify_recipe` confirms it or `data.confirmed` is `true`; credential outcomes
440
+ qualify only after unblocked extraction and vault storage. `none`, failed or
441
+ unconfirmed outcomes, and payment-sensitive sessions preserve the prior saved
442
+ snapshot. Finish first stops new calls and drains calls already using that
443
+ session within a bounded terminal transition, then closes its browser and
444
+ schedules private-profile removal. Sessions also close automatically after 10 minutes without an
380
445
  operation and begin terminal teardown at 30 minutes; only an active payment
381
446
  receives the short bounded close grace. Callers should finish promptly instead
382
447
  of treating an open browser as durable background state.
@@ -401,12 +466,14 @@ DOM-diagnostics pair is excluded from that surface; set
401
466
  post-submit outcome wait described above before handing back unresolved
402
467
  outcomes. Split checkouts use the `fill_card` then `confirm` flow described
403
468
  above.
404
- `operate_payment_status` follows the [payment guide](#one-prompt) polling
469
+ `operate_payment_status` follows the [payment guide](#one-prompt) bounded-wait
405
470
  contract. It returns the session ID and includes it in every follow-up tool
406
- hint, so an approval is always resumed in its originating browser. Malformed calls return the same
471
+ hint, so an approval or submitted outcome is always observed in its originating
472
+ browser. Malformed calls return the same
407
473
  `error.guidance` repair fields as `operate_act`, including a safe resolution
408
474
  when `card_ref` and `card_label` conflict.
409
475
  - `list_credentials` and `use_credential` find saved credentials and make authenticated API calls without returning raw values.
476
+ - `fetch_credential` returns a credential's raw value to the agent — the one path that does. It first returns an approval link and no value; you open it and sign with your passkey; the agent resumes with the returned `approval_id` and receives the value once. Denial or expiry releases nothing, and a mutation or payment approval cannot be used here. Reach for it only when the key must land somewhere the agent controls (a GitHub Actions secret, a `.env`) with no server-side injection path — `use_credential` is the right tool for calling an API.
410
477
  - `edit_credential` changes only an existing credential's non-secret name,
411
478
  `allowed_hosts`, or `login_hosts`; `delete_credential` soft-deletes one. Each
412
479
  first returns a Telegram/passkey approval link bound to the operation, exact
@@ -414,7 +481,12 @@ DOM-diagnostics pair is excluded from that surface; set
414
481
  `approval_id`. Neither tool can read or alter the secret value; use
415
482
  `store_credential` to rotate a secret.
416
483
  - `grant_app_access` and `revoke_app_access` create and remove scoped backend access.
417
- - `audit_log` reports credential activity without exposing credential values.
484
+ - `audit_log` reports credential activity without exposing credential values. It
485
+ defaults to a shaped security ledger: lifecycle events and anomalies (non-2xx,
486
+ 429, rejected calls) as rows, routine proxied egress collapsed into per
487
+ credential/host/burst rollups with per-grant running totals. Pass a rollup's
488
+ `id` as `expand` for its individual calls, or `view: "raw"` for the flat
489
+ per-request stream.
418
490
 
419
491
  ## One README for GitHub and npm
420
492
 
@@ -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
+ }