@inspectr/mcplab 1.6.0 → 1.8.0

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 (50) hide show
  1. package/README.md +14 -1
  2. package/dist/app/assets/index-DHs9MRuO.css +1 -0
  3. package/dist/app/assets/index-UuO3yMWK.js +253 -0
  4. package/dist/app/index.html +2 -2
  5. package/dist/app-server/app-context.d.ts +2 -0
  6. package/dist/app-server/app-context.d.ts.map +1 -1
  7. package/dist/app-server/oauth-debugger-domain.d.ts +181 -194
  8. package/dist/app-server/oauth-debugger-domain.d.ts.map +1 -1
  9. package/dist/app-server/oauth-debugger-domain.js +149 -33
  10. package/dist/app-server/oauth-debugger-domain.js.map +1 -1
  11. package/dist/app-server/oauth-runtime-domain.d.ts +71 -0
  12. package/dist/app-server/oauth-runtime-domain.d.ts.map +1 -0
  13. package/dist/app-server/oauth-runtime-domain.js +196 -0
  14. package/dist/app-server/oauth-runtime-domain.js.map +1 -0
  15. package/dist/app-server/oauth-runtime-routes.d.ts +20 -0
  16. package/dist/app-server/oauth-runtime-routes.d.ts.map +1 -0
  17. package/dist/app-server/oauth-runtime-routes.js +135 -0
  18. package/dist/app-server/oauth-runtime-routes.js.map +1 -0
  19. package/dist/app-server/oauth-session-manager.d.ts +51 -0
  20. package/dist/app-server/oauth-session-manager.d.ts.map +1 -0
  21. package/dist/app-server/oauth-session-manager.js +338 -0
  22. package/dist/app-server/oauth-session-manager.js.map +1 -0
  23. package/dist/app-server/router.d.ts.map +1 -1
  24. package/dist/app-server/router.js +25 -0
  25. package/dist/app-server/router.js.map +1 -1
  26. package/dist/app-server/runs-routes.d.ts +28 -53
  27. package/dist/app-server/runs-routes.d.ts.map +1 -1
  28. package/dist/app-server/runs-routes.js +243 -11
  29. package/dist/app-server/runs-routes.js.map +1 -1
  30. package/dist/app-server/scenario-assistant-domain.d.ts +133 -139
  31. package/dist/app-server/scenario-assistant-domain.d.ts.map +1 -1
  32. package/dist/app-server/scenario-assistant-domain.js +270 -5
  33. package/dist/app-server/scenario-assistant-domain.js.map +1 -1
  34. package/dist/app-server/scenario-assistant.d.ts +11 -25
  35. package/dist/app-server/scenario-assistant.d.ts.map +1 -1
  36. package/dist/app-server/scenario-assistant.js +18 -2
  37. package/dist/app-server/scenario-assistant.js.map +1 -1
  38. package/dist/app-server/tool-analysis-domain.d.ts +165 -173
  39. package/dist/app-server/tool-analysis-domain.d.ts.map +1 -1
  40. package/dist/app-server/tool-analysis-domain.js +23 -4
  41. package/dist/app-server/tool-analysis-domain.js.map +1 -1
  42. package/dist/app-server/tool-analysis.d.ts +11 -18
  43. package/dist/app-server/tool-analysis.d.ts.map +1 -1
  44. package/dist/app-server/tool-analysis.js +26 -2
  45. package/dist/app-server/tool-analysis.js.map +1 -1
  46. package/dist/cli.js +18 -0
  47. package/dist/cli.js.map +1 -1
  48. package/package.json +4 -4
  49. package/dist/app/assets/index-2w0pnVLj.js +0 -249
  50. package/dist/app/assets/index-C9_UrDPP.css +0 -1
@@ -3,162 +3,156 @@ import { McpClientManager } from '@inspectr/mcplab-core';
3
3
  import { readLibraries } from './libraries-store.js';
4
4
  export { truncateJson } from './assistant-common.js';
