@f5-sales-demo/xcsh 20.1.2 → 20.2.2

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.
@@ -1,9 +1,9 @@
1
1
  {
2
- "contractVersion": "1.12.0",
2
+ "contractVersion": "2.0.0",
3
3
  "schemas": {
4
4
  "chat_request": {
5
5
  "type": "object",
6
- "required": ["type", "id", "text", "context", "mode"],
6
+ "required": ["type", "id", "text", "context", "mode", "tabId", "sessionKey"],
7
7
  "properties": {
8
8
  "type": {
9
9
  "const": "chat_request",
@@ -125,6 +125,13 @@
125
125
  }
126
126
  ]
127
127
  },
128
+ "tabId": {
129
+ "type": "number"
130
+ },
131
+ "sessionKey": {
132
+ "minLength": 1,
133
+ "type": "string"
134
+ },
128
135
  "history_hint": {
129
136
  "type": "string"
130
137
  }
@@ -198,7 +205,7 @@
198
205
  },
199
206
  "chat_error": {
200
207
  "type": "object",
201
- "required": ["type", "id", "error"],
208
+ "required": ["type", "id", "reason"],
202
209
  "properties": {
203
210
  "type": {
204
211
  "const": "chat_error",
@@ -208,9 +215,6 @@
208
215
  "pattern": "^c-",
209
216
  "type": "string"
210
217
  },
211
- "error": {
212
- "type": "string"
213
- },
214
218
  "reason": {
215
219
  "anyOf": [
216
220
  {
@@ -356,52 +360,6 @@
356
360
  }
357
361
  }
358
362
  },
359
- "configure": {
360
- "type": "object",
361
- "required": ["type", "token"],
362
- "properties": {
363
- "type": {
364
- "const": "configure",
365
- "type": "string"
366
- },
367
- "baseUrl": {
368
- "type": "string"
369
- },
370
- "token": {
371
- "type": "string",
372
- "minLength": 1
373
- },
374
- "model": {
375
- "type": "string"
376
- }
377
- }
378
- },
379
- "configure_ack": {
380
- "type": "object",
381
- "required": ["type", "model"],
382
- "properties": {
383
- "type": {
384
- "const": "configure_ack",
385
- "type": "string"
386
- },
387
- "model": {
388
- "type": "string"
389
- }
390
- }
391
- },
392
- "configure_error": {
393
- "type": "object",
394
- "required": ["type", "error"],
395
- "properties": {
396
- "type": {
397
- "const": "configure_error",
398
- "type": "string"
399
- },
400
- "error": {
401
- "type": "string"
402
- }
403
- }
404
- },
405
363
  "host_tool_call": {
406
364
  "type": "object",
407
365
  "required": ["type", "id", "toolCallId", "toolName", "arguments"],
@@ -599,68 +557,6 @@
599
557
  "type": "boolean"
600
558
  }
601
559
  }
602
- },
603
- "list_skills": {
604
- "type": "object",
605
- "required": ["type"],
606
- "properties": {
607
- "type": {
608
- "const": "list_skills",
609
- "type": "string"
610
- }
611
- }
612
- },
613
- "skills": {
614
- "type": "object",
615
- "required": ["type", "skills"],
616
- "properties": {
617
- "type": {
618
- "const": "skills",
619
- "type": "string"
620
- },
621
- "skills": {
622
- "type": "array",
623
- "items": {
624
- "type": "object",
625
- "required": ["name", "description"],
626
- "properties": {
627
- "name": { "type": "string", "minLength": 1 },
628
- "description": { "type": "string" }
629
- }
630
- }
631
- }
632
- }
633
- },
634
- "list_commands": {
635
- "type": "object",
636
- "required": ["type"],
637
- "properties": {
638
- "type": {
639
- "const": "list_commands",
640
- "type": "string"
641
- }
642
- }
643
- },
644
- "commands": {
645
- "type": "object",
646
- "required": ["type", "commands"],
647
- "properties": {
648
- "type": {
649
- "const": "commands",
650
- "type": "string"
651
- },
652
- "commands": {
653
- "type": "array",
654
- "items": {
655
- "type": "object",
656
- "required": ["name", "description"],
657
- "properties": {
658
- "name": { "type": "string", "minLength": 1 },
659
- "description": { "type": "string" }
660
- }
661
- }
662
- }
663
- }
664
560
  }
665
561
  },
