@trusty-squire/mcp 1.1.13-rc.8 → 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 +139 -62
  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 +134 -77
  13. package/dist/bot/browser.d.ts.map +1 -1
  14. package/dist/bot/browser.js +1788 -1543
  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 +127 -0
  53. package/dist/bot/operator-browser-watchdog.d.ts.map +1 -0
  54. package/dist/bot/operator-browser-watchdog.js +438 -0
  55. package/dist/bot/operator-browser-watchdog.js.map +1 -0
  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 +13 -2
  65. package/dist/bot/pay-operator.d.ts.map +1 -1
  66. package/dist/bot/pay-operator.js +319 -123
  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 +60 -164
  75. package/dist/bot/provision-session.d.ts.map +1 -1
  76. package/dist/bot/provision-session.js +2049 -1546
  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 +163 -96
  142. package/dist/tools/operate-pay.js.map +1 -1
  143. package/dist/tools/provision-drive.d.ts +43 -35
  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,18 +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. A detected
102
- challenge that remains unresolved on timeout stays
103
- `payment_3ds_required` with `needs_user.wall: "3ds"`, handing control back for user
104
- completion. Neither status is success or permits blind resubmission: manually check
105
- 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.
106
123
 
107
124
  Every payment response includes its `session_id`. Pass that same ID to every
108
- follow-up `operate_pay` and canonical `operate_payment_status` call. Pass
109
- `wait_seconds` (0-15, default 0) to bound-wait for a change instead of an instant
110
- peek. Omitting `session_id` remains compatible only while this MCP process has
111
- 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.
112
127
 
113
128
  Some split checkouts collect the card before the final order-confirmation step. On the
114
129
  card-entry page, `operate_pay { phase: "fill_card" }` first reads the live total. A
@@ -121,8 +136,9 @@ when the checkout origin still matches. One phone approval
121
136
  binds that amount and releases the card; Trusty Squire fills the card without
122
137
  submitting and its role in the purchase ends there. It fills only the merchant's own
123
138
  HTTPS frames or recognized payment-provider frames. The card stays in the page as
124
- sealed, observation-masked fields while the agent advances to the review step and
125
- 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
126
142
  `amount_cents`/currency yourself before placing the order; Trusty Squire no longer
127
143
  re-reads the total or submits anything. For `click` and `js_click`, a control whose
128
144
  label looks like pay/place-order may fire only once for that approval. A second
@@ -148,8 +164,7 @@ or Shopify PCI card fields. Trusty Squire does not sign in to PayPal or use vaul
148
164
  PayPal credentials. After any submit that has not yet reached a confirmed order,
149
165
  Trusty Squire waits 180 seconds by default for native completion, including
150
166
  out-of-band bank-app approval. A linked Telegram chat receives a challenge-specific
151
- nudge when 3-D Secure is detected, or cautious bank-app guidance when no on-page
152
- 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
153
168
  CardinalCommerce or Stripe challenge frames classify the first case only when the
154
169
  containing frame is visibly rendered. Hidden 3-D Secure Method pre-authentication and
155
170
  captcha-hosted frames never count as 3-D Secure, and an ordinary Shopify PCI card-field
@@ -170,7 +185,16 @@ request.
170
185
  npx @trusty-squire/mcp connect
171
186
  ```
172
187
 
173
- `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.
174
198
 
175
199
  To choose a target explicitly:
176
200
 
@@ -187,7 +211,12 @@ remote CDP, macOS, and Windows operator sessions are not supported in this migra
187
211
 
188
212
  1. Your coding agent names the website and the outcome it needs: an account,
189
213
  authenticated setup, app publishing, a purchase, a gift, or a booking.
190
- 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.
191
220
  3. If the flow produces an API key or client secret, Trusty Squire captures it
192
221
  into the vault without returning the raw value through its credential tools.
193
222
  4. The agent can make an authenticated request, create a host-scoped app grant,
@@ -238,7 +267,7 @@ The result contains a host-scoped egress `base_url` and a `token`, not the Clerk
238
267
 
239
268
  ## Security and threat model
