@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.
- package/CHANGELOG.md +44 -28
- package/package.json +8 -8
- package/src/browser/capabilities.generated.ts +5 -72
- package/src/browser/capabilities.json +3 -51
- package/src/browser/chat-conformance.json +120 -229
- package/src/browser/chat-handler.ts +20 -18
- package/src/browser/chat-protocol.ts +23 -19
- package/src/browser/extension-bridge-tools.ts +0 -1
- package/src/browser/extension-bridge.ts +6 -9
- package/src/browser/extension-identity.ts +2 -0
- package/src/browser/extension-provider.ts +11 -54
- package/src/browser/host-profiles.ts +1 -1
- package/src/cli/chrome-cli.ts +3 -2
- package/src/cli/root-command-routing.ts +72 -0
- package/src/cli/sandbox-check.ts +43 -2
- package/src/cli.ts +39 -1
- package/src/commands/manager.ts +8 -8
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/main.ts +6 -8
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"contractVersion": "
|
|
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", "
|
|
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
|
|
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
|
-
//
|
|
111
|
-
// session memory at runtime (never persisted), then ack
|
|
112
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
/**
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
|
|
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
|
|
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
|
-
"
|
|
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
|
|
179
|
-
*
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
265
|
+
// Office provider configuration channel
|
|
266
266
|
//
|
|
267
|
-
// Lets
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
//
|
|
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).
|
|
294
|
+
* API key). The fixed reason prevents provider text from crossing the bridge. */
|
|
296
295
|
export interface ConfigureError {
|
|
297
296
|
type: "configure_error";
|
|
298
|
-
|
|
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
|
|
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
|
|