666
562
  "examples": {
@@ -738,6 +634,8 @@
738
634
  "truncated": false
739
635
  },
740
636
  "mode": "educational",
637
+ "tabId": 7,
638
+ "sessionKey": "example-corp|production",
741
639
  "history_hint": "conv-1"
742
640
  },
743
641
  "chat_request_no_context": {
@@ -745,7 +643,9 @@
745
643
  "id": "c-2222",
746
644
  "text": "help me build a WAF policy",
747
645
  "context": null,
748
- "mode": "configuration"
646
+ "mode": "configuration",
647
+ "tabId": 7,
648
+ "sessionKey": "example-corp|production"
749
649
  },
750
650
  "chat_stop": {
751
651
  "type": "chat_stop",
@@ -781,7 +681,6 @@
781
681
  "chat_error": {
782
682
  "type": "chat_error",
783
683
  "id": "c-1111",
784
- "error": "HTTP 403 forbidden",
785
684
  "reason": "provider-4xx"
786
685
  },
787
686
  "chat_tool_notice": {
@@ -821,24 +720,6 @@
821
720
  "type": "set_host_tools_error",
822
721
  "error": "Host tool \"office_read_range\" must provide a non-empty description"
823
722
  },
824
- "configure": {
825
- "type": "configure",
826
- "baseUrl": "https://f5ai.pd.f5net.com/anthropic",
827
- "token": "sk-example-token-123",
828
- "model": "claude-opus-4-8"
829
- },
830
- "configure_no_baseUrl": {
831
- "type": "configure",
832
- "token": "sk-example-token-123"
833
- },
834
- "configure_ack": {
835
- "type": "configure_ack",
836
- "model": "claude-opus-4-8"
837
- },
838
- "configure_error": {
839
- "type": "configure_error",
840
- "error": "No model anthropic/claude-opus-4-8 available"
841
- },
842
723
  "host_tool_call": {
843
724
  "type": "host_tool_call",
844
725
  "id": "7295551234567890",
@@ -876,38 +757,6 @@
876
757
  "type": "host_tool_cancel",
877
758
  "id": "7295551234567891",
878
759
  "targetId": "7295551234567890"
879
- },
880
- "list_skills": {
881
- "type": "list_skills"
882
- },
883
- "skills": {
884
- "type": "skills",
885
- "skills": [
886
- {
887
- "name": "meddpicc:deal-review",
888
- "description": "Facilitate a structured weekly MEDDPICC deal review"
889
- }
890
- ]
891
- },
892
- "skills_empty": {
893
- "type": "skills",
894
- "skills": []
895
- },
896
- "list_commands": {
897
- "type": "list_commands"
898
- },
899
- "commands": {
900
- "type": "commands",
901
- "commands": [
902
- {
903
- "name": "meddpicc:qualify-deal",
904
- "description": "Score and qualify a deal using the MEDDPICC framework"
905
- }
906
- ]
907
- },
908
- "commands_empty": {
909
- "type": "commands",
910
- "commands": []
911
760
  }
912
761
  },
913
762
  "invalid": [
@@ -953,6 +802,8 @@
953
802
  "truncated": false
954
803
  },
955
804
  "mode": "educational",
805
+ "tabId": 7,
806
+ "sessionKey": "example-corp|production",
956
807
  "history_hint": "conv-1"
957
808
  }
958
809
  },
@@ -998,6 +849,8 @@
998
849
  "truncated": false
999
850
  },
1000
851
  "mode": "wizard",
852
+ "tabId": 7,
853
+ "sessionKey": "example-corp|production",
1001
854
  "history_hint": "conv-1"
1002
855
  }
1003
856
  },
@@ -1008,7 +861,107 @@
1008
861
  "type": "chat_request",
1009
862
  "id": "c-1",
1010
863
  "context": null,