240
269
 
241
- - 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.
242
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.
243
272
  - App grants are host-scoped, auditable, rate-limitable, and independently revocable. A leaked grant can be revoked without rotating the provider key.
244
273
  - You connect Google or GitHub in a real browser. Trusty Squire does not ask the coding agent to type those passwords.
@@ -264,42 +293,69 @@ for the system and data flows.
264
293
 
265
294
  ## MCP tools
266
295
 
267
- The default MCP registry exposes 19 tools. The essential operator surface
268
- is `operate_start`, `operate_observe`, `operate_act`, `operate_pay`,
269
- `operate_payment_status`, `operate_finish`, `operate_recipe_run`, and
270
- `operate_recipe_save` — every former standalone workflow/lifecycle/login tool
271
- name was dropped and its behavior folded into `operate_act` as a `kind` (or into
272
- `operate_finish`'s `outcome`); no delegating aliases remain. Poll payment status
273
- 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,
274
306
  frame_index?, frame_url_contains?, full_page?)` is a read-only debugging capture
275
307
  (page or one isolated frame, e.g. a cross-origin 3-D Secure/captcha challenge)
276
- returned as an actual MCP image. It refuses during an active card fill, when the
277
- requested capture still contains a sealed or card-shaped value, or when any
278
- included frame cannot be checked; capture-time pixel redaction remains a second
279
- 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.
280
310
  The maintainer-only `list_extract_failures` → `get_extract_failure`
281
311
  DOM-diagnostics pair is excluded from that surface; set
282
312
  `TRUSTY_SQUIRE_DIAGNOSTICS=1` in the MCP server environment to opt into the
283
- 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).
284
331
 
285
332
  - Rejected tool calls return a JSON `error` envelope with a stable `code` and
286
333
  message. Malformed and unknown calls fail only that request; they do not stop
287
334
  the shared stdio process or discard its active in-memory operator session.
288
335
  `server_unavailable` includes `retry.max_attempts: 1`: retry once, and never
289
336
  kill or restart the shared operator process.
290
- - `operate_start`, `operate_observe`, and `operate_act` open a website, inspect
291
- the current state, and perform one browser action at a time. Ordinary controls
292
- inside same- and cross-origin frames are included in observations with a
293
- `frame_origin`; known captcha challenge frames stay behind the dedicated
294
- captcha flow. Frame refs and live `text=…`/`css=…` locators preserve that
295
- 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
296
343
  must pass the same domain scope as `goto`/`allow_host`, opaque frames are
297
344
  refused, and `type_secret` never targets any cross-domain frame. Frame refs
298
345
  currently support `click`, `js_click`, `type`, `type_secret`, and `select`;
299
346
  `upload`, `oauth_click`, and `oauth_login` fail closed. If a visible control
300
- has no observed ref, the four locator-capable actions (`click`, `js_click`,
301
- `type`, and `type_secret`) can use a live locator; that one-off fallback is not
302
- 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.
303
359
  In a live operator session, in-page XHR/fetch calls to merchant API sibling
304
360
  subdomains are automatically in scope only when they share the registrable
305
361
  domain of a host trusted at session start. Calls outside the session scope fail
@@ -309,28 +365,35 @@ DOM-diagnostics pair is excluded from that surface; set
309
365
  providers, and 3-D Secure ACS/directory-server hosts) is exempt from that
310
366
  session-start-trust requirement — otherwise a checkout's own out-of-band 3DS
311
367
  challenge could never complete its own status poll.
312
- For a task gated by the user's connected Google account, pass
313
- `require_live_identity: true` to `operate_start`. The start fails closed with
314
- a connect handoff if that Google session is unavailable; otherwise it uses the
315
- 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.
316
371
  To route only that browser session through a proxy, pass `proxy` to
317
372
  `operate_start` as an HTTP or HTTPS URL (credentials are optional), or as an
318
373
  unauthenticated SOCKS5 URL. The value is launch-only and sensitive: it is not
319
374
  returned in session status, action traces, or saved recipes. Omitting it uses
320
375
  direct egress.
321
- When DOM churn invalidates an `@e:` ref, `operate_act` returns `target_stale`
322
- with the last observation generation, `reobserve_required: true`, best-effort
323
- label-keyed `replacement_candidates`, and
324
- `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"`.
325
381
  Malformed `operate_act` calls return `error.code: "invalid_arguments"` and an