5
5
  interface ScenarioAssistantContextInput {
6
- configSnapshotPolicy?: {
7
- enabled?: boolean;
8
- mode?: 'warn' | 'fail_on_drift';
9
- baselineSnapshotId?: string;
10
- };
11
- scenario: {
12
- id: string;
13
- name?: string;
14
- prompt: string;
15
- serverNames: string[];
16
- evalRules: Array<{
17
- type: string;
18
- value: string;
6
+ configSnapshotPolicy?: {
7
+ enabled?: boolean;
8
+ mode?: 'warn' | 'fail_on_drift';
9
+ baselineSnapshotId?: string;
10
+ };
11
+ scenario: {
12
+ id: string;
13
+ name?: string;
14
+ prompt: string;
15
+ serverNames: string[];
16
+ evalRules: Array<{
17
+ type: string;
18
+ value?: string;
19
+ path?: string;
20
+ equals?: string | number | boolean;
21
+ }>;
22
+ extractRules: Array<{
23
+ name: string;
24
+ pattern: string;
25
+ }>;
26
+ snapshotEval?: {
27
+ enabled?: boolean;
28
+ baselineSnapshotId?: string;
29
+ };
30
+ };
31
+ availableServers?: Array<{
32
+ name: string;
33
+ url?: string;
19
34
  }>;
20
- extractRules: Array<{
21
- name: string;
22
- pattern: string;
35
+ availableAgents?: Array<{
36
+ name: string;
37
+ provider: string;
38
+ model: string;
23
39
  }>;
24
- snapshotEval?: {
25
- enabled?: boolean;
26
- baselineSnapshotId?: string;
27
- };
28
- };
29
- availableServers?: Array<{
30
- name: string;
31
- url?: string;
32
- }>;
33
- availableAgents?: Array<{
34
- name: string;
35
- provider: string;
36
- model: string;
37
- }>;
38
40
  }
39
41
  interface ScenarioAssistantSuggestionBundle {
40
- prompt?: {
41
- replacement: string;
42
- rationale?: string;
43
- };
44
- evalRules?: {
45
- replacement: Array<{
46
- type: string;
47
- value: string;
48
- }>;
49
- rationale?: string;
50
- };
51
- extractRules?: {
52
- replacement: Array<{
53
- name: string;
54
- pattern: string;
55
- }>;
56
- rationale?: string;
57
- };
58
- snapshotEval?: {
59
- patch: {
60
- enabled?: boolean;
61
- baselineSnapshotId?: string;
42
+ prompt?: {
43
+ replacement: string;
44
+ rationale?: string;
45
+ };
46
+ evalRules?: {
47
+ replacement: Array<{
48
+ type: string;
49
+ value?: string;
50
+ path?: string;
51
+ equals?: string | number | boolean;
52
+ }>;
53
+ rationale?: string;
54
+ };
55
+ extractRules?: {
56
+ replacement: Array<{
57
+ name: string;
58
+ pattern: string;
59
+ }>;
60
+ rationale?: string;
61
+ };
62
+ snapshotEval?: {
63
+ patch: {
64
+ enabled?: boolean;
65
+ baselineSnapshotId?: string;
66
+ };
67
+ rationale?: string;
62
68
  };
63
- rationale?: string;
64
- };
65
- notes?: string[];
69
+ notes?: string[];
70
+ }
71
+ interface ScenarioAssistantEvalRuleSuggestion {
72
+ type: string;
73
+ value?: string;
74
+ path?: string;
75
+ equals?: string | number | boolean;
66
76
  }
67
77
  interface AssistantPendingToolCall {
68
- id: string;
69
- server: string;
70
- tool: string;
71
- publicToolName: string;
72
- arguments: unknown;
73
- status: 'pending' | 'approved' | 'denied' | 'error';
74
- createdAt: string;
75
- resultPreview?: string;
76
- error?: string;
78
+ id: string;
79
+ server: string;
80
+ tool: string;
81
+ publicToolName: string;
82
+ arguments: unknown;
83
+ status: 'pending' | 'approved' | 'denied' | 'error';
84
+ createdAt: string;
85
+ resultPreview?: string;
86
+ error?: string;
77
87
  }
78
88
  interface AssistantChatMessage {
79
- id: string;
80
- role: 'user' | 'assistant' | 'tool' | 'system';
81
- text: string;
82
- createdAt: string;
83
- suggestions?: ScenarioAssistantSuggestionBundle;
84
- pendingToolCallId?: string;
85
- pendingToolCallIds?: string[];
86
- toolRequestServer?: string;
87
- toolRequestName?: string;
88
- toolRequestPublicName?: string;
89
+ id: string;
90
+ role: 'user' | 'assistant' | 'tool' | 'system';
91
+ text: string;
92
+ createdAt: string;
93
+ suggestions?: ScenarioAssistantSuggestionBundle;
94
+ pendingToolCallId?: string;
95
+ pendingToolCallIds?: string[];
96
+ toolRequestServer?: string;
97
+ toolRequestName?: string;
98
+ toolRequestPublicName?: string;
89
99
  }
90
100
  export interface ScenarioAssistantSession {
91
- id: string;
92
- createdAt: number;
93
- lastTouchedAt: number;
94
- configPath?: string;
95
- selectedAssistantAgentName: string;
96
- context: ScenarioAssistantContextInput;
97
- agentConfig: AgentConfig;
98
- mcp: McpClientManager;
99
- tools: ToolDef[];
100
- toolPublicMap: Map<
101
- string,
102
- {
103
- server: string;
104
- tool: string;
105
- }
106
- >;
107
- pendingToolCalls: AssistantPendingToolCall[];
108
- chatMessages: AssistantChatMessage[];
109
- llmMessages: LlmMessage[];
110
- warnings: string[];
111
- systemPromptCache?: string;
101
+ id: string;
102
+ createdAt: number;
103
+ lastTouchedAt: number;
104
+ configPath?: string;
105
+ selectedAssistantAgentName: string;
106
+ context: ScenarioAssistantContextInput;
107
+ agentConfig: AgentConfig;
108
+ mcp: McpClientManager;
109
+ tools: ToolDef[];
110
+ toolPublicMap: Map<string, {
111
+ server: string;
112
+ tool: string;
113
+ }>;
114
+ pendingToolCalls: AssistantPendingToolCall[];
115
+ chatMessages: AssistantChatMessage[];
116
+ llmMessages: LlmMessage[];
117
+ warnings: string[];
118
+ systemPromptCache?: string;
112
119
  }
113
- export declare function cleanupAssistantSessions(
114
- sessions: Map<string, ScenarioAssistantSession>,
115
- now?: number
116
- ): void;
120
+ export declare function cleanupAssistantSessions(sessions: Map<string, ScenarioAssistantSession>, now?: number): void;
117
121
  export declare function touchAssistantSession(session: ScenarioAssistantSession): void;
118
122
  export declare function assistantSessionView(session: ScenarioAssistantSession): {
119
- id: string;
120
- createdAt: string;
121
- updatedAt: string;
122
- selectedAssistantAgentName: string;
123
- model: string;
124
- provider: 'openai' | 'anthropic' | 'azure_openai';
125
- warnings: string[];
126
- toolsLoaded: number;
127
- toolServers: string[];
128
- messages: AssistantChatMessage[];
129
- pendingToolCalls: AssistantPendingToolCall[];
123
+ id: string;
124
+ createdAt: string;
125
+ updatedAt: string;
126
+ selectedAssistantAgentName: string;
127
+ model: string;
128
+ provider: "openai" | "anthropic" | "azure_openai";
129
+ warnings: string[];
130
+ toolsLoaded: number;
131
+ toolServers: string[];
132
+ messages: AssistantChatMessage[];
133
+ pendingToolCalls: AssistantPendingToolCall[];
130
134
  };
131
- export declare function preloadAssistantTools(
132
- session: ScenarioAssistantSession,
133
- serversByName: Record<string, EvalConfig['servers'][string]>,
134
- selectedServerNames: string[]
135
- ): Promise<void>;
135
+ export declare function preloadAssistantTools(session: ScenarioAssistantSession, serversByName: Record<string, EvalConfig['servers'][string]>, selectedServerNames: string[], options?: {
136
+ serverAuthHeaders?: Record<string, Record<string, string>>;
137
+ }): Promise<void>;
138
+ export declare function normalizeScenarioAssistantEvalRules(replacement: ScenarioAssistantEvalRuleSuggestion[]): ScenarioAssistantEvalRuleSuggestion[];
136
139
  export declare function continueAssistantTurn(session: ScenarioAssistantSession): Promise<{
137
- session: ReturnType<typeof assistantSessionView>;
138
- response: {
139
- type: 'assistant_message' | 'tool_call_request';
140
- text: string;
141
- suggestions?: ScenarioAssistantSuggestionBundle;
142
- pendingToolCall?: AssistantPendingToolCall;
143
- pendingToolCalls?: AssistantPendingToolCall[];
144
- };
140
+ session: ReturnType<typeof assistantSessionView>;
141
+ response: {
142
+ type: 'assistant_message' | 'tool_call_request';
143
+ text: string;
144
+ suggestions?: ScenarioAssistantSuggestionBundle;
145
+ pendingToolCall?: AssistantPendingToolCall;
146
+ pendingToolCalls?: AssistantPendingToolCall[];
147
+ };
145
148
  }>;
146
- export declare function executeAssistantToolCall(
147
- session: ScenarioAssistantSession,
148
- pending: AssistantPendingToolCall
149
- ): Promise<unknown>;
149
+ export declare function executeAssistantToolCall(session: ScenarioAssistantSession, pending: AssistantPendingToolCall): Promise<unknown>;
150
150
  export declare function summarizeToolResultForAssistant(result: unknown): string;
151
- export declare function resolveAssistantAgentFromConfig(
152
- config: EvalConfig,
153
- selectedAssistantAgentName: string
154
- ): AgentConfig;
155
- export declare function resolveAssistantAgentFromLibraries(
156
- libraries: ReturnType<typeof readLibraries>,
157
- selectedAssistantAgentName: string
158
- ): AgentConfig;
151
+ export declare function resolveAssistantAgentFromConfig(config: EvalConfig, selectedAssistantAgentName: string): AgentConfig;
152
+ export declare function resolveAssistantAgentFromLibraries(libraries: ReturnType<typeof readLibraries>, selectedAssistantAgentName: string): AgentConfig;
159
153
  export declare function pickDefaultAssistantAgentName(params: {
160
- requested?: string;
161
- settingsDefault?: string;
162
- agentNames: string[];
154
+ requested?: string;
155
+ settingsDefault?: string;
156
+ agentNames: string[];
163
157
  }): string;
164
- //# sourceMappingURL=scenario-assistant-domain.d.ts.map
158
+ //# sourceMappingURL=scenario-assistant-domain.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scenario-assistant-domain.d.ts","sourceRoot":"","sources":["../../src/app-server/scenario-assistant-domain.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scenario-assistant-domain.d.ts","sourceRoot":"","sources":["../../src/app-server/scenario-assistant-domain.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAUzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,UAAU,6BAA6B;IACrC,oBAAoB,CAAC,EAAE;QACrB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,GAAG,eAAe,CAAC;QAChC,kBAAkB,CAAC,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,QAAQ,EAAE;QACR,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,SAAS,EAAE,KAAK,CAAC;YACf,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;SACpC,CAAC,CAAC;QACH,YAAY,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACvD,YAAY,CAAC,EAAE;YACb,OAAO,CAAC,EAAE,OAAO,CAAC;YAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;SAC7B,CAAC;KACH,CAAC;IACF,gBAAgB,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,eAAe,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5E;AAED,UAAU,iCAAiC;IACzC,MAAM,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,SAAS,CAAC,EAAE;QACV,WAAW,EAAE,KAAK,CAAC;YACjB,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;SACpC,CAAC,CAAC;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,YAAY,CAAC,EAAE;QACb,WAAW,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACtD,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,YAAY,CAAC,EAAE;QACb,KAAK,EAAE;YACL,OAAO,CAAC,EAAE,OAAO,CAAC;YAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;SAC7B,CAAC;QACF,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,UAAU,mCAAmC;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CACpC;AAcD,UAAU,wBAAwB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,oBAAoB;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,iCAAiC,CAAC;IAChD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0BAA0B,EAAE,MAAM,CAAC;IACnC,OAAO,EAAE,6BAA6B,CAAC;IACvC,WAAW,EAAE,WAAW,CAAC;IACzB,GAAG,EAAE,gBAAgB,CAAC;IACtB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,gBAAgB,EAAE,wBAAwB,EAAE,CAAC;IAC7C,YAAY,EAAE,oBAAoB,EAAE,CAAC;IACrC,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAMD,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,wBAAwB,CAAC,EAC/C,GAAG,SAAa,GACf,IAAI,CAEN;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI,CAE7E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,wBAAwB;;;;;;;;;;;;EAcrE;AAsED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,wBAAwB,EACjC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,EAC5D,mBAAmB,EAAE,MAAM,EAAE,EAC7B,OAAO,CAAC,EAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,GACvE,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAyQD,wBAAgB,mCAAmC,CACjD,WAAW,EAAE,mCAAmC,EAAE,GACjD,mCAAmC,EAAE,CAmCvC;AAiED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC;IACtF,OAAO,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;IACjD,QAAQ,EAAE;QACR,IAAI,EAAE,mBAAmB,GAAG,mBAAmB,CAAC;QAChD,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,iCAAiC,CAAC;QAChD,eAAe,CAAC,EAAE,wBAAwB,CAAC;QAC3C,gBAAgB,CAAC,EAAE,wBAAwB,EAAE,CAAC;KAC/C,CAAC;CACH,CAAC,CA+FD;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,wBAAwB,EACjC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAEvE;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,UAAU,EAClB,0BAA0B,EAAE,MAAM,GACjC,WAAW,CAQb;AAED,wBAAgB,kCAAkC,CAChD,SAAS,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,EAC3C,0BAA0B,EAAE,MAAM,GACjC,WAAW,CAQb;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,GAAG,MAAM,CAMT"}
@@ -44,12 +44,16 @@ function assistantSystemPrompt(session) {
44
44
  `{"type":"tool_call_request","text":"...","toolCall":{"name":"PUBLIC_TOOL_NAME","arguments":{}},"suggestions":{...optional...}}`,
45
45
  'For suggestions, use keys: prompt, evalRules, extractRules, snapshotEval, notes.',
46
46
  'prompt: { replacement: string, rationale?: string }',
47
- 'evalRules: { replacement: [{ type, value }...], rationale?: string }',
47
+ 'evalRules: { replacement: [{ type, value?, path?, equals? }...], rationale?: string }',
48
48
  'extractRules: { replacement: [{ name, pattern }...], rationale?: string }',
49
49
  'snapshotEval: { patch: { enabled?: boolean, baselineSnapshotId?: string }, rationale?: string }',
50
50
  'If you propose any edits to the scenario (prompt, Checks, Value Capture Rules, or snapshot settings), you MUST include the corresponding structured suggestions payload.',
51
51
  'Do not describe "suggested updates" in text only. Include suggestions so the UI can render Apply actions.',
52
- 'Keep rule types limited to: required_tool, forbidden_tool, response_contains, response_not_contains.',
52
+ 'Keep rule types limited to: required_tool, forbidden_tool, response_contains, response_not_contains, response_starts_with, response_ends_with, response_equals, response_regex, response_jsonpath, response_jsonpath_exists, response_jsonpath_not_exists.',
53
+ 'Preference policy: prefer non-regex checks first (response_contains, response_not_contains, response_starts_with, response_ends_with, response_equals).',
54
+ 'Use response_regex only for genuinely variable/complex patterns (IDs, dates, currency, alternation, optional tokens, quantifiers, character classes).',
55
+ 'Never include both response_regex and an equivalent literal check for the same intent.',
56
+ 'Prefer concise, positive checks over brittle near-miss negatives. Avoid paired off-by-one guards like "not 8 tags" and "not 10 tags" when "contains 9 tags" captures intent.',
53
57
  'IMPORTANT: For required_tool and forbidden_tool eval rules, use the raw MCP tool name (the "tool=" value shown in the tool listing), NOT the prefixed public name. For example, use "value_based_search" not "trendminer__value_based_search".',
54
58
  'Ask clarifying questions if the scenario intent is unclear.',
55
59
  `Scenario context: ${JSON.stringify({
@@ -82,7 +86,7 @@ function formatAssistantMcpPreloadError(serverName, error) {
82
86
  }
83
87
  return `Scenario Assistant MCP preload failed for server '${serverName}': ${raw}`;
84
88
  }
85
- export async function preloadAssistantTools(session, serversByName, selectedServerNames) {
89
+ export async function preloadAssistantTools(session, serversByName, selectedServerNames, options) {
86
90
  const usedNames = new Set();
87
91
  for (const serverName of selectedServerNames) {
88
92
  const server = serversByName[serverName];
@@ -91,7 +95,9 @@ export async function preloadAssistantTools(session, serversByName, selectedServ
91
95
  continue;
92
96
  }
93
97
  try {
94
- await session.mcp.connectAll({ [serverName]: server });
98
+ await session.mcp.connectAll({ [serverName]: server }, undefined, {
99
+ serverAuthHeaders: options?.serverAuthHeaders
100
+ });
95
101
  const tools = await session.mcp.listTools(serverName);
96
102
  for (const tool of tools) {
97
103
  const publicName = makeAssistantToolPublicName(serverName, tool.name, usedNames);
@@ -112,7 +118,7 @@ function normalizeEvalRuleToolNames(suggestions, toolPublicMap) {
112
118
  if (!suggestions?.evalRules?.replacement)
113
119
  return;
114
120
  for (const rule of suggestions.evalRules.replacement) {
115
- if (rule.type === 'required_tool' || rule.type === 'forbidden_tool') {
121
+ if ((rule.type === 'required_tool' || rule.type === 'forbidden_tool') && rule.value) {
116
122
  const mapping = toolPublicMap.get(rule.value);
117
123
  if (mapping) {
118
124
  rule.value = mapping.tool;
@@ -120,6 +126,264 @@ function normalizeEvalRuleToolNames(suggestions, toolPublicMap) {
120
126
  }
121
127
  }
122
128
  }
129
+ function tryParseRegexAsLiteral(pattern) {
130
+ const trimmed = pattern.trim();
131
+ if (!trimmed)
132
+ return null;
133
+ const anchoredStart = trimmed.startsWith('^');
134
+ const anchoredEnd = trimmed.endsWith('$');
135
+ const body = anchoredStart || anchoredEnd
136
+ ? trimmed.slice(anchoredStart ? 1 : 0, anchoredEnd ? -1 : undefined)
137
+ : trimmed;
138
+ const regexSpecial = new Set(['.', '^', '$', '*', '+', '?', '(', ')', '[', ']', '{', '}', '|']);
139
+ const complexEscapes = new Set(['d', 'D', 's', 'S', 'w', 'W', 'b', 'B', 'p', 'P']);
140
+ let literal = '';
141
+ let escaped = false;
142
+ for (const char of body) {
143
+ if (escaped) {
144
+ if (complexEscapes.has(char))
145
+ return null;
146
+ literal += char;
147
+ escaped = false;
148
+ continue;
149
+ }
150
+ if (char === '\\') {
151
+ escaped = true;
152
+ continue;
153
+ }
154
+ if (regexSpecial.has(char)) {
155
+ return null;
156
+ }
157
+ literal += char;
158
+ }
159
+ if (escaped)
160
+ return null;
161
+ return { literal, anchored: anchoredStart && anchoredEnd };
162
+ }
163
+ function evalRuleKey(rule) {
164
+ return `${rule.type}::${rule.value ?? ''}::${rule.path ?? ''}::${rule.equals === undefined ? '' : String(rule.equals)}`;
165
+ }
166
+ function hasEquivalentLiteralRule(rules, literalValue) {
167
+ return rules.some((rule) => (rule.type === 'response_contains' ||
168
+ rule.type === 'response_not_contains' ||
169
+ rule.type === 'response_starts_with' ||
170
+ rule.type === 'response_ends_with' ||
171
+ rule.type === 'response_equals') &&
172
+ rule.value === literalValue);
173
+ }
174
+ function parseCountWithSuffix(value) {
175
+ const trimmed = value.trim();
176
+ const match = trimmed.match(/^(\d+)(\s+.+)$/);
177
+ if (!match)
178
+ return null;
179
+ return {
180
+ count: Number.parseInt(match[1], 10),
181
+ suffix: match[2]
182
+ };
183
+ }
184
+ function collapseOffByOneCountGuards(rules) {
185
+ const removeIndices = new Set();
186
+ const replaceContains = new Map();
187
+ for (let i = 0; i < rules.length; i += 1) {
188
+ const rule = rules[i];
189
+ if (rule.type !== 'response_contains' || !rule.value)
190
+ continue;
191
+ const explicitPositive = parseCountWithSuffix(rule.value);
192
+ const numericOnly = !explicitPositive && rule.value.trim().match(/^\d+$/);
193
+ if (!explicitPositive && !numericOnly)
194
+ continue;
195
+ const count = explicitPositive
196
+ ? explicitPositive.count
197
+ : Number.parseInt(rule.value.trim(), 10);
198
+ const preferredSuffix = explicitPositive?.suffix;
199
+ const offByOneCandidates = new Map();
200
+ for (let j = 0; j < rules.length; j += 1) {
201
+ const candidate = rules[j];
202
+ if (candidate.type !== 'response_not_contains' || !candidate.value)
203
+ continue;
204
+ const parsed = parseCountWithSuffix(candidate.value);
205
+ if (!parsed)
206
+ continue;
207
+ if (preferredSuffix && parsed.suffix !== preferredSuffix)
208
+ continue;
209
+ if (parsed.count !== count - 1 && parsed.count !== count + 1)
210
+ continue;
211
+ const bucket = offByOneCandidates.get(parsed.suffix) ?? {};
212
+ if (parsed.count === count - 1)
213
+ bucket.lowerIdx = j;
214
+ if (parsed.count === count + 1)
215
+ bucket.upperIdx = j;
216
+ offByOneCandidates.set(parsed.suffix, bucket);
217
+ }
218
+ const selected = [...offByOneCandidates.entries()].find(([, pair]) => pair.lowerIdx !== undefined && pair.upperIdx !== undefined);
219
+ if (!selected)
220
+ continue;
221
+ const [suffix, pair] = selected;
222
+ removeIndices.add(pair.lowerIdx);
223
+ removeIndices.add(pair.upperIdx);
224
+ if (!explicitPositive) {
225
+ replaceContains.set(i, `${count}${suffix}`);
226
+ }
227
+ }
228
+ const result = [];
229
+ for (let i = 0; i < rules.length; i += 1) {
230
+ if (removeIndices.has(i))
231
+ continue;
232
+ const rule = rules[i];
233
+ const replacement = replaceContains.get(i);
234
+ if (replacement) {
235
+ result.push({ ...rule, value: replacement });
236
+ continue;
237
+ }
238
+ result.push(rule);
239
+ }
240
+ return result;
241
+ }
242
+ function normalizeIntentValue(value) {
243
+ return value.trim().toLowerCase().replace(/\s+/g, ' ');
244
+ }
245
+ function positiveLiteralRank(type) {
246
+ switch (type) {
247
+ case 'response_equals':
248
+ return 4;
249
+ case 'response_starts_with':
250
+ case 'response_ends_with':
251
+ return 3;
252
+ case 'response_contains':
253
+ return 2;
254
+ default:
255
+ return 1;
256
+ }
257
+ }
258
+ function intentDeduplicateEvalRules(rules) {
259
+ const result = [];
260
+ const positiveByIntent = new Map();
261
+ const containsIndices = [];
262
+ for (const rule of rules) {
263
+ if ((rule.type === 'response_contains' ||
264
+ rule.type === 'response_starts_with' ||
265
+ rule.type === 'response_ends_with' ||
266
+ rule.type === 'response_equals') &&
267
+ rule.value) {
268
+ const intent = normalizeIntentValue(rule.value);
269
+ const existingIdx = positiveByIntent.get(intent);
270
+ if (existingIdx !== undefined) {
271
+ const existing = result[existingIdx];
272
+ if (positiveLiteralRank(rule.type) > positiveLiteralRank(existing.type)) {
273
+ result[existingIdx] = rule;
274
+ if (existing.type === 'response_contains' && rule.type !== 'response_contains') {
275
+ const containsIndex = containsIndices.indexOf(existingIdx);
276
+ if (containsIndex >= 0)
277
+ containsIndices.splice(containsIndex, 1);
278
+ }
279
+ }
280
+ continue;
281
+ }
282
+ if (rule.type === 'response_contains') {
283
+ const newIntent = intent;
284
+ let droppedAsMoreSpecific = false;
285
+ for (const idx of containsIndices) {
286
+ const existing = result[idx];
287
+ if (!existing?.value)
288
+ continue;
289
+ const existingIntent = normalizeIntentValue(existing.value);
290
+ if (newIntent.includes(existingIntent)) {
291
+ droppedAsMoreSpecific = true;
292
+ break;
293
+ }
294
+ if (existingIntent.includes(newIntent)) {
295
+ positiveByIntent.delete(existingIntent);
296
+ result[idx] = rule;
297
+ positiveByIntent.set(newIntent, idx);
298
+ droppedAsMoreSpecific = true;
299
+ break;
300
+ }
301
+ }
302
+ if (droppedAsMoreSpecific) {
303
+ continue;
304
+ }
305
+ }
306
+ const idx = result.push(rule) - 1;
307
+ positiveByIntent.set(intent, idx);
308
+ if (rule.type === 'response_contains')
309
+ containsIndices.push(idx);
310
+ continue;
311
+ }
312
+ result.push(rule);
313
+ }
314
+ return result;
315
+ }
316
+ function lintContradictoryEvalRules(rules) {
317
+ const requiredTools = new Set();
318
+ const positiveLiteralIntents = new Set();
319
+ const existingJsonPaths = new Set();
320
+ for (const rule of rules) {
321
+ if (rule.type === 'required_tool' && rule.value) {
322
+ requiredTools.add(rule.value.trim());
323
+ }
324
+ if ((rule.type === 'response_contains' ||
325
+ rule.type === 'response_starts_with' ||
326
+ rule.type === 'response_ends_with' ||
327
+ rule.type === 'response_equals') &&
328
+ rule.value) {
329
+ positiveLiteralIntents.add(normalizeIntentValue(rule.value));
330
+ }
331
+ if (rule.type === 'response_jsonpath_exists' && rule.path) {
332
+ existingJsonPaths.add(rule.path.trim());
333
+ }
334
+ }
335
+ return rules.filter((rule) => {
336
+ if (rule.type === 'forbidden_tool' && rule.value) {
337
+ return !requiredTools.has(rule.value.trim());
338
+ }
339
+ if (rule.type === 'response_not_contains' && rule.value) {
340
+ return !positiveLiteralIntents.has(normalizeIntentValue(rule.value));
341
+ }
342
+ if (rule.type === 'response_jsonpath_not_exists' && rule.path) {
343
+ return !existingJsonPaths.has(rule.path.trim());
344
+ }
345
+ return true;
346
+ });
347
+ }
348
+ export function normalizeScenarioAssistantEvalRules(replacement) {
349
+ const normalized = [];
350
+ for (const rawRule of replacement) {
351
+ const rule = {
352
+ ...rawRule,
353
+ ...(typeof rawRule.value === 'string' ? { value: rawRule.value.trim() } : {}),
354
+ ...(typeof rawRule.path === 'string' ? { path: rawRule.path.trim() } : {})
355
+ };
356
+ if (rule.type === 'response_regex' && rule.value) {
357
+ const parsed = tryParseRegexAsLiteral(rule.value);
358
+ if (parsed) {
359
+ if (hasEquivalentLiteralRule(normalized, parsed.literal)) {
360
+ continue;
361
+ }
362
+ normalized.push({
363
+ type: parsed.anchored ? 'response_equals' : 'response_contains',
364
+ value: parsed.literal
365
+ });
366
+ continue;
367
+ }
368
+ }
369
+ normalized.push(rule);
370
+ }
371
+ const deduped = [];
372
+ const seen = new Set();
373
+ for (const rule of normalized) {
374
+ const key = evalRuleKey(rule);
375
+ if (seen.has(key))
376
+ continue;
377
+ seen.add(key);
378
+ deduped.push(rule);
379
+ }
380
+ return lintContradictoryEvalRules(intentDeduplicateEvalRules(collapseOffByOneCountGuards(deduped)));
381
+ }
382
+ function normalizeEvalRuleSuggestions(suggestions) {
383
+ if (!suggestions?.evalRules?.replacement)
384
+ return;
385
+ suggestions.evalRules.replacement = normalizeScenarioAssistantEvalRules(suggestions.evalRules.replacement);
386
+ }
123
387
  function parseAssistantModelOutput(text) {
124
388
  const cleaned = text.trim();
125
389
  let parsed;
@@ -180,6 +444,7 @@ export async function continueAssistantTurn(session) {
180
444
  }
181
445
  const modelOutput = await assistantChatModel(session);
182
446
  normalizeEvalRuleToolNames(modelOutput.suggestions, session.toolPublicMap);
447
+ normalizeEvalRuleSuggestions(modelOutput.suggestions);
183
448
  if (modelOutput.type === 'tool_call_request') {
184
449
  const requestedCalls = 'toolCalls' in modelOutput && Array.isArray(modelOutput.toolCalls)
185
450
  ? modelOutput.toolCalls