1011
- "mode": "educational"
864
+ "mode": "educational",
865
+ "tabId": 7,
866
+ "sessionKey": "example-corp|production"
867
+ }
868
+ },
869
+ {
870
+ "schema": "chat_request",
871
+ "why": "missing tabId",
872
+ "value": {
873
+ "type": "chat_request",
874
+ "id": "c-1111",
875
+ "text": "What does this load balancer do?",
876
+ "context": {
877
+ "v": 1,
878
+ "capturedAt": 1719000000000,
879
+ "tabId": 7,
880
+ "url": "https://example-corp.console.ves.volterra.io/web/namespaces/default/http_loadbalancers/lb1",
881
+ "path": "/web/namespaces/default/http_loadbalancers/lb1",
882
+ "title": "lb1 — Distributed Cloud",
883
+ "ax": {
884
+ "role": "WebArea",
885
+ "name": "lb1",
886
+ "children": [
887
+ {
888
+ "role": "button",
889
+ "name": "Edit",
890
+ "ref": "e12"
891
+ }
892
+ ]
893
+ },
894
+ "api": {
895
+ "url": "/api/config/namespaces/default/http_loadbalancers/lb1",
896
+ "status": 200,
897
+ "resourceType": "http_loadbalancers",
898
+ "body": {
899
+ "metadata": {
900
+ "name": "lb1"
901
+ },
902
+ "spec": {
903
+ "domains": ["lb1.example.com"]
904
+ }
905
+ },
906
+ "truncated": false
907
+ },
908
+ "truncated": false
909
+ },
910
+ "mode": "educational",
911
+ "sessionKey": "example-corp|production"
912
+ }
913
+ },
914
+ {
915
+ "schema": "chat_request",
916
+ "why": "missing sessionKey",
917
+ "value": {
918
+ "type": "chat_request",
919
+ "id": "c-1111",
920
+ "text": "What does this load balancer do?",
921
+ "context": {
922
+ "v": 1,
923
+ "capturedAt": 1719000000000,
924
+ "tabId": 7,
925
+ "url": "https://example-corp.console.ves.volterra.io/web/namespaces/default/http_loadbalancers/lb1",
926
+ "path": "/web/namespaces/default/http_loadbalancers/lb1",
927
+ "title": "lb1 — Distributed Cloud",
928
+ "ax": {
929
+ "role": "WebArea",
930
+ "name": "lb1",
931
+ "children": [
932
+ {
933
+ "role": "button",
934
+ "name": "Edit",
935
+ "ref": "e12"
936
+ }
937
+ ]
938
+ },
939
+ "api": {
940
+ "url": "/api/config/namespaces/default/http_loadbalancers/lb1",
941
+ "status": 200,
942
+ "resourceType": "http_loadbalancers",
943
+ "body": {
944
+ "metadata": {
945
+ "name": "lb1"
946
+ },
947
+ "spec": {
948
+ "domains": ["lb1.example.com"]
949
+ }
950
+ },
951
+ "truncated": false
952
+ },
953
+ "truncated": false
954
+ },
955
+ "mode": "educational",
956
+ "tabId": 7
957
+ }
958
+ },
959
+ {
960
+ "schema": "chat_error",
961
+ "why": "missing reason",
962
+ "value": {
963
+ "type": "chat_error",
964
+ "id": "c-1"
1012
965
  }
1013
966
  },
1014
967
  {
@@ -1091,68 +1044,6 @@
1091
1044
  "content": "nope"
1092
1045
  }
1093
1046
  }
1094
- },
1095
- {
1096
- "schema": "configure",
1097
- "why": "token missing",
1098
- "value": {
1099
- "type": "configure",
1100
- "baseUrl": "https://f5ai.pd.f5net.com/anthropic",
1101
- "model": "claude-opus-4-8"
1102
- }
1103
- },
1104
- {
1105
- "schema": "configure",
1106
- "why": "token empty",
1107
- "value": {
1108
- "type": "configure",
1109
- "token": ""
1110
- }
1111
- },
1112
- {
1113
- "schema": "commands",
1114
- "why": "commands must be an array, not a name-keyed map",
1115
- "value": {
1116
- "type": "commands",
1117
- "commands": {
1118
- "meddpicc:qualify-deal": "Score and qualify a deal"
1119
- }
1120
- }
1121
- },
1122
- {
1123
- "schema": "commands",
1124
- "why": "an entry with no description has nothing to render in the menu",
1125
- "value": {
1126
- "type": "commands",
1127
- "commands": [
1128
- {
1129
- "name": "meddpicc:qualify-deal"
1130
- }
1131
- ]
1132
- }
1133
- },
1134
- {
1135
- "schema": "commands",
1136
- "why": "an entry with an empty name cannot be sent back as a prompt",
1137
- "value": {
1138
- "type": "commands",
1139
- "commands": [
1140
- {
1141
- "name": "",
1142
- "description": "Score and qualify a deal"
1143
- }
1144
- ]
1145
- }
1146
- },
1147
- {
1148
- "schema": "skills",
1149
- "why": "skills must be an array, not a name-keyed map",
1150
- "value": {
1151
- "type": "skills",
1152
- "skills": {
1153
- "meddpicc:coach": "MEDDPICC coaching"
1154
- }
1155
- }
1156
1047
  }