326
382
  `error.guidance` repair object with the allowed kinds, missing fields, a valid
327
383
  example, and a safe alternative instead of only a validation string.
328
384
  For a provider login, pass the observed provider-button ref to the atomic
329
385
  `oauth_login` action. It retains the product tab across provider-owned popup
330
386
  redirects and closes, then returns the post-login product observation even if
331
- `detail` is `none`. `oauth_click` and `oauth_settle` remain for legacy replay
332
- compatibility. If an observation races that legacy transition, the response
333
- 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.
334
397
  - `operate_act` also owns eight consolidated workflow/lifecycle kinds — the
335
398
  entire operator surface beyond navigation, payment, finish, and recipe
336
399
  replay is reached through `operate_act`'s `kind`:
@@ -355,8 +418,9 @@ DOM-diagnostics pair is excluded from that surface; set
355
418
  - `solve_captcha` drives the in-session captcha gate and returns the
356
419
  fail-fast `needs_user` handoff when it cannot be cleared.
357
420
  - `await_verification` reads the user's own inbox for an email verification
358
- code/link, with sender-scoped search, explicit inbox consent, and
359
- 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.
360
424
  - `login_prepare_signup`, `login_store_signup`, and `login_load_saved` own
361
425
  the sealed username/password lifecycle. `login_prepare_signup` seals the
362
426
  user's captured email and a generated password, `login_store_signup`
@@ -371,10 +435,16 @@ DOM-diagnostics pair is excluded from that surface; set
371
435
  - `operate_finish` closes the session and optionally accepts a nested `outcome`.
372
436
  `none` only closes; `credentials` requires `store` and preserves credential
373
437
  extraction, vault storage, and auto-promotion; `result` requires `summary` or
374
- `data` and can run `verify_recipe` before closing. Finish first stops new
375
- calls and drains calls already using that session. Payment state never blocks
376
- teardown: finish clears any remaining payment state and destroys, rather than
377
- pools, a payment-sensitive browser profile.
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
445
+ operation and begin terminal teardown at 30 minutes; only an active payment
446
+ receives the short bounded close grace. Callers should finish promptly instead
447
+ of treating an open browser as durable background state.
378
448
  - `operate_recipe_save` saves a postcondition-verified local recipe under a
379
449
  closed task verb plus the service's registrable domain. It records stable target
380
450
  attributes and exact provenance for Squire-supplied values, not observed refs
@@ -396,12 +466,14 @@ DOM-diagnostics pair is excluded from that surface; set
396
466
  post-submit outcome wait described above before handing back unresolved
397
467
  outcomes. Split checkouts use the `fill_card` then `confirm` flow described
398
468
  above.
399
- `operate_payment_status` follows the [payment guide](#one-prompt) polling
469
+ `operate_payment_status` follows the [payment guide](#one-prompt) bounded-wait
400
470
  contract. It returns the session ID and includes it in every follow-up tool
401
- 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
402
473
  `error.guidance` repair fields as `operate_act`, including a safe resolution
403
474
  when `card_ref` and `card_label` conflict.
404
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.
405
477
  - `edit_credential` changes only an existing credential's non-secret name,
406
478
  `allowed_hosts`, or `login_hosts`; `delete_credential` soft-deletes one. Each
407
479
  first returns a Telegram/passkey approval link bound to the operation, exact
@@ -409,7 +481,12 @@ DOM-diagnostics pair is excluded from that surface; set
409
481
  `approval_id`. Neither tool can read or alter the secret value; use
410
482
  `store_credential` to rotate a secret.
411
483
  - `grant_app_access` and `revoke_app_access` create and remove scoped backend access.
412
- - `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.
413
490
 
414
491
  ## One README for GitHub and npm
415
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
+ }