1157
1048
  ]
1158
1049
  }
@@ -102,14 +102,18 @@ export class ChatHandler {
102
102
 
103
103
  attach(): void {
104
104
  this.#server.onMessage(msg => {
105
- if (isChatRequest(msg)) this.#handleChatRequest(msg as unknown as ChatRequest);
105
+ if (isChatRequest(msg, this.#server.serveKind === "browser" ? "browser" : "transport"))
106
+ this.#handleChatRequest(msg as unknown as ChatRequest);
106
107
  else if (isChatStop(msg)) this.#handleChatStop(msg as unknown as { id: string });
107
108
  // Host-tool channel (#2046): register client tools, then route the client's
108
109
  // result/update frames back to the correlated pending call in the bridge.
109
110
  else if (isSetHostTools(msg)) this.#handleSetHostTools(msg as unknown as SetHostTools);
110
- // Provider configuration channel (#2095): swap the LLM provider/model in
111
- // session memory at runtime (never persisted), then ack or nack.
112
- else if (isConfigure(msg)) this.#handleConfigure(msg as unknown as Configure);
111
+ // Office-only provider configuration channel (#2095): swap the LLM
112
+ // provider/model in session memory at runtime (never persisted), then ack
113
+ // or nack. Contract-2 Chrome clients cannot configure credentials.
114
+ else if (this.#server.serveKind === "office" && isConfigure(msg)) {
115
+ this.#handleConfigure(msg as unknown as Configure);
116
+ }
113
117
  // Skills enumeration (#2311): the pane asks for the loaded skills to populate
114
118
  // the composer's Skills submenu.
115
119
  else if (isListSkills(msg)) this.#handleListSkills();
@@ -128,7 +132,6 @@ export class ChatHandler {
128
132
  this.#sendTerminal(chat, {
129
133
  type: "chat_error",
130
134
  id: chat.id,
131
- error: "bridge disconnected",
132
135
  reason: "bridge-disconnected",
133
136
  });
134
137
  chat.unsubscribe();
@@ -212,7 +215,7 @@ export class ChatHandler {
212
215
  await this.#session.prompt(prompt, { expandPromptTemplates: false, synthetic: false, images, serverTools });
213
216
  } catch (err: unknown) {
214
217
  const message = err instanceof Error ? err.message : "unknown error";
215
- this.#sendTerminal(chat, { type: "chat_error", id, error: message, reason: classifyChatErrorReason(message) });
218
+ this.#sendTerminal(chat, { type: "chat_error", id, reason: classifyChatErrorReason(message) });
216
219
  } finally {
217
220
  if (!chat.terminalSent) {
218
221
  this.#sendTerminal(chat, { type: "chat_done", id });
@@ -323,7 +326,6 @@ export class ChatHandler {
323
326
  this.#sendTerminal(chat, {
324
327
  type: "chat_error",
325
328
  id: chat.id,
326
- error: errorMsg,
327
329
  reason: classifyChatErrorReason(errorMsg),
328
330
  });
329
331
  } else if (msg.stopReason === "toolUse" || msg.content.some(part => part.type === "toolCall")) {
@@ -421,12 +423,15 @@ export class ChatHandler {
421
423
  await this.#session.setModel(model);
422
424
 
423
425
  this.#server.send({ type: "configure_ack", model: model.id } satisfies ConfigureAck);
424
- } catch (err) {
426
+ } catch {
427
+ // Collapse provider and validation details at the runtime boundary. The
428
+ // Office pane needs only a stable recovery signal and must never receive
429
+ // identity-bearing upstream error text.
425
430
  // Nack instead of throwing (set_host_tools parity): a client awaiting
426
431
  // configure_ack would otherwise hang on a bad frame or missing key.
427
432
  this.#server.send({
428
433
  type: "configure_error",
429
- error: err instanceof Error ? err.message : String(err),
434
+ reason: "configuration-rejected",
430
435
  } satisfies ConfigureError);
431
436
  }
432
437
  }
@@ -476,7 +481,6 @@ export class ChatHandler {
476
481
  this.#sendTerminal(chat, {
477
482
  type: "chat_error",
478
483
  id: chat.id,
479
- error: "session disposed",
480
484
  reason: "session-disposed",
481
485
  });
482
486
  chat.unsubscribe();
@@ -503,12 +507,10 @@ export function requireHttpsUrl(raw: string): string {
503
507
  return raw;
504
508
  }
505
509
 
506
- /** Best-effort classification of an upstream/provider error message into a
507
- * ChatErrorReason so the panel can pick a distinct, actionable message. Returns
508
- * undefined for an unclassified error (the panel then shows the raw error text).
509
- * Token checks run before the generic 4xx check (a 401 is more useful as an
510
- * expired-token hint than a bare client error). */
511
- export function classifyChatErrorReason(message: string): ChatErrorReason | undefined {
510
+ /** Classify an upstream/provider error into the closed public reason vocabulary.
511
+ * Token checks run before the generic 4xx check; unknown failures fail closed to
512
+ * provider-5xx instead of crossing the runtime boundary as raw text. */
513
+ export function classifyChatErrorReason(message: string): ChatErrorReason {
512
514
  const m = message.toLowerCase();
513
515
  if (/\btoken\b[^.]*\b(expir|invalid)|\b(expir|invalid)[^.]*\btoken\b|aws sso|\/context (create|validate)/.test(m)) {
514
516
  return "token-expired";
@@ -523,7 +525,7 @@ export function classifyChatErrorReason(message: string): ChatErrorReason | unde
523
525
  if (/\b4\d\d\b|forbidden|unauthorized|invalid model|bad request|not found|too many requests|rate limit/.test(m)) {
524
526
  return "provider-4xx";
525
527
  }
526
- return undefined;
528
+ return "provider-5xx";
527
529
  }
528
530
 
529
531
  const MODE_INSTRUCTIONS: Record<InteractionMode, string> = {
@@ -661,7 +663,7 @@ function composeBrowserPageContext(parts: string[], context: PageContextSnapshot
661
663
  if (pageState.operation === "login") {
662
664
  parts.push("Page: LOGIN — session expired or first login. The user is on the Keycloak authentication page.");
663
665
  parts.push(
664
- "Use the login tool with their email and password to log in. The login tool handles both production and staging Keycloak (including login-staging.volterra.us). Do NOT bypass it or claim it doesn't support staging.",
666
+ "Ask the user to authenticate directly in the browser. Never request, accept, or enter their username, password, token, or other authentication secret.",
665
667
  );
666
668
  } else if (pageState.resource) {
667
669
  const opLabel = pageState.operation.toUpperCase();
@@ -83,6 +83,10 @@ export interface ChatRequest {
83
83
  context: PageContextSnapshot | null;
84
84
  mode: InteractionMode;
85
85
  history_hint?: string;
86
+ /** Required for the Chrome host, whose side panel routes each turn to an
87
+ * explicitly bound tab and tenant session. Office routing is transport-bound. */
88
+ tabId?: number;
89
+ sessionKey?: string;
86
90
  /** Optional photo/image attachments, sent to the model as vision blocks. */
87
91
  images?: ChatImage[];
88
92
  /** Absolute local paths (files/folders) the user attached as context. The engine
@@ -175,11 +179,8 @@ export interface ChatDone {
175
179
  references?: ChatReference[];
176
180
  }
177
181
 
178
- /** Machine-readable cause of a terminal chat_error, so the panel can render a
179
- * distinct, actionable message (and decide whether to auto-recover) instead of a
180
- * generic failure. Additive/optional on the wire (contract 1.6.0); an omitted
181
- * reason means an unclassified error (legacy behavior — show the error text).
182
- * Shared vocabulary with the extension (keep both lists identical). */
182
+ /** Machine-readable cause of a terminal chat_error. Raw provider error text is
183
+ * deliberately absent because it can carry identity-bearing response content. */
183
184
  export const CHAT_ERROR_REASONS = [
184
185
  "bridge-disconnected", // the worker's bridge closed mid-turn
185
186
  "bridge-unresponsive", // the socket looked open but the worker never answered
@@ -197,8 +198,7 @@ export type ChatErrorReason = (typeof CHAT_ERROR_REASONS)[number];
197
198
  export interface ChatError {
198
199
  type: "chat_error";
199
200
  id: string;
200
- error: string;
201
- reason?: ChatErrorReason;
201
+ reason: ChatErrorReason;
202
202
  }
203
203
 
204
204
  /** Liveness signal (contract 1.7.0): the worker is actively working the turn — e.g.
@@ -217,7 +217,7 @@ export interface ChatKeepalive {
217
217
  // to whatever host is driving the WS bridge (the chrome extension, an Office
218
218
  // add-in, etc.). The frames are FIELD-IDENTICAL to the transport-neutral
219
219
  // `RpcHostTool*` vocabulary (`src/host-tools/`), so they are re-exported here
220
- // rather than re-declared — one vocabulary across every transport, no drift.
220
+ // rather than redeclared — one vocabulary across every transport, no drift.
221
221
  //
222
222
  // CRITICAL: `host_tool_result.result` and `host_tool_update.partialResult` are
223
223
  // `AgentToolResult` values — a `content[]` array — NOT a `{ data }` object. The
@@ -262,14 +262,13 @@ export interface SetHostToolsError {
262
262
  }
263
263
 
264
264
  // ---------------------------------------------------------------------------
265
- // Provider configuration channel (contract 1.9.0)
265
+ // Office provider configuration channel
266
266
  //
267
- // Lets a bridge client (the Chrome extension, the office-xcsh add-in) configure
268
- // xcsh's LLM provider at runtime — after the socket is connected — without
269
- // restarting the worker and WITHOUT persisting the token to disk. xcsh stays the
270
- // intelligence engine; this only swaps the provider credentials/model in session
271
- // memory. Single config in-flight, so — like `set_host_tools` — there is no `id`
272
- // correlation field. Mirrors the set_host_tools ack/nack shape exactly.
267
+ // Lets the office-xcsh add-in configure xcsh's LLM provider at runtime after the
268
+ // socket is connected, without restarting the worker or persisting the token.
269
+ // Contract-2 Chrome clients have no credential-configuration interface. Single
270
+ // config in-flight, so — like `set_host_tools` — there is no `id` correlation
271
+ // field. Mirrors the set_host_tools ack/nack shape exactly.
273
272
  // ---------------------------------------------------------------------------
274
273
 
275
274
  /** Inbound: the client configures the LLM provider. `token` is required and
@@ -292,10 +291,10 @@ export interface ConfigureAck {
292
291
  }
293
292
 
294
293
  /** Outbound: nacks a `configure` that failed (bad frame, unknown model, missing
295
- * API key). Emitted instead of the ack so a client awaiting it never hangs. */
294
+ * API key). The fixed reason prevents provider text from crossing the bridge. */
296
295
  export interface ConfigureError {
297
296
  type: "configure_error";
298
- error: string;
297
+ reason: "configuration-rejected";
299
298
  }
300
299
 
301
300
  // ---------------------------------------------------------------------------
@@ -317,14 +316,19 @@ function isValidChatImages(v: unknown): boolean {
317
316
  });
318
317
  }
319
318
 
320
- export function isChatRequest(msg: Record<string, unknown>): boolean {
319
+ export function isChatRequest(msg: Record<string, unknown>, routing: "browser" | "transport"): boolean {
321
320
  return (
322
321
  msg.type === "chat_request" &&
323
322
  hasChatIdPrefix(msg.id) &&
324
323
  typeof msg.text === "string" &&
325
324
  typeof msg.mode === "string" &&
326
325
  VALID_MODES.has(msg.mode) &&
327
- isValidChatImages(msg.images)
326
+ isValidChatImages(msg.images) &&
327
+ (routing === "transport" ||
328
+ (typeof msg.tabId === "number" &&
329
+ Number.isFinite(msg.tabId) &&
330
+ typeof msg.sessionKey === "string" &&
331
+ msg.sessionKey.length > 0))
328
332
  );
329
333
  }
330
334
 
@@ -84,7 +84,6 @@ export const BROWSER_TOOL_NAMES: readonly string[] = [
84
84
  "fill",
85
85
  "type_text",
86
86
  "screenshot",
87
- "login",
88
87
  "read_ax",
89
88
  "get_page_context",
90
89
  "query_dom",